import { HttpResponse, http } from "msw"; import { setupServer } from "msw/node"; import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { createHttpClient } from "../../src/adapters/http/client.ts"; import { TEST_HTTP_CONTRACT } from "../helpers/http-contract-fixture.ts"; let attempts = 0; const server = setupServer( http.get("https://api.test/api/entities", () => { attempts += 1; if (attempts < 3) { return HttpResponse.json( { success: false, error: { code: "TEMPORARY" } }, { status: 503 }, ); } return HttpResponse.json({ success: true, data: [{ id: "resource-1", name: "Example" }], meta: { requestId: "request-1", traceId: "trace-1" }, }); }), ); beforeAll(() => server.listen({ onUnhandledRequest: "error" })); afterEach(() => { attempts = 0; server.resetHandlers(); }); afterAll(() => server.close()); const clock = { now: () => 0, sleep: async () => {}, }; type HttpDependencies = Parameters[0]; function testClient(options: HttpDependencies) { return createHttpClient({ ...TEST_HTTP_CONTRACT, ...options }); } describe("shared HTTP client", () => { it("retries a safe request at most twice and returns validated data", async () => { const client = testClient({ baseUrl: "https://api.test", clock, random: () => 0, }); await expect( client.execute("LIST_ENTITIES", { routeId: "TEST_ROUTE" }), ).resolves.toMatchObject({ ok: true, value: [{ id: "resource-1", displayName: "Example" }], meta: { requestId: "request-1" }, }); expect(attempts).toBe(3); }); it("rejects a non-JSON response without exposing its body", async () => { server.use( http.get( "https://api.test/api/entities", () => new HttpResponse("raw body", { status: 502 }), ), ); const client = testClient({ baseUrl: "https://api.test", clock }); const result = await client.execute("LIST_ENTITIES"); expect(result).toMatchObject({ ok: false, error: { kind: "CONTENT_TYPE_MISMATCH" }, }); expect(JSON.stringify(result)).not.toContain("raw body"); }); it("classifies malformed JSON and invalid payloads at the boundary", async () => { server.use( http.get( "https://api.test/api/entities", () => new HttpResponse("{", { headers: { "Content-Type": "application/json" }, }), ), ); const client = testClient({ baseUrl: "https://api.test", clock }); await expect(client.execute("LIST_ENTITIES")).resolves.toMatchObject({ ok: false, error: { kind: "MALFORMED_JSON" }, }); server.use( http.get("https://api.test/api/entities", () => HttpResponse.json({ success: true, data: [{ id: "resource-1", name: 42 }], meta: { requestId: "request-1", traceId: "trace-1" }, }), ), ); await expect(client.execute("LIST_ENTITIES")).resolves.toMatchObject({ ok: false, error: { kind: "SCHEMA_MISMATCH" }, }); }); it("projects only approved 422 issue path and code metadata", async () => { server.use( http.post("https://api.test/api/entities", () => HttpResponse.json( { success: false, error: { code: "INVALID_INPUT", message: "raw backend secret", details: { issues: [ { path: "name", code: "REQUIRED", message: "raw field copy" }, { path: 42, code: "INVALID" }, ], }, }, meta: { requestId: "request-422", traceId: "trace-422" }, }, { status: 422 }, ), ), ); const client = testClient({ baseUrl: "https://api.test", clock }); const result = await client.execute("CREATE_ENTITY", { routeId: "TEST_FORM", body: { name: "Valid" }, }); expect(result).toMatchObject({ ok: false, error: { kind: "VALIDATION_REJECTED", validationIssues: [{ path: "name", code: "REQUIRED" }], }, }); expect(JSON.stringify(result)).not.toContain("raw backend secret"); expect(JSON.stringify(result)).not.toContain("raw field copy"); }); it("guards mapper exceptions as a mapping contract violation", async () => { server.use( http.get("https://api.test/api/entities", () => HttpResponse.json({ success: true, data: [{ id: "resource-1", name: "Example" }], meta: { requestId: "request-1", traceId: "trace-1" }, }), ), ); const client = testClient({ baseUrl: "https://api.test", clock, mapPayload: () => { throw new Error("raw mapper detail"); }, }); const result = await client.execute("LIST_ENTITIES"); expect(result).toMatchObject({ ok: false, error: { kind: "MAPPING_CONTRACT_VIOLATION" }, }); expect(JSON.stringify(result)).not.toContain("raw mapper detail"); }); });