node/test/parallel/test-stream-duplex-destroy.js
Benjamin Gruenbaum 75fe4f35d4
stream: support dispose in writable
Add support to Symbol.asyncDispose in writable streams.
Additionally add a test for writable, transform and duplex streams
who inherit from readable/writable to avoid breakage.

Co-authored-by: Robert Nagy <ronagy@icloud.com>
Co-authored-by: atlowChemi <chemi@atlow.co.il>
PR-URL: https://github.com/nodejs/node/pull/48547
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2024-06-15 23:41:59 +00:00

287 lines
6.1 KiB
JavaScript

'use strict';
const common = require('../common');
const { Duplex } = require('stream');
const assert = require('assert');
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {}
});
duplex.resume();
duplex.on('end', common.mustNotCall());
duplex.on('finish', common.mustNotCall());
duplex.on('close', common.mustCall());
duplex.destroy();
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {}
});
duplex.resume();
const expected = new Error('kaboom');
duplex.on('end', common.mustNotCall());
duplex.on('finish', common.mustNotCall());
duplex.on('error', common.mustCall((err) => {
assert.strictEqual(err, expected);
}));
duplex.destroy(expected);
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {}
});
duplex._destroy = common.mustCall(function(err, cb) {
assert.strictEqual(err, expected);
cb(err);
});
const expected = new Error('kaboom');
duplex.on('finish', common.mustNotCall('no finish event'));
duplex.on('error', common.mustCall((err) => {
assert.strictEqual(err, expected);
}));
duplex.destroy(expected);
assert.strictEqual(duplex.destroyed, true);
}
{
const expected = new Error('kaboom');
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {},
destroy: common.mustCall(function(err, cb) {
assert.strictEqual(err, expected);
cb();
})
});
duplex.resume();
duplex.on('end', common.mustNotCall('no end event'));
duplex.on('finish', common.mustNotCall('no finish event'));
// Error is swallowed by the custom _destroy
duplex.on('error', common.mustNotCall('no error event'));
duplex.on('close', common.mustCall());
duplex.destroy(expected);
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {}
});
duplex._destroy = common.mustCall(function(err, cb) {
assert.strictEqual(err, null);
cb();
});
duplex.destroy();
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {}
});
duplex.resume();
duplex._destroy = common.mustCall(function(err, cb) {
assert.strictEqual(err, null);
process.nextTick(() => {
this.push(null);
this.end();
cb();
});
});
const fail = common.mustNotCall('no finish or end event');
duplex.on('finish', fail);
duplex.on('end', fail);
duplex.destroy();
duplex.removeListener('end', fail);
duplex.removeListener('finish', fail);
duplex.on('end', common.mustNotCall());
duplex.on('finish', common.mustNotCall());
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {}
});
const expected = new Error('kaboom');
duplex._destroy = common.mustCall(function(err, cb) {
assert.strictEqual(err, null);
cb(expected);
});
duplex.on('finish', common.mustNotCall('no finish event'));
duplex.on('end', common.mustNotCall('no end event'));
duplex.on('error', common.mustCall((err) => {
assert.strictEqual(err, expected);
}));
duplex.destroy();
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {},
allowHalfOpen: true
});
duplex.resume();
duplex.on('finish', common.mustNotCall());
duplex.on('end', common.mustNotCall());
duplex.destroy();
assert.strictEqual(duplex.destroyed, true);
}
{
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {},
});
duplex.destroyed = true;
assert.strictEqual(duplex.destroyed, true);
// The internal destroy() mechanism should not be triggered
duplex.on('finish', common.mustNotCall());
duplex.on('end', common.mustNotCall());
duplex.destroy();
}
{
function MyDuplex() {
assert.strictEqual(this.destroyed, false);
this.destroyed = false;
Duplex.call(this);
}
Object.setPrototypeOf(MyDuplex.prototype, Duplex.prototype);
Object.setPrototypeOf(MyDuplex, Duplex);
new MyDuplex();
}
{
const duplex = new Duplex({
writable: false,
autoDestroy: true,
write(chunk, enc, cb) { cb(); },
read() {},
});
duplex.push(null);
duplex.resume();
duplex.on('close', common.mustCall());
}
{
const duplex = new Duplex({
readable: false,
autoDestroy: true,
write(chunk, enc, cb) { cb(); },
read() {},
});
duplex.end();
duplex.on('close', common.mustCall());
}
{
const duplex = new Duplex({
allowHalfOpen: false,
autoDestroy: true,
write(chunk, enc, cb) { cb(); },
read() {},
});
duplex.push(null);
duplex.resume();
const orgEnd = duplex.end;
duplex.end = common.mustNotCall();
duplex.on('end', () => {
// Ensure end() is called in next tick to allow
// any pending writes to be invoked first.
process.nextTick(() => {
duplex.end = common.mustCall(orgEnd);
});
});
duplex.on('close', common.mustCall());
}
{
// Check abort signal
const controller = new AbortController();
const { signal } = controller;
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {},
signal,
});
let count = 0;
duplex.on('error', common.mustCall((e) => {
assert.strictEqual(count++, 0); // Ensure not called twice
assert.strictEqual(e.name, 'AbortError');
}));
duplex.on('close', common.mustCall());
controller.abort();
}
{
const duplex = new Duplex({
read() {},
write(chunk, enc, cb) { cb(); }
});
duplex.cork();
duplex.write('foo', common.mustCall((err) => {
assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
}));
duplex.destroy();
}
{
// Check Symbol.asyncDispose
const duplex = new Duplex({
write(chunk, enc, cb) { cb(); },
read() {},
});
let count = 0;
duplex.on('error', common.mustCall((e) => {
assert.strictEqual(count++, 0); // Ensure not called twice
assert.strictEqual(e.name, 'AbortError');
}));
duplex.on('close', common.mustCall());
duplex[Symbol.asyncDispose]().then(common.mustCall());
}