#!/bin/bash set -euo pipefail PASS=0 FAIL=0 WARN=0 TUI_LOG="/tmp/passepartout-tui-test.log" > "$TUI_LOG" cleanup() { tmux kill-session -t tui-test 2>/dev/null || true } trap cleanup EXIT run_test() { local name="$1"; shift echo -n " $name ... " if "$@" 2>/dev/null; then echo "PASS" PASS=$((PASS + 1)) else echo "FAIL" FAIL=$((FAIL + 1)) fi } # ---- Setup ---- echo "Starting TUI in tmux (daemon must already be running on port 9105)..." tmux new-session -d -s tui-test "passepartout tui 2>&1 | tee $TUI_LOG" for i in $(seq 1 20); do sleep 3 if tmux capture-pane -t tui-test -p 2>/dev/null | grep -q 'Connected v[0-9]'; then echo " TUI ready after $((i*3))s" break fi if [ "$i" -eq 20 ]; then echo " WARNING: TUI did not render after 60s" fi done # ---- Tests ---- test_cascade_parsing() { # Via /eval, check that *provider-cascade* contains clean keywords. tmux send-keys -t tui-test "/eval *provider-cascade*" Enter sleep 3 local pane pane=$(tmux capture-pane -t tui-test -p -S -15 2>/dev/null) echo "$pane" | grep -q ':DEEPSEEK\|:OPENROUTER\|:OPENAI\|:ANTHROPIC\|:GROQ\|:GEMINI\|:NVIDIA' } test_eval_command() { tmux send-keys -t tui-test "/eval (+ 1 2)" Enter sleep 3 tmux capture-pane -t tui-test -p -S -10 2>/dev/null | grep -q '=> 3' } test_status_bar() { tmux capture-pane -t tui-test -p -S -20 2>/dev/null | grep -q 'msgs:' } # ---- Diagnostic: rendering pipeline isolation ---- test_add_msg_render() { # Stage A: can the TUI render an agent message at all? # Inject a message directly via /eval — bypasses daemon entirely. tmux send-keys -t tui-test "/eval (passepartout.gateway-tui:add-msg :agent \"RENDER-TEST-OK\")" Enter sleep 2 tmux capture-pane -t tui-test -p -S -10 2>/dev/null | grep -q 'RENDER-TEST-OK' } test_daemon_msg_roundtrip() { # Stage B: does the daemon's LLM response reach the TUI's message list? # Sends a message, waits, then checks via /eval that an :agent message exists. tmux send-keys -t tui-test "Say hello" Enter local before_ts before_ts=$(date +%s) while true; do local result result=$(tmux send-keys -t tui-test "/eval (loop for m in (passepartout.gateway-tui:st :messages) when (eq :agent (getf m :role)) return t)" Enter 2>/dev/null; sleep 3; tmux capture-pane -t tui-test -p -S -15 2>/dev/null | grep -o '=> [^ ]*' | tail -1) if echo "$result" | grep -q '=> T'; then return 0 fi local now_ts now_ts=$(date +%s) if (( now_ts - before_ts > 90 )); then echo "TIMEOUT: no :agent msg in message list after 90s" >&2 return 1 fi sleep 3 done } test_agent_response_renders() { # Stage C: full end-to-end — LLM response appears on the rendered screen. # Must show actual response text, not a cascade failure. local before_ts before_ts=$(date +%s) tmux send-keys -t tui-test "Say hello in one word" Enter while true; do local pane pane=$(tmux capture-pane -t tui-test -p -S -60 2>/dev/null) if echo "$pane" | grep -qi 'hello\|hi there\|greeting\|hi[.!?]\|hey[.!?]'; then if echo "$pane" | grep -qi 'cascade.*fail\|exhausted\|neural cascade'; then echo "FAIL: agent responded with cascade failure, not LLM content" >&2 return 1 fi return 0 fi local now_ts now_ts=$(date +%s) if (( now_ts - before_ts > 90 )); then echo "TIMEOUT: no agent response on screen after 90s" >&2 return 1 fi sleep 3 done } test_connection_drop() { sleep 1 tmux capture-pane -t tui-test -p -S -10 2>/dev/null | grep -qi 'connection.*lost\|ERROR.*Connection\|error.*connect' || true return 0 } run_test "cascade-parsing" test_cascade_parsing run_test "eval-command" test_eval_command run_test "status-bar" test_status_bar run_test "add-msg-render" test_add_msg_render run_test "daemon-msg-roundtrip" test_daemon_msg_roundtrip run_test "agent-response-renders" test_agent_response_renders run_test "connection-drop" test_connection_drop # ---- Summary ---- echo "" echo "===== $PASS passed, $FAIL failed, $WARN warnings =====" exit $(( FAIL > 0 ? 1 : 0 ))