diff --git a/e2e/cohort.ts b/e2e/cohort.ts index c335fef..fe56e65 100644 --- a/e2e/cohort.ts +++ b/e2e/cohort.ts @@ -275,19 +275,21 @@ export const ANON_TIER = 'anonymous' * * `forceAnon` pins the anonymous fast-hot-pool path even when a minted session * exists. Used wherever the AUTHED prod provision is too slow / hangs to finish - * inside the LIVE per-test timeout (playwright.live.config.ts, 90s): - * - DB & VECTOR: the authed (pro) /db/new & /vector/new paths provision a - * DEDICATED Postgres / pgvector Postgres for the team — slow on prod (15s - * warm, >90s cold). The anon path uses the fast (pgvector) hot-pool. + * inside the LIVE per-test timeout (playwright.live.config.ts): + * - DB, VECTOR & NOSQL: the authed (pro) /db/new, /vector/new & /nosql/new + * paths each provision a DEDICATED backing DB for the team (Postgres / + * pgvector Postgres / MongoDB) — slow on prod (15s warm, >90s cold). The + * anon path uses the fast hot-pool. These are ALL the dedicated-backing-DB + * services; none may hit the slow authed-dedicated-provision path. * - QUEUE: the authed /queue/new isolated per-tenant NATS path HANGS on prod * until the operator seeds the NATS operator/sys NKeys (CLAUDE.md P1 known * gap). The anon path returns fast with auth_mode=legacy_open. * In every forceAnon case the resulting anon resource is reaped by its 24h TTL * (no authed DELETE exists for anon resources); the spec treats a no-bearer * resource's reap as best-effort (TTL-backed) rather than asserting a 200 it can - * never get. The genuinely-fast services (cache/nosql/storage/webhook) stay on - * the authed-minted path so the authed/minted-account + authed-reap legs are - * still exercised. + * never get. The genuinely-fast services (cache/storage/webhook) have NO + * dedicated backing DB and stay on the authed-minted path so the + * authed/minted-account + authed-reap legs are still exercised. */ export function provisionIdentity( extra: Record = {}, diff --git a/e2e/live-anon-provision.spec.ts b/e2e/live-anon-provision.spec.ts index 95e25b7..b56c8bd 100644 --- a/e2e/live-anon-provision.spec.ts +++ b/e2e/live-anon-provision.spec.ts @@ -80,11 +80,16 @@ interface ProvisionFlow { /** Optional extra per-service assertion on the parsed JSON body. */ extra?: (body: Record) => void /** - * Pin the anonymous bypass path even when a minted pro session exists. Only - * `queue` sets this: the authed /queue/new path attempts isolated per-tenant - * NATS provisioning which HANGS on prod (operator NKeys unseeded — CLAUDE.md - * P1). The anon path returns fast with auth_mode=legacy_open; the anon - * resource is TTL-reaped (no authed DELETE for anon resources). + * Pin the anonymous bypass path even when a minted pro session exists. Set by + * every service whose authed path is too slow / hangs to finish inside the + * per-test timeout: + * - `vector` & `nosql`: the authed path provisions a DEDICATED backing DB + * (pgvector Postgres / MongoDB) for the team — cold-provision > 90s on + * prod. (`db` does the same in the smoke spec.) + * - `queue`: the authed /queue/new path attempts isolated per-tenant NATS + * provisioning which HANGS on prod (operator NKeys unseeded — CLAUDE.md P1). + * The anon path returns fast (hot-pool / legacy_open); the anon resource is + * TTL-reaped (no authed DELETE for anon resources). */ forceAnon?: boolean } @@ -97,29 +102,44 @@ interface ProvisionFlow { // Each flow runs as EITHER the minted PRO account (authed, exercises the // minted-account/authed-reap path) OR the anonymous fast-hot-pool path // (forceAnon). The deciding factor is whether the AUTHED prod provision is fast -// enough to finish inside the 90s per-test timeout (playwright.live.config.ts): +// enough to finish inside the per-test timeout (playwright.live.config.ts): // -// service | path | why -// ---------|----------------|---------------------------------------------- -// db | anon (forceAnon, in smoke spec) | authed = DEDICATED Postgres, -// | | cold-provision > 90s on prod; anon = hot-pool (fast) -// vector | anon (forceAnon)| authed = DEDICATED pgvector Postgres, same -// | | slow dedicated-provision as db (15s warm, >90s -// | | cold) → TIMED OUT the suite (run 26999448643). -// | | anon = fast hot-pool; anon resource TTL-reaped. -// queue | anon (forceAnon)| authed isolated-NATS path HANGS on prod (P1 -// | | NKeys gap); anon returns fast (legacy_open). -// cache | authed-minted | Redis provision is fast (no dedicated DB build); -// | | KEEPS authed/minted-account + authed-reap coverage. -// nosql | authed-minted | Mongo provision is fast; KEEPS authed coverage. -// storage | authed-minted | DO Spaces tenant-prefix (no DB build) → fast; -// | | KEEPS authed coverage. -// webhook | authed-minted | Redis-backed receiver, fast; KEEPS authed coverage. +// service | path | backing DB? | why +// ---------|----------------|--------------|------------------------------- +// db | ANON (forceAnon, in smoke spec) | DEDICATED Postgres | authed +// | | | cold-provision > 90s on prod; +// | | | anon = hot-pool (fast). +// vector | ANON (forceAnon)| DEDICATED pgvector Postgres | same slow +// | | | dedicated-provision as db (15s +// | | | warm, >90s cold) → TIMED OUT the +// | | | suite (run 26999448643). anon = +// | | | fast hot-pool; TTL-reaped. +// nosql | ANON (forceAnon)| DEDICATED Mongo | authed = DEDICATED MongoDB; +// | | | cold-provision > 90s on prod +// | | | (run 27000380873 timed out on +// | | | /nosql/new). anon = fast hot-pool; +// | | | TTL-reaped. The LAST dedicated-DB +// | | | service to move off the slow path. +// queue | ANON (forceAnon)| none (NATS) | authed isolated-NATS path HANGS +// | | | on prod (P1 NKeys gap); anon +// | | | returns fast (legacy_open). +// cache | authed-minted | none (Redis) | Redis provision is fast (no +// | | | dedicated DB build); KEEPS authed/ +// | | | minted-account + authed-reap. +// storage | authed-minted | none (Spaces)| DO Spaces tenant-prefix (no DB +// | | | build) → fast; KEEPS authed. +// webhook | authed-minted | none (Redis) | Redis-backed receiver, fast; +// | | | KEEPS authed coverage. // -// Net: the two genuinely-slow DEDICATED-DB provisions (db, vector) and the -// hanging queue path use the fast anon hot-pool so they can NEVER exceed the -// timeout; cache/nosql/storage/webhook still exercise the authed/minted-account -// (and authed-reap) path. That keeps both paths covered with zero flake. +// Net: EVERY service whose authed path provisions a DEDICATED backing DB +// (db, vector, nosql) — plus the hanging queue path — uses the fast anon +// hot-pool so it can NEVER exceed the per-test timeout. The remaining services +// (cache/storage/webhook) have NO dedicated backing DB, provision fast, and +// stay on the authed/minted-account (and authed-reap) path so the +// minted-account + authed-reap legs are still exercised on every run. The auth +// specs, the claim flow, and the minted-account lifecycle continue to exercise +// the on-the-fly minted account — "test accounts on the fly" coverage is +// retained. Both paths covered, zero dedicated-DB-provision flake. const PROVISION_FLOWS: ProvisionFlow[] = [ { label: 'vector', @@ -147,6 +167,14 @@ const PROVISION_FLOWS: ProvisionFlow[] = [ path: '/nosql/new', connKey: 'connection_url', connPattern: /^mongodb(\+srv)?:\/\//, + // Pin anon: the authed (pro) /nosql/new path provisions a DEDICATED MongoDB + // for the team — slow on prod (>90s cold), which timed out the suite at the + // per-test limit (flaky run 27000380873 failed on /nosql/new). The anon path + // uses the fast Mongo hot-pool and returns quickly; the anon resource is + // TTL-reaped (no authed DELETE for anon resources). Same treatment as /db/new + // and /vector/new — all three are dedicated-DB provisions. nosql was the LAST + // dedicated-DB service still on the slow authed path. + forceAnon: true, }, { label: 'queue', diff --git a/e2e/live-provision-smoke.spec.ts b/e2e/live-provision-smoke.spec.ts index 8d42d29..20827c0 100644 --- a/e2e/live-provision-smoke.spec.ts +++ b/e2e/live-provision-smoke.spec.ts @@ -101,9 +101,11 @@ test.describe('LIVE smoke — anonymous provision → backend-assert → reap', // anon path does cleanly: the X-E2E-Test-Token + X-E2E-Source-IP fingerprint // bypass gets past the recycle gate (fresh fingerprint per call), and the // anon resource is TTL-reaped (no authed DELETE for anon resources — the - // reap below is best-effort for the no-bearer case). The full authed-provision - // coverage lives in live-anon-provision.spec.ts (vector/cache/nosql as the - // minted pro account). /db/new REQUIRES a name (CLAUDE.md) — sent below. + // reap below is best-effort for the no-bearer case). The authed/minted-account + // (and authed-reap) coverage lives in live-anon-provision.spec.ts via the + // fast, no-dedicated-DB services (cache/storage/webhook); the dedicated-DB + // services (db/vector/nosql) all use the fast anon hot-pool there too. + // /db/new REQUIRES a name (CLAUDE.md) — sent below. const id = provisionIdentity({}, /* forceAnon */ true) const resp = await request.fetch(`${API_URL}/db/new`, { method: 'POST', diff --git a/playwright.live.config.ts b/playwright.live.config.ts index 3d9514a..e7306f5 100644 --- a/playwright.live.config.ts +++ b/playwright.live.config.ts @@ -36,7 +36,12 @@ export default defineConfig({ forbidOnly: !!process.env.CI, retries: process.env.CI ? 1 : 0, workers: 1, - timeout: 90_000, + // 120s per test (was 90s). Secondary guard only: the PRIMARY fix is pinning + // every dedicated-backing-DB provision (db/vector/nosql) to the fast anon + // hot-pool via forceAnon (see e2e/cohort.ts + live-anon-provision.spec.ts) so + // no test depends on a slow authed-dedicated-provision finishing in time. The + // modest bump gives extra headroom for a cold hot-pool / network slowness. + timeout: 120_000, expect: { timeout: 20_000 }, reporter: process.env.CI