#!/usr/bin/env bash # Test suite for SX HTTP server app config handling. # Starts the server with different __app-config values and verifies behavior. set -euo pipefail cd "$(dirname "$0")/.." PROJECT_DIR="$(pwd)" SERVER="hosts/ocaml/_build/default/bin/sx_server.exe" BASE_PORT=8090 PASS=0 FAIL=0 ERRORS="" SERVER_PID="" CURRENT_PORT="" # ── Helpers ────────────────────────────────────────────────────────── TMP_DIR=$(mktemp -d) cleanup() { if [ -n "${SERVER_PID:-}" ] && kill -0 "$SERVER_PID" 2>/dev/null; then kill "$SERVER_PID" 2>/dev/null wait "$SERVER_PID" 2>/dev/null || true fi rm -rf "$TMP_DIR" } trap cleanup EXIT start_server() { local port="$1" local extra_env="${2:-}" CURRENT_PORT="$port" # Kill anything on this port kill $(lsof -ti :"$port") 2>/dev/null || true sleep 1 # Start server eval "$extra_env SX_PROJECT_DIR=$PROJECT_DIR $SERVER --http $port" \ > "$TMP_DIR/stdout" 2> "$TMP_DIR/stderr" & SERVER_PID=$! # Wait for "Listening" message local tries=0 while ! grep -q "Listening" "$TMP_DIR/stderr" 2>/dev/null; do sleep 1 tries=$((tries + 1)) if [ $tries -gt 120 ]; then echo "TIMEOUT waiting for server on port $port" cat "$TMP_DIR/stderr" return 1 fi if ! kill -0 "$SERVER_PID" 2>/dev/null; then echo "Server died during startup on port $port" cat "$TMP_DIR/stderr" return 1 fi done echo " (server ready on port $port)" } stop_server() { if [ -n "${SERVER_PID:-}" ] && kill -0 "$SERVER_PID" 2>/dev/null; then kill "$SERVER_PID" 2>/dev/null wait "$SERVER_PID" 2>/dev/null || true sleep 1 fi SERVER_PID="" } url() { echo "http://localhost:$CURRENT_PORT$1"; } assert_status() { local desc="$1" path="$2" expected="$3" shift 3 local actual actual=$(curl -s -o /dev/null -w "%{http_code}" "$@" "$(url "$path")" 2>/dev/null) if [ "$actual" = "$expected" ]; then PASS=$((PASS + 1)) echo " PASS: $desc" else FAIL=$((FAIL + 1)) ERRORS="$ERRORS\n FAIL: $desc — expected $expected, got $actual" echo " FAIL: $desc — expected $expected, got $actual" fi } assert_redirect() { local desc="$1" path="$2" expected_path="$3" local actual actual=$(curl -s -o /dev/null -w "%{redirect_url}" "$(url "$path")" 2>/dev/null) local expected="http://localhost:$CURRENT_PORT$expected_path" if [ "$actual" = "$expected" ]; then PASS=$((PASS + 1)) echo " PASS: $desc" else FAIL=$((FAIL + 1)) ERRORS="$ERRORS\n FAIL: $desc — expected → $expected, got → $actual" echo " FAIL: $desc — expected → $expected_path, got → $actual" fi } assert_body_contains() { local desc="$1" path="$2" expected="$3" shift 3 curl -s "$@" "$(url "$path")" > "$TMP_DIR/body" 2>/dev/null if grep -qF "$expected" "$TMP_DIR/body"; then PASS=$((PASS + 1)) echo " PASS: $desc" else FAIL=$((FAIL + 1)) local size=$(wc -c < "$TMP_DIR/body") ERRORS="$ERRORS\n FAIL: $desc — body ($size bytes) missing '$expected'" echo " FAIL: $desc — body ($size bytes) missing '$expected'" fi } assert_stderr_contains() { local desc="$1" expected="$2" if grep -qF "$expected" "$TMP_DIR/stderr" 2>/dev/null; then PASS=$((PASS + 1)) echo " PASS: $desc" else FAIL=$((FAIL + 1)) ERRORS="$ERRORS\n FAIL: $desc — stderr missing '$expected'" echo " FAIL: $desc — stderr missing '$expected'" fi } assert_stderr_not_contains() { local desc="$1" expected="$2" if grep -qF "$expected" "$TMP_DIR/stderr" 2>/dev/null; then FAIL=$((FAIL + 1)) ERRORS="$ERRORS\n FAIL: $desc — stderr should NOT contain '$expected'" echo " FAIL: $desc — stderr should NOT contain '$expected'" else PASS=$((PASS + 1)) echo " PASS: $desc" fi } # ── Setup ──────────────────────────────────────────────────────────── if [ ! -x "$SERVER" ]; then echo "ERROR: Server binary not found at $SERVER" echo "Run: cd hosts/ocaml && eval \$(opam env) && dune build" exit 1 fi echo "=== SX HTTP Server Config Tests ===" echo "" # ── Test Group 1: Default config (standard app-config.sx) ─────────── echo "── Group 1: Standard config ──" start_server $((BASE_PORT + 1)) assert_stderr_contains "config loaded" "App config loaded: title=SX prefix=/sx/" assert_stderr_contains "warmup ran 9 pages" "Pre-warmed 9 pages" assert_stderr_not_contains "no stepper cookie refs" "sx-home-stepper" assert_status "homepage 200" "/sx/" 200 assert_status "AJAX homepage 200" "/sx/" 200 -H "sx-request: true" assert_status "geography 200" "/sx/(geography)" 200 assert_status "language 200" "/sx/(language)" 200 assert_status "applications 200" "/sx/(applications)" 200 assert_redirect "root → /sx/" "/" "/sx/" assert_status "handler 200" "/sx/(api.spec-detail)?name=render-to-html" 200 assert_status "static 200" "/static/styles/tw.css" 200 assert_status "debug 200" "/sx/_debug/eval?expr=(%2B%201%202)" 200 assert_status "unknown 404" "/nope" 404 assert_body_contains "title is SX" "/sx/" "