1'use strict'; 2 3const common = require('../common'); 4const { Duplex } = require('stream'); 5const assert = require('assert'); 6 7{ 8 const duplex = new Duplex({ 9 write(chunk, enc, cb) { cb(); }, 10 read() {} 11 }); 12 13 duplex.resume(); 14 15 duplex.on('end', common.mustNotCall()); 16 duplex.on('finish', common.mustNotCall()); 17 duplex.on('close', common.mustCall()); 18 19 duplex.destroy(); 20 assert.strictEqual(duplex.destroyed, true); 21} 22 23{ 24 const duplex = new Duplex({ 25 write(chunk, enc, cb) { cb(); }, 26 read() {} 27 }); 28 duplex.resume(); 29 30 const expected = new Error('kaboom'); 31 32 duplex.on('end', common.mustNotCall()); 33 duplex.on('finish', common.mustNotCall()); 34 duplex.on('error', common.mustCall((err) => { 35 assert.strictEqual(err, expected); 36 })); 37 38 duplex.destroy(expected); 39 assert.strictEqual(duplex.destroyed, true); 40} 41 42{ 43 const duplex = new Duplex({ 44 write(chunk, enc, cb) { cb(); }, 45 read() {} 46 }); 47 48 duplex._destroy = common.mustCall(function(err, cb) { 49 assert.strictEqual(err, expected); 50 cb(err); 51 }); 52 53 const expected = new Error('kaboom'); 54 55 duplex.on('finish', common.mustNotCall('no finish event')); 56 duplex.on('error', common.mustCall((err) => { 57 assert.strictEqual(err, expected); 58 })); 59 60 duplex.destroy(expected); 61 assert.strictEqual(duplex.destroyed, true); 62} 63 64{ 65 const expected = new Error('kaboom'); 66 const duplex = new Duplex({ 67 write(chunk, enc, cb) { cb(); }, 68 read() {}, 69 destroy: common.mustCall(function(err, cb) { 70 assert.strictEqual(err, expected); 71 cb(); 72 }) 73 }); 74 duplex.resume(); 75 76 duplex.on('end', common.mustNotCall('no end event')); 77 duplex.on('finish', common.mustNotCall('no finish event')); 78 79 // Error is swallowed by the custom _destroy 80 duplex.on('error', common.mustNotCall('no error event')); 81 duplex.on('close', common.mustCall()); 82 83 duplex.destroy(expected); 84 assert.strictEqual(duplex.destroyed, true); 85} 86 87{ 88 const duplex = new Duplex({ 89 write(chunk, enc, cb) { cb(); }, 90 read() {} 91 }); 92 93 duplex._destroy = common.mustCall(function(err, cb) { 94 assert.strictEqual(err, null); 95 cb(); 96 }); 97 98 duplex.destroy(); 99 assert.strictEqual(duplex.destroyed, true); 100} 101 102{ 103 const duplex = new Duplex({ 104 write(chunk, enc, cb) { cb(); }, 105 read() {} 106 }); 107 duplex.resume(); 108 109 duplex._destroy = common.mustCall(function(err, cb) { 110 assert.strictEqual(err, null); 111 process.nextTick(() => { 112 this.push(null); 113 this.end(); 114 cb(); 115 }); 116 }); 117 118 const fail = common.mustNotCall('no finish or end event'); 119 120 duplex.on('finish', fail); 121 duplex.on('end', fail); 122 123 duplex.destroy(); 124 125 duplex.removeListener('end', fail); 126 duplex.removeListener('finish', fail); 127 duplex.on('end', common.mustNotCall()); 128 duplex.on('finish', common.mustNotCall()); 129 assert.strictEqual(duplex.destroyed, true); 130} 131 132{ 133 const duplex = new Duplex({ 134 write(chunk, enc, cb) { cb(); }, 135 read() {} 136 }); 137 138 const expected = new Error('kaboom'); 139 140 duplex._destroy = common.mustCall(function(err, cb) { 141 assert.strictEqual(err, null); 142 cb(expected); 143 }); 144 145 duplex.on('finish', common.mustNotCall('no finish event')); 146 duplex.on('end', common.mustNotCall('no end event')); 147 duplex.on('error', common.mustCall((err) => { 148 assert.strictEqual(err, expected); 149 })); 150 151 duplex.destroy(); 152 assert.strictEqual(duplex.destroyed, true); 153} 154 155{ 156 const duplex = new Duplex({ 157 write(chunk, enc, cb) { cb(); }, 158 read() {}, 159 allowHalfOpen: true 160 }); 161 duplex.resume(); 162 163 duplex.on('finish', common.mustNotCall()); 164 duplex.on('end', common.mustNotCall()); 165 166 duplex.destroy(); 167 assert.strictEqual(duplex.destroyed, true); 168} 169 170{ 171 const duplex = new Duplex({ 172 write(chunk, enc, cb) { cb(); }, 173 read() {}, 174 }); 175 176 duplex.destroyed = true; 177 assert.strictEqual(duplex.destroyed, true); 178 179 // The internal destroy() mechanism should not be triggered 180 duplex.on('finish', common.mustNotCall()); 181 duplex.on('end', common.mustNotCall()); 182 duplex.destroy(); 183} 184 185{ 186 function MyDuplex() { 187 assert.strictEqual(this.destroyed, false); 188 this.destroyed = false; 189 Duplex.call(this); 190 } 191 192 Object.setPrototypeOf(MyDuplex.prototype, Duplex.prototype); 193 Object.setPrototypeOf(MyDuplex, Duplex); 194 195 new MyDuplex(); 196} 197 198{ 199 const duplex = new Duplex({ 200 writable: false, 201 autoDestroy: true, 202 write(chunk, enc, cb) { cb(); }, 203 read() {}, 204 }); 205 duplex.push(null); 206 duplex.resume(); 207 duplex.on('close', common.mustCall()); 208} 209 210{ 211 const duplex = new Duplex({ 212 readable: false, 213 autoDestroy: true, 214 write(chunk, enc, cb) { cb(); }, 215 read() {}, 216 }); 217 duplex.end(); 218 duplex.on('close', common.mustCall()); 219} 220 221{ 222 const duplex = new Duplex({ 223 allowHalfOpen: false, 224 autoDestroy: true, 225 write(chunk, enc, cb) { cb(); }, 226 read() {}, 227 }); 228 duplex.push(null); 229 duplex.resume(); 230 const orgEnd = duplex.end; 231 duplex.end = common.mustNotCall(); 232 duplex.on('end', () => { 233 // Ensure end() is called in next tick to allow 234 // any pending writes to be invoked first. 235 process.nextTick(() => { 236 duplex.end = common.mustCall(orgEnd); 237 }); 238 }); 239 duplex.on('close', common.mustCall()); 240} 241{ 242 // Check abort signal 243 const controller = new AbortController(); 244 const { signal } = controller; 245 const duplex = new Duplex({ 246 write(chunk, enc, cb) { cb(); }, 247 read() {}, 248 signal, 249 }); 250 let count = 0; 251 duplex.on('error', common.mustCall((e) => { 252 assert.strictEqual(count++, 0); // Ensure not called twice 253 assert.strictEqual(e.name, 'AbortError'); 254 })); 255 duplex.on('close', common.mustCall()); 256 controller.abort(); 257} 258