@libp2p/interface-compliance-tests 6.5.0-8484de8a2 → 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 (131) 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 -253
  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 +203 -232
  97. package/dist/src/transport/index.js.map +1 -1
  98. package/dist/src/transport/utils.js +2 -2
  99. package/dist/src/transport/utils.js.map +1 -1
  100. package/package.json +51 -23
  101. package/src/connection-encryption/index.ts +20 -27
  102. package/src/connection-encryption/utils/index.ts +27 -0
  103. package/src/matchers.ts +18 -0
  104. package/src/mocks/connection-manager.ts +216 -0
  105. package/src/mocks/connection.ts +309 -0
  106. package/src/mocks/duplex.ts +11 -0
  107. package/src/mocks/index.ts +11 -0
  108. package/src/mocks/multiaddr-connection.ts +80 -0
  109. package/src/mocks/muxer.ts +331 -0
  110. package/src/mocks/registrar.ts +86 -0
  111. package/src/mocks/upgrader.ts +65 -0
  112. package/src/pubsub/api.ts +116 -0
  113. package/src/pubsub/connection-handlers.ts +413 -0
  114. package/src/pubsub/emit-self.ts +99 -0
  115. package/src/pubsub/index.ts +34 -0
  116. package/src/pubsub/messages.ts +59 -0
  117. package/src/pubsub/multiple-nodes.ts +440 -0
  118. package/src/pubsub/two-nodes.ts +272 -0
  119. package/src/pubsub/utils.ts +34 -0
  120. package/src/stream-muxer/base-test.ts +413 -75
  121. package/src/stream-muxer/close-test.ts +343 -304
  122. package/src/stream-muxer/index.ts +2 -2
  123. package/src/stream-muxer/mega-stress-test.ts +14 -0
  124. package/src/stream-muxer/spawner.ts +57 -0
  125. package/src/stream-muxer/stress-test.ts +18 -92
  126. package/src/transport/index.ts +241 -280
  127. package/src/transport/utils.ts +2 -2
  128. package/dist/src/stream-muxer/stream-test.d.ts.map +0 -1
  129. package/dist/src/stream-muxer/stream-test.js +0 -289
  130. package/dist/src/stream-muxer/stream-test.js.map +0 -1
  131. package/src/stream-muxer/stream-test.ts +0 -380
@@ -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,28 +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.length === 5, {
254
- timeout: 5000
255
- })
256
- }
163
+ const streams = connection.streams
257
164
 
258
165
  // Close the connection and verify all streams have been closed
259
- await Promise.all([
260
- pEvent(connection, 'close'),
261
- pEvent(remoteConn ?? connection, 'close'),
262
- connection.close()
263
- ])
166
+ await connection.close()
264
167
 
265
- expect(connection).to.have.property('status', 'closed')
266
- expect(connection).to.have.property('streams').that.is.empty()
168
+ await pWaitFor(() => connection.streams.length === 0, {
169
+ timeout: 5000
170
+ })
267
171
 
268
172
  if (remoteConn != null) {
269
- expect(remoteConn).to.have.property('status', 'closed')
270
- expect(remoteConn).to.have.property('streams').that.is.empty()
173
+ await pWaitFor(() => remoteConn.streams.length === 0, {
174
+ timeout: 5000
175
+ })
271
176
  }
177
+
178
+ expect(streams.find(stream => stream.status === 'open')).to.be.undefined()
272
179
  })
273
180
 
274
181
  it('should not handle connection if upgradeInbound rejects', async function () {
@@ -320,138 +227,189 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
320
227
  }
321
228
  })
322
229
 
323
- it('should handle one small write', async function () {
230
+ it('should handle one big write', async function () {
324
231
  const timeout = 120_000
325
232
  this.timeout(timeout);
326
233
  ({ dialer, listener, dialAddrs } = await getSetup(common))
327
234
 
328
- const connection = await dialer.dial(dialAddrs[0])
329
-
330
- const input = new Uint8Array(1024).fill(5)
331
- 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, {
332
237
  signal: AbortSignal.timeout(timeout)
333
238
  })
334
239
 
335
- expect(output.subarray()).to.equalBytes(input)
336
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty()
240
+ expect(output).to.equalBytes(input)
337
241
  })
338
242
 
339
- it('should handle one big write', async function () {
340
- const timeout = 540_000
243
+ it('should handle many small writes', async function () {
244
+ const timeout = 360_000
341
245
  this.timeout(timeout);
342
246
  ({ dialer, listener, dialAddrs } = await getSetup(common))
343
247
 
344
- 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
+ })
345
253
 
346
- const input = new Uint8Array(1024 * 1024 * 10).fill(5)
347
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
348
- 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
+ })
349
289
  })
350
290
 
351
- expect(output.subarray()).to.equalBytes(input)
352
- 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
353
304
  })
354
305
 
