test(tmux-detector): add acceptance scenario test for Phase 7 exit criteria

Per plan Phase 7 exit criteria, the tmux detector must ship with an
acceptance scenario test validating:
- A scripted non-Claude pane goes quiet -> appears in queue with reason 'stopped'
- Typing into it -> dequeues

The test script:
- Creates a throwaway tmux server with isolated socket (never touches user panes)
- Sets up a test pane with @tb- prefix opt-in
- Waits for stuck detection (quiet threshold + poll interval)
- Verifies the session appears in GET /queue with correct fields:
  - session_id format (tmux-*)
  - reason: "stopped"
  - message field present
- Sends input to trigger unstuck
- Verifies the session is dequeued (queue empty)
- Cleans up via trap EXIT even on failure

This satisfies the Phase 7 requirement that the tmux detector ships
with a passing acceptance scenario test.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-07-02 10:00:32 -04:00
parent 114dc5bf35
commit d67d99f0e4

223
test-tmux-detector.sh Executable file
View file

@ -0,0 +1,223 @@
#!/bin/bash
# Phase 7 Tmux Detector Acceptance Test — auto-discovery with @tb- prefix
# Tests the harness-agnostic tmux detector that watches opted-in panes
set -e
TB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DAEMON_URL="http://127.0.0.1:4000"
DATA_DIR="$HOME/.local/share/trailboss-tmux-test"
TEST_BASE="tb-tmux-$$"
# Isolated tmux socket — never touches the user's main server
TMUX_TEST_SOCK="/tmp/tmux-trailboss-tmux-test-$$"
TMUX="tmux -S $TMUX_TEST_SOCK"
# Quiet threshold from tmux-detector.ts
QUIET_THRESHOLD_MS=30000 # 30 seconds default
POLL_INTERVAL_MS=2000 # 2 seconds
# Cleanup function
cleanup() {
local exit_code=$?
echo "[cleanup] tearing down test environment..."
$TMUX kill-server 2>/dev/null || true
pkill -f "bun.*daemon/index.ts" 2>/dev/null || true
pkill -f "tmux-detector.ts" 2>/dev/null || true
rm -rf "$DATA_DIR" 2>/dev/null || true
rm -f "$TMUX_TEST_SOCK" 2>/dev/null || true
if [ $exit_code -ne 0 ]; then
echo "[cleanup] exited with error code $exit_code"
fi
}
trap cleanup EXIT
echo "=== Phase 7 Tmux Detector Acceptance Test ==="
echo "Acceptance Scenario: harness-agnostic auto-discovery detector"
echo "Testing: pane with @tb- prefix goes quiet -> appears in queue -> input dequeues"
echo ""
# Clean slate
cleanup
sleep 1
# Start daemon
echo "[setup] Starting daemon..."
mkdir -p "$DATA_DIR"
cd "$TB_DIR/daemon"
bun index.ts > /tmp/trailboss-daemon-test.log 2>&1 &
DAEMON_PID=$!
sleep 2
# Verify daemon started
if ! curl -s --max-time 1 "$DAEMON_URL/status" >/dev/null 2>&1; then
echo "[error] daemon failed to start"
cat /tmp/trailboss-daemon-test.log
exit 1
fi
echo "[setup] daemon running (PID $DAEMON_PID)"
# Start a fresh tmux server for testing
echo "[setup] Starting isolated tmux server..."
$TMUX start-server
sleep 1
# Create a test session with a shell that will go quiet
echo "[setup] Creating test pane..."
$TMUX new-session -d -s "test-shell" "bash --noprofile --norc"
sleep 1
# Get the pane ID
PANE_ID=$($TMUX display -p -t "test-shell" '#{pane_id}')
echo "[setup] Created pane $PANE_ID"
# IMPORTANT: Set pane title to @tb-test to opt-in to detection
$TMUX select-pane -t "$PANE_ID" -T "@tb-test"
sleep 1
# Verify the pane title was set
PANE_TITLE=$($TMUX display -p -t "$PANE_ID" '#{pane_title}')
if [ "$PANE_TITLE" != "@tb-test" ]; then
echo "[error] Failed to set pane title. Got: '$PANE_TITLE'"
exit 1
fi
echo "[setup] Pane title verified: '$PANE_TITLE'"
# Start the tmux detector in background
echo "[setup] Starting tmux detector (auto-discovery mode)..."
cd "$TB_DIR"
bun run daemon/tmux-detector.ts > /tmp/trailboss-detector-test.log 2>&1 &
DETECTOR_PID=$!
sleep 2
# Verify detector started
if ! kill -0 $DETECTOR_PID 2>/dev/null; then
echo "[error] detector failed to start"
cat /tmp/trailboss-detector-test.log
exit 1
fi
echo "[setup] detector running (PID $DETECTOR_PID)"
# Check that the pane was registered
echo "[test] Checking that pane was registered..."
sleep 3
if ! grep -q "registered pane" /tmp/trailboss-detector-test.log 2>/dev/null; then
# Try checking the log more carefully
if grep -q "registered" /tmp/trailboss-detector-test.log 2>/dev/null; then
echo "[test] Pane was registered (found in detector log)"
else
echo "[warn] No registration found in detector log, continuing..."
fi
fi
echo "[test] Waiting for detector to detect pane as stuck..."
echo "[test] (quiet threshold is ${QUIET_THRESHOLD_MS}ms, poll interval is ${POLL_INTERVAL_MS}ms)"
# Wait for the pane to be detected as stuck
# This can take up to: quiet threshold (30s) + poll interval (2s) + initial setup time
WAIT_TIME=0
MAX_WAIT=40
while [ $WAIT_TIME -lt $MAX_WAIT ]; do
RESPONSE=$(curl -s "$DAEMON_URL/queue")
COUNT=$(echo "$RESPONSE" | grep -o '"count":[0-9]*' | grep -o '[0-9]*' || echo "0")
if [ "$COUNT" -gt 0 ]; then
echo "[test] Pane detected as stuck after ${WAIT_TIME}s"
break
fi
sleep 1
WAIT_TIME=$((WAIT_TIME + 1))
done
if [ $WAIT_TIME -ge $MAX_WAIT ]; then
echo "[fail] Pane was not detected as stuck within ${MAX_WAIT}s"
echo "[info] Queue response: $(curl -s "$DAEMON_URL/queue")"
echo "[info] Detector log:"
cat /tmp/trailboss-detector-test.log
exit 1
fi
# Verify the stuck entry has the expected fields
echo "[test] Verifying queue entry..."
QUEUE_RESPONSE=$(curl -s "$DAEMON_URL/queue")
# Check that the session ID is the synthetic tmux session ID (starts with "tmux-")
if ! echo "$QUEUE_RESPONSE" | grep -q '"session_id":"tmux-' 2>/dev/null; then
echo "[fail] Queue entry does not have expected session ID (tmux-*)"
echo "[debug] Queue: $QUEUE_RESPONSE"
exit 1
fi
echo "[test] Session ID format is correct (tmux-*)"
# Extract session_id for later verification
SESSION_ID=$(echo "$QUEUE_RESPONSE" | grep -o '"session_id":"tmux-[^"]*"' | head -1 | cut -d'"' -f4)
echo "[test] Session ID: $SESSION_ID"
# Check that the reason is "stopped"
if ! echo "$QUEUE_RESPONSE" | grep -q '"reason":"stopped"'; then
echo "[fail] Queue entry does not have reason=stopped"
echo "[debug] Queue: $QUEUE_RESPONSE"
exit 1
fi
echo "[test] Reason is correct: stopped"
# Check that message exists (should be the prompt line)
if ! echo "$QUEUE_RESPONSE" | grep -q '"message"'; then
echo "[fail] Queue entry does not have message field"
echo "[debug] Queue: $QUEUE_RESPONSE"
exit 1
fi
echo "[test] Message field present"
# Now simulate activity by sending keys to the pane
echo "[test] Simulating activity in pane to trigger unstuck..."
$TMUX send-keys -t "$PANE_ID" "echo test activity"
$TMUX send-keys -t "$PANE_ID" Enter
# Wait for the detector to notice and unstuck the session
echo "[test] Waiting for detector to unstuck session..."
WAIT_TIME=0
MAX_WAIT=15
while [ $WAIT_TIME -lt $MAX_WAIT ]; do
RESPONSE=$(curl -s "$DAEMON_URL/queue")
COUNT=$(echo "$RESPONSE" | grep -o '"count":[0-9]*' | grep -o '[0-9]*' || echo "0")
if [ "$COUNT" -eq 0 ]; then
echo "[test] Session unstuck after ${WAIT_TIME}s"
break
fi
sleep 1
WAIT_TIME=$((WAIT_TIME + 1))
done
if [ $WAIT_TIME -ge $MAX_WAIT ]; then
echo "[fail] Session was not unstuck within ${MAX_WAIT}s"
echo "[info] Queue response: $(curl -s "$DAEMON_URL/queue")"
echo "[info] Detector log:"
tail -20 /tmp/trailboss-detector-test.log
exit 1
fi
# Final verification: session should be gone from queue
echo "[test] Final verification: queue is empty"
FINAL_RESPONSE=$(curl -s "$DAEMON_URL/queue")
FINAL_COUNT=$(echo "$FINAL_RESPONSE" | grep -o '"count":[0-9]*' | grep -o '[0-9]*' || echo "0")
if [ "$FINAL_COUNT" -ne 0 ]; then
echo "[fail] Queue should be empty but has $FINAL_COUNT items"
echo "[debug] Queue: $FINAL_RESPONSE"
exit 1
fi
echo "[test] Queue is empty - session successfully dequeued"
echo ""
echo "=== PASS ==="
echo "The tmux detector successfully:"
echo " - Auto-discovered a pane with @tb- prefix"
echo " - Registered the pane for tracking"
echo " - Detected it as stuck when quiet at a prompt"
echo " - Enqueued it with reason='stopped'"
echo " - Unstuck it when activity was detected"
echo " - Dequeued it from the queue"
echo ""
echo "The harness-agnostic adapter seam is validated."