@libp2p/interface-compliance-tests 6.5.0-6059227cb → 6.5.0-87bc8d4fb

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 +15 -24
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/connection-encryption/utils/index.d.ts +3 -0
  5. package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
  6. package/dist/src/connection-encryption/utils/index.js +21 -0
  7. package/dist/src/connection-encryption/utils/index.js.map +1 -0
  8. package/dist/src/matchers.d.ts +12 -0
  9. package/dist/src/matchers.d.ts.map +1 -0
  10. package/dist/src/matchers.js +14 -0
  11. package/dist/src/matchers.js.map +1 -0
  12. package/dist/src/mocks/connection-manager.d.ts +27 -0
  13. package/dist/src/mocks/connection-manager.d.ts.map +1 -0
  14. package/dist/src/mocks/connection-manager.js +147 -0
  15. package/dist/src/mocks/connection-manager.js.map +1 -0
  16. package/dist/src/mocks/connection.d.ts +41 -0
  17. package/dist/src/mocks/connection.d.ts.map +1 -0
  18. package/dist/src/mocks/connection.js +236 -0
  19. package/dist/src/mocks/connection.js.map +1 -0
  20. package/dist/src/mocks/duplex.d.ts +4 -0
  21. package/dist/src/mocks/duplex.d.ts.map +1 -0
  22. package/dist/src/mocks/duplex.js +9 -0
  23. package/dist/src/mocks/duplex.js.map +1 -0
  24. package/dist/src/mocks/index.d.ts +12 -0
  25. package/dist/src/mocks/index.d.ts.map +1 -0
  26. package/dist/src/mocks/index.js +8 -0
  27. package/dist/src/mocks/index.js.map +1 -0
  28. package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
  29. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
  30. package/dist/src/mocks/multiaddr-connection.js +64 -0
  31. package/dist/src/mocks/multiaddr-connection.js.map +1 -0
  32. package/dist/src/mocks/muxer.d.ts +36 -0
  33. package/dist/src/mocks/muxer.d.ts.map +1 -0
  34. package/dist/src/mocks/muxer.js +234 -0
  35. package/dist/src/mocks/muxer.js.map +1 -0
  36. package/dist/src/mocks/registrar.d.ts +16 -0
  37. package/dist/src/mocks/registrar.d.ts.map +1 -0
  38. package/dist/src/mocks/registrar.js +66 -0
  39. package/dist/src/mocks/registrar.js.map +1 -0
  40. package/dist/src/mocks/upgrader.d.ts +9 -0
  41. package/dist/src/mocks/upgrader.d.ts.map +1 -0
  42. package/dist/src/mocks/upgrader.js +46 -0
  43. package/dist/src/mocks/upgrader.js.map +1 -0
  44. package/dist/src/pubsub/api.d.ts +6 -0
  45. package/dist/src/pubsub/api.d.ts.map +1 -0
  46. package/dist/src/pubsub/api.js +88 -0
  47. package/dist/src/pubsub/api.js.map +1 -0
  48. package/dist/src/pubsub/connection-handlers.d.ts +6 -0
  49. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
  50. package/dist/src/pubsub/connection-handlers.js +329 -0
  51. package/dist/src/pubsub/connection-handlers.js.map +1 -0
  52. package/dist/src/pubsub/emit-self.d.ts +6 -0
  53. package/dist/src/pubsub/emit-self.d.ts.map +1 -0
  54. package/dist/src/pubsub/emit-self.js +80 -0
  55. package/dist/src/pubsub/emit-self.js.map +1 -0
  56. package/dist/src/pubsub/index.d.ts +18 -0
  57. package/dist/src/pubsub/index.d.ts.map +1 -0
  58. package/dist/src/pubsub/index.js +17 -0
  59. package/dist/src/pubsub/index.js.map +1 -0
  60. package/dist/src/pubsub/messages.d.ts +6 -0
  61. package/dist/src/pubsub/messages.d.ts.map +1 -0
  62. package/dist/src/pubsub/messages.js +48 -0
  63. package/dist/src/pubsub/messages.js.map +1 -0
  64. package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
  65. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
  66. package/dist/src/pubsub/multiple-nodes.js +350 -0
  67. package/dist/src/pubsub/multiple-nodes.js.map +1 -0
  68. package/dist/src/pubsub/two-nodes.d.ts +6 -0
  69. package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
  70. package/dist/src/pubsub/two-nodes.js +216 -0
  71. package/dist/src/pubsub/two-nodes.js.map +1 -0
  72. package/dist/src/pubsub/utils.d.ts +5 -0
  73. package/dist/src/pubsub/utils.d.ts.map +1 -0
  74. package/dist/src/pubsub/utils.js +27 -0
  75. package/dist/src/pubsub/utils.js.map +1 -0
  76. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  77. package/dist/src/stream-muxer/base-test.js +345 -62
  78. package/dist/src/stream-muxer/base-test.js.map +1 -1
  79. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  80. package/dist/src/stream-muxer/close-test.js +320 -254
  81. package/dist/src/stream-muxer/close-test.js.map +1 -1
  82. package/dist/src/stream-muxer/index.js +2 -2
  83. package/dist/src/stream-muxer/index.js.map +1 -1
  84. package/dist/src/stream-muxer/{stream-test.d.ts → mega-stress-test.d.ts} +2 -2
  85. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  86. package/dist/src/stream-muxer/mega-stress-test.js +11 -0
  87. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  88. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  89. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  90. package/dist/src/stream-muxer/spawner.js +39 -0
  91. package/dist/src/stream-muxer/spawner.js.map +1 -0
  92. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  93. package/dist/src/stream-muxer/stress-test.js +16 -70
  94. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  95. package/dist/src/transport/index.d.ts.map +1 -1
  96. package/dist/src/transport/index.js +205 -235
  97. package/dist/src/transport/index.js.map +1 -1
  98. package/dist/src/transport/utils.d.ts.map +1 -1
  99. package/dist/src/transport/utils.js +2 -3
  100. package/dist/src/transport/utils.js.map +1 -1
  101. package/package.json +51 -23
  102. package/src/connection-encryption/index.ts +20 -27
  103. package/src/connection-encryption/utils/index.ts +27 -0
  104. package/src/matchers.ts +18 -0
  105. package/src/mocks/connection-manager.ts +216 -0
  106. package/src/mocks/connection.ts +309 -0
  107. package/src/mocks/duplex.ts +11 -0
  108. package/src/mocks/index.ts +11 -0
  109. package/src/mocks/multiaddr-connection.ts +80 -0
  110. package/src/mocks/muxer.ts +331 -0
  111. package/src/mocks/registrar.ts +86 -0
  112. package/src/mocks/upgrader.ts +65 -0
  113. package/src/pubsub/api.ts +116 -0
  114. package/src/pubsub/connection-handlers.ts +413 -0
  115. package/src/pubsub/emit-self.ts +99 -0
  116. package/src/pubsub/index.ts +34 -0
  117. package/src/pubsub/messages.ts +59 -0
  118. package/src/pubsub/multiple-nodes.ts +440 -0
  119. package/src/pubsub/two-nodes.ts +272 -0
  120. package/src/pubsub/utils.ts +34 -0
  121. package/src/stream-muxer/base-test.ts +413 -75
  122. package/src/stream-muxer/close-test.ts +343 -305
  123. package/src/stream-muxer/index.ts +2 -2
  124. package/src/stream-muxer/mega-stress-test.ts +14 -0
  125. package/src/stream-muxer/spawner.ts +57 -0
  126. package/src/stream-muxer/stress-test.ts +18 -92
  127. package/src/transport/index.ts +241 -281
  128. package/src/transport/utils.ts +2 -3
  129. package/dist/src/stream-muxer/stream-test.d.ts.map +0 -1
  130. package/dist/src/stream-muxer/stream-test.js +0 -290
  131. package/dist/src/stream-muxer/stream-test.js.map +0 -1
  132. package/src/stream-muxer/stream-test.ts +0 -381
