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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/src/connection-encryption/index.d.ts.map +1 -1
  2. package/dist/src/connection-encryption/index.js +24 -15
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  5. package/dist/src/stream-muxer/base-test.js +62 -345
  6. package/dist/src/stream-muxer/base-test.js.map +1 -1
  7. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  8. package/dist/src/stream-muxer/close-test.js +254 -320
  9. package/dist/src/stream-muxer/close-test.js.map +1 -1
  10. package/dist/src/stream-muxer/index.js +2 -2
  11. package/dist/src/stream-muxer/index.js.map +1 -1
  12. package/dist/src/stream-muxer/{mega-stress-test.d.ts → stream-test.d.ts} +2 -2
  13. package/dist/src/stream-muxer/stream-test.d.ts.map +1 -0
  14. package/dist/src/stream-muxer/stream-test.js +290 -0
  15. package/dist/src/stream-muxer/stream-test.js.map +1 -0
  16. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  17. package/dist/src/stream-muxer/stress-test.js +70 -16
  18. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  19. package/dist/src/transport/index.d.ts.map +1 -1
  20. package/dist/src/transport/index.js +235 -205
  21. package/dist/src/transport/index.js.map +1 -1
  22. package/dist/src/transport/utils.d.ts.map +1 -1
  23. package/dist/src/transport/utils.js +3 -2
  24. package/dist/src/transport/utils.js.map +1 -1
  25. package/package.json +23 -51
  26. package/src/connection-encryption/index.ts +27 -20
  27. package/src/stream-muxer/base-test.ts +75 -413
  28. package/src/stream-muxer/close-test.ts +305 -343
  29. package/src/stream-muxer/index.ts +2 -2
  30. package/src/stream-muxer/stream-test.ts +381 -0
  31. package/src/stream-muxer/stress-test.ts +92 -18
  32. package/src/transport/index.ts +281 -241
  33. package/src/transport/utils.ts +3 -2
  34. package/dist/src/connection-encryption/utils/index.d.ts +0 -3
  35. package/dist/src/connection-encryption/utils/index.d.ts.map +0 -1
  36. package/dist/src/connection-encryption/utils/index.js +0 -21
  37. package/dist/src/connection-encryption/utils/index.js.map +0 -1
  38. package/dist/src/matchers.d.ts +0 -12
  39. package/dist/src/matchers.d.ts.map +0 -1
  40. package/dist/src/matchers.js +0 -14
  41. package/dist/src/matchers.js.map +0 -1
  42. package/dist/src/mocks/connection-manager.d.ts +0 -27
  43. package/dist/src/mocks/connection-manager.d.ts.map +0 -1
  44. package/dist/src/mocks/connection-manager.js +0 -147
  45. package/dist/src/mocks/connection-manager.js.map +0 -1
  46. package/dist/src/mocks/connection.d.ts +0 -41
  47. package/dist/src/mocks/connection.d.ts.map +0 -1
  48. package/dist/src/mocks/connection.js +0 -236
  49. package/dist/src/mocks/connection.js.map +0 -1
  50. package/dist/src/mocks/duplex.d.ts +0 -4
  51. package/dist/src/mocks/duplex.d.ts.map +0 -1
  52. package/dist/src/mocks/duplex.js +0 -9
  53. package/dist/src/mocks/duplex.js.map +0 -1
  54. package/dist/src/mocks/index.d.ts +0 -12
  55. package/dist/src/mocks/index.d.ts.map +0 -1
  56. package/dist/src/mocks/index.js +0 -8
  57. package/dist/src/mocks/index.js.map +0 -1
  58. package/dist/src/mocks/multiaddr-connection.d.ts +0 -17
  59. package/dist/src/mocks/multiaddr-connection.d.ts.map +0 -1
  60. package/dist/src/mocks/multiaddr-connection.js +0 -64
  61. package/dist/src/mocks/multiaddr-connection.js.map +0 -1
  62. package/dist/src/mocks/muxer.d.ts +0 -36
  63. package/dist/src/mocks/muxer.d.ts.map +0 -1
  64. package/dist/src/mocks/muxer.js +0 -234
  65. package/dist/src/mocks/muxer.js.map +0 -1
  66. package/dist/src/mocks/registrar.d.ts +0 -16
  67. package/dist/src/mocks/registrar.d.ts.map +0 -1
  68. package/dist/src/mocks/registrar.js +0 -66
  69. package/dist/src/mocks/registrar.js.map +0 -1
  70. package/dist/src/mocks/upgrader.d.ts +0 -9
  71. package/dist/src/mocks/upgrader.d.ts.map +0 -1
  72. package/dist/src/mocks/upgrader.js +0 -46
  73. package/dist/src/mocks/upgrader.js.map +0 -1
  74. package/dist/src/pubsub/api.d.ts +0 -6
  75. package/dist/src/pubsub/api.d.ts.map +0 -1
  76. package/dist/src/pubsub/api.js +0 -88
  77. package/dist/src/pubsub/api.js.map +0 -1
  78. package/dist/src/pubsub/connection-handlers.d.ts +0 -6
  79. package/dist/src/pubsub/connection-handlers.d.ts.map +0 -1
  80. package/dist/src/pubsub/connection-handlers.js +0 -329
  81. package/dist/src/pubsub/connection-handlers.js.map +0 -1
  82. package/dist/src/pubsub/emit-self.d.ts +0 -6
  83. package/dist/src/pubsub/emit-self.d.ts.map +0 -1
  84. package/dist/src/pubsub/emit-self.js +0 -80
  85. package/dist/src/pubsub/emit-self.js.map +0 -1
  86. package/dist/src/pubsub/index.d.ts +0 -18
  87. package/dist/src/pubsub/index.d.ts.map +0 -1
  88. package/dist/src/pubsub/index.js +0 -17
  89. package/dist/src/pubsub/index.js.map +0 -1
  90. package/dist/src/pubsub/messages.d.ts +0 -6
  91. package/dist/src/pubsub/messages.d.ts.map +0 -1
  92. package/dist/src/pubsub/messages.js +0 -48
  93. package/dist/src/pubsub/messages.js.map +0 -1
  94. package/dist/src/pubsub/multiple-nodes.d.ts +0 -6
  95. package/dist/src/pubsub/multiple-nodes.d.ts.map +0 -1
  96. package/dist/src/pubsub/multiple-nodes.js +0 -350
  97. package/dist/src/pubsub/multiple-nodes.js.map +0 -1
  98. package/dist/src/pubsub/two-nodes.d.ts +0 -6
  99. package/dist/src/pubsub/two-nodes.d.ts.map +0 -1
  100. package/dist/src/pubsub/two-nodes.js +0 -216
  101. package/dist/src/pubsub/two-nodes.js.map +0 -1
  102. package/dist/src/pubsub/utils.d.ts +0 -5
  103. package/dist/src/pubsub/utils.d.ts.map +0 -1
  104. package/dist/src/pubsub/utils.js +0 -27
  105. package/dist/src/pubsub/utils.js.map +0 -1
  106. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +0 -1
  107. package/dist/src/stream-muxer/mega-stress-test.js +0 -11
  108. package/dist/src/stream-muxer/mega-stress-test.js.map +0 -1
  109. package/dist/src/stream-muxer/spawner.d.ts +0 -4
  110. package/dist/src/stream-muxer/spawner.d.ts.map +0 -1
  111. package/dist/src/stream-muxer/spawner.js +0 -39
  112. package/dist/src/stream-muxer/spawner.js.map +0 -1
  113. package/dist/typedoc-urls.json +0 -44
  114. package/src/connection-encryption/utils/index.ts +0 -27
  115. package/src/matchers.ts +0 -18
  116. package/src/mocks/connection-manager.ts +0 -216
  117. package/src/mocks/connection.ts +0 -309
  118. package/src/mocks/duplex.ts +0 -11
  119. package/src/mocks/index.ts +0 -11
  120. package/src/mocks/multiaddr-connection.ts +0 -80
  121. package/src/mocks/muxer.ts +0 -331
  122. package/src/mocks/registrar.ts +0 -86
  123. package/src/mocks/upgrader.ts +0 -65
  124. package/src/pubsub/api.ts +0 -116
  125. package/src/pubsub/connection-handlers.ts +0 -413
  126. package/src/pubsub/emit-self.ts +0 -99
  127. package/src/pubsub/index.ts +0 -34
  128. package/src/pubsub/messages.ts +0 -59
  129. package/src/pubsub/multiple-nodes.ts +0 -440
  130. package/src/pubsub/two-nodes.ts +0 -272
  131. package/src/pubsub/utils.ts +0 -34
  132. package/src/stream-muxer/mega-stress-test.ts +0 -14
  133. package/src/stream-muxer/spawner.ts +0 -57
