#!/usr/bin/env bash # verify_docker.sh — IBKR Dashboard Docker Deployment Verification # Usage: chmod +x verify_docker.sh && ./verify_docker.sh # Requires: docker compose up -d already running # Note: tests against port 8000 directly (local testing without Traefik hostname) set -uo pipefail BASE="http://localhost:8000" PASS=0 FAIL=0 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' pass() { echo -e "${GREEN}[PASS]${NC} $1"; PASS=$((PASS + 1)); } fail() { echo -e "${RED}[FAIL]${NC} $1"; FAIL=$((FAIL + 1)); } info() { echo -e "${YELLOW}[INFO]${NC} $1"; } check_body() { local label="$1" result="$2" expected="$3" if echo "$result" | grep -q "$expected"; then pass "$label" else fail "$label (got: $(echo "$result" | head -c 120))" fi } check_status() { local label="$1" url="$2" expected_code="$3" local code code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>/dev/null || echo "000") if [ "$code" = "$expected_code" ]; then pass "$label" else fail "$label (HTTP $code, expected $expected_code)" fi } echo "" echo "════════════════════════════════════════════════════" echo " IBKR Dashboard — Docker Deployment Verification" echo " Reverse proxy: Traefik | Testing via port 8000" echo "════════════════════════════════════════════════════" echo "" # ── Container status ────────────────────────────────────────────────────────── echo "── Container Status ──────────────────────────────" for svc in ib-gateway ibkr-dashboard; do DOCKER_STATUS=$(docker ps --format "{{.Names}} {{.Status}}" 2>/dev/null \ | grep "$svc" | grep -i "up" || echo "") if [ -n "$DOCKER_STATUS" ]; then pass "Container $svc is Up" else fail "Container $svc is Up" fi done echo "" echo "── Port Checks ───────────────────────────────────" if nc -zv 127.0.0.1 4002 2>/dev/null; then pass "Port 4002 open (IB Gateway paper trading)" else fail "Port 4002 open (IB Gateway paper trading)" fi if nc -zv 127.0.0.1 8000 2>/dev/null; then pass "Port 8000 open (FastAPI direct)" else fail "Port 8000 open (FastAPI direct)" fi echo "" echo "── HTTP Endpoint Checks (direct → port 8000) ────" check_status "GET /health → 200" "$BASE/health" "200" R=$(curl -s --max-time 10 "$BASE/health" 2>/dev/null || echo "{}") check_body "GET /health → ib_connected field present" "$R" "ib_connected" check_body "GET /health → db_ok field present" "$R" "db_ok" check_status "GET / → 200" "$BASE/" "200" R=$(curl -s --max-time 20 "$BASE/history?symbol=AAPL" 2>/dev/null || echo "[]") if echo "$R" | python3 -c " import sys, json try: d = json.load(sys.stdin) sys.exit(0 if isinstance(d, list) and len(d) > 0 else 1) except Exception: sys.exit(1) " 2>/dev/null; then pass "GET /history?symbol=AAPL → non-empty list" else fail "GET /history?symbol=AAPL → non-empty list (got: $(echo "$R" | head -c 80))" fi check_status "GET /portfolio/data → 200" "$BASE/portfolio/data" "200" R=$(curl -s --max-time 15 "$BASE/portfolio/pnl" 2>/dev/null || echo "{}") check_body "GET /portfolio/pnl → contains NetLiquidation" "$R" "NetLiquidation" R=$(curl -s --max-time 10 "$BASE/risk/status" 2>/dev/null || echo "{}") check_body "GET /risk/status → contains max_daily_loss" "$R" "max_daily_loss" check_status "GET /scanner → 200" "$BASE/scanner" "200" check_status "GET /tradelog → 200" "$BASE/tradelog" "200" R=$(curl -s --max-time 15 -X POST "$BASE/subscribe" \ -H "Content-Type: application/json" \ -d '{"symbol":"SPY"}' 2>/dev/null || echo "{}") check_body "POST /subscribe {symbol:SPY} → contains status" "$R" '"status"' CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \ -X POST "$BASE/webhook" \ -H "Content-Type: application/json" \ -d '{"secret":"wrong_secret_xyz","symbol":"AAPL","strategy":{"order_action":"BUY","order_contracts":1}}' \ 2>/dev/null || echo "000") if [ "$CODE" = "403" ]; then pass "POST /webhook wrong secret → 403" else fail "POST /webhook wrong secret → 403 (got: HTTP $CODE)" fi echo "" echo "── IBKR Connection Log Check ─────────────────────" if docker compose logs ibkr-dashboard 2>/dev/null | grep -q "Connected to IB"; then pass "IBKR connection confirmed in logs" else fail "IBKR connection confirmed in logs" info "Check: docker compose logs ibkr-dashboard" fi echo "" echo "── pytest Inside Container ───────────────────────" info "Running: docker compose exec ibkr-dashboard pytest tests/ -q --tb=short" if docker compose exec ibkr-dashboard pytest tests/ -q --tb=short 2>&1; then pass "pytest inside container" else fail "pytest inside container" fi TOTAL=$((PASS + FAIL)) echo "" echo "════════════════════════════════════════════════════" printf " Summary: ${GREEN}PASS %d${NC} / %d ${RED}FAIL %d${NC} / %d\n" \ "$PASS" "$TOTAL" "$FAIL" "$TOTAL" echo "════════════════════════════════════════════════════" if [ "$FAIL" -eq 0 ]; then echo -e "${GREEN}ALL CHECKS PASSED — deployment is healthy.${NC}" exit 0 else echo -e "${RED}${FAIL} CHECK(S) FAILED — review above.${NC}" exit 1 fi