1'use strict'; 2 3const common = require('../common'); 4const { Writable } = require('stream'); 5const assert = require('assert'); 6 7{ 8 const write = new Writable({ 9 write(chunk, enc, cb) { cb(); } 10 }); 11 12 write.on('finish', common.mustNotCall()); 13 write.on('close', common.mustCall()); 14 15 write.destroy(); 16 assert.strictEqual(write.destroyed, true); 17} 18 19{ 20 const write = new Writable({ 21 write(chunk, enc, cb) { 22 this.destroy(new Error('asd')); 23 cb(); 24 } 25 }); 26 27 write.on('error', common.mustCall()); 28 write.on('finish', common.mustNotCall()); 29 write.end('asd'); 30 assert.strictEqual(write.destroyed, true); 31} 32 33{ 34 const write = new Writable({ 35 write(chunk, enc, cb) { cb(); } 36 }); 37 38 const expected = new Error('kaboom'); 39 40 write.on('finish', common.mustNotCall()); 41 write.on('close', common.mustCall()); 42 write.on('error', common.mustCall((err) => { 43 assert.strictEqual(err, expected); 44 })); 45 46 write.destroy(expected); 47 assert.strictEqual(write.destroyed, true); 48} 49 50{ 51 const write = new Writable({ 52 write(chunk, enc, cb) { cb(); } 53 }); 54 55 write._destroy = function(err, cb) { 56 assert.strictEqual(err, expected); 57 cb(err); 58 }; 59 60 const expected = new Error('kaboom'); 61 62 write.on('finish', common.mustNotCall('no finish event')); 63 write.on('close', common.mustCall()); 64 write.on('error', common.mustCall((err) => { 65 assert.strictEqual(err, expected); 66 })); 67 68 write.destroy(expected); 69 assert.strictEqual(write.destroyed, true); 70} 71 72{ 73 const write = new Writable({ 74 write(chunk, enc, cb) { cb(); }, 75 destroy: common.mustCall(function(err, cb) { 76 assert.strictEqual(err, expected); 77 cb(); 78 }) 79 }); 80 81 const expected = new Error('kaboom'); 82 83 write.on('finish', common.mustNotCall('no finish event')); 84 write.on('close', common.mustCall()); 85 86 // Error is swallowed by the custom _destroy 87 write.on('error', common.mustNotCall('no error event')); 88 89 write.destroy(expected); 90 assert.strictEqual(write.destroyed, true); 91} 92 93{ 94 const write = new Writable({ 95 write(chunk, enc, cb) { cb(); } 96 }); 97 98 write._destroy = common.mustCall(function(err, cb) { 99 assert.strictEqual(err, null); 100 cb(); 101 }); 102 103 write.destroy(); 104 assert.strictEqual(write.destroyed, true); 105} 106 107{ 108 const write = new Writable({ 109 write(chunk, enc, cb) { cb(); } 110 }); 111 112 write._destroy = common.mustCall(function(err, cb) { 113 assert.strictEqual(err, null); 114 process.nextTick(() => { 115 this.end(); 116 cb(); 117 }); 118 }); 119 120 const fail = common.mustNotCall('no finish event'); 121 122 write.on('finish', fail); 123 write.on('close', common.mustCall()); 124 125 write.destroy(); 126 127 write.removeListener('finish', fail); 128 write.on('finish', common.mustCall()); 129 assert.strictEqual(write.destroyed, true); 130} 131 132{ 133 const write = new Writable({ 134 write(chunk, enc, cb) { cb(); } 135 }); 136 137 const expected = new Error('kaboom'); 138 139 write._destroy = common.mustCall(function(err, cb) { 140 assert.strictEqual(err, null); 141 cb(expected); 142 }); 143 144 write.on('close', common.mustCall()); 145 write.on('finish', common.mustNotCall('no finish event')); 146 write.on('error', common.mustCall((err) => { 147 assert.strictEqual(err, expected); 148 })); 149 150 write.destroy(); 151 assert.strictEqual(write.destroyed, true); 152} 153 154{ 155 // double error case 156 const write = new Writable({ 157 write(chunk, enc, cb) { cb(); } 158 }); 159 160 let ticked = false; 161 write.on('close', common.mustCall(() => { 162 assert.strictEqual(ticked, true); 163 })); 164 write.on('error', common.mustCall((err) => { 165 assert.strictEqual(ticked, true); 166 assert.strictEqual(err.message, 'kaboom 1'); 167 assert.strictEqual(write._writableState.errorEmitted, true); 168 })); 169 170 const expected = new Error('kaboom 1'); 171 write.destroy(expected); 172 write.destroy(new Error('kaboom 2')); 173 assert.strictEqual(write._writableState.errored, expected); 174 assert.strictEqual(write._writableState.errorEmitted, false); 175 assert.strictEqual(write.destroyed, true); 176 ticked = true; 177} 178 179{ 180 const writable = new Writable({ 181 destroy: common.mustCall(function(err, cb) { 182 process.nextTick(cb, new Error('kaboom 1')); 183 }), 184 write(chunk, enc, cb) { 185 cb(); 186 } 187 }); 188 189 let ticked = false; 190 writable.on('close', common.mustCall(() => { 191 writable.on('error', common.mustNotCall()); 192 writable.destroy(new Error('hello')); 193 assert.strictEqual(ticked, true); 194 assert.strictEqual(writable._writableState.errorEmitted, true); 195 })); 196 writable.on('error', common.mustCall((err) => { 197 assert.strictEqual(ticked, true); 198 assert.strictEqual(err.message, 'kaboom 1'); 199 assert.strictEqual(writable._writableState.errorEmitted, true); 200 })); 201 202 writable.destroy(); 203 assert.strictEqual(writable.destroyed, true); 204 assert.strictEqual(writable._writableState.errored, null); 205 assert.strictEqual(writable._writableState.errorEmitted, false); 206 207 // Test case where `writable.destroy()` is called again with an error before 208 // the `_destroy()` callback is called. 209 writable.destroy(new Error('kaboom 2')); 210 assert.strictEqual(writable._writableState.errorEmitted, false); 211 assert.strictEqual(writable._writableState.errored, null); 212 213 ticked = true; 214} 215 216{ 217 const write = new Writable({ 218 write(chunk, enc, cb) { cb(); } 219 }); 220 221 write.destroyed = true; 222 assert.strictEqual(write.destroyed, true); 223 224 // The internal destroy() mechanism should not be triggered 225 write.on('close', common.mustNotCall()); 226 write.destroy(); 227} 228 229{ 230 function MyWritable() { 231 assert.strictEqual(this.destroyed, false); 232 this.destroyed = false; 233 Writable.call(this); 234 } 235 236 Object.setPrototypeOf(MyWritable.prototype, Writable.prototype); 237 Object.setPrototypeOf(MyWritable, Writable); 238 239 new MyWritable(); 240} 241 242{ 243 // Destroy and destroy callback 244 const write = new Writable({ 245 write(chunk, enc, cb) { cb(); } 246 }); 247 248 write.destroy(); 249 250 const expected = new Error('kaboom'); 251 252 write.destroy(expected, common.mustCall((err) => { 253 assert.strictEqual(err, undefined); 254 })); 255} 256 257{ 258 // Checks that `._undestroy()` restores the state so that `final` will be 259 // called again. 260 const write = new Writable({ 261 write: common.mustNotCall(), 262 final: common.mustCall((cb) => cb(), 2), 263 autoDestroy: true 264 }); 265 266 write.end(); 267 write.once('close', common.mustCall(() => { 268 write._undestroy(); 269 write.end(); 270 })); 271} 272 273{ 274 const write = new Writable(); 275 276 write.destroy(); 277 write.on('error', common.mustNotCall()); 278 write.write('asd', common.expectsError({ 279 name: 'Error', 280 code: 'ERR_STREAM_DESTROYED', 281 message: 'Cannot call write after a stream was destroyed' 282 })); 283} 284 285{ 286 const write = new Writable({ 287 write(chunk, enc, cb) { cb(); } 288 }); 289 290 write.on('error', common.mustNotCall()); 291 292 write.cork(); 293 write.write('asd', common.mustCall()); 294 write.uncork(); 295 296 write.cork(); 297 write.write('asd', common.expectsError({ 298 name: 'Error', 299 code: 'ERR_STREAM_DESTROYED', 300 message: 'Cannot call write after a stream was destroyed' 301 })); 302 write.destroy(); 303 write.write('asd', common.expectsError({ 304 name: 'Error', 305 code: 'ERR_STREAM_DESTROYED', 306 message: 'Cannot call write after a stream was destroyed' 307 })); 308 write.uncork(); 309} 310 311{ 312 // Call end(cb) after error & destroy 313 314 const write = new Writable({ 315 write(chunk, enc, cb) { cb(new Error('asd')); } 316 }); 317 write.on('error', common.mustCall(() => { 318 write.destroy(); 319 let ticked = false; 320 write.end(common.mustCall((err) => { 321 assert.strictEqual(ticked, true); 322 assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); 323 })); 324 ticked = true; 325 })); 326 write.write('asd'); 327} 328 329{ 330 // Call end(cb) after finish & destroy 331 332 const write = new Writable({ 333 write(chunk, enc, cb) { cb(); } 334 }); 335 write.on('finish', common.mustCall(() => { 336 write.destroy(); 337 let ticked = false; 338 write.end(common.mustCall((err) => { 339 assert.strictEqual(ticked, true); 340 assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED'); 341 })); 342 ticked = true; 343 })); 344 write.end(); 345} 346 347{ 348 // Call end(cb) after error & destroy and don't trigger 349 // unhandled exception. 350 351 const write = new Writable({ 352 write(chunk, enc, cb) { process.nextTick(cb); } 353 }); 354 write.once('error', common.mustCall((err) => { 355 assert.strictEqual(err.message, 'asd'); 356 })); 357 write.end('asd', common.mustCall((err) => { 358 assert.strictEqual(err.message, 'asd'); 359 })); 360 write.destroy(new Error('asd')); 361} 362 363{ 364 // Call buffered write callback with error 365 366 const write = new Writable({ 367 write(chunk, enc, cb) { 368 process.nextTick(cb, new Error('asd')); 369 }, 370 autoDestroy: false 371 }); 372 write.cork(); 373 write.write('asd', common.mustCall((err) => { 374 assert.strictEqual(err.message, 'asd'); 375 })); 376 write.write('asd', common.mustCall((err) => { 377 assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); 378 })); 379 write.on('error', common.mustCall((err) => { 380 assert.strictEqual(err.message, 'asd'); 381 })); 382 write.uncork(); 383} 384 385{ 386 // Ensure callback order. 387 388 let state = 0; 389 const write = new Writable({ 390 write(chunk, enc, cb) { 391 // `setImmediate()` is used on purpose to ensure the callback is called 392 // after `process.nextTick()` callbacks. 393 setImmediate(cb); 394 } 395 }); 396 write.write('asd', common.mustCall(() => { 397 assert.strictEqual(state++, 0); 398 })); 399 write.write('asd', common.mustCall((err) => { 400 assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); 401 assert.strictEqual(state++, 1); 402 })); 403 write.destroy(); 404} 405 406{ 407 const write = new Writable({ 408 autoDestroy: false, 409 write(chunk, enc, cb) { 410 cb(); 411 cb(); 412 } 413 }); 414 415 write.on('error', common.mustCall(() => { 416 assert(write._writableState.errored); 417 })); 418 write.write('asd'); 419} 420 421{ 422 // Destroy twice 423 const write = new Writable({ 424 write(chunk, enc, cb) { cb(); } 425 }); 426 427 write.end(common.mustCall()); 428 write.destroy(); 429 write.destroy(); 430} 431