#!/usr/bin/env bash # scripts/run-tests.sh — run all 3 test suites with hard timeout. # Design spec: tests never take >60s. Hang = failure, not silent. # Each suite gets 60s. Total cap = 180s. Exits non-zero on timeout OR failure. set -euo pipefail TIMEOUT_BIN=gtimeout command -v gtimeout >/dev/null 2>&1 || TIMEOUT_BIN=timeout if ! command -v "$TIMEOUT_BIN" >/dev/null 2>&1; then echo "ERROR: need 'timeout' (coreutils) or 'gtimeout' (brew coreutils)" >&2 exit 2 fi run_suite () { local label="$1"; shift local cmd="$1"; shift local secs="${1:-60}" echo "=== $label (${secs}s cap) ===" if $TIMEOUT_BIN --signal=KILL "$secs" bash -c "$cmd"; then echo "=== $label: PASS ===" else local rc=$? if [ "$rc" -eq 137 ] || [ "$rc" -eq 124 ]; then echo "=== $label: FAIL — timeout (${secs}s exceeded, killed) ===" >&2 else echo "=== $label: FAIL (exit $rc) ===" >&2 fi exit "$rc" fi } # Pre-flight: refuse to run if any test is skipped/disabled. # Skipped tests rot silently. Catch them before they hide coverage gaps. check_for_skips () { echo "=== skip-guard ===" local hits hits=$(grep -rnE '\.(skip|todo)\(|xdescribe\(|xit\(' src/tests shared/tests server/tests 2>/dev/null || true) if [ -n "$hits" ]; then echo "FAIL: skipped/disabled tests found:" >&2 echo "$hits" >&2 echo "" >&2 echo "Fix or delete. Do not skip." >&2 exit 1 fi echo "=== skip-guard: PASS ===" } check_for_skips run_suite "app" "CI=true npx react-scripts test --watchAll=false" 60 run_suite "shared" "npm test --workspace shared" 30 run_suite "server" "npm test --workspace server" 30 echo "=== ALL SUITES GREEN ==="