@libp2p/interface-compliance-tests 6.5.0 → 7.0.0-049bfa0fa

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/src/connection-encryption/index.d.ts.map +1 -1
  2. package/dist/src/connection-encryption/index.js +24 -15
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  5. package/dist/src/stream-muxer/base-test.js +62 -345
  6. package/dist/src/stream-muxer/base-test.js.map +1 -1
  7. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  8. package/dist/src/stream-muxer/close-test.js +254 -320
  9. package/dist/src/stream-muxer/close-test.js.map +1 -1
  10. package/dist/src/stream-muxer/index.js +2 -2
  11. package/dist/src/stream-muxer/index.js.map +1 -1
  12. package/dist/src/stream-muxer/{mega-stress-test.d.ts → stream-test.d.ts} +2 -2
  13. package/dist/src/stream-muxer/stream-test.d.ts.map +1 -0
  14. package/dist/src/stream-muxer/stream-test.js +290 -0
  15. package/dist/src/stream-muxer/stream-test.js.map +1 -0
  16. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  17. package/dist/src/stream-muxer/stress-test.js +70 -16
  18. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  19. package/dist/src/transport/index.d.ts.map +1 -1
  20. package/dist/src/transport/index.js +235 -205
  21. package/dist/src/transport/index.js.map +1 -1
  22. package/dist/src/transport/utils.d.ts.map +1 -1
  23. package/dist/src/transport/utils.js +3 -2
  24. package/dist/src/transport/utils.js.map +1 -1
  25. package/package.json +23 -51
  26. package/src/connection-encryption/index.ts +27 -20
  27. package/src/stream-muxer/base-test.ts +75 -413
  28. package/src/stream-muxer/close-test.ts +305 -343
  29. package/src/stream-muxer/index.ts +2 -2
  30. package/src/stream-muxer/stream-test.ts +381 -0
  31. package/src/stream-muxer/stress-test.ts +92 -18
  32. package/src/transport/index.ts +281 -241
  33. package/src/transport/utils.ts +3 -2
  34. package/dist/src/connection-encryption/utils/index.d.ts +0 -3
  35. package/dist/src/connection-encryption/utils/index.d.ts.map +0 -1
  36. package/dist/src/connection-encryption/utils/index.js +0 -21
  37. package/dist/src/connection-encryption/utils/index.js.map +0 -1
  38. package/dist/src/matchers.d.ts +0 -12
  39. package/dist/src/matchers.d.ts.map +0 -1
  40. package/dist/src/matchers.js +0 -14
  41. package/dist/src/matchers.js.map +0 -1
  42. package/dist/src/mocks/connection-manager.d.ts +0 -27
  43. package/dist/src/mocks/connection-manager.d.ts.map +0 -1
  44. package/dist/src/mocks/connection-manager.js +0 -147
  45. package/dist/src/mocks/connection-manager.js.map +0 -1
  46. package/dist/src/mocks/connection.d.ts +0 -41
  47. package/dist/src/mocks/connection.d.ts.map +0 -1
  48. package/dist/src/mocks/connection.js +0 -236
  49. package/dist/src/mocks/connection.js.map +0 -1
  50. package/dist/src/mocks/duplex.d.ts +0 -4
  51. package/dist/src/mocks/duplex.d.ts.map +0 -1
  52. package/dist/src/mocks/duplex.js +0 -9
  53. package/dist/src/mocks/duplex.js.map +0 -1
  54. package/dist/src/mocks/index.d.ts +0 -12
  55. package/dist/src/mocks/index.d.ts.map +0 -1
  56. package/dist/src/mocks/index.js +0 -8
  57. package/dist/src/mocks/index.js.map +0 -1
  58. package/dist/src/mocks/multiaddr-connection.d.ts +0 -17
  59. package/dist/src/mocks/multiaddr-connection.d.ts.map +0 -1
  60. package/dist/src/mocks/multiaddr-connection.js +0 -64
  61. package/dist/src/mocks/multiaddr-connection.js.map +0 -1
  62. package/dist/src/mocks/muxer.d.ts +0 -36
  63. package/dist/src/mocks/muxer.d.ts.map +0 -1
  64. package/dist/src/mocks/muxer.js +0 -234
  65. package/dist/src/mocks/muxer.js.map +0 -1
  66. package/dist/src/mocks/registrar.d.ts +0 -16
  67. package/dist/src/mocks/registrar.d.ts.map +0 -1
  68. package/dist/src/mocks/registrar.js +0 -66
  69. package/dist/src/mocks/registrar.js.map +0 -1
  70. package/dist/src/mocks/upgrader.d.ts +0 -9
  71. package/dist/src/mocks/upgrader.d.ts.map +0 -1
  72. package/dist/src/mocks/upgrader.js +0 -46
  73. package/dist/src/mocks/upgrader.js.map +0 -1
  74. package/dist/src/pubsub/api.d.ts +0 -6
  75. package/dist/src/pubsub/api.d.ts.map +0 -1
  76. package/dist/src/pubsub/api.js +0 -88
  77. package/dist/src/pubsub/api.js.map +0 -1
  78. package/dist/src/pubsub/connection-handlers.d.ts +0 -6
  79. package/dist/src/pubsub/connection-handlers.d.ts.map +0 -1
  80. package/dist/src/pubsub/connection-handlers.js +0 -329
  81. package/dist/src/pubsub/connection-handlers.js.map +0 -1
  82. package/dist/src/pubsub/emit-self.d.ts +0 -6
  83. package/dist/src/pubsub/emit-self.d.ts.map +0 -1
  84. package/dist/src/pubsub/emit-self.js +0 -80
  85. package/dist/src/pubsub/emit-self.js.map +0 -1
  86. package/dist/src/pubsub/index.d.ts +0 -18
  87. package/dist/src/pubsub/index.d.ts.map +0 -1
  88. package/dist/src/pubsub/index.js +0 -17
  89. package/dist/src/pubsub/index.js.map +0 -1
  90. package/dist/src/pubsub/messages.d.ts +0 -6
  91. package/dist/src/pubsub/messages.d.ts.map +0 -1
  92. package/dist/src/pubsub/messages.js +0 -48
  93. package/dist/src/pubsub/messages.js.map +0 -1
  94. package/dist/src/pubsub/multiple-nodes.d.ts +0 -6
  95. package/dist/src/pubsub/multiple-nodes.d.ts.map +0 -1
  96. package/dist/src/pubsub/multiple-nodes.js +0 -350
  97. package/dist/src/pubsub/multiple-nodes.js.map +0 -1
  98. package/dist/src/pubsub/two-nodes.d.ts +0 -6
  99. package/dist/src/pubsub/two-nodes.d.ts.map +0 -1
  100. package/dist/src/pubsub/two-nodes.js +0 -216
  101. package/dist/src/pubsub/two-nodes.js.map +0 -1
  102. package/dist/src/pubsub/utils.d.ts +0 -5
  103. package/dist/src/pubsub/utils.d.ts.map +0 -1
  104. package/dist/src/pubsub/utils.js +0 -27
  105. package/dist/src/pubsub/utils.js.map +0 -1
  106. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +0 -1
  107. package/dist/src/stream-muxer/mega-stress-test.js +0 -11
  108. package/dist/src/stream-muxer/mega-stress-test.js.map +0 -1
  109. package/dist/src/stream-muxer/spawner.d.ts +0 -4
  110. package/dist/src/stream-muxer/spawner.d.ts.map +0 -1
  111. package/dist/src/stream-muxer/spawner.js +0 -39
  112. package/dist/src/stream-muxer/spawner.js.map +0 -1
  113. package/dist/typedoc-urls.json +0 -44
  114. package/src/connection-encryption/utils/index.ts +0 -27
  115. package/src/matchers.ts +0 -18
  116. package/src/mocks/connection-manager.ts +0 -216
  117. package/src/mocks/connection.ts +0 -309
  118. package/src/mocks/duplex.ts +0 -11
  119. package/src/mocks/index.ts +0 -11
  120. package/src/mocks/multiaddr-connection.ts +0 -80
  121. package/src/mocks/muxer.ts +0 -331
  122. package/src/mocks/registrar.ts +0 -86
  123. package/src/mocks/upgrader.ts +0 -65
  124. package/src/pubsub/api.ts +0 -116
  125. package/src/pubsub/connection-handlers.ts +0 -413
  126. package/src/pubsub/emit-self.ts +0 -99
  127. package/src/pubsub/index.ts +0 -34
  128. package/src/pubsub/messages.ts +0 -59
  129. package/src/pubsub/multiple-nodes.ts +0 -440
  130. package/src/pubsub/two-nodes.ts +0 -272
  131. package/src/pubsub/utils.ts +0 -34
  132. package/src/stream-muxer/mega-stress-test.ts +0 -14
  133. package/src/stream-muxer/spawner.ts +0 -57