@@ -1,15 +1,13 @@
1
- import { stop, TimeoutError } from '@libp2p/interface'
2
- import { prefixLogger } from '@libp2p/logger'
1
+ import { stop } from '@libp2p/interface'
3
2
  import { expect } from 'aegir/chai'
4
3
  import delay from 'delay'
5
- import all from 'it-all'
6
4
  import drain from 'it-drain'
7
5
  import { pushable } from 'it-pushable'
6
+ import pDefer from 'p-defer'
8
7
  import { pEvent } from 'p-event'
9
8
  import pRetry from 'p-retry'
10
9
  import pWaitFor from 'p-wait-for'
11
10
  import { raceSignal } from 'race-signal'
12
- import { Uint8ArrayList } from 'uint8arraylist'
13
11
  import { isValidTick } from '../is-valid-tick.js'
14
12
  import { createPeer, getTransportManager, getUpgrader } from './utils.js'
15
13
  import type { TestSetup } from '../index.js'
@@ -18,6 +16,7 @@ import type { Connection, Libp2p, Stream, StreamHandler } from '@libp2p/interfac
18
16
  import type { Multiaddr } from '@multiformats/multiaddr'
19
17
  import type { MultiaddrMatcher } from '@multiformats/multiaddr-matcher'
20
18
  import type { Libp2pInit } from 'libp2p'
