import { describe, expect, it, vi } from "vitest"; import { createRuntimeAdapters } from "../../src/bootstrap/runtime-adapters.ts"; import { TECH_LOG_FEATURE_ID } from "../../src/features/tech-log/application/tech-log-feature-input.ts"; type Runtime = Parameters[0]["runtime"]; type Release = Parameters[0]["release"]; /** * The public site is read by people who are not signed in — that is the whole * point of it — and the composition root is the only place that decides whether * an operation is allowed to leave the browser. * * Two defects met there and took the entire public surface down in production. * `attachCredentials` asked the Studio credential helper first, which returns * null for a profile it does not own; the fallback below it read the session * and refused anything not authenticated, so every ANONYMOUS operation resolved * to UNAUTHENTICATED with no request ever dispatched. And the public gateway * detected "not found" by reading `status`/`code` off the problem body, fields * the ADR-006 envelope does not have — so a 404 raised the terminal-error * surface instead of the page's own not-found state. * * Neither was caught by the gateway tests (which stub the executor) or the * screen tests (which stub the gateway). This is the seam between them. */ const runtime = { config: { APP_ENV: "local", API_BASE_URL: "http://api.test", TELEMETRY_ENABLED: false, AUTH_MODE: "demo", REQUEST_TIMEOUT_MS: 5000, MAX_RETRY_ATTEMPTS: 0, RELEASE_MANIFEST_URL: "/release-manifest.json", CONFIG_SCHEMA_VERSION: "2.0", CAPABILITY_OVERRIDES: { REALTIME: "DEFAULT", WEB_WORKER: "DEFAULT", SERVICE_WORKER: "DEFAULT", OFFLINE_COMMANDS: "DEFAULT", }, FEATURE_OVERRIDES: {}, TECH_LOG_STUDIO_SOURCE: "HTTP", TECH_LOG_PUBLIC_SOURCE: "HTTP", }, configSchema: "V2", build: { buildId: "build-a", commitSha: "abc123", routerBasePath: "/", runtimeConfigUrl: "/config.json", }, validationDurationMs: 0, } as unknown as Runtime; const release = { releaseId: "release-a", manifestUrl: "/release-manifest.json", } as unknown as Release; // Copied verbatim from the deployed backend's answer for a slug that does not // exist. The code is PUBLIC_RESOURCE_NOT_FOUND, not NOT_FOUND, and there is no // top-level `status` — the two facts the old detection assumed away. const NOT_FOUND_BODY = JSON.stringify({ success: false, data: null, error: { code: "PUBLIC_RESOURCE_NOT_FOUND", category: "NOT_FOUND", message: "요청한 자료를 찾을 수 없습니다", retryable: false, details: null, }, meta: { requestId: "dd674218-a0c0-4a1b-a3d3-d0b8563783a2", traceId: "78b4fdd3dba6436281e9d4ac884ecd72", correlationId: "26c37920-7df6-4a06-afe1-61b6f1b094ae", page: null, }, }); describe("public reads by a signed-out visitor", () => { it("dispatches the request and reads a 404 envelope as absent", async () => { const fetcher = vi.fn( async () => new Response(NOT_FOUND_BODY, { status: 404, headers: { "content-type": "application/json" }, }), ); const adapters = await createRuntimeAdapters({ runtime, release, host: {}, fetcher: fetcher as unknown as typeof fetch, }); // The precondition that made the bug: nobody is signed in. expect(adapters.outputPorts.session.getState()).toBe("unauthenticated"); const { publicContent } = adapters.featureInputs[TECH_LOG_FEATURE_ID]; await expect(publicContent.getRecord("CASE", "missing-slug")).resolves.toBeUndefined(); // The load-bearing assertion. Before the fix this was 0: the operation was // refused as UNAUTHENTICATED inside the composition root. expect(fetcher).toHaveBeenCalled(); adapters.infrastructure.dispose(); }); });