// Cloud-only: every mounted /api plane ANSWERS — nothing silently falls // through to the SPA's 200-HTML fallback (the billing-404 regression class). // Docs and the spec are served, the billing proxy and the protected API gate // reject anonymous callers with their own structured JSON, and unknown /api // paths are a real 404. All raw fetch: this is about raw wire dispatch. import { expect } from "@effect/vitest"; import { Effect } from "effect"; import { scenario } from "../src/scenario"; import { Api, Billing, Target } from "../src/services"; scenario( "Surfaces · Swagger UI and the OpenAPI spec document the mounted API", {}, Effect.gen(function* () { // Gate: the REST API plane is mounted on this target. yield* Api; const target = yield* Target; const docs = yield* Effect.promise(() => fetch(new URL("/api/docs", target.baseUrl))); expect(docs.status, "Swagger UI is served").toBe(200); expect(docs.headers.get("content-type"), "as an HTML page").toContain("text/html"); const docsHtml = yield* Effect.promise(() => docs.text()); expect(docsHtml.toLowerCase(), "it is the Swagger UI shell").toContain("swagger"); const spec = yield* Effect.promise(() => fetch(new URL("/api/openapi.json", target.baseUrl))); expect(spec.status, "the OpenAPI spec is served").toBe(200); expect(spec.headers.get("content-type"), "as JSON").toContain("application/json"); const parsed = (yield* Effect.promise(() => spec.json())) as { paths?: Record; }; const paths = Object.keys(parsed.paths ?? {}); expect(paths, "the core protected API is documented").toContain("/api/integrations"); expect(paths, "the session-auth plane is documented").toContain("/api/auth/me"); expect(paths, "the org plane is documented").toContain("/api/org/domains"); expect(paths, "path-param routes are documented").toContain( "/api/executions/{executionId}/resume", ); // The account and admin-users groups. These are composed into the spec by // hand (`CloudOpenApi` in `extensions/routes.ts`), and the composition is // the ONLY thing that documents them — the routes serve either way, so a // group dropped from the `.add(...)` chain leaves a mounted, undocumented // plane and nothing else fails. Asserted against the SERVED spec rather // than the module: cloud builds the same composition twice (here and in // `extensions/docs.ts`) and only this one reaches the runtime, so importing // either module could pass while the wire is wrong. expect(paths, "the account plane is documented").toContain("/api/account/me"); expect(paths, "including the org-key surface the console reads").toContain( "/api/account/org-api-keys", ); expect(paths, "the tenant-wide admin users plane is documented").toContain("/api/admin/users"); expect(paths, "including the joined view the console list consumes").toContain( "/api/admin/users/with-connections", ); expect(paths, "and the per-user connections read").toContain( "/api/admin/users/{externalId}/connections", ); expect(paths, "and the single-user read").toContain("/api/admin/users/{identifier}"); }), ); scenario( "Surfaces · the billing proxy answers with its own 401 JSON, not the SPA fallback", {}, Effect.gen(function* () { // Gates: the REST API plane is mounted and billing is enforced here. yield* Api; yield* Billing; const target = yield* Target; const response = yield* Effect.promise(() => fetch(new URL("/api/billing/customer", target.baseUrl), { method: "POST", headers: { "content-type": "application/json" }, body: "{}", }), ); // The regression returned the SPA fallback (200 text/html). The real // billing route is reached and rejects the unauthenticated call itself. expect(response.status, "the billing route rejects the anonymous call").toBe(401); expect(response.headers.get("content-type"), "with a JSON body").toContain("application/json"); const body = yield* Effect.promise(() => response.json()); expect(body, "the structured refusal a client can act on").toEqual({ error: "Unauthorized", code: "unauthorized", }); }), ); scenario( "Surfaces · every protected plane refuses anonymous callers with structured JSON", {}, Effect.gen(function* () { // Gate: the REST API plane is mounted on this target. yield* Api; const target = yield* Target; // The core protected API: anonymous callers hit the auth gate, which // answers in JSON (never the SPA) with the documented refusal body. const core = yield* Effect.promise(() => fetch(new URL("/api/integrations", target.baseUrl))); expect(core.status, "no credentials → no organization to act in").toBe(403); expect(core.headers.get("content-type"), "the gate answers in JSON").toContain( "application/json", ); expect(yield* Effect.promise(() => core.json()), "the documented refusal body").toEqual({ error: "No organization in session", code: "no_organization", }); // The session-auth plane is mounted and gated. const sessionMe = yield* Effect.promise(() => fetch(new URL("/api/auth/me", target.baseUrl))); expect(sessionMe.status, "the session API requires a session").toBe(401); // The org plane is mounted and gated. const orgDomains = yield* Effect.promise(() => fetch(new URL("/api/org/domains", target.baseUrl)), ); expect(orgDomains.status, "the org API requires a session").toBe(401); }), ); scenario( "Surfaces · unknown /api routes are a real 404, not the SPA fallback", {}, Effect.gen(function* () { // Gate: the REST API plane is mounted on this target. yield* Api; const target = yield* Target; const response = yield* Effect.promise(() => fetch(new URL("/api/this-route-does-not-exist", target.baseUrl)), ); expect(response.status, "the API plane owns its 404s").toBe(404); const body = yield* Effect.promise(() => response.text()); expect(body, "no SPA HTML leaks out of the API plane").not.toContain("