@@ -1,6 +1,6 @@
1
1
  import baseTest from './base-test.js'
2
2
  import closeTest from './close-test.js'
3
- import megaStressTest from './mega-stress-test.js'
3
+ import steamTest from './stream-test.js'
4
4
  import stressTest from './stress-test.js'
5
5
  import type { TestSetup } from '../index.js'
6
6
  import type { StreamMuxerFactory } from '@libp2p/interface'
@@ -9,7 +9,7 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
9
9
  describe('interface-stream-muxer', () => {
10
10
  baseTest(common)
11
11
  closeTest(common)
12
+ steamTest(common)
12
13
  stressTest(common)
13
- megaStressTest(common)
14
14
  })
15
15
  }
@@ -0,0 +1,381 @@
1
+ import { StreamCloseEvent, StreamMessageEvent } from '@libp2p/interface'
2
+ import { multiaddrConnectionPair } from '@libp2p/utils'
3
+ import { expect } from 'aegir/chai'
4
+ import delay from 'delay'
5
+ import all from 'it-all'
6
+ import { pEvent } from 'p-event'
7
+ import Sinon from 'sinon'
8
+ import { Uint8ArrayList } from 'uint8arraylist'
9
+ import { isValidTick } from '../is-valid-tick.ts'
10
+ import type { TestSetup } from '../index.ts'
11
+ import type { MultiaddrConnection, Stream, StreamMuxer, StreamMuxerFactory } from '@libp2p/interface'
12
+
13
+ export default (common: TestSetup<StreamMuxerFactory>): void => {
14
+ describe('streams', () => {
15
+ let dialer: StreamMuxer
16
+ let listener: StreamMuxer
17
+ let outboundStream: Stream
18
+ let inboundStream: Stream
19
+ let streams: [Stream, Stream]
20
+ let outboundConnection: MultiaddrConnection
21
+ let inboundConnection: MultiaddrConnection
22
+
23
+ beforeEach(async () => {
24
+ ([outboundConnection, inboundConnection] = multiaddrConnectionPair())
25
+
26
+ const dialerFactory = await common.setup()
27
+ dialer = dialerFactory.createStreamMuxer(outboundConnection)
28
+
29
+ const listenerFactory = await common.setup()
30
+ listener = listenerFactory.createStreamMuxer(inboundConnection)
31
+
32
+ streams = await Promise.all([
33
+ pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
34
+ dialer.createStream()
35
+ ])
36
+
37
+ inboundStream = streams[0]
38
+ outboundStream = streams[1]
39
+ })
40
+
41
+ afterEach(async () => {
42
+ await dialer?.close()
43
+ await listener?.close()
44
+ })
45
+
46
+ it('should have correct status after opening', () => {
47
+ streams.forEach(stream => {
48
+ expect(stream).to.have.property('status', 'open', `${stream.direction} stream status was incorrect`)
49
+ expect(stream).to.have.property('writeStatus', 'writable', `${stream.direction} stream writeStatus was incorrect`)
50
+ expect(stream).to.have.property('readStatus', 'readable', `${stream.direction} stream readStatus was incorrect`)
51
+ })
52
+ })
53
+
54
+ it('should have correct timeline after opening', () => {
55
+ streams.forEach(stream => {
56
+ expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
57
+ expect(stream).to.not.have.nested.property('timeline.close', `${stream.direction} stream timeline.close was incorrect`)
58
+ expect(stream).to.not.have.nested.property('timeline.closeRead', `${stream.direction} stream timeline.closeRead was incorrect`)
59
+ expect(stream).to.not.have.nested.property('timeline.closeWrite', `${stream.direction} stream timeline.closeWrite was incorrect`)
60
+ expect(stream).to.not.have.nested.property('timeline.reset', `${stream.direction} stream timeline.reset was incorrect`)
61
+ expect(stream).to.not.have.nested.property('timeline.abort', `${stream.direction} stream timeline.abort was incorrect`)
62
+ })
63
+ })
64
+
65
+ it('outbound stream sends data', async () => {
66
+ const messageEventPromise = pEvent<'message', StreamMessageEvent>(inboundStream, 'message')
67
+ const data = Uint8Array.from([0, 1, 2, 3, 4])
68
+
69
+ outboundStream.send(data)
70
+
71
+ const evt = await messageEventPromise
72
+ expect(evt.data.subarray()).to.equalBytes(data)
73
+ })
74
+
75
+ it('inbound stream sends data', async () => {
76
+ const messageEventPromise = pEvent<'message', StreamMessageEvent>(outboundStream, 'message')
77
+ const data = Uint8Array.from([0, 1, 2, 3, 4])
78
+
79
+ inboundStream.send(data)
80
+
81
+ const evt = await messageEventPromise
82
+ expect(evt.data.subarray()).to.equalBytes(data)
83
+ })
84
+
85
+ it('closes', async () => {
86
+ const signal = AbortSignal.timeout(1_000)
87
+
88
+ void outboundStream.close({
89
+ signal
90
+ })
91
+ void inboundStream.close({
92
+ signal
93
+ })
94
+
95
+ expect(outboundStream).to.have.property('status', 'open')
96
+ expect(outboundStream).to.have.property('readStatus', 'readable')
97
+ expect(outboundStream).to.have.property('writeStatus', 'closing')
98
+
99
+ await Promise.all([
100
+ pEvent(outboundStream, 'close', {
101
+ signal
102
+ }),
103
+ pEvent(inboundStream, 'close', {
104
+ signal
105
+ })
106
+ ])
107
+
108
+ streams.forEach(stream => {
109
+ expect(stream).to.have.property('status', 'closed', `${stream.direction} stream status was incorrect`)
110
+ expect(stream).to.have.property('writeStatus', 'closed', `${stream.direction} stream writeStatus was incorrect`)
111
+ expect(stream).to.have.property('readStatus', 'closed', `${stream.direction} stream readStatus was incorrect`)
112
+
113
+ expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
114
+ expect(isValidTick(stream.timeline.close)).to.equal(true, `${stream.direction} stream timeline.close was incorrect`)
115
+
116
+ expect(stream).to.not.have.nested.property('timeline.reset', `${stream.direction} stream timeline.reset was incorrect`)
117
+ expect(stream).to.not.have.nested.property('timeline.abort', `${stream.direction} stream timeline.abort was incorrect`)
118
+ })
119
+ })
120
+
121
+ it('should send large amounts of data in both directions', async function () {
122
+ const timeout = 360_000
123
+ this.timeout(timeout)
124
+
125
+ const sent = new Array(10)
126
+ .fill(0)
127
+ .map((val, index) => new Uint8Array(1024 * 1024).fill(index))
128
+
129
+ // send data in both directions simultaneously
130
+ const [
131
+ outboundReceived,
132
+ inboundReceived
133
+ ] = await Promise.all([
134
+ all(outboundStream),
135
+ all(inboundStream),
136
+ (async () => {
137
+ for (const buf of sent) {
138
+ if (!outboundStream.send(buf)) {
139
+ await pEvent(outboundStream, 'drain', {
140
+ rejectionEvents: [
141
+ 'close'
142
+ ]
143
+ })
144
+ }
145
+ }
146
+
147
+ await outboundStream.close()
148
+ })(),
149
+ (async () => {
150
+ for (const buf of sent) {
151
+ if (!inboundStream.send(buf)) {
152
+ await pEvent(inboundStream, 'drain', {
153
+ rejectionEvents: [
154
+ 'close'
155
+ ]
156
+ })
157
+ }
158
+ }
159
+
160
+ await inboundStream.close()
161
+ })()
162
+ ])
163
+
164
+ expect(new Uint8ArrayList(...outboundReceived)).to.have.property('byteLength', new Uint8ArrayList(...sent).byteLength)
165
+ expect(new Uint8ArrayList(...inboundReceived)).to.have.property('byteLength', new Uint8ArrayList(...sent).byteLength)
166
+ })
167
+
168
+ it('closes for writing', async () => {
169
+ const signal = AbortSignal.timeout(1_000)
170
+
171
+ const eventPromise = pEvent(inboundStream, 'remoteCloseWrite')
172
+
173
+ void outboundStream.close({
174
+ signal
175
+ })
176
+
177
+ expect(outboundStream).to.have.property('writeStatus', 'closing')
178
+
179
+ await delay(100)
180
+
181
+ expect(inboundStream).to.have.property('readStatus', 'readable')
182
+
183
+ await eventPromise
184
+
185
+ streams.forEach(stream => {
186
+ expect(stream).to.have.property('status', 'open', `${stream.direction} stream status was incorrect`)
187
+
188
+ expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
189
+
190
+ expect(stream).to.not.have.nested.property('timeline.close', `${stream.direction} stream timeline.close was incorrect`)
191
+ expect(stream).to.not.have.nested.property('timeline.reset', `${stream.direction} stream timeline.reset was incorrect`)
192
+ expect(stream).to.not.have.nested.property('timeline.abort', `${stream.direction} stream timeline.abort was incorrect`)
193
+ })
194
+
195
+ expect(outboundStream).to.have.property('writeStatus', 'closed', 'outbound stream writeStatus was incorrect')
196
+ expect(outboundStream).to.have.property('readStatus', 'readable', 'inbound stream readStatus was incorrect')
197
+
198
+ expect(outboundStream).to.not.have.nested.property('timeline.closeRead', 'inbound stream timeline.closeRead was incorrect')
199
+
200
+ expect(inboundStream).to.have.property('writeStatus', 'writable', 'inbound stream writeStatus was incorrect')
201
+ expect(inboundStream).to.have.property('readStatus', 'readable', 'inbound stream readStatus was incorrect')
202
+ })
203
+
204
+ it('aborts', async () => {
205
+ const eventPromises = Promise.all([
206
+ pEvent<'close', StreamCloseEvent>(outboundStream, 'close'),
207
+ pEvent<'close', StreamCloseEvent>(inboundStream, 'close')
208
+ ])
209
+
210
+ const err = new Error('Urk!')
211
+ outboundStream.abort(err)
212
+
213
+ const [outboundEvent, inboundEvent] = await eventPromises
214
+
215
+ streams.forEach(stream => {
216
+ expect(stream).to.have.property('writeStatus', 'closed', `${stream.direction} stream writeStatus was incorrect`)
217
+ expect(stream).to.have.property('readStatus', 'closed', `${stream.direction} stream readStatus was incorrect`)
218
+
219
+ expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
220
+
221
+ expect(stream).to.not.have.nested.property('timeline.close', `${stream.direction} stream timeline.close was incorrect`)
222
+ })
223
+
224
+ expect(outboundStream).to.have.property('status', 'aborted', 'outbound stream status was incorrect')
225
+ expect(isValidTick(outboundStream.timeline.close)).to.equal(true, 'outbound stream timeline.abort was incorrect')
226
+
227
+ expect(inboundStream).to.have.property('status', 'reset', 'inbound stream status was incorrect')
228
+ expect(isValidTick(inboundStream.timeline.close)).to.equal(true, 'inbound stream timeline.reset was incorrect')
229
+
230
+ expect(() => outboundStream.send(Uint8Array.from([0, 1, 2, 3]))).to.throw()
231
+ .with.property('name', 'StreamStateError', 'could still write to aborted stream')
232
+
233
+ expect(() => inboundStream.send(Uint8Array.from([0, 1, 2, 3]))).to.throw()
234
+ .with.property('name', 'StreamStateError', 'could still write to reset stream')
235
+
236
+ expect(outboundEvent).to.have.property('error', err)
237
+ expect(inboundEvent).to.have.nested.property('error.name', 'StreamResetError')
238
+ })
239
+
240
+ it('does not send close read when remote closes write', async () => {
241
+ // @ts-expect-error internal method of AbstractMessageStream
242
+ const sendCloseReadSpy = Sinon.spy(outboundStream, 'sendCloseRead')
243
+
244
+ await Promise.all([
245
+ pEvent(outboundStream, 'remoteCloseWrite'),
246
+ inboundStream.close()
247
+ ])
248
+
249
+ await delay(100)
250
+
251
+ expect(sendCloseReadSpy.called).to.be.false()
252
+ })
253
+
254
+ it('does not send close read or write when remote resets', async () => {
255
+ // @ts-expect-error internal method of AbstractMessageStream
256
+ const sendCloseReadSpy = Sinon.spy(outboundStream, 'sendCloseRead')
257
+ // @ts-expect-error internal method of AbstractMessageStream
258
+ const sendCloseWriteSpy = Sinon.spy(outboundStream, 'sendCloseWrite')
259
+
260
+ await Promise.all([
261
+ pEvent(outboundStream, 'close'),
262
+ // eslint-disable-next-line @typescript-eslint/await-thenable
263
+ inboundStream.abort(new Error('Urk!'))
264
+ ])
265
+
266
+ await delay(100)
267
+
268
+ await outboundStream.close()
269
+
270
+ await delay(100)
271
+
272
+ expect(sendCloseReadSpy.called).to.be.false()
273
+ expect(sendCloseWriteSpy.called).to.be.false()
274
+ })
275
+
276
+ it('should wait for sending data to finish when closing gracefully', async () => {
277
+ let sent = 0
278
+ let received = 0
279
+ let filledBuffer = false
280
+ const receivedAll = Promise.withResolvers<boolean>()
281
+
282
+ inboundStream.addEventListener('message', (evt) => {
283
+ received += evt.data.byteLength
284
+
285
+ if (filledBuffer && received === sent) {
286
+ receivedAll.resolve(true)
287
+ }
288
+ })
289
+
290
+ // fill the send buffer
291
+ while (true) {
292
+ const length = 1024
293
+ sent += length
294
+ const sendMore = outboundStream.send(new Uint8Array(length))
295
+
296
+ if (sendMore === false) {
297
+ filledBuffer = true
298
+ break
299
+ }
300
+ }
301
+
302
+ expect(outboundStream.writeStatus).to.equal('writable')
303
+ expect(outboundStream.writableNeedsDrain).to.be.true()
304
+
305
+ // close gracefully
306
+ await outboundStream.close()
307
+
308
+ await expect(receivedAll.promise).to.eventually.be.true('did not receive all data')
309
+ })
310
+
311
+ it('should wait for sending data to finish when closing the writable end gracefully', async () => {
312
+ let sent = 0
313
+ let received = 0
314
+ let filledBuffer = false
315
+ const receivedAll = Promise.withResolvers<boolean>()
316
+
317
+ inboundStream.addEventListener('message', (evt) => {
318
+ received += evt.data.byteLength
319
+
320
+ if (filledBuffer && received === sent) {
321
+ receivedAll.resolve(true)
322
+ }
323
+ })
324
+
325
+ // fill the send buffer
326
+ while (true) {
327
+ const length = 1024
328
+ sent += length
329
+ const sendMore = outboundStream.send(new Uint8Array(length))
330
+
331
+ if (sendMore === false) {
332
+ filledBuffer = true
333
+ break
334
+ }
335
+ }
336
+
337
+ expect(outboundStream.writeStatus).to.equal('writable')
338
+ expect(outboundStream.writableNeedsDrain).to.be.true()
339
+
340
+ // close gracefully
341
+ await outboundStream.close()
342
+
343
+ await expect(receivedAll.promise).to.eventually.be.true('did not receive all data')
344
+ })
345
+
346
+ it('should abort close due to timeout with slow sender', async () => {
347
+ const chunkSize = 1024
348
+
349
+ // make the 'drain' event slow to fire
350
+ // @ts-expect-error private fields
351
+ outboundConnection.local.delay = 1000
352
+
353
+ inboundStream = streams[0]
354
+ outboundStream = streams[1]
355
+
356
+ // ensure there are bytes left in the write queue
357
+ // @ts-expect-error private fields
358
+ outboundStream.maxMessageSize = chunkSize - 1
359
+
360
+ // fill the send buffer
361
+ while (true) {
362
+ const sendMore = outboundStream.send(new Uint8Array(chunkSize * 10))
363
+
364
+ if (sendMore === false) {
365
+ expect(outboundStream).to.have.nested.property('writeBuffer.byteLength').that.is.greaterThan(0)
366
+
367
+ break
368
+ }
369
+ }
370
+
371
+ expect(outboundStream.writeStatus).to.equal('writable')
372
+ expect(outboundStream.writableNeedsDrain).to.be.true()
373
+
374
+ // close stream, should be aborted as drain event will not have fired
375
+ await expect(outboundStream.close({
376
+ signal: AbortSignal.timeout(10)
377
+ })).to.eventually.be.rejected
378
+ .with.property('name', 'TimeoutError')
379
+ })
380
+ })
381
+ }
@@ -1,27 +1,101 @@
1
- import spawn from './spawner.js'
1
+ import { multiaddrConnectionPair, echo } from '@libp2p/utils'
2
+ import { expect } from 'aegir/chai'
3
+ import { pEvent } from 'p-event'
4
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
5
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
2
6
  import type { TestSetup } from '../index.js'
