From 7e2f425c492ceb268e5a3d81f95ca05d07f10fde Mon Sep 17 00:00:00 2001 From: solodmd Date: Mon, 29 Jun 2026 13:12:53 +0800 Subject: [PATCH] test(provider-usage): cover bounded shared response reads --- src/infra/provider-usage.fetch.shared.test.ts | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/infra/provider-usage.fetch.shared.test.ts b/src/infra/provider-usage.fetch.shared.test.ts index 939c709f4c69..dd3065762b56 100644 --- a/src/infra/provider-usage.fetch.shared.test.ts +++ b/src/infra/provider-usage.fetch.shared.test.ts @@ -8,6 +8,7 @@ import { discardUsageResponseBody, fetchJson, parseFiniteNumber, + readUsageJson, } from "./provider-usage.fetch.shared.js"; function requireFetchCall( @@ -148,4 +149,88 @@ describe("provider usage fetch shared helpers", () => { expect(snapshot.error).toBe("HTTP 429"); }); + + describe("readUsageJson", () => { + it("parses a normal-sized JSON response", async () => { + const response = new Response( + JSON.stringify({ windows: [{ label: "5h", usedPercent: 42 }] }), + { status: 200, headers: { "Content-Type": "application/json" } }, + ); + + await expect(readUsageJson("anthropic", response)).resolves.toEqual({ + ok: true, + data: { windows: [{ label: "5h", usedPercent: 42 }] }, + }); + }); + + it("parses UTF-8 BOM-prefixed JSON with fetch-compatible semantics", async () => { + const bom = new Uint8Array([0xef, 0xbb, 0xbf]); + const json = new TextEncoder().encode(JSON.stringify({ windows: [] })); + const combined = new Uint8Array(bom.length + json.length); + combined.set(bom); + combined.set(json, bom.length); + const response = new Response(combined, { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + + await expect(readUsageJson("anthropic", response)).resolves.toEqual({ + ok: true, + data: { windows: [] }, + }); + }); + + it("rejects an oversized JSON response and cancels the stream", async () => { + let pullCount = 0; + const cancel = vi.fn(async () => undefined); + const oversizedStream = new ReadableStream({ + pull(controller) { + pullCount += 1; + controller.enqueue(new Uint8Array(pullCount === 1 ? 16 * 1024 * 1024 + 1 : 1)); + }, + cancel, + }); + const response = new Response(oversizedStream, { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + + await expect(readUsageJson("anthropic", response)).resolves.toEqual({ + ok: false, + snapshot: expect.objectContaining({ + provider: "anthropic", + error: "Malformed usage response", + }), + }); + expect(pullCount).toBeLessThanOrEqual(2); + expect(cancel).toHaveBeenCalledOnce(); + }); + + it("handles a JSON parse error gracefully", async () => { + const response = new Response("not-json", { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + + await expect(readUsageJson("openai", response)).resolves.toEqual({ + ok: false, + snapshot: expect.objectContaining({ + provider: "openai", + error: "Malformed usage response", + }), + }); + }); + + it("preserves provider name in malformed error snapshots", async () => { + const response = new Response("", { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + + await expect(readUsageJson("deepseek", response)).resolves.toEqual({ + ok: false, + snapshot: expect.objectContaining({ provider: "deepseek" }), + }); + }); + }); });