mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Signed-off-by: James M Snell <jasnell@gmail.com> Assisted-by: Opencode/Opus PR-URL: https://github.com/nodejs/node/pull/64464 Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
139 lines
3.8 KiB
JavaScript
139 lines
3.8 KiB
JavaScript
// Flags: --experimental-stream-iter
|
|
'use strict';
|
|
|
|
// Coverage tests for broadcast.js: signal abort on pending write,
|
|
// sync iterable from, ringbuffer grow.
|
|
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
const {
|
|
broadcast,
|
|
Broadcast,
|
|
text,
|
|
} = require('stream/iter');
|
|
|
|
// Signal abort on pending write (covers wireBroadcastWriteSignal + removeAt)
|
|
async function testBroadcastWriteAbort() {
|
|
const { writer, broadcast: bc } = broadcast({
|
|
budget: 16384,
|
|
backpressure: 'unbounded',
|
|
});
|
|
const consumer = bc.push();
|
|
|
|
// Fill the buffer to capacity
|
|
writer.writeSync(new Uint8Array(16384).fill(1));
|
|
|
|
// Next write will block — pass a signal
|
|
const ac = new AbortController();
|
|
const writePromise = writer.write(new Uint8Array(16384).fill(2),
|
|
{ signal: ac.signal });
|
|
|
|
// Abort the signal
|
|
ac.abort();
|
|
|
|
await assert.rejects(writePromise, { name: 'AbortError' });
|
|
|
|
// Clean up
|
|
writer.endSync();
|
|
// Drain the consumer
|
|
const result = [];
|
|
for await (const batch of consumer) {
|
|
result.push(...batch);
|
|
}
|
|
assert.ok(result.length >= 1);
|
|
}
|
|
|
|
// Broadcast.from with sync iterable (generator)
|
|
async function testBroadcastFromSyncIterable() {
|
|
function* source() {
|
|
yield [new Uint8Array([10, 20])];
|
|
yield [new Uint8Array([30, 40])];
|
|
}
|
|
|
|
const { broadcast: bc } = Broadcast.from(source());
|
|
const consumer = bc.push();
|
|
// Just verify it completes without error and produces data
|
|
let count = 0;
|
|
for await (const batch of consumer) {
|
|
count += batch.length;
|
|
}
|
|
assert.ok(count > 0);
|
|
}
|
|
|
|
// Broadcast.from with sync iterable — string chunks
|
|
async function testBroadcastFromSyncIterableStrings() {
|
|
function* source() {
|
|
yield 'hello';
|
|
yield ' world';
|
|
}
|
|
const { broadcast: bc } = Broadcast.from(source());
|
|
const consumer = bc.push();
|
|
const result = await text(consumer);
|
|
assert.strictEqual(result, 'hello world');
|
|
}
|
|
|
|
// Ringbuffer grow — push > 16 items without consumer draining
|
|
async function testRingbufferGrow() {
|
|
const { writer, broadcast: bc } = broadcast({ budget: 16384 });
|
|
const consumer = bc.push();
|
|
|
|
// Push 20 items (exceeds default ringbuffer capacity of 16)
|
|
for (let i = 0; i < 20; i++) {
|
|
writer.writeSync(new Uint8Array([i]));
|
|
}
|
|
writer.endSync();
|
|
|
|
// Read all items back and verify order
|
|
const items = [];
|
|
for await (const batch of consumer) {
|
|
for (const chunk of batch) {
|
|
items.push(chunk[0]);
|
|
}
|
|
}
|
|
assert.strictEqual(items.length, 20);
|
|
for (let i = 0; i < 20; i++) {
|
|
assert.strictEqual(items[i], i);
|
|
}
|
|
}
|
|
|
|
// Multiple consumers at the minimum cursor should trim only after the last
|
|
// one advances or detaches.
|
|
async function testFanOutMinCursorTrimming() {
|
|
const { writer, broadcast: bc } = broadcast({ budget: 16384 });
|
|
const iter1 = bc.push()[Symbol.asyncIterator]();
|
|
const iter2 = bc.push()[Symbol.asyncIterator]();
|
|
|
|
writer.writeSync(new Uint8Array([1]));
|
|
writer.writeSync(new Uint8Array([2]));
|
|
|
|
assert.strictEqual((await iter1.next()).done, false);
|
|
|
|
assert.strictEqual((await iter2.next()).done, false);
|
|
|
|
await iter1.return();
|
|
|
|
assert.strictEqual((await iter2.next()).done, false);
|
|
|
|
writer.endSync();
|
|
assert.strictEqual((await iter2.next()).done, true);
|
|
}
|
|
|
|
// Broadcast drainableProtocol after close returns null
|
|
async function testDrainableAfterClose() {
|
|
const { drainableProtocol } = require('stream/iter');
|
|
const { writer } = broadcast();
|
|
writer.endSync();
|
|
const result = writer[drainableProtocol]();
|
|
// After close, desired should be null
|
|
assert.strictEqual(result, null);
|
|
}
|
|
|
|
Promise.all([
|
|
testBroadcastWriteAbort(),
|
|
testBroadcastFromSyncIterable(),
|
|
testBroadcastFromSyncIterableStrings(),
|
|
testRingbufferGrow(),
|
|
testFanOutMinCursorTrimming(),
|
|
testDrainableAfterClose(),
|
|
]).then(common.mustCall());
|