@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,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,380 @@
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
+ inboundStream.abort(new Error('Urk!'))
263
+ ])
264
+
265
+ await delay(100)
266
+
267
+ await outboundStream.close()
268
+
269
+ await delay(100)
270
+
271
+ expect(sendCloseReadSpy.called).to.be.false()
272
+ expect(sendCloseWriteSpy.called).to.be.false()
273
+ })
274
+
275
+ it('should wait for sending data to finish when closing gracefully', async () => {
276
+ let sent = 0
277
+ let received = 0
278
+ let filledBuffer = false
279
+ const receivedAll = Promise.withResolvers<boolean>()
280
+
281
+ inboundStream.addEventListener('message', (evt) => {
282
+ received += evt.data.byteLength
283
+
284
+ if (filledBuffer && received === sent) {
285
+ receivedAll.resolve(true)
286
+ }
287
+ })
288
+
289
+ // fill the send buffer
290
+ while (true) {
291
+ const length = 1024
292
+ sent += length
293
+ const sendMore = outboundStream.send(new Uint8Array(length))
294
+
295
+ if (sendMore === false) {
296
+ filledBuffer = true
297
+ break
298
+ }
299
+ }
300
+
301
+ expect(outboundStream.writeStatus).to.equal('writable')
302
+ expect(outboundStream.writableNeedsDrain).to.be.true()
303
+
304
+ // close gracefully
305
+ await outboundStream.close()
306
+
307
+ await expect(receivedAll.promise).to.eventually.be.true('did not receive all data')
308
+ })
309
+
310
+ it('should wait for sending data to finish when closing the writable end gracefully', async () => {
311
+ let sent = 0
312
+ let received = 0
313
+ let filledBuffer = false
314
+ const receivedAll = Promise.withResolvers<boolean>()
315
+
316
+ inboundStream.addEventListener('message', (evt) => {
317
+ received += evt.data.byteLength
318
+
319
+ if (filledBuffer && received === sent) {
320
+ receivedAll.resolve(true)
321
+ }
322
+ })
323
+
324
+ // fill the send buffer
325
+ while (true) {
326
+ const length = 1024
327
+ sent += length
328
+ const sendMore = outboundStream.send(new Uint8Array(length))
329
+
330
+ if (sendMore === false) {
331
+ filledBuffer = true
332
+ break
333
+ }
334
+ }
335
+
336
+ expect(outboundStream.writeStatus).to.equal('writable')
337
+ expect(outboundStream.writableNeedsDrain).to.be.true()
338
+
339
+ // close gracefully
340
+ await outboundStream.close()
341
+
342
+ await expect(receivedAll.promise).to.eventually.be.true('did not receive all data')
343
+ })
344
+
345
+ it('should abort close due to timeout with slow sender', async () => {
346
+ const chunkSize = 1024
347
+
348
+ // make the 'drain' event slow to fire
349
+ // @ts-expect-error private fields
350
+ outboundConnection.local.delay = 1000
351
+
352
+ inboundStream = streams[0]
353
+ outboundStream = streams[1]
354
+
355
+ // ensure there are bytes left in the write queue
356
+ // @ts-expect-error private fields
357
+ outboundStream.maxMessageSize = chunkSize - 1
358
+
359
+ // fill the send buffer
360
+ while (true) {
361
+ const sendMore = outboundStream.send(new Uint8Array(chunkSize * 10))
362
+
363
+ if (sendMore === false) {
364
+ expect(outboundStream).to.have.nested.property('writeBuffer.byteLength').that.is.greaterThan(0)
365
+
366
+ break
367
+ }
368
+ }
369
+
370
+ expect(outboundStream.writeStatus).to.equal('writable')
371
+ expect(outboundStream.writableNeedsDrain).to.be.true()
372
+
373
+ // close stream, should be aborted as drain event will not have fired
374
+ await expect(outboundStream.close({
375
+ signal: AbortSignal.timeout(10)
376
+ })).to.eventually.be.rejected
377
+ .with.property('name', 'TimeoutError')
378
+ })
379
+ })
380
+ }
@@ -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
  }