Files
rfc-app/testing
Ben Stull 83eafe72ee feat(e2e): deployed-environment E2E harness + gated test-auth (v0.52.0)
The §9 pipeline's PPE+E2E stage was unreachable: e2e/metadata.spec.js was
bound to Tier-1-only scaffolding (docker-seeded faceted collection,
SQLite-injected owner, Mailpit OTC sink). This makes the same suite run
against a deployed host.

- backend: POST /auth/test/login — fail-closed, secret-gated, single-
  identity owner test-login (404 unless E2E_TEST_AUTH_SECRET +
  E2E_TEST_AUTH_EMAIL both set; constant-time compare; loud startup warn).
  6 vertical tests; backend 665 green. Documented in backend/.env.example.
- e2e/lib/auth.js: branch on E2E_TEST_AUTH_SECRET (deployed test-login vs
  local Mailpit OTC); OWNER_EMAIL from E2E_OWNER_EMAIL. Spec unchanged so
  the localhost Tier-1 path keeps working.
- testing/seed-ppe.sh: seed a dedicated, prod-untouching PPE registry +
  content repo (faceted bdd collection) — real OHM content never touched.
- docs/design/2026-06-07-deployed-env-e2e-harness.md; CHANGELOG; VERSION
  + frontend/package.json -> 0.52.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:35:53 -07:00
..

Test harness (handbook §10.3 two-tier testing)

One environment-agnostic suite, two targets.

Tier 1 — local Docker (every PR)

make e2e-install     # one-time: install Playwright + chromium (cold checkout)
make tier1-up        # build + start: gitea(seeded) + backend + web(nginx) + mailpit
make e2e             # run Playwright against http://localhost:8080
make fe-unit         # run Vitest frontend unit tests
make tier1-down      # stop + wipe volumes

The stack is hermetic and disposable — fresh SQLite + fresh seeded Gitea each tier1-up. e2e signs in via the email OTC flow, reading the code back from Mailpit, so no real OAuth provider is needed.

CI note: the backend enforces a per-IP OTC request limiter (5 requests / 300s, backend/app/ratelimit.py). A single make e2e run uses exactly one OTC request, so the normal "fresh tier1-up then one e2e" flow is well clear of it. Do not retry make e2e more than ~4 times in a 5-minute window against the same running stack, or the 6th OTC request will 429. Restarting the backend container (or tier1-down/tier1-up) resets the in-process limiter. Note: this is distinct from the per-email 60s OTC cooldown (OTC_REQUEST_COOLDOWN_SECONDS, backend/app/otc.py); the smoke spec handles that separately by using a unique email address per run.

Tier 2 — PPE (deploy gate)

The SAME suite, pointed at the PPE instance (once rfc-app-ppe.<base> is stood up via flotilla — see the engineering handbook §10.1/§10.3):

cd e2e && BASE_URL=https://rfc-app-ppe.<base> MAILSINK_URL=<ppe-mailpit-api> npm run e2e

PPE provides the real nginx/systemd/SQLite topology + its own isolated Gitea + always-pass Turnstile keys. Standing up the PPE VM is an operator task, not part of this repo.