import { describe, expect, it, vi } from "vitest"; import { declaredContentLength, decodeJsonBytes, isEffectivelyEmpty, isJsonMediaType, probeForbiddenBody, readBoundedBytes, } from "../../src/adapters/http/bounded-body-reader.ts"; function responseWithBody( body: ReadableStream | null, contentLength?: string, ): Response { return new Response(body, { headers: contentLength === undefined ? undefined : { "content-length": contentLength }, }); } describe("bounded body reader", () => { it.each([ [null, false], ["", false], [" application/json ; charset=utf-8 ", true], ["APPLICATION/PROBLEM+JSON", true], ["text/json", false], ["application/jsonp", false], ] as const)("classifies JSON media type %j", (header, expected) => { expect(isJsonMediaType(header)).toBe(expected); }); it.each([ [undefined, null], ["0", 0], ["12", 12], ["-1", null], ["NaN", null], ["Infinity", null], ] as const)("parses declared content length %s", (header, expected) => { expect(declaredContentLength(responseWithBody(null, header))).toBe(expected); }); it("rejects an oversized declared body and tolerates cancellation failure", async () => { const cancel = vi.fn(async () => { throw new Error("already settled"); }); const response = { headers: new Headers({ "content-length": "9" }), body: { cancel }, } as unknown as Response; await expect(readBoundedBytes(response, 8)).resolves.toEqual({ ok: false, code: "RESPONSE_TOO_LARGE", }); expect(cancel).toHaveBeenCalledOnce(); }); it("returns empty bytes when a successful response has no body", async () => { const result = await readBoundedBytes(responseWithBody(null), 8); expect(result).toEqual({ ok: true, bytes: new Uint8Array(0) }); }); it("joins chunks without retaining empty chunks", async () => { const body = new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(0)); controller.enqueue(Uint8Array.of(1, 2)); controller.enqueue(Uint8Array.of(3)); controller.close(); }, }); await expect(readBoundedBytes(responseWithBody(body), 3)).resolves.toEqual({ ok: true, bytes: Uint8Array.of(1, 2, 3), }); }); it("cancels streaming input as soon as the accumulated limit is exceeded", async () => { const cancel = vi.fn().mockRejectedValue(new Error("cancel failed")); const reader = { read: vi .fn() .mockResolvedValueOnce({ done: false, value: Uint8Array.of(1, 2) }) .mockResolvedValueOnce({ done: false, value: Uint8Array.of(3, 4) }), cancel, releaseLock: vi.fn(), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; await expect(readBoundedBytes(response, 3)).resolves.toEqual({ ok: false, code: "RESPONSE_TOO_LARGE", }); expect(cancel).toHaveBeenCalledOnce(); expect(reader.releaseLock).toHaveBeenCalledOnce(); }); it("maps reader failure and cancellation failure to a stream failure", async () => { const reader = { read: vi.fn().mockRejectedValue(new Error("stream failed")), cancel: vi.fn().mockRejectedValue(new Error("cancel failed")), releaseLock: vi.fn(() => { throw new Error("already released"); }), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; await expect(readBoundedBytes(response, 3)).resolves.toEqual({ ok: false, code: "RESPONSE_STREAM_FAILURE", }); }); it("abandons and cancels a bounded read when its lifetime aborts", async () => { const controller = new AbortController(); const reader = { read: vi.fn(() => new Promise(() => {})), cancel: vi.fn().mockRejectedValue(new Error("abandoned cancel failed")), releaseLock: vi.fn(), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; const outcome = readBoundedBytes(response, 3, controller.signal); controller.abort(); await expect(outcome).resolves.toEqual({ ok: false, code: "RESPONSE_STREAM_FAILURE", }); expect(reader.cancel).toHaveBeenCalledOnce(); expect(reader.releaseLock).toHaveBeenCalledOnce(); }); it("detects a forbidden body from declared length without reading it", async () => { const cancel = vi.fn(); const response = { headers: new Headers({ "content-length": "1" }), body: { cancel }, } as unknown as Response; await expect(probeForbiddenBody(response)).resolves.toEqual({ ok: true, present: true, }); expect(cancel).toHaveBeenCalledOnce(); }); it("accepts an absent, completed, or zero-byte forbidden body", async () => { await expect(probeForbiddenBody(responseWithBody(null))).resolves.toEqual({ ok: true, present: false, }); for (const next of [ { done: true, value: undefined }, { done: false, value: new Uint8Array(0) }, ]) { const reader = { read: vi.fn().mockResolvedValue(next), cancel: vi.fn(), releaseLock: vi.fn(), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; await expect(probeForbiddenBody(response)).resolves.toEqual({ ok: true, present: false, }); expect(reader.cancel).not.toHaveBeenCalled(); } }); it("probes only one present byte and cancels the remaining body", async () => { const reader = { read: vi.fn().mockResolvedValue({ done: false, value: Uint8Array.of(1) }), cancel: vi.fn().mockRejectedValue(new Error("cancel failed")), releaseLock: vi.fn(), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; await expect(probeForbiddenBody(response)).resolves.toEqual({ ok: true, present: true, }); expect(reader.read).toHaveBeenCalledOnce(); expect(reader.cancel).toHaveBeenCalledOnce(); }); it("maps a forbidden-body probe error even when cleanup also fails", async () => { const reader = { read: vi.fn().mockRejectedValue(new Error("probe failed")), cancel: vi.fn().mockRejectedValue(new Error("cancel failed")), releaseLock: vi.fn(() => { throw new Error("released"); }), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; await expect(probeForbiddenBody(response)).resolves.toEqual({ ok: false, code: "RESPONSE_STREAM_FAILURE", }); }); it("abandons and cancels a forbidden-body probe when its lifetime aborts", async () => { const controller = new AbortController(); const reader = { read: vi.fn(() => new Promise(() => {})), cancel: vi.fn().mockRejectedValue(new Error("abandoned cancel failed")), releaseLock: vi.fn(), }; const response = { headers: new Headers(), body: { getReader: () => reader }, } as unknown as Response; const outcome = probeForbiddenBody(response, controller.signal); controller.abort(); await expect(outcome).resolves.toEqual({ ok: false, code: "RESPONSE_STREAM_FAILURE", }); expect(reader.cancel).toHaveBeenCalledOnce(); expect(reader.releaseLock).toHaveBeenCalledOnce(); }); it("decodes valid JSON and distinguishes UTF-8 from JSON failures", () => { expect(decodeJsonBytes(new TextEncoder().encode('{"ok":true}'))).toEqual({ ok: true, value: { ok: true }, }); expect(decodeJsonBytes(Uint8Array.of(0xc3, 0x28))).toEqual({ ok: false, code: "UTF8_INVALID", }); expect(decodeJsonBytes(new TextEncoder().encode("{"))).toEqual({ ok: false, code: "JSON_INVALID", }); }); it.each([ [new Uint8Array(0), true], [Uint8Array.of(0x20, 0x09, 0x0a, 0x0d), true], [Uint8Array.of(0x20, 0x00), false], ])("classifies effective emptiness %#", (bytes, expected) => { expect(isEffectivelyEmpty(bytes)).toBe(expected); }); });