node/test/parallel/test-stream-iter-transform-coverage.js
James M Snell 2fa9c1bc58 test: add tests for experimental stream/iter implementation
Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-By: Claude/Opus 4.6
PR-URL: https://github.com/nodejs/node/pull/62066
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2026-03-27 19:55:17 -07:00

119 lines
3.6 KiB
JavaScript

// Flags: --experimental-stream-iter
'use strict';
// Coverage tests for transform.js: abort cleanup, strategy/params options,
// early consumer exit triggering finally block.
const common = require('../common');
const assert = require('assert');
const {
from,
pull,
bytes,
text,
} = require('stream/iter');
const {
compressGzip,
decompressGzip,
compressDeflate,
decompressDeflate,
compressBrotli,
decompressBrotli,
compressZstd,
decompressZstd,
} = require('zlib/iter');
const { constants } = require('zlib');
async function roundTrip(input, compress, decompress) {
return text(pull(from(input), compress, decompress));
}
// Abort mid-compression triggers finally cleanup
async function testAbortMidCompression() {
const ac = new AbortController();
const largeInput = 'x'.repeat(100_000);
const compressed = pull(from(largeInput), compressGzip(),
{ signal: ac.signal });
const iter = compressed[Symbol.asyncIterator]();
// Read one batch then abort
const first = await iter.next();
assert.strictEqual(first.done, false);
ac.abort();
await assert.rejects(iter.next(), { name: 'AbortError' });
}
// Early consumer exit (break from for-await) triggers finally
async function testEarlyConsumerExit() {
const largeInput = 'y'.repeat(100_000);
const compressed = pull(from(largeInput), compressGzip());
// eslint-disable-next-line no-unused-vars
for await (const batch of compressed) {
break; // Early exit — should trigger finally block cleanup
}
// If we get here without hanging or crashing, cleanup worked
}
// Gzip with explicit strategy option
async function testGzipWithStrategy() {
const input = 'strategy test data '.repeat(100);
const c = compressGzip({ strategy: constants.Z_DEFAULT_STRATEGY });
const result = await roundTrip(input, c, decompressGzip());
assert.strictEqual(result, input);
}
// Deflate with Z_FIXED strategy
async function testDeflateWithFixedStrategy() {
const input = 'fixed strategy '.repeat(100);
const c = compressDeflate({ strategy: constants.Z_FIXED });
const result = await roundTrip(input, c, decompressDeflate());
assert.strictEqual(result, input);
}
// Brotli with custom quality param
async function testBrotliWithParams() {
const input = 'brotli params test '.repeat(100);
const params = { [constants.BROTLI_PARAM_QUALITY]: 5 };
const result = await roundTrip(input, compressBrotli({ params }),
decompressBrotli());
assert.strictEqual(result, input);
}
// Zstd with custom compression level param
async function testZstdWithParams() {
const input = 'zstd params test '.repeat(100);
const params = { [constants.ZSTD_c_compressionLevel]: 10 };
const result = await roundTrip(input, compressZstd({ params }),
decompressZstd());
assert.strictEqual(result, input);
}
// Gzip with custom chunkSize
async function testGzipWithChunkSize() {
const input = 'chunk size test';
const c = compressGzip({ chunkSize: 256 });
const d = decompressGzip({ chunkSize: 256 });
const result = await roundTrip(input, c, d);
assert.strictEqual(result, input);
}
// Invalid chunkSize throws when transform is invoked
async function testInvalidChunkSize() {
const tx = compressGzip({ chunkSize: 8 });
await assert.rejects(
async () => await bytes(pull(from('data'), tx)),
{ code: 'ERR_OUT_OF_RANGE' },
);
}
Promise.all([
testAbortMidCompression(),
testEarlyConsumerExit(),
testGzipWithStrategy(),
testDeflateWithFixedStrategy(),
testBrotliWithParams(),
testZstdWithParams(),
testGzipWithChunkSize(),
testInvalidChunkSize(),
]).then(common.mustCall());