@@ -1,408 +1,342 @@
1
1
  /* eslint max-nested-callbacks: ["error", 8] */
2
- import { abortableSource } from 'abortable-iterator';
2
+ import { multiaddrConnectionPair, echo, pbStream } from '@libp2p/utils';
3
3
  import { expect } from 'aegir/chai';
4
4
  import delay from 'delay';
5
5
  import all from 'it-all';
6
- import drain from 'it-drain';
7
- import { duplexPair } from 'it-pair/duplex';
8
- import { pipe } from 'it-pipe';
9
- import { pbStream } from 'it-protobuf-stream';
10
- import toBuffer from 'it-to-buffer';
11
- import pDefer from 'p-defer';
6
+ import map from 'it-map';
7
+ import { pEvent } from 'p-event';
12
8
  import { Uint8ArrayList } from 'uint8arraylist';
13
9
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
14
10
  import { Message } from './fixtures/pb/message.js';
15
11
  function randomBuffer() {
16
12
  return uint8ArrayFromString(Math.random().toString());
17
13
  }
18
- function infiniteRandom() {
19
- let done = false;
20
- const generator = {
21
- [Symbol.asyncIterator]: () => {
22
- return generator;
23
- },
24
- async next() {
25
- await delay(10);
26
- if (done instanceof Error) {
27
- throw done;
28
- }
29
- if (done) {
30
- return {
31
- done: true,
32
- value: undefined
33
- };
34
- }
35
- return {
36
- done: false,
37
- value: new Uint8ArrayList(randomBuffer())
38
- };
39
- },
40
- async return() {
41
- done = true;
42
- return {
43
- done: true,
44
- value: undefined
45
- };
46
- },
47
- async throw(err) {
48
- done = err;
49
- return {
50
- done: true,
51
- value: undefined
52
- };
53
- }
54
- };
55
- return generator;
14
+ async function* infiniteRandom() {
15
+ while (true) {
16
+ await delay(10);
17
+ yield new Uint8ArrayList(randomBuffer());
18
+ }
56
19
  }
57
20
  export default (common) => {
58
21
  describe('close', () => {
59
- it('closing underlying socket closes streams', async () => {
60
- let openedStreams = 0;
61
- const expectedStreams = 5;
22
+ let outboundConnection;
23
+ let inboundConnection;
24
+ let dialer;
25
+ let listener;
26
+ beforeEach(async () => {
27
+ [outboundConnection, inboundConnection] = multiaddrConnectionPair();
62
28
  const dialerFactory = await common.setup();
63
- const dialer = dialerFactory.createStreamMuxer({
64
- direction: 'outbound'
65
- });
66
- // Listener is echo server :)
29
+ dialer = dialerFactory.createStreamMuxer(outboundConnection);
67
30
  const listenerFactory = await common.setup();
68
- const listener = listenerFactory.createStreamMuxer({
69
- direction: 'inbound',
70
- onIncomingStream: (stream) => {
71
- openedStreams++;
72
- void pipe(stream, stream);
73
- }
31
+ listener = listenerFactory.createStreamMuxer(inboundConnection);
32
+ });
33
+ afterEach(async () => {
34
+ await dialer?.close();
35
+ await listener?.close();
36
+ });
37
+ it('closing underlying MultiaddrConnection closes streams', async () => {
38
+ let openedStreams = 0;
39
+ const expectedStreams = 5;
40
+ listener.addEventListener('stream', (evt) => {
41
+ openedStreams++;
42
+ echo(evt.detail);
74
43
  });
75
- const p = duplexPair();
76
- void pipe(p[0], dialer, p[0]);
77
- void pipe(p[1], listener, p[1]);
78
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()));
44
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()));
79
45
  void Promise.all(streams.map(async (stream) => {
80
- await pipe(infiniteRandom(), stream, drain);
46
+ for await (const buf of infiniteRandom()) {
47
+ if (stream.status !== 'open') {
48
+ return;
49
+ }
50
+ const sendMore = stream.send(buf);
51
+ if (!sendMore) {
52
+ await pEvent(stream, 'drain', {
53
+ rejectionEvents: [
54
+ 'close'
55
+ ]
56
+ });
57
+ }
58
+ }
81
59
  }));
82
60
  expect(dialer.streams).to.have.lengthOf(expectedStreams);
83
61
  // Pause, and then close the dialer
84
62
  await delay(50);
85
- await pipe(async function* () { }, dialer, drain);
63
+ await inboundConnection.close();
64
+ await outboundConnection.close();
65
+ await delay(50);
86
66
  expect(openedStreams).to.have.equal(expectedStreams);
87
67
  expect(dialer.streams).to.have.lengthOf(0);
88
68
  });
89
69
  it('calling close closes streams', async () => {
90
70
  let openedStreams = 0;
91
71
  const expectedStreams = 5;
92
- const dialerFactory = await common.setup();
93
- const dialer = dialerFactory.createStreamMuxer({
94
- direction: 'outbound'
72
+ listener.addEventListener('stream', (evt) => {
73
+ openedStreams++;
74
+ echo(evt.detail);
95
75
  });
96
- // Listener is echo server :)
97
- const listenerFactory = await common.setup();
98
- const listener = listenerFactory.createStreamMuxer({
99
- direction: 'inbound',
100
- onIncomingStream: (stream) => {
101
- openedStreams++;
102
- void pipe(stream, stream).catch(() => { });
76
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()));
77
+ void Promise.all(streams.map(async (stream) => {
78
+ for await (const buf of infiniteRandom()) {
79
+ if (stream.status !== 'open') {
80
+ return;
81
+ }
82
+ const sendMore = stream.send(buf);
83
+ if (!sendMore) {
84
+ await pEvent(stream, 'drain', {
85
+ rejectionEvents: [
86
+ 'close'
87
+ ]
88
+ });
89
+ }
103
90
  }
91
+ }))
92
+ .catch(() => {
93
+ // calling .send on a closed stream will throw so swallow any errors
104
94
  });
105
- const p = duplexPair();
106
- void pipe(p[0], dialer, p[0]);
107
- void pipe(p[1], listener, p[1]);
108
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()));
109
- void Promise.all(streams.map(async (stream) => {
110
- await pipe(infiniteRandom(), stream, drain);
111
- }));
112
95
  expect(dialer.streams, 'dialer - number of opened streams should match number of calls to newStream').to.have.lengthOf(expectedStreams);
113
96
  // Pause, and then close the dialer
114
97
  await delay(50);
115
98
  await dialer.close();
99
+ await delay(50);
116
100
  expect(openedStreams, 'listener - number of opened streams should match number of calls to newStream').to.have.equal(expectedStreams);
117
101
  expect(dialer.streams, 'all tracked streams should be deleted after the muxer has called close').to.have.lengthOf(0);
118
102
  });
119
- it('calling close with an error aborts streams', async () => {
103
+ it('calling abort aborts streams', async () => {
120
104
  let openedStreams = 0;
121
105
  const expectedStreams = 5;
122
- const dialerFactory = await common.setup();
123
- const dialer = dialerFactory.createStreamMuxer({
124
- direction: 'outbound'
125
- });
126
- // Listener is echo server :)
127
- const listenerFactory = await common.setup();
128
- const listener = listenerFactory.createStreamMuxer({
129
- direction: 'inbound',
130
- onIncomingStream: (stream) => {
131
- openedStreams++;
132
- void pipe(stream, stream).catch(() => { });
133
- }
106
+ listener.addEventListener('stream', (evt) => {
107
+ openedStreams++;
108
+ echo(evt.detail);
134
109
  });
135
- const p = duplexPair();
136
- void pipe(p[0], dialer, p[0]);
137
- void pipe(p[1], listener, p[1]);
138
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()));
110
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()));
139
111
  const streamPipes = streams.map(async (stream) => {
140
- await pipe(infiniteRandom(), stream, drain);
112
+ for await (const buf of infiniteRandom()) {
113
+ if (stream.writeStatus !== 'writable') {
114
+ break;
115
+ }
116
+ const sendMore = stream.send(buf);
117
+ if (!sendMore) {
118
+ await pEvent(stream, 'drain', {
119
+ rejectionEvents: ['close']
120
+ });
121
+ }
122
+ }
141
123
  });
142
- expect(dialer.streams, 'dialer - number of opened streams should match number of calls to newStream').to.have.lengthOf(expectedStreams);
143
- // Pause, and then close the dialer
144
- await delay(50);
145
- // close _with an error_
146
- dialer.abort(new Error('Oh no!'));
124
+ expect(dialer.streams).to.have.lengthOf(expectedStreams, 'dialer - number of opened streams should match number of calls to createStream');
147
125
  const timeoutError = new Error('timeout');
148
- for (const pipe of streamPipes) {
149
- try {
150
- await Promise.race([
151
- pipe,
152
- new Promise((_resolve, reject) => setTimeout(() => { reject(timeoutError); }, 20))
153
- ]);
154
- expect.fail('stream pipe with infinite source should never return');
155
- }
156
- catch (e) {
157
- if (e === timeoutError) {
158
- expect.fail('expected stream pipe to throw an error after muxer closed with error');
126
+ await Promise.all([
127
+ // Pause, and then close the dialer
128
+ delay(50).then(() => {
129
+ // close _with an error_
130
+ dialer.abort(new Error('Oh no!'));
131
+ }),
132
+ ...streamPipes.map(async (pipe) => {
133
+ try {
134
+ await Promise.race([
135
+ pipe,
136
+ new Promise((resolve, reject) => {
137
+ setTimeout(() => {
138
+ reject(timeoutError);
139
+ }, 70);
140
+ })
141
+ ]);
142
+ expect.fail('stream pipe with infinite source should never return');
159
143
  }
160
- }
161
- }
162
- expect(openedStreams, 'listener - number of opened streams should match number of calls to newStream').to.have.equal(expectedStreams);
163
- expect(dialer.streams, 'all tracked streams should be deleted after the muxer has called close').to.have.lengthOf(0);
144
+ catch (e) {
145
+ if (e === timeoutError) {
146
+ expect.fail('expected stream pipe to throw an error after muxer closed with error');
147
+ }
148
+ }
149
+ })
150
+ ]);
151
+ expect(openedStreams).to.equal(expectedStreams, 'listener - number of opened streams should match number of calls to createStream');
152
+ expect(dialer.streams).to.have.lengthOf(0, 'all tracked streams should be deleted after the muxer has called close');
164
153
  });
165
154
  it('calling newStream after close throws an error', async () => {
166
- const dialerFactory = await common.setup();
167
- const dialer = dialerFactory.createStreamMuxer({
168
- direction: 'outbound'
169
- });
170
155
  await dialer.close();
171
- try {
172
- await dialer.newStream();
173
- expect.fail('newStream should throw if called after close');
174
- }
175
- catch (e) {
176
- expect(dialer.streams, 'closed muxer should have no streams').to.have.lengthOf(0);
177
- }
156
+ await expect(dialer.createStream()).to.eventually.rejected.with.property('name', 'MuxerClosedError');
157
+ expect(dialer.streams).to.have.lengthOf(0, 'closed muxer should have no streams');
178
158
  });
179
159
  it('closing one of the muxed streams doesn\'t close others', async () => {
180
- const p = duplexPair();
181
- const dialerFactory = await common.setup();
182
- const dialer = dialerFactory.createStreamMuxer({
183
- direction: 'outbound'
184
- });
185
- // Listener is echo server :)
186
- const listenerFactory = await common.setup();
187
- const listener = listenerFactory.createStreamMuxer({
188
- direction: 'inbound',
189
- onIncomingStream: (stream) => {
190
- void pipe(stream, stream).catch(() => { });
191
- }
192
- });
193
- void pipe(p[0], dialer, p[0]);
194
- void pipe(p[1], listener, p[1]);
195
- const stream = await dialer.newStream();
196
- const streams = await Promise.all(Array.from(Array(5), async () => dialer.newStream()));
197
- let closed = false;
198
- const controllers = [];
199
- const streamResults = streams.map(async (stream) => {
200
- const controller = new AbortController();
201
- controllers.push(controller);
202
- try {
203
- const abortableRand = abortableSource(infiniteRandom(), controller.signal, {
204
- abortName: 'TestAbortError'
205
- });
206
- await pipe(abortableRand, stream, drain);
207
- }
208
- catch (err) {
209
- if (err.name !== 'TestAbortError') {
210
- throw err;
211
- }
212
- }
213
- if (!closed) {
214
- throw new Error('stream should not have ended yet!');
160
+ const streamCount = 5;
161
+ const allStreamsOpen = Promise.withResolvers();
162
+ listener.addEventListener('stream', (evt) => {
163
+ echo(evt.detail).catch(() => { });
164
+ if (listener.streams.length === streamCount) {
165
+ allStreamsOpen.resolve();
215
166
  }
216
167
  });
217
- // Pause, and then send some data and close the first stream
218
- await delay(50);
219
- await pipe([new Uint8ArrayList(randomBuffer())], stream, drain);
220
- closed = true;
221
- // Abort all the other streams later
222
- await delay(50);
223
- controllers.forEach(c => { c.abort(); });
224
- // These should now all resolve without error
225
- await Promise.all(streamResults);
168
+ const streams = await Promise.all(Array.from(Array(streamCount), async () => dialer.createStream()));
169
+ await allStreamsOpen.promise;
170
+ expect(dialer.streams).to.have.lengthOf(streamCount);
171
+ expect(listener.streams).to.have.lengthOf(streamCount);
172
+ expect(dialer.streams.map(s => s.status)).to.deep.equal(new Array(streamCount).fill('open'));
173
+ expect(listener.streams.map(s => s.status)).to.deep.equal(new Array(streamCount).fill('open'));
174
+ const localStream = streams[0];
175
+ const remoteStream = listener.streams[0];
176
+ await Promise.all([
177
+ pEvent(remoteStream, 'close'),
178
+ pEvent(localStream, 'close'),
179
+ localStream.close()
180
+ ]);
181
+ expect(dialer.streams).to.have.lengthOf(streamCount - 1);
182
+ expect(listener.streams).to.have.lengthOf(streamCount - 1);
183
+ expect(dialer.streams.map(s => s.status)).to.deep.equal(new Array(streamCount - 1).fill('open'));
184
+ expect(listener.streams.map(s => s.status)).to.deep.equal(new Array(streamCount - 1).fill('open'));
226
185
  });
227
186
  it('can close a stream for writing', async () => {
228
- const deferred = pDefer();
229
- const p = duplexPair();
230
- const dialerFactory = await common.setup();
231
- const dialer = dialerFactory.createStreamMuxer({
232
- direction: 'outbound'
233
- });
234
- const data = [randomBuffer(), randomBuffer()];
235
- const listenerFactory = await common.setup();
236
- const listener = listenerFactory.createStreamMuxer({
237
- direction: 'inbound',
238
- onIncomingStream: (stream) => {
239
- void Promise.resolve().then(async () => {
187
+ const deferred = Promise.withResolvers();
188
+ const data = [Uint8Array.from([0, 1, 2, 3, 4]), Uint8Array.from([5, 6, 7, 8, 9])];
189
+ listener.addEventListener('stream', (evt) => {
190
+ void Promise.resolve().then(async () => {
191
+ try {
240
192
  // Immediate close for write
241
- await stream.closeWrite();
242
- const results = await pipe(stream, async (source) => {
243
- const data = [];
244
- for await (const chunk of source) {
245
- data.push(chunk.slice());
246
- }
247
- return data;
193
+ await evt.detail.close({
194
+ signal: AbortSignal.timeout(1_000)
248
195
  });
249
- expect(results).to.eql(data);
196
+ const results = await all(map(evt.detail, (buf) => {
197
+ return buf.subarray();
198
+ }));
199
+ expect(results).to.deep.equal(data);
250
200
  try {
251
- await stream.sink([new Uint8ArrayList(randomBuffer())]);
201
+ evt.detail.send(randomBuffer());
252
202
  }
253
203
  catch (err) {
254
204
  deferred.resolve(err);
255
205
  }
256
- deferred.reject(new Error('should not support writing to closed writer'));
206
+ throw new Error('should not support writing to closed writer');
207
+ }
208
+ catch (err) {
209
+ deferred.reject(err);
210
+ }
211
+ });
212
+ });
213
+ const stream = await dialer.createStream();
214
+ for (const buf of data) {
215
+ if (!stream.send(buf)) {
216
+ await pEvent(stream, 'drain', {
217
+ rejectionEvents: [
218
+ 'close'
219
+ ]
257
220
  });
258
221
  }
222
+ }
223
+ await stream.close({
224
+ signal: AbortSignal.timeout(1_000)
259
225
  });
260
- void pipe(p[0], dialer, p[0]);
261
- void pipe(p[1], listener, p[1]);
262
- const stream = await dialer.newStream();
263
- await stream.sink(data);
264
226
  const err = await deferred.promise;
265
227
  expect(err).to.have.property('name', 'StreamStateError');
266
228
  });
267
- it('can close a stream for reading', async () => {
268
- const deferred = pDefer();
269
- const p = duplexPair();
270
- const dialerFactory = await common.setup();
271
- const dialer = dialerFactory.createStreamMuxer({
272
- direction: 'outbound'
273
- });
274
- const data = [randomBuffer(), randomBuffer()].map(d => new Uint8ArrayList(d));
275
- const expected = toBuffer(data.map(d => d.subarray()));
276
- const listenerFactory = await common.setup();
277
- const listener = listenerFactory.createStreamMuxer({
278
- direction: 'inbound',
279
- onIncomingStream: (stream) => {
280
- void all(stream.source).then(deferred.resolve, deferred.reject);
281
- }
282
- });
283
- void pipe(p[0], dialer, p[0]);
284
- void pipe(p[1], listener, p[1]);
285
- const stream = await dialer.newStream();
286
- await stream.closeRead();
287
- // Source should be done
288
- void Promise.resolve().then(async () => {
289
- expect(await stream.source.next()).to.have.property('done', true);
290
- await stream.sink(data);
229
+ it('should emit a close event for closed streams not previously written', async () => {
230
+ listener.addEventListener('stream', async (evt) => {
231
+ void evt.detail.close();
291
232
  });
292
- const results = await deferred.promise;
293
- expect(toBuffer(results.map(b => b.subarray()))).to.equalBytes(expected);
294
- });
295
- it('calls onStreamEnd for closed streams not previously written', async () => {
296
- const deferred = pDefer();
297
- const onStreamEnd = () => { deferred.resolve(); };
298
- const dialerFactory = await common.setup();
299
- const dialer = dialerFactory.createStreamMuxer({
300
- direction: 'outbound',
301
- onStreamEnd
233
+ const deferred = Promise.withResolvers();
234
+ const stream = await dialer.createStream();
235
+ stream.addEventListener('close', () => {
236
+ deferred.resolve();
302
237
  });
303
- const stream = await dialer.newStream();
304
238
  await stream.close();
305
239
  await deferred.promise;
306
240
  });
307
- it('calls onStreamEnd for read and write closed streams not previously written', async () => {
308
- const deferred = pDefer();
309
- const onStreamEnd = () => { deferred.resolve(); };
310
- const dialerFactory = await common.setup();
311
- const dialer = dialerFactory.createStreamMuxer({
312
- direction: 'outbound',
313
- onStreamEnd
241
+ it('should emit a close event for aborted streams not previously written', async () => {
242
+ const deferred = Promise.withResolvers();
243
+ const stream = await dialer.createStream();
244
+ stream.addEventListener('close', () => {
245
+ deferred.resolve();
314
246
  });
315
- const stream = await dialer.newStream();
316
- await stream.closeWrite();
317
- await stream.closeRead();
247
+ stream.abort(new Error('Urk!'));
318
248
  await deferred.promise;
319
249
  });
320
250
  it('should wait for all data to be sent when closing streams', async () => {
321
- const deferred = pDefer();
322
- const p = duplexPair();
323
- const dialerFactory = await common.setup();
324
- const dialer = dialerFactory.createStreamMuxer({
325
- direction: 'outbound'
326
- });
327
- const listenerFactory = await common.setup();
328
- const listener = listenerFactory.createStreamMuxer({
329
- direction: 'inbound',
330
- onIncomingStream: (stream) => {
331
- const pb = pbStream(stream);
332
- void pb.read(Message)
333
- .then(async (message) => {
334
- deferred.resolve(message);
335
- await pb.unwrap().close();
336
- })
337
- .catch(err => {
338
- deferred.reject(err);
339
- });
340
- }
251
+ const deferred = Promise.withResolvers();
252
+ listener.addEventListener('stream', (evt) => {
253
+ const pb = pbStream(evt.detail);
254
+ void pb.read(Message)
255
+ .then(async (message) => {
256
+ deferred.resolve(message);
257
+ await evt.detail.close();
258
+ })
259
+ .catch(err => {
260
+ deferred.reject(err);
261
+ });
341
262
  });
342
- void pipe(p[0], dialer, p[0]);
343
- void pipe(p[1], listener, p[1]);
344
263
  const message = {
345
264
  message: 'hello world',
346
265
  value: 5,
347
266
  flag: true
348
267
  };
349
- const stream = await dialer.newStream();
268
+ const stream = await dialer.createStream();
350
269
  const pb = pbStream(stream);
351
270
  await pb.write(message, Message);
352
- await pb.unwrap().close();
271
+ await stream.close();
353
272
  await expect(deferred.promise).to.eventually.deep.equal(message);
354
273
  });
355
- /*
356
- it('should abort closing a stream with outstanding data to read', async () => {
357
- const deferred = pDefer<Message>()
358
-
359
- const p = duplexPair<Uint8Array | Uint8ArrayList>()
360
- const dialerFactory = await common.setup()
361
- const dialer = dialerFactory.createStreamMuxer({ direction: 'outbound' })
362
-
363
- const listenerFactory = await common.setup()
364
- const listener = listenerFactory.createStreamMuxer({
365
- direction: 'inbound',
366
- onIncomingStream: (stream) => {
367
- const pb = pbStream(stream)
368
-
369
- void pb.read(Message)
370
- .then(async message => {
371
- await pb.write(message, Message)
372
- await pb.unwrap().close()
373
- deferred.resolve(message)
374
- })
375
- .catch(err => {
376
- deferred.reject(err)
377
- })
378
- }
379
- })
380
-
381
- void pipe(p[0], dialer, p[0])
382
- void pipe(p[1], listener, p[1])
383
-
384
- const message = {
385
- message: 'hello world',
386
- value: 5,
387
- flag: true
388
- }
389
-
390
- const stream = await dialer.newStream()
391
-
392
- const pb = pbStream(stream)
393
- await pb.write(message, Message)
394
-
395
- console.info('await write back')
396
- await deferred.promise
397
-
398
- // let message arrive
399
- await delay(100)
400
-
401
- // close should time out as message is never read
402
- await expect(pb.unwrap().close()).to.eventually.be.rejected
403
- .with.property('name', 'TimeoutError')
404
- })
405
- */
274
+ it('should remove a stream in the streams list after aborting', async () => {
275
+ const [listenerStream, dialerStream] = await Promise.all([
276
+ pEvent(listener, 'stream').then(evt => evt.detail),
277
+ dialer.createStream()
278
+ ]);
279
+ expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
280
+ expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
281
+ await Promise.all([
282
+ pEvent(listenerStream, 'close'),
283
+ // eslint-disable-next-line @typescript-eslint/await-thenable
284
+ dialerStream.abort(new Error('Urk!'))
285
+ ]);
286
+ expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close');
287
+ expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close');
288
+ });
289
+ it('should remove a stream in the streams list after closing', async () => {
290
+ const [listenerStream, dialerStream] = await Promise.all([
291
+ pEvent(listener, 'stream').then(evt => evt.detail),
292
+ dialer.createStream()
293
+ ]);
294
+ expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
295
+ expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
296
+ await Promise.all([
297
+ dialerStream.close(),
298
+ listenerStream.close()
299
+ ]);
300
+ await delay(10);
301
+ expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close');
302
+ expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close');
303
+ });
304
+ it('should not remove a half-closed outbound stream', async () => {
305
+ const [listenerStream, dialerStream] = await Promise.all([
306
+ pEvent(listener, 'stream').then(evt => evt.detail),
307
+ dialer.createStream()
308
+ ]);
309
+ await dialerStream.close();
310
+ expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
311
+ expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
312
+ });
313
+ it('should not remove a half-closed inbound stream', async () => {
314
+ const [listenerStream, dialerStream] = await Promise.all([
315
+ pEvent(listener, 'stream').then(evt => evt.detail),
316
+ dialer.createStream()
317
+ ]);
318
+ await listenerStream.close();
319
+ expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
320
+ expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
321
+ });
322
+ it('should remove a stream half closed from both ends', async () => {
323
+ const [listenerStream, dialerStream] = await Promise.all([
324
+ pEvent(listener, 'stream').then(evt => evt.detail),
325
+ dialer.createStream()
326
+ ]);
327
+ expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
328
+ expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
329
+ await listenerStream.close();
330
+ expect(dialer.streams).to.include(dialerStream, 'dialer removed outbound stream before fully closing');
331
+ expect(listener.streams).to.include(listenerStream, 'listener removed inbound stream before fully closing');
332
+ await Promise.all([
333
+ pEvent(listenerStream, 'close'),
334
+ dialerStream.close()
335
+ ]);
336
+ await delay(10);
337
+ expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close');
338
+ expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close');
339
+ });
406
340
  });
407
341
  };
408
342
  //# sourceMappingURL=close-test.js.map