3
- import type { StreamMuxerFactory, StreamMuxerInit, StreamMuxer } from '@libp2p/interface'
7
+ import type { StreamMuxerFactory, StreamMuxer, MultiaddrConnection } from '@libp2p/interface'
8
+
9
+ async function * messages (nMsg: number): AsyncGenerator<Uint8Array> {
10
+ for (let i = 0; i < nMsg; i++) {
11
+ yield uint8ArrayFromString(`message ${i + 1}/${nMsg}`)
12
+ }
13
+ }
14
+
15
+ async function spawn (createMuxer: (maConn: MultiaddrConnection) => Promise<StreamMuxer>, nStreams: number, nMsg: number): Promise<void> {
16
+ const [outboundConnection, inboundConnection] = multiaddrConnectionPair()
17
+
18
+ const listener = await createMuxer(inboundConnection)
19
+ listener.addEventListener('stream', function echoStreamHandler (evt) {
20
+ echo(evt.detail)
21
+ .catch(err => {
22
+ evt.detail.abort(err)
23
+ })
24
+ })
25
+
26
+ const dialer = await createMuxer(outboundConnection)
27
+
28
+ const spawnStream = async (): Promise<void> => {
29
+ let receivedBytes = 0
30
+ let sentBytes = 0
31
+
32
+ for await (const buf of messages(nMsg)) {
33
+ sentBytes += buf.byteLength
34
+ }
35
+
36
+ const receivedAllMessagesPromise = Promise.withResolvers<void>()
37
+ const outboundStream = await dialer.createStream()
38
+
39
+ outboundStream.addEventListener('message', function countMessages (evt) {
40
+ receivedBytes += evt.data.byteLength
41
+
42
+ outboundStream.log('%s - echoed bytes %d/%d', uint8ArrayToString(evt.data.subarray()), receivedBytes, sentBytes)
43
+
44
+ if (receivedBytes === sentBytes) {
45
+ outboundStream.log('received all bytes')
46
+ receivedAllMessagesPromise.resolve()
47
+ }
48
+ })
49
+
50
+ for await (const buf of messages(nMsg)) {
51
+ const sendMore = outboundStream.send(buf)
52
+
53
+ if (sendMore === false) {
54
+ await pEvent(outboundStream, 'drain', {
55
+ rejectionEvents: ['close']
56
+ })
57
+ }
58
+ }
59
+
60
+ await receivedAllMessagesPromise.promise
61
+ outboundStream.log('sent and received all messages %d/%d', receivedBytes, sentBytes)
62
+
63
+ await Promise.all([
64
+ pEvent(outboundStream, 'close'),
65
+ outboundStream.close()
66
+ ])
67
+
68
+ expect(receivedBytes).to.equal(sentBytes)
69
+ }
70
+
71
+ await Promise.all(
72
+ Array.from(Array(nStreams), async () => {
73
+ await spawnStream()
74
+ })
75
+ )
76
+
77
+ await listener.close()
78
+ await dialer.close()
79
+ }
4
80
 
