import assert from "node:assert/strict"; import { test } from "vitest"; import { STUDIO_ERROR_CODES, toStudioGatewayError, } from "../../../src/features/tech-log/adapters/http/studio-error-mapping.ts"; import { createCsrfTokenProvider } from "../../../src/features/tech-log/adapters/http/studio-session-csrf.ts"; import { isStudioGatewayError } from "../../../src/features/tech-log/application/ports/studio-gateway-error.ts"; test("covers every canonical error code exactly once", () => { assert.equal(STUDIO_ERROR_CODES.length, 23); assert.equal(new Set(STUDIO_ERROR_CODES).size, 23); for (const code of ["IDEMPOTENCY_KEY_REUSED", "WARNING_ACKNOWLEDGEMENT_REQUIRED", "ASSET_QUARANTINED"]) { assert.ok(STUDIO_ERROR_CODES.includes(code as never), `${code} is missing`); } }); test("maps a PROBLEM outcome onto the port error, preserving the code", () => { const error = toStudioGatewayError( { kind: "PROBLEM", problem: { type: "https://techlog.local/problems/version-conflict", title: "VERSION_CONFLICT", status: 409, detail: "Expected 3; current 4.", code: "VERSION_CONFLICT", }, metadata: { status: 409 }, effect: "NOT_APPLIED", } as never, "saveStudioDocument", ); assert.ok(isStudioGatewayError(error)); assert.equal(error.code, "VERSION_CONFLICT"); assert.equal(error.status, 409); }); test("maps a transport failure onto STUDIO_UNAVAILABLE without inventing a domain code", () => { const error = toStudioGatewayError( { kind: "TRANSPORT_FAILURE", failure: { kind: "TIMEOUT" }, effect: "MAYBE_APPLIED" } as never, "getStudioDashboard", ); assert.equal(error.code, "STUDIO_UNAVAILABLE"); assert.equal(error.retryable, true); }); test("maps UNAUTHENTICATED onto AUTHENTICATION_REQUIRED", () => { const error = toStudioGatewayError( { kind: "UNAUTHENTICATED", effect: "NOT_APPLIED" } as never, "getStudioDashboard", ); assert.equal(error.code, "AUTHENTICATION_REQUIRED"); assert.equal(error.status, 401); }); test("falls back to STUDIO_UNAVAILABLE and drops an uncontracted problem code", () => { const error = toStudioGatewayError( { kind: "PROBLEM", problem: { type: "https://techlog.local/problems/bogus", title: "BOGUS", status: 418, detail: "server sent a code outside the contract", code: "TOTALLY_MADE_UP_CODE", }, metadata: { status: 418 }, effect: "NOT_APPLIED", } as never, "saveStudioDocument", ); assert.equal(error.code, "STUDIO_UNAVAILABLE"); assert.equal(error.status, 418); // The unrecognised code must not survive into the resulting error anywhere, // not just be absent from `.code` — otherwise a caller reading `.problem` // could still observe it. assert.ok( !JSON.stringify(error.problem).includes("TOTALLY_MADE_UP_CODE"), "the bogus code leaked into the synthesized problem", ); }); test("maps FORBIDDEN onto STUDIO_ACCESS_DENIED", () => { const error = toStudioGatewayError( { kind: "FORBIDDEN", effect: "NOT_APPLIED" } as never, "getStudioDashboard", ); assert.equal(error.code, "STUDIO_ACCESS_DENIED"); assert.equal(error.status, 403); assert.equal(error.retryable, false); }); test("maps RATE_LIMITED onto a retryable STUDIO_UNAVAILABLE", () => { const error = toStudioGatewayError( { kind: "RATE_LIMITED", effect: "NOT_APPLIED" } as never, "getStudioDashboard", ); assert.equal(error.code, "STUDIO_UNAVAILABLE"); assert.equal(error.status, 429); assert.equal(error.retryable, true); }); test("maps CANCELLED onto a non-retryable STUDIO_UNAVAILABLE", () => { const error = toStudioGatewayError( { kind: "CANCELLED", effect: "NOT_STARTED" } as never, "getStudioDashboard", ); assert.equal(error.code, "STUDIO_UNAVAILABLE"); assert.equal(error.status, 499); assert.equal(error.retryable, false); }); test("maps CONTRACT_VIOLATION and AUTH_INTEGRATION_FAILURE onto the same non-retryable STUDIO_UNAVAILABLE", () => { const contractViolation = toStudioGatewayError( { kind: "CONTRACT_VIOLATION", violation: { kind: "UNEXPECTED_STATUS", operation: "RESPONSE" }, effect: "NOT_APPLICABLE", } as never, "getStudioDashboard", ); const authIntegrationFailure = toStudioGatewayError( { kind: "AUTH_INTEGRATION_FAILURE", reason: "UNKNOWN_AUTH_PROFILE", effect: "NOT_APPLICABLE" } as never, "getStudioDashboard", ); for (const error of [contractViolation, authIntegrationFailure]) { assert.equal(error.code, "STUDIO_UNAVAILABLE"); assert.equal(error.status, 502); assert.equal(error.retryable, false); } }); test("throws for a SUCCESS outcome instead of returning a fabricated error", () => { assert.throws( () => toStudioGatewayError( { kind: "SUCCESS", value: undefined, metadata: { status: 200 }, effect: "NOT_APPLICABLE" } as never, "getStudioDashboard", ), /success outcome is not an error/, ); }); test("fetches the CSRF token once and reuses it until invalidated", async () => { let calls = 0; const provider = createCsrfTokenProvider({ async execute() { calls += 1; return { csrfToken: `token-${calls}`, csrfHeaderName: "X-CSRF-TOKEN" }; }, }); assert.equal(await provider.token(), "token-1"); assert.equal(await provider.token(), "token-1"); assert.equal(calls, 1); provider.invalidate(); assert.equal(await provider.token(), "token-2"); assert.equal(calls, 2); }); test("does not stampede concurrent CSRF requests", async () => { let calls = 0; const provider = createCsrfTokenProvider({ async execute() { calls += 1; await Promise.resolve(); return { csrfToken: "token", csrfHeaderName: "X-CSRF-TOKEN" }; }, }); await Promise.all([provider.token(), provider.token(), provider.token()]); assert.equal(calls, 1); }); test("resolves the header name from the same cached snapshot as the token", async () => { let calls = 0; const provider = createCsrfTokenProvider({ async execute() { calls += 1; return { csrfToken: "token", csrfHeaderName: "X-CSRF-TOKEN" }; }, }); assert.equal(await provider.headerName(), "X-CSRF-TOKEN"); assert.equal(await provider.token(), "token"); assert.equal(calls, 1); }); test("does not cache a rejected CSRF fetch and retries cleanly on the next call", async () => { let calls = 0; const provider = createCsrfTokenProvider({ async execute() { calls += 1; if (calls === 1) throw new Error("network failure"); return { csrfToken: "token", csrfHeaderName: "X-CSRF-TOKEN" }; }, }); await assert.rejects(provider.token()); assert.equal(calls, 1); assert.equal(await provider.token(), "token"); assert.equal(calls, 2); });