File
Blob: src/workerd/api/tests/streams-no-auto-allocate-test.js
| 1 | // Copyright (c) 2026 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | // Test for streams_no_default_auto_allocate_chunk_size compat flag |
| 5 | import { strictEqual, ok, deepStrictEqual } from 'node:assert'; |
| 6 | |
| 7 | export const byobRequestIsNullWithoutAutoAllocate = { |
| 8 | async test() { |
| 9 | // When autoAllocateChunkSize is NOT set, byobRequest should be null |
| 10 | // for non-BYOB reads (spec-compliant behavior) |
| 11 | let byobRequestWasNull = false; |
| 12 | let pullCalled = false; |
| 13 | |
| 14 | const rs = new ReadableStream({ |
| 15 | type: 'bytes', |
| 16 | pull(controller) { |
| 17 | pullCalled = true; |
| 18 | byobRequestWasNull = controller.byobRequest === null; |
| 19 | // Must use enqueue() since byobRequest is null |
| 20 | controller.enqueue(new Uint8Array([1, 2, 3, 4])); |
| 21 | controller.close(); |
| 22 | }, |
| 23 | }); |
| 24 | |
| 25 | const reader = rs.getReader(); |
| 26 | const result = await reader.read(); |
| 27 | |
| 28 | ok(pullCalled, 'pull should have been called'); |
| 29 | ok( |
| 30 | byobRequestWasNull, |
| 31 | 'byobRequest should be null when autoAllocateChunkSize is not set' |
| 32 | ); |
| 33 | strictEqual(result.done, false); |
| 34 | deepStrictEqual([...result.value], [1, 2, 3, 4]); |
| 35 | }, |
| 36 | }; |
| 37 | |
| 38 | export const byobRequestAvailableWithAutoAllocate = { |
| 39 | async test() { |
| 40 | // When autoAllocateChunkSize IS set, byobRequest should be available |
| 41 | let byobRequestWasAvailable = false; |
| 42 | let pullCalled = false; |
| 43 | |
| 44 | const rs = new ReadableStream({ |
| 45 | type: 'bytes', |
| 46 | autoAllocateChunkSize: 1024, |
| 47 | pull(controller) { |
| 48 | pullCalled = true; |
| 49 | byobRequestWasAvailable = controller.byobRequest !== null; |
| 50 | if (controller.byobRequest) { |
| 51 | // Use the BYOB request |
| 52 | const view = controller.byobRequest.view; |
| 53 | new Uint8Array(view.buffer, view.byteOffset, 4).set([5, 6, 7, 8]); |
| 54 | controller.byobRequest.respond(4); |
| 55 | } else { |
| 56 | controller.enqueue(new Uint8Array([5, 6, 7, 8])); |
| 57 | } |
| 58 | controller.close(); |
| 59 | }, |
| 60 | }); |
| 61 | |
| 62 | const reader = rs.getReader(); |
| 63 | const result = await reader.read(); |
| 64 | |
| 65 | ok(pullCalled, 'pull should have been called'); |
| 66 | ok( |
| 67 | byobRequestWasAvailable, |
| 68 | 'byobRequest should be available when autoAllocateChunkSize is set' |
| 69 | ); |
| 70 | strictEqual(result.done, false); |
| 71 | deepStrictEqual([...result.value], [5, 6, 7, 8]); |
| 72 | }, |
| 73 | }; |
| 74 | |
| 75 | export const byobReadStillWorks = { |
| 76 | async test() { |
| 77 | // BYOB reads should still work regardless of autoAllocateChunkSize |
| 78 | let byobRequestWasAvailable = false; |
| 79 | |
| 80 | const rs = new ReadableStream({ |
| 81 | type: 'bytes', |
| 82 | // No autoAllocateChunkSize set |
| 83 | pull(controller) { |
| 84 | byobRequestWasAvailable = controller.byobRequest !== null; |
| 85 | if (controller.byobRequest) { |
| 86 | const view = controller.byobRequest.view; |
| 87 | new Uint8Array(view.buffer, view.byteOffset, 4).set([9, 10, 11, 12]); |
| 88 | controller.byobRequest.respond(4); |
| 89 | } |
| 90 | controller.close(); |
| 91 | }, |
| 92 | }); |
| 93 | |
| 94 | // Use BYOB reader |
| 95 | const reader = rs.getReader({ mode: 'byob' }); |
| 96 | const buffer = new Uint8Array(16); |
| 97 | const result = await reader.read(buffer); |
| 98 | |
| 99 | ok( |
| 100 | byobRequestWasAvailable, |
| 101 | 'byobRequest should be available for BYOB reads' |
| 102 | ); |
| 103 | strictEqual(result.done, false); |
| 104 | deepStrictEqual([...result.value], [9, 10, 11, 12]); |
| 105 | }, |
| 106 | }; |
| 107 | |
| 108 | export const multipleReadsWithEnqueue = { |
| 109 | async test() { |
| 110 | // Test that multiple reads work correctly with enqueue() |
| 111 | let pullCount = 0; |
| 112 | |
| 113 | const rs = new ReadableStream({ |
| 114 | type: 'bytes', |
| 115 | pull(controller) { |
| 116 | pullCount++; |
| 117 | if (pullCount <= 3) { |
| 118 | controller.enqueue(new Uint8Array([pullCount])); |
| 119 | } else { |
| 120 | controller.close(); |
| 121 | } |
| 122 | }, |
| 123 | }); |
| 124 | |
| 125 | const reader = rs.getReader(); |
| 126 | const chunks = []; |
| 127 | |
| 128 | while (true) { |
| 129 | const { value, done } = await reader.read(); |
| 130 | if (done) break; |
| 131 | chunks.push([...value]); |
| 132 | } |
| 133 | |
| 134 | strictEqual(chunks.length, 3); |
| 135 | deepStrictEqual(chunks[0], [1]); |
| 136 | deepStrictEqual(chunks[1], [2]); |
| 137 | deepStrictEqual(chunks[2], [3]); |
| 138 | }, |
| 139 | }; |
| 140 | |
| 141 | export const pipeToWorksWithoutAutoAllocate = { |
| 142 | async test() { |
| 143 | // Test that pipeTo works correctly without autoAllocateChunkSize |
| 144 | const allBytes = []; |
| 145 | |
| 146 | const rs = new ReadableStream({ |
| 147 | type: 'bytes', |
| 148 | start(controller) { |
| 149 | controller.enqueue(new Uint8Array([1, 2])); |
| 150 | controller.enqueue(new Uint8Array([3, 4])); |
| 151 | controller.close(); |
| 152 | }, |
| 153 | }); |
| 154 | |
| 155 | const ws = new WritableStream({ |
| 156 | write(chunk) { |
| 157 | allBytes.push(...chunk); |
| 158 | }, |
| 159 | }); |
| 160 | |
| 161 | await rs.pipeTo(ws); |
| 162 | |
| 163 | // Verify all bytes were received (chunks may be combined) |
| 164 | deepStrictEqual(allBytes, [1, 2, 3, 4]); |
| 165 | }, |
| 166 | }; |