• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const { Writable, addAbortSignal } = 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  assert.strictEqual(write.destroyed, true);
128}
129
130{
131  const write = new Writable({
132    write(chunk, enc, cb) { cb(); }
133  });
134
135  const expected = new Error('kaboom');
136
137  write._destroy = common.mustCall(function(err, cb) {
138    assert.strictEqual(err, null);
139    cb(expected);
140  });
141
142  write.on('close', common.mustCall());
143  write.on('finish', common.mustNotCall('no finish event'));
144  write.on('error', common.mustCall((err) => {
145    assert.strictEqual(err, expected);
146  }));
147
148  write.destroy();
149  assert.strictEqual(write.destroyed, true);
150}
151
152{
153  // double error case
154  const write = new Writable({
155    write(chunk, enc, cb) { cb(); }
156  });
157
158  let ticked = false;
159  write.on('close', common.mustCall(() => {
160    assert.strictEqual(ticked, true);
161  }));
162  write.on('error', common.mustCall((err) => {
163    assert.strictEqual(ticked, true);
164    assert.strictEqual(err.message, 'kaboom 1');
165    assert.strictEqual(write._writableState.errorEmitted, true);
166  }));
167
168  const expected = new Error('kaboom 1');
169  write.destroy(expected);
170  write.destroy(new Error('kaboom 2'));
171  assert.strictEqual(write._writableState.errored, expected);
172  assert.strictEqual(write._writableState.errorEmitted, false);
173  assert.strictEqual(write.destroyed, true);
174  ticked = true;
175}
176
177{
178  const writable = new Writable({
179    destroy: common.mustCall(function(err, cb) {
180      process.nextTick(cb, new Error('kaboom 1'));
181    }),
182    write(chunk, enc, cb) {
183      cb();
184    }
185  });
186
187  let ticked = false;
188  writable.on('close', common.mustCall(() => {
189    writable.on('error', common.mustNotCall());
190    writable.destroy(new Error('hello'));
191    assert.strictEqual(ticked, true);
192    assert.strictEqual(writable._writableState.errorEmitted, true);
193  }));
194  writable.on('error', common.mustCall((err) => {
195    assert.strictEqual(ticked, true);
196    assert.strictEqual(err.message, 'kaboom 1');
197    assert.strictEqual(writable._writableState.errorEmitted, true);
198  }));
199
200  writable.destroy();
201  assert.strictEqual(writable.destroyed, true);
202  assert.strictEqual(writable._writableState.errored, null);
203  assert.strictEqual(writable._writableState.errorEmitted, false);
204
205  // Test case where `writable.destroy()` is called again with an error before
206  // the `_destroy()` callback is called.
207  writable.destroy(new Error('kaboom 2'));
208  assert.strictEqual(writable._writableState.errorEmitted, false);
209  assert.strictEqual(writable._writableState.errored, null);
210
211  ticked = true;
212}
213
214{
215  const write = new Writable({
216    write(chunk, enc, cb) { cb(); }
217  });
218
219  write.destroyed = true;
220  assert.strictEqual(write.destroyed, true);
221
222  // The internal destroy() mechanism should not be triggered
223  write.on('close', common.mustNotCall());
224  write.destroy();
225}
226
227{
228  function MyWritable() {
229    assert.strictEqual(this.destroyed, false);
230    this.destroyed = false;
231    Writable.call(this);
232  }
233
234  Object.setPrototypeOf(MyWritable.prototype, Writable.prototype);
235  Object.setPrototypeOf(MyWritable, Writable);
236
237  new MyWritable();
238}
239
240{
241  // Destroy and destroy callback
242  const write = new Writable({
243    write(chunk, enc, cb) { cb(); }
244  });
245
246  write.destroy();
247
248  const expected = new Error('kaboom');
249
250  write.destroy(expected, common.mustCall((err) => {
251    assert.strictEqual(err, undefined);
252  }));
253}
254
255{
256  // Checks that `._undestroy()` restores the state so that `final` will be
257  // called again.
258  const write = new Writable({
259    write: common.mustNotCall(),
260    final: common.mustCall((cb) => cb(), 2),
261    autoDestroy: true
262  });
263
264  write.end();
265  write.once('close', common.mustCall(() => {
266    write._undestroy();
267    write.end();
268  }));
269}
270
271{
272  const write = new Writable();
273
274  write.destroy();
275  write.on('error', common.mustNotCall());
276  write.write('asd', common.expectsError({
277    name: 'Error',
278    code: 'ERR_STREAM_DESTROYED',
279    message: 'Cannot call write after a stream was destroyed'
280  }));
281}
282
283{
284  const write = new Writable({
285    write(chunk, enc, cb) { cb(); }
286  });
287
288  write.on('error', common.mustNotCall());
289
290  write.cork();
291  write.write('asd', common.mustCall());
292  write.uncork();
293
294  write.cork();
295  write.write('asd', common.expectsError({
296    name: 'Error',
297    code: 'ERR_STREAM_DESTROYED',
298    message: 'Cannot call write after a stream was destroyed'
299  }));
300  write.destroy();
301  write.write('asd', common.expectsError({
302    name: 'Error',
303    code: 'ERR_STREAM_DESTROYED',
304    message: 'Cannot call write after a stream was destroyed'
305  }));
306  write.uncork();
307}
308
309{
310  // Call end(cb) after error & destroy
311
312  const write = new Writable({
313    write(chunk, enc, cb) { cb(new Error('asd')); }
314  });
315  write.on('error', common.mustCall(() => {
316    write.destroy();
317    let ticked = false;
318    write.end(common.mustCall((err) => {
319      assert.strictEqual(ticked, true);
320      assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
321    }));
322    ticked = true;
323  }));
324  write.write('asd');
325}
326
327{
328  // Call end(cb) after finish & destroy
329
330  const write = new Writable({
331    write(chunk, enc, cb) { cb(); }
332  });
333  write.on('finish', common.mustCall(() => {
334    write.destroy();
335    let ticked = false;
336    write.end(common.mustCall((err) => {
337      assert.strictEqual(ticked, true);
338      assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED');
339    }));
340    ticked = true;
341  }));
342  write.end();
343}
344
345{
346  // Call end(cb) after error & destroy and don't trigger
347  // unhandled exception.
348
349  const write = new Writable({
350    write(chunk, enc, cb) { process.nextTick(cb); }
351  });
352  const _err = new Error('asd');
353  write.once('error', common.mustCall((err) => {
354    assert.strictEqual(err.message, 'asd');
355  }));
356  write.end('asd', common.mustCall((err) => {
357    assert.strictEqual(err, _err);
358  }));
359  write.destroy(_err);
360}
361
362{
363  // Call buffered write callback with error
364
365  const _err = new Error('asd');
366  const write = new Writable({
367    write(chunk, enc, cb) {
368      process.nextTick(cb, _err);
369    },
370    autoDestroy: false
371  });
372  write.cork();
373  write.write('asd', common.mustCall((err) => {
374    assert.strictEqual(err, _err);
375  }));
376  write.write('asd', common.mustCall((err) => {
377    assert.strictEqual(err, _err);
378  }));
379  write.on('error', common.mustCall((err) => {
380    assert.strictEqual(err, _err);
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  const ac = new AbortController();
423  const write = addAbortSignal(ac.signal, new Writable({
424    write(chunk, enc, cb) { cb(); }
425  }));
426
427  write.on('error', common.mustCall((e) => {
428    assert.strictEqual(e.name, 'AbortError');
429    assert.strictEqual(write.destroyed, true);
430  }));
431  write.write('asd');
432  ac.abort();
433}
434
435{
436  const ac = new AbortController();
437  const write = new Writable({
438    signal: ac.signal,
439    write(chunk, enc, cb) { cb(); }
440  });
441
442  write.on('error', common.mustCall((e) => {
443    assert.strictEqual(e.name, 'AbortError');
444    assert.strictEqual(write.destroyed, true);
445  }));
446  write.write('asd');
447  ac.abort();
448}
449
450{
451  const signal = AbortSignal.abort();
452
453  const write = new Writable({
454    signal,
455    write(chunk, enc, cb) { cb(); }
456  });
457
458  write.on('error', common.mustCall((e) => {
459    assert.strictEqual(e.name, 'AbortError');
460    assert.strictEqual(write.destroyed, true);
461  }));
462}
463
464{
465  // Destroy twice
466  const write = new Writable({
467    write(chunk, enc, cb) { cb(); }
468  });
469
470  write.end(common.mustCall());
471  write.destroy();
472  write.destroy();
473}
474
475{
476  // https://github.com/nodejs/node/issues/39356
477  const s = new Writable({
478    final() {}
479  });
480  const _err = new Error('oh no');
481  // Remove `callback` and it works
482  s.end(common.mustCall((err) => {
483    assert.strictEqual(err, _err);
484  }));
485  s.on('error', common.mustCall((err) => {
486    assert.strictEqual(err, _err);
487  }));
488  s.destroy(_err);
489}
490