matchday-v3 / scripts /run-local-e2e.sh
mzidan000's picture
Publish verified MatchDay v3 source
6d528b3 verified
Raw
History Blame Contribute Delete
4.15 kB
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
API_PORT="${MATCHDAY_E2E_API_PORT:-8180}"
WEB_PORT="${MATCHDAY_E2E_WEB_PORT:-3100}"
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/matchday-v3-e2e.XXXXXX")"
API_LOG="$TMP_DIR/api.log"
WEB_LOG="$TMP_DIR/web.log"
NEXT_DIST_DIR=".next-e2e"
NEXT_ENV_BACKUP="$TMP_DIR/next-env.d.ts"
cp "$ROOT/next-env.d.ts" "$NEXT_ENV_BACKUP"
terminate_group() {
local pid="${1:-}"
if [[ -z "$pid" ]]; then
return
fi
kill -TERM -- "-$pid" 2>/dev/null || true
for _ in $(seq 1 30); do
if ! kill -0 "$pid" 2>/dev/null; then
break
fi
sleep 0.1
done
if kill -0 "$pid" 2>/dev/null; then
kill -KILL -- "-$pid" 2>/dev/null || true
fi
wait "$pid" 2>/dev/null || true
}
cleanup() {
local status=$?
terminate_group "${API_PID:-}"
terminate_group "${WEB_PID:-}"
rm -rf "$ROOT/$NEXT_DIST_DIR"
cp "$NEXT_ENV_BACKUP" "$ROOT/next-env.d.ts"
if [[ "$status" -eq 0 ]]; then
rm -rf "$TMP_DIR"
elif [[ -s "$API_LOG" || -s "$WEB_LOG" ]]; then
printf 'E2E failed; logs preserved at %s\n' "$TMP_DIR" >&2
else
rm -rf "$TMP_DIR"
fi
return "$status"
}
trap cleanup EXIT INT TERM
assert_port_free() {
local label="$1"
local port="$2"
if ! python3 - "$port" <<'PY'
import socket
import sys
port = int(sys.argv[1])
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
try:
listener.bind(("127.0.0.1", port))
except OSError as exc:
print(f"port {port} is unavailable: {exc}", file=sys.stderr)
raise SystemExit(1) from exc
PY
then
printf 'E2E %s port %s is already in use; refusing to test an unrelated process.\n' "$label" "$port" >&2
exit 1
fi
}
assert_port_free "API" "$API_PORT"
assert_port_free "web" "$WEB_PORT"
cd "$ROOT"
export MATCHDAY_ENV=test
export MATCHDAY_QWEN_MODE=scripted
# Keep regression E2E deterministic; live provider behavior has a separate contract test/smoke.
export SERPAPI_API_KEY=""
export MATCHDAY_BACKEND_SIGNING_SECRET=matchday-e2e-local-signing-secret
export MATCHDAY_AUTH_NONCE_STORE=memory
export MATCHDAY_AGENT_API_URL="http://127.0.0.1:$API_PORT"
export DATABASE_URL="sqlite:///$TMP_DIR/matchday.db"
export MATCHDAY_RUN_LIMIT_PER_USER_HOUR=100
export MATCHDAY_RUN_LIMIT_GLOBAL_HOUR=200
# Keep relative "upcoming" fixture journeys stable as real tournament dates pass.
# The backend rejects this clock outside MATCHDAY_ENV=test.
export MATCHDAY_FIXTURE_REFERENCE_TIME="2026-07-12T12:00:00Z"
export NEXT_TELEMETRY_DISABLED=1
export NEXT_DIST_DIR
export PYTHONPATH=backend
python3 -c 'import os, sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])' \
python3 -m uvicorn matchday.main:app \
--host 127.0.0.1 --port "$API_PORT" >"$API_LOG" 2>&1 &
API_PID=$!
python3 -c 'import os, sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])' \
node "$ROOT/node_modules/next/dist/bin/next" dev \
--hostname 127.0.0.1 --port "$WEB_PORT" >"$WEB_LOG" 2>&1 &
WEB_PID=$!
for _ in $(seq 1 60); do
if ! kill -0 "$API_PID" 2>/dev/null; then
printf 'E2E API process exited before readiness (port %s may already be in use).\n' "$API_PORT" >&2
cat "$API_LOG" >&2
exit 1
fi
if ! kill -0 "$WEB_PID" 2>/dev/null; then
printf 'E2E web process exited before readiness (port %s may already be in use).\n' "$WEB_PORT" >&2
cat "$WEB_LOG" >&2
exit 1
fi
if curl -fsS "http://127.0.0.1:$API_PORT/healthz" >/dev/null 2>&1 && \
curl -fsS "http://127.0.0.1:$WEB_PORT" >/dev/null 2>&1; then
break
fi
sleep 0.5
done
if ! kill -0 "$API_PID" 2>/dev/null; then
printf 'E2E API process is not the process serving port %s.\n' "$API_PORT" >&2
cat "$API_LOG" >&2
exit 1
fi
if ! kill -0 "$WEB_PID" 2>/dev/null; then
printf 'E2E web process is not the process serving port %s.\n' "$WEB_PORT" >&2
cat "$WEB_LOG" >&2
exit 1
fi
if ! curl -fsS "http://127.0.0.1:$API_PORT/healthz" >/dev/null 2>&1; then
cat "$API_LOG"
exit 1
fi
if ! curl -fsS "http://127.0.0.1:$WEB_PORT" >/dev/null 2>&1; then
cat "$WEB_LOG"
exit 1
fi
MATCHDAY_E2E_BASE_URL="http://127.0.0.1:$WEB_PORT" \
python3 -m pytest e2e -q "$@"