| #!/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 |
| |
| 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 |
| |
| |
| 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 "$@" |
|
|