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