Skip to content

Commit 99747ca

Browse files
committed
test(bounded-body): lock the explicit maxBytes budget and fragmented reassembly
The maxBytes option was mutation-surviving: deleting it and always using the 64 KiB default left the whole suite green, because the only oversize test used a 33 MiB body that exceeds both ceilings. Nothing proved that the one caller the option exists for — the non-streaming upstream JSON read, at a 32 MiB ceiling — can actually accept a response larger than an error body. Four cases now pin it: a body between the default and the custom cap succeeds, the exact cap succeeds, one byte past it fails closed with the prefix discarded, and a 20k-chunk fragmented body under the cap reassembles byte-exactly (the observable half of the geometric single-buffer accumulation). Driven red by re-ignoring the option.
1 parent 5d8acf9 commit 99747ca

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

tests/bounded-body.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,65 @@ describe("readBoundedResponseBody", () => {
105105
expect(cancelled).toBe(true);
106106
});
107107

108+
/**
109+
* The `maxBytes` option exists so one caller — the non-streaming upstream JSON
110+
* read in responses/core.ts — can accept a whole completion (32 MiB ceiling)
111+
* while the other eight callers keep the 64 KiB error-body default. Without
112+
* these three tests the option was mutation-surviving: ignoring `maxBytes`
113+
* entirely left the suite green, because the only oversize test used a body
114+
* that exceeds BOTH ceilings.
115+
*/
116+
describe("an explicit maxBytes budget", () => {
117+
const CUSTOM_CAP = BOUNDED_BODY_MAX_BYTES * 4;
118+
119+
test("accepts a body larger than the default but within the custom cap", async () => {
120+
const size = BOUNDED_BODY_MAX_BYTES * 2;
121+
const response = responseFromChunks(new Uint8Array(size).fill(0x61));
122+
123+
const result = await readBoundedResponseBody(response, { maxBytes: CUSTOM_CAP });
124+
125+
expect(result.text.length).toBe(size);
126+
expect(result.oversized).toBe(false);
127+
expect(result.truncated).toBe(false);
128+
expect(result.displaySafe).toBe(true);
129+
});
130+
131+
test("accepts exactly the custom cap", async () => {
132+
const response = responseFromChunks(new Uint8Array(CUSTOM_CAP).fill(0x61));
133+
134+
const result = await readBoundedResponseBody(response, { maxBytes: CUSTOM_CAP });
135+
136+
expect(result.text.length).toBe(CUSTOM_CAP);
137+
expect(result.oversized).toBe(false);
138+
});
139+
140+
test("rejects one byte past the custom cap and discards the prefix", async () => {
141+
const response = responseFromChunks(new Uint8Array(CUSTOM_CAP + 1).fill(0x61));
142+
143+
const result = await readBoundedResponseBody(response, { maxBytes: CUSTOM_CAP });
144+
145+
expect(result.text).toBe("");
146+
expect(result.oversized).toBe(true);
147+
expect(result.displaySafe).toBe(false);
148+
});
149+
150+
test("a highly fragmented body under the cap is reassembled exactly", async () => {
151+
// Guards the geometric single-buffer accumulation: the previous per-chunk
152+
// array retained one object per transport chunk, which a peer can inflate
153+
// far beyond the payload ceiling. Correctness here is the observable part —
154+
// 20k one-byte chunks must still decode to exactly their content.
155+
const chunkCount = 20_000;
156+
const chunks = Array.from({ length: chunkCount }, () => new Uint8Array([0x61]));
157+
const response = responseFromChunks(...chunks);
158+
159+
const result = await readBoundedResponseBody(response, { maxBytes: CUSTOM_CAP });
160+
161+
expect(result.text.length).toBe(chunkCount);
162+
expect(result.text).toBe("a".repeat(chunkCount));
163+
expect(result.oversized).toBe(false);
164+
});
165+
});
166+
108167
test("parent abort rejects with the exact reason object", async () => {
109168
const controller = new AbortController();
110169
const reason = { code: "parent-stopped" };

0 commit comments

Comments
 (0)