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:
parent
114dc5bf35
commit
d67d99f0e4
1 changed files with 223 additions and 0 deletions
223
test-tmux-detector.sh
Executable file
223
test-tmux-detector.sh
Executable 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."
|
||||
Loading…
Add table
Reference in a new issue