5
81
  export default (common: TestSetup<StreamMuxerFactory>): void => {
6
- const createMuxer = async (init?: StreamMuxerInit): Promise<StreamMuxer> => {
82
+ const createMuxer = async (maConn: MultiaddrConnection): Promise<StreamMuxer> => {
7
83
  const factory = await common.setup()
8
- return factory.createStreamMuxer(init)
84
+ return factory.createStreamMuxer(maConn)
9
85
  }
10
86
 
87
+ const streams = [1, 10, 100, 1000]
88
+ const messages = [1, 10, 100]
89
+
11
90
  describe('stress test', function () {
12
- this.timeout(1600000)
13
-
14
- it('1 stream with 1 msg', async () => { await spawn(createMuxer, 1, 1) })
15
- it('1 stream with 10 msg', async () => { await spawn(createMuxer, 1, 10) })
16
- it('1 stream with 100 msg', async () => { await spawn(createMuxer, 1, 100) })
17
- it('10 streams with 1 msg', async () => { await spawn(createMuxer, 10, 1) })
18
- it('10 streams with 10 msg', async () => { await spawn(createMuxer, 10, 10) })
19
- it('10 streams with 100 msg', async () => { await spawn(createMuxer, 10, 100) })
20
- it('100 streams with 1 msg', async () => { await spawn(createMuxer, 100, 1) })
21
- it('100 streams with 10 msg', async () => { await spawn(createMuxer, 100, 10) })
22
- it('100 streams with 100 msg', async () => { await spawn(createMuxer, 100, 100) })
23
- it('1000 streams with 1 msg', async () => { await spawn(createMuxer, 1000, 1) })
24
- it('1000 streams with 10 msg', async () => { await spawn(createMuxer, 1000, 10) })
25
- it('1000 streams with 100 msg', async () => { await spawn(createMuxer, 1000, 100) })
91
+ this.timeout(1_600_000)
92
+
93
+ for (let i = 0; i < streams.length; i++) {
94
+ for (let j = 0; j < messages.length; j++) {
95
+ it(`${streams[i]} stream(s) with ${messages[j]} msg(s)`, async () => {
96
+ await spawn(createMuxer, streams[i], messages[j])
97
+ })
98
+ }
99
+ }
26
100
  })
27
101
  }