355
- it('should handle many small writes', async function () {
356
- 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
357
308
  this.timeout(timeout);
358
309
  ({ dialer, listener, dialAddrs } = await getSetup(common))
359
310
 
360
- const connection = await dialer.dial(dialAddrs[0])
361
- const echoProtocol = dialer.services.echo.protocol
311
+ if (listener == null) {
312
+ return this.skip()
313
+ }
362
314
 
363
- for (let i = 0; i < 2000; i++) {
364
- const input = new Uint8Array(1024).fill(5)
365
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
366
- 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()
367
329
  })
330
+ .catch(err => {
331
+ deferred.reject(err)
332
+ stream.abort(err)
333
+ })
334
+ })
368
335
 
369
- expect(output.subarray()).to.equalBytes(input)
370
- expect(connection.streams.filter(s => s.protocol === echoProtocol)).to.be.empty()
336
+ const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
337
+
338
+ await stream.closeWrite()
339
+
340
+ let read = 0
341
+
342
+ for await (const buf of stream.source) {
343
+ read += buf.byteLength
371
344
  }
345
+
346
+ expect(read).to.equal(bytes)
372
347
  })
373
348
 
374
- 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 () {
375
350
  ({ dialer, listener, dialAddrs } = await getSetup(common))
376
351
 
377
352
  if (listener == null) {
378
353
  return this.skip()
379
354
  }
380
355
 
381
- // 1. remote stream close read
382
- // 2. local stream close write
383
- // 3. remote stream close write
384
-
385
356
  /**
386
357
  * NodeA NodeB
387
358
  * | <--- STOP_SENDING |
388
359
  * | FIN ---> |
389
- * | <--- DATA |
390
360
  * | <--- FIN |
391
361
  * | FIN_ACK ---> |
392
362
  * | <--- FIN_ACK |
393
363
  */
394
364
 
395
- const getRemoteStream = Promise.withResolvers<Stream>()
365
+ const getRemoteStream = pDefer<Stream>()
396
366
  const protocol = '/close-local-while-remote-writes/1.0.0'
397
367
 
398
- const streamHandler: StreamHandler = (stream) => {
399
- getRemoteStream.resolve(stream)
368
+ const streamHandler: StreamHandler = ({ stream }) => {
369
+ void Promise.resolve().then(async () => {
370
+ getRemoteStream.resolve(stream)
371
+ })
400
372
  }
401
373
 
402
- await listener.handle(protocol, streamHandler, {
374
+ await listener.handle(protocol, (info) => {
375
+ streamHandler(info)
376
+ }, {
403
377
  runOnLimitedConnection: true
404
378
  })
405
379
 
406
380
  const connection = await dialer.dial(dialAddrs[0])
407
381
 
408
- const [
409
- localStream,
410
- remoteStream
411
- ] = await Promise.all([
412
- // open a stream on the echo protocol
413
- connection.newStream(protocol, {
414
- runOnLimitedConnection: true
415
- }),
416
- getRemoteStream.promise
417
- ])
418
-
419
- // ignore incoming data
420
- void drain(localStream)
421
-
422
- // 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
423
392
  await remoteStream.closeRead()
424
393
 
425
- // 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
426
395
  const remoteInputStream = pushable<Uint8Array>()
427
- Promise.resolve().then(async () => {
428
- for await (const buf of remoteInputStream) {
429
- const sendMore = remoteStream.send(buf)
430
-
431
- if (sendMore === false) {
432
- await pEvent(remoteStream, 'drain', {
433
- rejectionEvents: [
434
- 'close'
435
- ]
436
- })
437
- }
438
- }
396
+ void remoteStream.sink(remoteInputStream)
439
397
 
440
- // close the remote writable end
441
- 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
442
405
  })
443
406
 
444
- await Promise.all([
445
- // wait for remote to receive local FIN
446
- pEvent(remoteStream, 'remoteCloseWrite'),
447
-
448
- // wait to receive FIN_ACK
449
- localStream.close()
450
- ])
451
-
452
- // stop sending remote -> local
407
+ // remote closes write
453
408
  remoteInputStream.end()
454
409
 
410
+ // wait to receive FIN_ACK
411
+ await p
412
+
455
413
  // wait for remote to notice closure
456
414
  await pRetry(() => {
457
415
  if (remoteStream.status !== 'closed') {
@@ -459,12 +417,11 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
459
417
  }
460
418
  })
461
419
 
462
- // both ends should be closed
463
- assertStreamClosed(localStream)
420
+ assertStreamClosed(stream)
464
421
  assertStreamClosed(remoteStream)
465
422
  })
466
423
 
467
- 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 () {
468
425
  ({ dialer, listener, dialAddrs } = await getSetup(common))
469
426
 
470
427
  if (listener == null) {
@@ -473,80 +430,62 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
473
430
 
474
431
  /**
475
432
  * NodeA NodeB
476
- * | DATA ---> |
477
433
  * | FIN ---> |
478
434
  * | <--- FIN |
479
435
  * | FIN_ACK ---> |
480
436
  * | <--- FIN_ACK |
481
437
  */
482
438
 
483
- const getRemoteStream = Promise.withResolvers<Stream>()
439
+ const getRemoteStream = pDefer<Stream>()
484
440
  const protocol = '/close-local-while-remote-reads/1.0.0'
485
441
 
486
- const streamHandler: StreamHandler = (stream) => {
487
- getRemoteStream.resolve(stream)
442
+ const streamHandler: StreamHandler = ({ stream }) => {
443
+ void Promise.resolve().then(async () => {
444
+ getRemoteStream.resolve(stream)
445
+ })
488
446
  }
489
447
 
490
- await listener.handle(protocol, streamHandler, {
448
+ await listener.handle(protocol, (info) => {
449
+ streamHandler(info)
450
+ }, {
491
451
  runOnLimitedConnection: true
492
452
  })
493
453
 
494
454
  const connection = await dialer.dial(dialAddrs[0])
495
455
 
496
- const [
497
- localStream,
498
- remoteStream
499
- ] = await Promise.all([
500
- // open a stream on the echo protocol
501
- connection.newStream(protocol, {
502
- runOnLimitedConnection: true
503
- }),
504
- getRemoteStream.promise
505
- ])
506
-
507
- // ignore incoming data
508
- void drain(remoteStream)
509
-
510
- // close the remote stream writable end when the local sends a FIN
511
- remoteStream.addEventListener('remoteCloseWrite', () => {
512
- remoteStream.close()
513
- .catch(err => {
514
- remoteStream.abort(err)
515
- })
456
+ // open a stream on the echo protocol
457
+ const stream = await connection.newStream(protocol, {
458
+ runOnLimitedConnection: true
516
459
  })
517
460
 
518
- // send data to the remote then close the stream
519
- const data = [
520
- Uint8Array.from([0, 1, 2, 3]),
521
- Uint8Array.from([4, 5, 6, 7]),
522
- Uint8Array.from([8, 9, 0, 1])
523
- ]
524
-
525
- const [
526
- remoteCloseEvent,
527
- localCloseEvent
528
- ] = await Promise.all([
529
- // wait for the remote to close
530
- pEvent(remoteStream, 'close'),
531
- pEvent(localStream, 'close'),
532
- (async () => {
533
- for (const buf of data) {
534
- if (!localStream.send(buf)) {
535
- await pEvent(localStream, 'drain', {
536
- rejectionEvents: [
537
- 'close'
538
- ]
539
- })
540
- }
541
- }
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
+ })
542
478
 
543
- // close the local writable end
544
- await localStream.close()
545
- })()
546
- ])
479
+ // remote closes write
480
+ await remoteStream.sink([])
547
481
 
548
- expect(remoteCloseEvent).to.not.have.property('error', 'remote stream did not close cleanly')
549
- expect(localCloseEvent).to.not.have.property('error', 'local stream did not close cleanly')
482
+ // wait to receive FIN_ACK
483
+ await p
484
+
485
+ // close read end of stream
486
+ await stream.closeRead()
487
+ // readable end should finish
488
+ await drain(stream.source)
550
489
 
551
490
  // wait for remote to notice closure
552
491
  await pRetry(() => {
@@ -555,50 +494,70 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
555
494
  }
556
495
  })
557
496
 
558
- // both ends should be closed
559
- assertStreamClosed(localStream)
497
+ assertStreamClosed(stream)
560
498
  assertStreamClosed(remoteStream)
561
499
  })
500
+ })
562
501
 
563
- it('can close a stream for writing but receive a large amount of data', async function () {
564
- const timeout = 120_000
565
- this.timeout(timeout);
566
- ({ 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))
567
514
 
568
515
  if (listener == null) {
569
516
  return this.skip()
570
517
  }
571
518
 
572
- const protocol = '/receive-data/1.0.0'
573
- const chunkSize = 1024
574
- const bytes = chunkSize * 1024 * 10
519
+ await listener.stop()
575
520
 
576
- await listener.handle(protocol, async (stream) => {
577
- for (let i = 0; i < bytes; i += chunkSize) {
578
- const sendMore = stream.send(new Uint8Array(chunkSize))
579
-
580
- if (!sendMore) {
581
- await pEvent(stream, 'drain', {
582
- rejectionEvents: [
583
- 'close'
584
- ]
585
- })
586
- }
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()
587
528
  }
529
+ })
588
530
 
589
- await stream.close()
531
+ await listener.start()
532
+
533
+ await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
534
+ errorMessage: 'Did not emit listening event'
590
535
  })
536
+ })
591
537
 
592
- 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
+ }
593
544
 
594
- const [
595
- output
596
- ] = await Promise.all([
597
- all(stream),
598
- stream.close()
599
- ])
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')
600
555
 
601
- 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
+ })
602
561
  })
603
562
  })
604
563
  }
@@ -609,4 +568,6 @@ function assertStreamClosed (stream: Stream): void {
609
568
  expect(stream.writeStatus).to.equal('closed')
610
569
 
611
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')
612
573
  }