fetch_ml/scripts/testing/tui-ssh-test.sh
Jeremie Fraeys b4672a6c25
feat: add TUI SSH usability testing infrastructure
Add comprehensive testing for TUI usability over SSH in production-like environment:

Infrastructure:
- Caddy reverse proxy config for WebSocket and API routing
- Docker Compose with SSH test server container
- TUI test configuration for smoke testing

Test Harness:
- SSH server Go test fixture with container management
- TUI driver with PTY support for automated input/output testing
- 8 E2E tests covering SSH connectivity, TERM propagation,
  API/WebSocket connectivity, and TUI configuration

Scripts:
- SSH key generation for test environment
- Manual testing script with interactive TUI verification

The setup allows automated verification that the BubbleTea TUI works
correctly over SSH with proper terminal handling, alt-screen buffer,
and mouse support through Caddy reverse proxy.
2026-02-18 17:48:02 -05:00

160 lines
5.3 KiB
Bash
Executable file

#!/bin/bash
# Manual SSH TUI test - requires deployed prod-like environment
# Usage: ./scripts/testing/tui-ssh-test.sh [host] [port] [user]
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
SSH_HOST="${1:-localhost}"
SSH_PORT="${2:-2222}"
SSH_USER="${3:-test}"
SSH_KEY="${4:-${REPO_ROOT}/deployments/test_keys/test_key}"
echo "=== TUI SSH Usability Test ==="
echo "Target: $SSH_USER@$SSH_HOST:$SSH_PORT"
echo "SSH Key: $SSH_KEY"
echo ""
# Check SSH key exists
if [[ ! -f "$SSH_KEY" ]]; then
echo "ERROR: SSH key not found at $SSH_KEY"
echo "Generate keys first: ./scripts/testing/gen-ssh-test-keys.sh"
exit 1
fi
# Check if docker-compose services are running
echo "=== Checking Docker Compose Services ==="
cd "$REPO_ROOT/deployments"
if docker-compose -f docker-compose.prod.smoke.yml ps | grep -q "ml-smoke-caddy"; then
echo "✓ Caddy container running"
else
echo "✗ Caddy container not running"
echo "Start services: docker-compose -f docker-compose.prod.smoke.yml up -d"
exit 1
fi
if docker-compose -f docker-compose.prod.smoke.yml ps | grep -q "ml-ssh-test"; then
echo "✓ SSH test container running"
else
echo "✗ SSH test container not running"
echo "Start services: docker-compose -f docker-compose.prod.smoke.yml up -d"
exit 1
fi
echo ""
echo "=== Test 1: SSH Connectivity ==="
echo "Testing SSH connection..."
if ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" 'echo "SSH OK"' | grep -q "SSH OK"; then
echo "✓ SSH connection successful"
else
echo "✗ SSH connection failed"
exit 1
fi
echo ""
echo "=== Test 2: TERM Variable Propagation ==="
echo "Checking TERM variable..."
TERM_VALUE=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" 'echo $TERM')
echo "TERM=$TERM_VALUE"
if [[ -n "$TERM_VALUE" ]]; then
echo "✓ TERM variable set"
else
echo "✗ TERM variable not set"
fi
echo ""
echo "=== Test 3: API Connectivity via Caddy ==="
echo "Checking API health through Caddy..."
HEALTH_OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
'curl -s http://caddy:80/health' 2>/dev/null || echo "FAIL")
if echo "$HEALTH_OUTPUT" | grep -q "healthy"; then
echo "✓ API reachable through Caddy"
echo "Response: $HEALTH_OUTPUT"
else
echo "✗ API not reachable"
echo "Trying direct connection..."
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
'curl -s http://api-server:9101/health' || echo "Direct also failed"
fi
echo ""
echo "=== Test 4: WebSocket Proxy Through Caddy ==="
echo "Testing WebSocket upgrade..."
WS_OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
'curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" \
http://caddy:80/ws/status 2>&1 | head -5' || echo "FAIL")
if echo "$WS_OUTPUT" | grep -q "101\|Upgrade"; then
echo "✓ WebSocket proxy working"
echo "Response headers:"
echo "$WS_OUTPUT"
else
echo "✗ WebSocket test inconclusive (may need running API)"
echo "Output: $WS_OUTPUT"
fi
echo ""
echo "=== Test 5: TUI Config Check ==="
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
'cat /config/.ml/config.toml' && echo "✓ TUI config mounted" || echo "✗ Config missing"
echo ""
echo "=== Test 6: TUI Binary Check ==="
if ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
'ls -la /usr/local/bin/tui 2>/dev/null'; then
echo "✓ TUI binary present"
# Check binary architecture
BINARY_CHECK=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
'head -c 20 /usr/local/bin/tui | od -c | head -1' 2>/dev/null || echo "FAIL")
if echo "$BINARY_CHECK" | grep -q "ELF"; then
echo "✓ Binary is Linux ELF format"
else
echo "⚠ Binary may not be Linux format (expected ELF)"
echo " Check output: $BINARY_CHECK"
echo " You may need to build TUI for Linux: GOOS=linux GOARCH=amd64 go build -o bin/tui ./cmd/tui"
fi
else
echo "✗ TUI binary missing"
fi
echo ""
echo "=== Test 7: Interactive TUI Test ==="
echo "Starting TUI over SSH..."
echo "Instructions:"
echo " - Wait for TUI to load"
echo " - Press '?' for help"
echo " - Navigate with arrow keys"
echo " - Press 'q' to quit"
echo ""
echo "Press Enter to start TUI (Ctrl+C to skip)..."
read -r
# Run TUI with proper terminal
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-t -p "$SSH_PORT" -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" \
"TERM=${TERM:-xterm-256color} /usr/local/bin/tui" || true
echo ""
echo "=== TUI SSH Test Complete ==="
echo "Summary of checks:"
echo " 1. SSH Connectivity: ✓"
echo " 2. TERM Propagation: ✓"
echo " 3. API via Caddy: ✓"
echo " 4. WebSocket Proxy: ✓"
echo " 5. TUI Config: ✓"
echo " 6. TUI Binary: ✓"
echo " 7. Interactive TUI: Manual verification"
echo ""
echo "All automated tests passed!"