19
+ import type { DeferredPromise } from 'p-defer'
21
20
 
22
21
  export interface TransportTestFixtures {
23
22
  /**
@@ -49,113 +48,34 @@ export interface TransportTestFixtures {
49
48
 
50
49
  async function getSetup (common: TestSetup<TransportTestFixtures>): Promise<{ dialer: Libp2p<{ echo: Echo }>, listener?: Libp2p<{ echo: Echo }>, dialAddrs: Multiaddr[], dialMultiaddrMatcher: MultiaddrMatcher, listenMultiaddrMatcher: MultiaddrMatcher }> {
51
50
  const setup = await common.setup()
52
- const dialer = await createPeer({
53
- logger: prefixLogger('dialer'),
54
- ...setup.dialer
55
- })
51
+ const dialer = await createPeer(setup.dialer)
56
52
  let listener
57
53
 
58
54
  if (setup.listener != null) {
59
- listener = await createPeer({
60
- logger: prefixLogger('listener'),
61
- ...setup.listener
62
- })
55
+ listener = await createPeer(setup.listener)
63
56
  }
64
57
 
65
- let dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs
58
+ const dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs
66
59
 
67
60
  if (dialAddrs == null) {
68
61
  throw new Error('Listener config or dial addresses must be specified')
69
62
  }
70
63
 
71
- dialAddrs = dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma))
72
-
73
- if (dialAddrs.length === 0) {
74
- throw new Error('Listener was not listening on any addresses that the dialMultiaddrMatcher matched')
75
- }
76
-
77
64
  return {
78
65
  dialer,
79
66
  listener,
80
- dialAddrs,
67
+ dialAddrs: dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma)),
81
68
  dialMultiaddrMatcher: setup.dialMultiaddrMatcher,
82
69
  listenMultiaddrMatcher: setup.listenMultiaddrMatcher
83
70
  }
84
71
  }
85
72
 
86
73
  export default (common: TestSetup<TransportTestFixtures>): void => {
87
- describe('transport events', () => {
88
- let dialer: Libp2p<{ echo: Echo }>
89
- let listener: Libp2p<{ echo: Echo }> | undefined
90
- let listenMultiaddrMatcher: MultiaddrMatcher
91
-
92
- afterEach(async () => {
93
- await stop(dialer, listener)
94
- await common.teardown()
95
- })
96
-
97
- it('emits listening', async function () {
98
- ({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common))
99
-
100
- if (listener == null) {
101
- return this.skip()
102
- }
103
-
104
- await listener.stop()
105
-
106
- const transportListeningPromise = Promise.withResolvers<void>()
107
-
108
- listener.addEventListener('transport:listening', (event) => {
109
- const transportListener = event.detail
110
-
111
- if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
112
- transportListeningPromise.resolve()
113
- }
114
- })
115
-
116
- await listener.start()
117
-
118
- await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
119
- translateError: () => {
120
- return new TimeoutError('Did not emit listening event')
121
- }
122
- })
123
- })
124
-
125
- it('emits close', async function () {
126
- ({ dialer, listener } = await getSetup(common))
127
-
128
- if (listener == null) {
129
- return this.skip()
130
- }
131
-
132
- const transportManager = getTransportManager(listener)
133
- const transportListener = transportManager.getListeners()
134
- .filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
135
- .pop()
136
-
137
- if (transportListener == null) {
138
- throw new Error('Could not find address listener')
139
- }
140
-
141
- const p = pEvent(transportListener, 'close')
142
-
143
- await listener.stop()
144
-
145
- await raceSignal(p, AbortSignal.timeout(1000), {
146
- translateError: () => {
147
- return new TimeoutError('Did not emit close event')
148
- }
149
- })
150
- })
151
- })
152
-
153
74
  describe('interface-transport', () => {
154
75
  let dialer: Libp2p<{ echo: Echo }>
155
76
  let listener: Libp2p<{ echo: Echo }> | undefined
156
77
  let dialAddrs: Multiaddr[]
157
78
  let dialMultiaddrMatcher: MultiaddrMatcher
158
- let listenMultiaddrMatcher: MultiaddrMatcher
159
79
 
160
80
  afterEach(async () => {
161
81
  await stop(dialer, listener)
@@ -170,7 +90,7 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
170
90
  signal: AbortSignal.timeout(5000)
171
91
  })
172
92
 
173
- expect(output.subarray()).to.equalBytes(input)
93
+ expect(output).to.equalBytes(input)
174
94
  })
175
95
 
176
96
  it('should listen on multiple addresses', async () => {
@@ -178,16 +98,13 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
178
98
 
179
99
  const input = Uint8Array.from([0, 1, 2, 3, 4])
180
100
 
181
- const output1 = await dialer.services.echo.echo(dialAddrs[0], input, {
101
+ await expect(dialer.services.echo.echo(dialAddrs[0], input, {
182
102
  signal: AbortSignal.timeout(5000)
183
- })
184
- expect(output1.subarray()).to.equalBytes(input)
103
+ })).to.eventually.deep.equal(input)
185
104
 
186
- const output2 = await dialer.services.echo.echo(dialAddrs[1], input, {
187
- signal: AbortSignal.timeout(5000),
188
- force: true
189
- })
190
- expect(output2.subarray()).to.equalBytes(input)
105
+ await expect(dialer.services.echo.echo(dialAddrs[1], input, {
106
+ signal: AbortSignal.timeout(5000)
107
+ })).to.eventually.deep.equal(input)
191
108
  })
192
109
 
193
110
  it('can close connections', async () => {
@@ -214,20 +131,16 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
214
131
  })
215
132
 
216
133
  it('should close all streams when the connection closes', async () => {
217
- ({ dialer, listener, dialAddrs, listenMultiaddrMatcher } = await getSetup(common))
134
+ ({ dialer, listener, dialAddrs } = await getSetup(common))
218
135
 
219
- let incomingConnectionPromise: PromiseWithResolvers<Connection> | undefined
136
+ let incomingConnectionPromise: DeferredPromise<Connection> | undefined
220
137
 
221
138
  if (listener != null) {
222
- incomingConnectionPromise = Promise.withResolvers<Connection>()
139
+ incomingConnectionPromise = pDefer<Connection>()
223
140
 
224
141
  listener.addEventListener('connection:open', (event) => {
225
142
  const conn = event.detail
226
143
 
227
- if (!listenMultiaddrMatcher.matches(conn.remoteAddr)) {
228
- return
229
- }
230
-
231
144
  if (conn.remotePeer.equals(dialer.peerId)) {
232
145
  incomingConnectionPromise?.resolve(conn)
233
146
  }
@@ -247,29 +160,22 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
247
160
  })
248
161
  }
249
162
 
250
- expect(connection).to.have.property('streams').that.has.lengthOf(5)
251
-
252
- if (remoteConn != null) {
253
- await pWaitFor(() => remoteConn.streams.filter(s => s.protocol === '/echo/1.0.0').length === 5, {
254
- timeout: 5_000,
255
- interval: 1_000
256
- })
257
- }
163
+ const streams = connection.streams
258
164
 
259
165
  // Close the connection and verify all streams have been closed
260
- await Promise.all([
261
- pEvent(connection, 'close'),
262
- pEvent(remoteConn ?? connection, 'close'),
263
- connection.close()
264
- ])
166
+ await connection.close()
265
167
 
266
- expect(connection).to.have.property('status', 'closed')
267
- expect(connection).to.have.property('streams').that.is.empty()
168
+ await pWaitFor(() => connection.streams.length === 0, {
169
+ timeout: 5000
170
+ })
268
171
 
269
172
  if (remoteConn != null) {
270
- expect(remoteConn).to.have.property('status', 'closed')
271
- expect(remoteConn).to.have.property('streams').that.is.empty()
173
+ await pWaitFor(() => remoteConn.streams.length === 0, {
174
+ timeout: 5000
175
+ })
272
176
  }
177
+
178
+ expect(streams.find(stream => stream.status === 'open')).to.be.undefined()
273
179
  })
274
180
 
275
181
  it('should not handle connection if upgradeInbound rejects', async function () {
@@ -321,138 +227,189 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
321
227
  }
322
228
  })
323
229
 
324
- it('should handle one small write', async function () {
230
+ it('should handle one big write', async function () {
325
231
  const timeout = 120_000
326
232
  this.timeout(timeout);
327
233
  ({ dialer, listener, dialAddrs } = await getSetup(common))
328
234
 
329
- const connection = await dialer.dial(dialAddrs[0])
330
-
331
- const input = new Uint8Array(1024).fill(5)
332
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
235
+ const input = new Uint8Array(1024 * 1024 * 10).fill(5)
236
+ const output = await dialer.services.echo.echo(dialAddrs[0], input, {
333
237
  signal: AbortSignal.timeout(timeout)
334
238
  })
335
239
 
336
- expect(output.subarray()).to.equalBytes(input)
337
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty()
240
+ expect(output).to.equalBytes(input)
338
241
  })
339
242
 
340
- it('should handle one big write', async function () {
341
- const timeout = 540_000
243
+ it('should handle many small writes', async function () {
244
+ const timeout = 360_000
342
245
  this.timeout(timeout);
343
246
  ({ dialer, listener, dialAddrs } = await getSetup(common))
344
247
 
345
- const connection = await dialer.dial(dialAddrs[0])
248
+ for (let i = 0; i < 2000; i++) {
249
+ const input = new Uint8Array(1024).fill(5)
250
+ const output = await dialer.services.echo.echo(dialAddrs[0], input, {
251
+ signal: AbortSignal.timeout(timeout)
252
+ })
346
253
 
347
- const input = new Uint8Array(1024 * 1024 * 10).fill(5)
348
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
349
- signal: AbortSignal.timeout(timeout)
254
+ expect(output).to.equalBytes(input)
255
+ }
256
+ })
257
+
258
+ it('can close a stream for reading but send a large amount of data', async function () {
259
+ const timeout = 120_000
260
+ this.timeout(timeout);
261
+ ({ dialer, listener, dialAddrs } = await getSetup(common))
262
+
263
+ if (listener == null) {
264
+ return this.skip()
265
+ }
266
+
267
+ const protocol = '/send-data/1.0.0'
268
+ const chunkSize = 1024
269
+ const bytes = chunkSize * 1024 * 10
270
+ const deferred = pDefer()
271
+
272
+ await listener.handle(protocol, ({ stream }) => {
273
+ Promise.resolve().then(async () => {
274
+ let read = 0
275
+
276
+ for await (const buf of stream.source) {
277
+ read += buf.byteLength
278
+
279
+ if (read === bytes) {
280
+ deferred.resolve()
281
+ break
282
+ }
283
+ }
284
+ })
285
+ .catch(err => {
286
+ deferred.reject(err)
287
+ stream.abort(err)
288
+ })
350
289
  })
351
290
 
352
- expect(output.subarray()).to.equalBytes(input)
353
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty()
291
+ const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
292
+
293
+ await stream.closeRead()
294
+
295
+ await stream.sink((async function * () {
296
+ for (let i = 0; i < bytes; i += chunkSize) {
297
+ yield new Uint8Array(chunkSize)
298
+ }
299
+ })())
300
+
301
+ await stream.close()
302
+
303
+ await deferred.promise
354
304
  })
355
305
 
356
- it('should handle many small writes', async function () {
357
- const timeout = 360_000
306
+ it('can close a stream for writing but receive a large amount of data', async function () {
307
+ const timeout = 120_000
358
308
  this.timeout(timeout);
359
309
  ({ dialer, listener, dialAddrs } = await getSetup(common))
360
310
 
361
- const connection = await dialer.dial(dialAddrs[0])
362
- const echoProtocol = dialer.services.echo.protocol
311
+ if (listener == null) {
312
+ return this.skip()
313
+ }
363
314
 
364
- for (let i = 0; i < 2_000; i++) {
365
- const input = new Uint8Array(1024).fill(5)
366
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
367
- signal: AbortSignal.timeout(timeout)
315
+ const protocol = '/receive-data/1.0.0'
316
+ const chunkSize = 1024
317
+ const bytes = chunkSize * 1024 * 10
318
+ const deferred = pDefer()
319
+
320
+ await listener.handle(protocol, ({ stream }) => {
321
+ Promise.resolve().then(async () => {
322
+ await stream.sink((async function * () {
323
+ for (let i = 0; i < bytes; i += chunkSize) {
324
+ yield new Uint8Array(chunkSize)
325
+ }
326
+ })())
327
+
328
+ await stream.close()
368
329
  })
330
+ .catch(err => {
331
+ deferred.reject(err)
332
+ stream.abort(err)
333
+ })
334
+ })
335
+
336
+ const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
337
+
338
+ await stream.closeWrite()
369
339
 
370
- expect(output.subarray()).to.equalBytes(input)
371
- expect(connection.streams.filter(s => s.protocol === echoProtocol)).to.be.empty()
340
+ let read = 0
341
+
342
+ for await (const buf of stream.source) {
343
+ read += buf.byteLength
372
344
  }
345
+
346
+ expect(read).to.equal(bytes)
373
347
  })
374
348
 
375
- it('can close local stream while a remote stream is writing', async function () {
349
+ it('can close local stream for writing and reading while a remote stream is writing', async function () {
376
350
  ({ dialer, listener, dialAddrs } = await getSetup(common))
377
351
 
378
352
  if (listener == null) {
379
353
  return this.skip()
380
354
  }
381
355
 
382
- // 1. remote stream close read
383
- // 2. local stream close write
384
- // 3. remote stream close write
385
-
386
356
  /**
387
357
  * NodeA NodeB
388
358
  * | <--- STOP_SENDING |
389
359
  * | FIN ---> |
390
- * | <--- DATA |
391
360
  * | <--- FIN |
392
361
  * | FIN_ACK ---> |
393
362
  * | <--- FIN_ACK |
394
363
  */
395
364
 
396
- const getRemoteStream = Promise.withResolvers<Stream>()
365
+ const getRemoteStream = pDefer<Stream>()
397
366
  const protocol = '/close-local-while-remote-writes/1.0.0'
398
367
 
399
- const streamHandler: StreamHandler = (stream) => {
400
- getRemoteStream.resolve(stream)
368
+ const streamHandler: StreamHandler = ({ stream }) => {
369
+ void Promise.resolve().then(async () => {
370
+ getRemoteStream.resolve(stream)
371
+ })
401
372
  }
402
373
 
403
- await listener.handle(protocol, streamHandler, {
374
+ await listener.handle(protocol, (info) => {
375
+ streamHandler(info)
376
+ }, {
404
377
  runOnLimitedConnection: true
405
378
  })
406
379
 
407
380
  const connection = await dialer.dial(dialAddrs[0])
408
381
 
409
- const [
410
- localStream,
411
- remoteStream
412
- ] = await Promise.all([
413
- // open a stream on the echo protocol
414
- connection.newStream(protocol, {
415
- runOnLimitedConnection: true
416
- }),
417
- getRemoteStream.promise
418
- ])
419
-
420
- // ignore incoming data
421
- void drain(localStream)
422
-
423
- // close the remote readable end
382
+ // open a stream on the echo protocol
383
+ const stream = await connection.newStream(protocol, {
384
+ runOnLimitedConnection: true
385
+ })
386
+
387
+ // close the write end immediately
388
+ const p = stream.closeWrite()
389
+
390
+ const remoteStream = await getRemoteStream.promise
391
+ // close the readable end of the remote stream
424
392
  await remoteStream.closeRead()
425
393
 
426
- // send data from the remote to the local
394
+ // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
427
395
  const remoteInputStream = pushable<Uint8Array>()
428
- Promise.resolve().then(async () => {
429
- for await (const buf of remoteInputStream) {
430
- const sendMore = remoteStream.send(buf)
431
-
432
- if (sendMore === false) {
433
- await pEvent(remoteStream, 'drain', {
434
- rejectionEvents: [
435
- 'close'
436
- ]
437
- })
438
- }
439
- }
396
+ void remoteStream.sink(remoteInputStream)
440
397
 
441
- // close the remote writable end
442
- await remoteStream.close()
398
+ // wait for remote to receive local close-write
399
+ await pRetry(() => {
400
+ if (remoteStream.readStatus !== 'closed') {
401
+ throw new Error('Remote stream read status ' + remoteStream.readStatus)
402
+ }
403
+ }, {
404
+ minTimeout: 100
443
405
  })
444
406
 
445
- await Promise.all([
446
- // wait for remote to receive local FIN
447
- pEvent(remoteStream, 'remoteCloseWrite'),
448
-
449
- // wait to receive FIN_ACK
450
- localStream.close()
451
- ])
452
-
453
- // stop sending remote -> local
407
+ // remote closes write
454
408
  remoteInputStream.end()
455
409
 
410
+ // wait to receive FIN_ACK
411
+ await p
412
+
456
413
  // wait for remote to notice closure
457
414
  await pRetry(() => {
458
415
  if (remoteStream.status !== 'closed') {
@@ -460,12 +417,11 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
460
417
  }
461
418
  })
462
419
 
463
- // both ends should be closed
464
- assertStreamClosed(localStream)
420
+ assertStreamClosed(stream)
465
421
  assertStreamClosed(remoteStream)
466
422
  })
467
423
 
468
- it('can close local stream for writing while a remote stream is reading', async function () {
424
+ it('can close local stream for writing and reading while a remote stream is writing using source/sink', async function () {
469
425
  ({ dialer, listener, dialAddrs } = await getSetup(common))
470
426
 
471
427
  if (listener == null) {
@@ -474,80 +430,62 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
474
430
 
475
431
  /**
476
432
  * NodeA NodeB
477
- * | DATA ---> |
478
433
  * | FIN ---> |
479
434
  * | <--- FIN |
480
435
  * | FIN_ACK ---> |
481
436
  * | <--- FIN_ACK |
482
437
  */
483
438
 
484
- const getRemoteStream = Promise.withResolvers<Stream>()
439
+ const getRemoteStream = pDefer<Stream>()
485
440
  const protocol = '/close-local-while-remote-reads/1.0.0'
486
441
 
487
- const streamHandler: StreamHandler = (stream) => {
488
- getRemoteStream.resolve(stream)
442
+ const streamHandler: StreamHandler = ({ stream }) => {
443
+ void Promise.resolve().then(async () => {
444
+ getRemoteStream.resolve(stream)
445
+ })
489
446
  }
490
447
 
491
- await listener.handle(protocol, streamHandler, {
448
+ await listener.handle(protocol, (info) => {
449
+ streamHandler(info)
450
+ }, {
492
451
  runOnLimitedConnection: true
493
452
  })
494
453
 
495
454
  const connection = await dialer.dial(dialAddrs[0])
496
455
 
497
- const [
498
- localStream,
499
- remoteStream
500
- ] = await Promise.all([
501
- // open a stream on the echo protocol
502
- connection.newStream(protocol, {
503
- runOnLimitedConnection: true
504
- }),
505
- getRemoteStream.promise
506
- ])
507
-
508
- // ignore incoming data
509
- void drain(remoteStream)
510
-
511
- // close the remote stream writable end when the local sends a FIN
512
- remoteStream.addEventListener('remoteCloseWrite', () => {
513
- remoteStream.close()
514
- .catch(err => {
515
- remoteStream.abort(err)
516
- })
456
+ // open a stream on the echo protocol
457
+ const stream = await connection.newStream(protocol, {
458
+ runOnLimitedConnection: true
517
459
  })
518
460
 
519
- // send data to the remote then close the stream
520
- const data = [
521
- Uint8Array.from([0, 1, 2, 3]),
522
- Uint8Array.from([4, 5, 6, 7]),
523
- Uint8Array.from([8, 9, 0, 1])
524
- ]
525
-
526
- const [
527
- remoteCloseEvent,
528
- localCloseEvent
529
- ] = await Promise.all([
530
- // wait for the remote to close
531
- pEvent(remoteStream, 'close'),
532
- pEvent(localStream, 'close'),
533
- (async () => {
534
- for (const buf of data) {
535
- if (!localStream.send(buf)) {
536
- await pEvent(localStream, 'drain', {
537
- rejectionEvents: [
538
- 'close'
539
- ]
540
- })
541
- }
542
- }
461
+ // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
462
+ const p = stream.sink([])
463
+
464
+ const remoteStream = await getRemoteStream.promise
465
+ // close the readable end of the remote stream
466
+ await remoteStream.closeRead()
467
+ // readable end should finish
468
+ await drain(remoteStream.source)
469
+
470
+ // wait for remote to receive local close-write
471
+ await pRetry(() => {
472
+ if (remoteStream.readStatus !== 'closed') {
473
+ throw new Error('Remote stream read status ' + remoteStream.readStatus)
474
+ }
475
+ }, {
476
+ minTimeout: 100
477
+ })
478
+
479
+ // remote closes write
480
+ await remoteStream.sink([])
543
481
 
544
- // close the local writable end
545
- await localStream.close()
546
- })()
547
- ])
482
+ // wait to receive FIN_ACK
483
+ await p
548
484
 
549
- expect(remoteCloseEvent).to.not.have.property('error', 'remote stream did not close cleanly')
550
- expect(localCloseEvent).to.not.have.property('error', 'local stream did not close cleanly')
485
+ // close read end of stream
486
+ await stream.closeRead()
487
+ // readable end should finish
488
+ await drain(stream.source)
551
489
 
552
490
  // wait for remote to notice closure
553
491
  await pRetry(() => {
@@ -556,50 +494,70 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
556
494
  }
557
495
  })
558
496
 
559
- // both ends should be closed
560
- assertStreamClosed(localStream)
497
+ assertStreamClosed(stream)
561
498
  assertStreamClosed(remoteStream)
562
499
  })
500
+ })
563
501
 
564
- it('can close a stream for writing but receive a large amount of data', async function () {
565
- const timeout = 120_000
566
- this.timeout(timeout);
567
- ({ dialer, listener, dialAddrs } = await getSetup(common))
502
+ describe('events', () => {
503
+ let dialer: Libp2p<{ echo: Echo }>
504
+ let listener: Libp2p<{ echo: Echo }> | undefined
505
+ let listenMultiaddrMatcher: MultiaddrMatcher
506
+
507
+ afterEach(async () => {
508
+ await stop(dialer, listener)
509
+ await common.teardown()
510
+ })
511
+
512
+ it('emits listening', async function () {
513
+ ({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common))
568
514
 
569
515
  if (listener == null) {
570
516
  return this.skip()
571
517
  }
572
518
 
573
- const protocol = '/receive-data/1.0.0'
574
- const chunkSize = 1024
575
- const bytes = chunkSize * 1024 * 10
519
+ await listener.stop()
576
520
 
577
- await listener.handle(protocol, async (stream) => {
578
- for (let i = 0; i < bytes; i += chunkSize) {
579
- const sendMore = stream.send(new Uint8Array(chunkSize))
580
-
581
- if (!sendMore) {
582
- await pEvent(stream, 'drain', {
583
- rejectionEvents: [
584
- 'close'
585
- ]
586
- })
587
- }
521
+ const transportListeningPromise = pDefer()
522
+
523
+ listener.addEventListener('transport:listening', (event) => {
524
+ const transportListener = event.detail
525
+
526
+ if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
527
+ transportListeningPromise.resolve()
588
528
  }
529
+ })
589
530
 
590
- await stream.close()
531
+ await listener.start()
532
+
533
+ await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
534
+ errorMessage: 'Did not emit listening event'
591
535
  })
536
+ })
592
537
 
593
- const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
538
+ it('emits close', async function () {
539
+ ({ dialer, listener } = await getSetup(common))
540
+
541
+ if (listener == null) {
542
+ return this.skip()
543
+ }
594
544
 
595
- const [
596
- output
597
- ] = await Promise.all([
598
- all(stream),
599
- stream.close()
600
- ])
545
+ const transportManager = getTransportManager(listener)
546
+ const transportListener = transportManager.getListeners()
547
+ .filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
548
+ .pop()
549
+
550
+ if (transportListener == null) {
551
+ throw new Error('Could not find address listener')
552
+ }
553
+
554
+ const p = pEvent(transportListener, 'close')
601
555
 
602
- expect(new Uint8ArrayList(...output).byteLength).to.equal(bytes)
556
+ await listener.stop()
557
+
558
+ await raceSignal(p, AbortSignal.timeout(1000), {
559
+ errorMessage: 'Did not emit close event'
560
+ })
603
561
  })
604
562
  })
605
563
  }
@@ -610,4 +568,6 @@ function assertStreamClosed (stream: Stream): void {
610
568
  expect(stream.writeStatus).to.equal('closed')
611
569
 
612
570
  expect(stream.timeline.close).to.be.a('number')
571
+ expect(stream.timeline.closeRead).to.be.a('number')
572
+ expect(stream.timeline.closeWrite).to.be.a('number')
613
573
  }