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.js"; let attempts = 0; const server = setupServer( http.get("https://api.test/api/sample/resources", () => { 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 () => {}, }; describe("shared HTTP client", () => { it("retries a safe request at most twice and returns validated data", async () => { const client = createHttpClient({ baseUrl: "https://api.test", clock, random: () => 0, }); await expect( client.execute("LIST_SAMPLE_RESOURCES", { routeId: "SAMPLE_RESOURCE_LIST" }), ).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/sample/resources", () => new HttpResponse("raw body", { status: 502 }), ), ); const client = createHttpClient({ baseUrl: "https://api.test", clock }); const result = await client.execute("LIST_SAMPLE_RESOURCES"); 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/sample/resources", () => new HttpResponse("{", { headers: { "Content-Type": "application/json" }, }), ), ); const client = createHttpClient({ baseUrl: "https://api.test", clock }); await expect(client.execute("LIST_SAMPLE_RESOURCES")).resolves.toMatchObject({ ok: false, error: { kind: "MALFORMED_JSON" }, }); server.use( http.get("https://api.test/api/sample/resources", () => HttpResponse.json({ success: true, data: [{ id: "resource-1", name: 42 }], meta: { requestId: "request-1", traceId: "trace-1" }, }), ), ); await expect(client.execute("LIST_SAMPLE_RESOURCES")).resolves.toMatchObject({ ok: false, error: { kind: "SCHEMA_MISMATCH" }, }); }); it("guards mapper exceptions as UNKNOWN_FAILURE", async () => { server.use( http.get("https://api.test/api/sample/resources", () => HttpResponse.json({ success: true, data: [{ id: "resource-1", name: "Example" }], meta: { requestId: "request-1", traceId: "trace-1" }, }), ), ); const client = createHttpClient({ baseUrl: "https://api.test", clock, mapPayload: () => { throw new Error("raw mapper detail"); }, }); const result = await client.execute("LIST_SAMPLE_RESOURCES"); expect(result).toMatchObject({ ok: false, error: { kind: "UNKNOWN_FAILURE" }, }); expect(JSON.stringify(result)).not.toContain("raw mapper detail"); }); });