mike/scripts/e2e-local-stack.sh
willchen96 62da94be67 test(e2e): self-bootstrapping local Supabase stack for local runs
npm run test:e2e:local (or plain test:e2e via the webServer hook) boots
Docker-checked local Supabase, loads schema + migrations + grants, wires
backend/.env and frontend/.env.local, and raises the backend rate-limit
caps the same way CI does — a full suite run exceeds the default
300-requests/15-min general cap, after which every call 429s and the
profile/list waits time out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 23:58:47 +08:00

116 lines
4.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# Boot the full local e2e stack and run the Playwright suite.
#
# Local-machine mirror of .github/workflows/e2e.yml: starts the Supabase CLI
# stack (Docker), loads backend/schema.sql + backend/migrations/ + the
# service_role grants (schema.sql assumes a hosted project where service_role
# is already privileged — see docs/e2e-ci.md), points backend/.env and
# frontend/.env.local at the local stack, then runs `npx playwright test`.
#
# Usage, from the repo root:
# npm run test:e2e:local # whole suite
# npm run test:e2e:local -- -g "display name" # extra args go to Playwright
#
# With --setup-only the script prepares the stack and exits without running
# Playwright — playwright.config.ts uses this in the backend webServer command
# so a plain `npm run test:e2e` also boots against a ready local stack.
#
# The first run rewrites the Supabase lines in your env files; the previous
# (e.g. hosted) versions are kept once as .env.hosted.bak / .env.local.hosted.bak.
# Restore those backups to point back at the hosted project.
set -euo pipefail
SETUP_ONLY=0
if [ "${1:-}" = "--setup-only" ]; then
SETUP_ONLY=1
shift
fi
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BACKEND="$ROOT/backend"
FRONTEND="$ROOT/frontend"
if ! docker info >/dev/null 2>&1; then
echo "Docker is not running — start it first (open -a Docker) and retry." >&2
exit 1
fi
cd "$BACKEND"
if [ ! -f supabase/config.toml ]; then
npx supabase init --force --with-vscode-settings=false ||
npx supabase init --force
fi
# Idempotent: if the stack is already up this is a no-op.
npx supabase start
STATUS=$(npx supabase status -o json)
DB_URL=$(jq -r '.DB_URL' <<<"$STATUS")
API_URL=$(jq -r '.API_URL' <<<"$STATUS")
ANON_KEY=$(jq -r '.ANON_KEY' <<<"$STATUS")
SERVICE_KEY=$(jq -r '.SERVICE_ROLE_KEY' <<<"$STATUS")
# schema.sql is not idempotent, so only load it into a virgin database; the
# dated migrations ARE re-runnable and fill any gap schema.sql has (it lags —
# see docs/e2e-ci.md), so apply them every time.
if [ "$(psql "$DB_URL" -tAc "SELECT to_regclass('public.user_profiles') IS NULL")" = "t" ]; then
echo "Loading schema.sql into fresh database…"
psql "$DB_URL" -v ON_ERROR_STOP=1 -q -f schema.sql
fi
for m in migrations/*.sql; do
psql "$DB_URL" -q -f "$m" >/dev/null 2>&1 ||
echo "warning: migration returned non-zero (already applied?): $m"
done
psql "$DB_URL" -v ON_ERROR_STOP=1 -q <<'SQL'
GRANT USAGE ON SCHEMA public TO service_role;
GRANT ALL ON ALL TABLES IN SCHEMA public TO service_role;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO service_role;
GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO service_role;
NOTIFY pgrst, 'reload schema';
SQL
# Rewrite only the Supabase lines of the env files, preserving everything else
# (API keys, R2 storage, …). Keep a one-time backup of the pre-local versions.
set_kv() {
local file=$1 key=$2 value=$3
if grep -q "^${key}=" "$file" 2>/dev/null; then
awk -v k="$key" -v v="$value" \
'index($0, k"=") == 1 { print k "=" v; next } { print }' \
"$file" >"$file.tmp" && mv "$file.tmp" "$file"
else
echo "${key}=${value}" >>"$file"
fi
}
[ -f .env ] || cp .env.example .env
[ -f .env.hosted.bak ] || cp .env .env.hosted.bak
set_kv .env SUPABASE_URL "$API_URL"
set_kv .env SUPABASE_SECRET_KEY "$SERVICE_KEY"
# The suite fires well over the backend's default 300-requests/15-min general
# cap in one run; once tripped every call 429s and profile/list waits time out.
# Same overrides CI uses — e2e is not testing throttling.
set_kv .env RATE_LIMIT_GENERAL_MAX 100000
set_kv .env RATE_LIMIT_CHAT_MAX 100000
set_kv .env RATE_LIMIT_CHAT_CREATE_MAX 100000
set_kv .env RATE_LIMIT_UPLOAD_MAX 100000
set_kv .env RATE_LIMIT_EXPORT_MAX 100000
set_kv .env RATE_LIMIT_DATA_DELETE_MAX 100000
touch "$FRONTEND/.env.local"
[ -f "$FRONTEND/.env.local.hosted.bak" ] || cp "$FRONTEND/.env.local" "$FRONTEND/.env.local.hosted.bak"
set_kv "$FRONTEND/.env.local" NEXT_PUBLIC_SUPABASE_URL "$API_URL"
set_kv "$FRONTEND/.env.local" NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY "$ANON_KEY"
set_kv "$FRONTEND/.env.local" NEXT_PUBLIC_API_BASE_URL "http://localhost:3001"
echo "Local stack ready: $API_URL (db: ${DB_URL%%\?*})"
if [ "$SETUP_ONLY" = "1" ]; then
exit 0
fi
echo "NOTE: kill any backend/frontend dev servers started before this script —"
echo "they hold the old env. playwright.config.ts starts fresh ones if none run."
cd "$ROOT"
npx playwright test "$@"