@libp2p/interface-compliance-tests 6.4.16 → 6.5.0-8484de8a2

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