fix(07): WR-02 explicit readiness flag + WR-01 /api/me dev-bypass gate in global-setup

This commit is contained in:
Lucas Berger
2026-06-11 07:51:51 -04:00
parent 5322cfc2b0
commit 9c38dd33ff
+27 -2
View File
@@ -47,23 +47,48 @@ export default async function globalSetup(): Promise<void> {
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:5173' const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:5173'
const deadline = Date.now() + 60_000 const deadline = Date.now() + 60_000
// Use an explicit success flag (WR-02): inferring success from `Date.now() >= deadline`
// after the loop can misreport a success that arrived in the final second as a timeout,
// because the `await fetch` itself can push the clock past the deadline before the
// post-loop check runs.
let ready = false
while (Date.now() < deadline) { while (Date.now() < deadline) {
try { try {
const res = await fetch(`${baseURL}/health`) const res = await fetch(`${baseURL}/health`)
if (res.ok) break if (res.ok) {
ready = true
break
}
} catch { } catch {
// ECONNREFUSED or network error — stack not ready yet, keep polling // ECONNREFUSED or network error — stack not ready yet, keep polling
} }
await new Promise<void>((r) => setTimeout(r, 1_000)) await new Promise<void>((r) => setTimeout(r, 1_000))
} }
if (Date.now() >= deadline) { if (!ready) {
throw new Error( throw new Error(
`health check never returned 200 at ${baseURL}/health — is the dev stack up?\n` + `health check never returned 200 at ${baseURL}/health — is the dev stack up?\n` +
`Ensure the API is running with DEV_AUTH_BYPASS=true and the Vite dev server is on ${baseURL}.`, `Ensure the API is running with DEV_AUTH_BYPASS=true and the Vite dev server is on ${baseURL}.`,
) )
} }
// ── Step 1b: DEV_AUTH_BYPASS reachability gate (WR-01) ──────────────────────
// /health is unauthenticated and returns 200 even if the API was started WITHOUT
// DEV_AUTH_BYPASS=true. In that case every spec would fail at the first /api/me or
// /api/events with a 302 redirect to Authelia. Probe /api/me here so a mis-started
// API fails loudly IN SETUP with a clear message instead of ~40 confusing spec failures.
// redirect:'manual' surfaces the Authelia redirect as an opaque/3xx response instead of
// silently following it.
const meRes = await fetch(`${baseURL}/api/me`, { redirect: 'manual' })
if (!meRes.ok) {
throw new Error(
`/api/me did not return 200 (got ${meRes.status} ${meRes.type}) at ${baseURL}/api/me — ` +
`the API is reachable but DEV_AUTH_BYPASS is almost certainly NOT set in the API process.\n` +
`A 3xx/opaqueredirect here means /api/me is redirecting to Authelia. ` +
`Restart the API with DEV_AUTH_BYPASS=true so it serves Dev User 1 without OIDC.`,
)
}
// ── Step 2: Reset-and-seed (D-06 deterministic, D-07 in globalSetup) ──────── // ── Step 2: Reset-and-seed (D-06 deterministic, D-07 in globalSetup) ────────
// Uses exact env-var names from apps/api/src/db/client.ts. // Uses exact env-var names from apps/api/src/db/client.ts.
// DB_HOST defaults to '127.0.0.1' (NOT 'localhost') — per project memory api-integration-test-db. // DB_HOST defaults to '127.0.0.1' (NOT 'localhost') — per project memory api-integration-test-db.