@libp2p/interface-compliance-tests 3.0.6 → 3.0.7-05abd49f

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 (136) hide show
  1. package/README.md +12 -3
  2. package/dist/src/connection/index.d.ts +5 -0
  3. package/dist/src/connection/index.d.ts.map +1 -0
  4. package/dist/src/connection/index.js +151 -0
  5. package/dist/src/connection/index.js.map +1 -0
  6. package/dist/src/connection-encryption/index.d.ts +5 -0
  7. package/dist/src/connection-encryption/index.d.ts.map +1 -0
  8. package/dist/src/connection-encryption/index.js +71 -0
  9. package/dist/src/connection-encryption/index.js.map +1 -0
  10. package/dist/src/connection-encryption/utils/index.d.ts +3 -0
  11. package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
  12. package/dist/src/connection-encryption/utils/index.js +18 -0
  13. package/dist/src/connection-encryption/utils/index.js.map +1 -0
  14. package/dist/src/index.d.ts +1 -1
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/mocks/connection-encrypter.d.ts +3 -0
  17. package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
  18. package/dist/src/mocks/connection-encrypter.js +98 -0
  19. package/dist/src/mocks/connection-encrypter.js.map +1 -0
  20. package/dist/src/mocks/connection-gater.d.ts +3 -0
  21. package/dist/src/mocks/connection-gater.d.ts.map +1 -0
  22. package/dist/src/mocks/connection-gater.js +17 -0
  23. package/dist/src/mocks/connection-gater.js.map +1 -0
  24. package/dist/src/mocks/connection-manager.d.ts +27 -0
  25. package/dist/src/mocks/connection-manager.d.ts.map +1 -0
  26. package/dist/src/mocks/connection-manager.js +145 -0
  27. package/dist/src/mocks/connection-manager.js.map +1 -0
  28. package/dist/src/mocks/connection.d.ts +32 -0
  29. package/dist/src/mocks/connection.d.ts.map +1 -0
  30. package/dist/src/mocks/connection.js +162 -0
  31. package/dist/src/mocks/connection.js.map +1 -0
  32. package/dist/src/mocks/duplex.d.ts +3 -0
  33. package/dist/src/mocks/duplex.d.ts.map +1 -0
  34. package/dist/src/mocks/duplex.js +9 -0
  35. package/dist/src/mocks/duplex.js.map +1 -0
  36. package/dist/src/mocks/index.d.ts +13 -0
  37. package/dist/src/mocks/index.d.ts.map +1 -0
  38. package/dist/src/mocks/index.js +11 -0
  39. package/dist/src/mocks/index.js.map +1 -0
  40. package/dist/src/mocks/metrics.d.ts +3 -0
  41. package/dist/src/mocks/metrics.d.ts.map +1 -0
  42. package/dist/src/mocks/metrics.js +115 -0
  43. package/dist/src/mocks/metrics.js.map +1 -0
  44. package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
  45. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
  46. package/dist/src/mocks/multiaddr-connection.js +51 -0
  47. package/dist/src/mocks/multiaddr-connection.js.map +1 -0
  48. package/dist/src/mocks/muxer.d.ts +8 -0
  49. package/dist/src/mocks/muxer.d.ts.map +1 -0
  50. package/dist/src/mocks/muxer.js +341 -0
  51. package/dist/src/mocks/muxer.js.map +1 -0
  52. package/dist/src/mocks/peer-discovery.d.ts +22 -0
  53. package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
  54. package/dist/src/mocks/peer-discovery.js +47 -0
  55. package/dist/src/mocks/peer-discovery.js.map +1 -0
  56. package/dist/src/mocks/registrar.d.ts +18 -0
  57. package/dist/src/mocks/registrar.d.ts.map +1 -0
  58. package/dist/src/mocks/registrar.js +66 -0
  59. package/dist/src/mocks/registrar.js.map +1 -0
  60. package/dist/src/mocks/upgrader.d.ts +10 -0
  61. package/dist/src/mocks/upgrader.d.ts.map +1 -0
  62. package/dist/src/mocks/upgrader.js +31 -0
  63. package/dist/src/mocks/upgrader.js.map +1 -0
  64. package/dist/src/peer-discovery/index.d.ts +5 -0
  65. package/dist/src/peer-discovery/index.d.ts.map +1 -0
  66. package/dist/src/peer-discovery/index.js +66 -0
  67. package/dist/src/peer-discovery/index.js.map +1 -0
  68. package/dist/src/stream-muxer/base-test.d.ts +5 -0
  69. package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
  70. package/dist/src/stream-muxer/base-test.js +153 -0
  71. package/dist/src/stream-muxer/base-test.js.map +1 -0
  72. package/dist/src/stream-muxer/close-test.d.ts +5 -0
  73. package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
  74. package/dist/src/stream-muxer/close-test.js +269 -0
  75. package/dist/src/stream-muxer/close-test.js.map +1 -0
  76. package/dist/src/stream-muxer/index.d.ts +5 -0
  77. package/dist/src/stream-muxer/index.d.ts.map +1 -0
  78. package/dist/src/stream-muxer/index.js +13 -0
  79. package/dist/src/stream-muxer/index.js.map +1 -0
  80. package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
  81. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  82. package/dist/src/stream-muxer/mega-stress-test.js +11 -0
  83. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  84. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  85. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  86. package/dist/src/stream-muxer/spawner.js +36 -0
  87. package/dist/src/stream-muxer/spawner.js.map +1 -0
  88. package/dist/src/stream-muxer/stress-test.d.ts +5 -0
  89. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
  90. package/dist/src/stream-muxer/stress-test.js +23 -0
  91. package/dist/src/stream-muxer/stress-test.js.map +1 -0
  92. package/dist/src/transport/dial-test.d.ts +5 -0
  93. package/dist/src/transport/dial-test.d.ts.map +1 -0
  94. package/dist/src/transport/dial-test.js +98 -0
  95. package/dist/src/transport/dial-test.js.map +1 -0
  96. package/dist/src/transport/filter-test.d.ts +5 -0
  97. package/dist/src/transport/filter-test.d.ts.map +1 -0
  98. package/dist/src/transport/filter-test.js +18 -0
  99. package/dist/src/transport/filter-test.js.map +1 -0
  100. package/dist/src/transport/index.d.ts +15 -0
  101. package/dist/src/transport/index.d.ts.map +1 -0
  102. package/dist/src/transport/index.js +11 -0
  103. package/dist/src/transport/index.js.map +1 -0
  104. package/dist/src/transport/listen-test.d.ts +5 -0
  105. package/dist/src/transport/listen-test.d.ts.map +1 -0
  106. package/dist/src/transport/listen-test.js +152 -0
  107. package/dist/src/transport/listen-test.js.map +1 -0
  108. package/package.json +66 -95
  109. package/src/connection/index.ts +184 -0
  110. package/src/connection-encryption/index.ts +97 -0
  111. package/src/connection-encryption/utils/index.ts +23 -0
  112. package/src/index.ts +1 -1
  113. package/src/mocks/connection-encrypter.ts +113 -0
  114. package/src/mocks/connection-gater.ts +18 -0
  115. package/src/mocks/connection-manager.ts +209 -0
  116. package/src/mocks/connection.ts +218 -0
  117. package/src/mocks/duplex.ts +10 -0
  118. package/src/mocks/index.ts +12 -0
  119. package/src/mocks/metrics.ts +162 -0
  120. package/src/mocks/multiaddr-connection.ts +67 -0
  121. package/src/mocks/muxer.ts +447 -0
  122. package/src/mocks/peer-discovery.ts +60 -0
  123. package/src/mocks/registrar.ts +88 -0
  124. package/src/mocks/upgrader.ts +49 -0
  125. package/src/peer-discovery/index.ts +90 -0
  126. package/src/stream-muxer/base-test.ts +196 -0
  127. package/src/stream-muxer/close-test.ts +346 -0
  128. package/src/stream-muxer/index.ts +15 -0
  129. package/src/stream-muxer/mega-stress-test.ts +14 -0
  130. package/src/stream-muxer/spawner.ts +54 -0
  131. package/src/stream-muxer/stress-test.ts +27 -0
  132. package/src/transport/dial-test.ts +124 -0
  133. package/src/transport/filter-test.ts +25 -0
  134. package/src/transport/index.ts +25 -0
  135. package/src/transport/listen-test.ts +191 -0
  136. package/dist/typedoc-urls.json +0 -3
@@ -0,0 +1,447 @@
1
+ import { CodeError } from '@libp2p/interface/errors'
2
+ import { type Logger, logger } from '@libp2p/logger'
3
+ import { abortableSource } from 'abortable-iterator'
4
+ import { anySignal } from 'any-signal'
5
+ import map from 'it-map'
6
+ import * as ndjson from 'it-ndjson'
7
+ import { pipe } from 'it-pipe'
8
+ import { type Pushable, pushable } from 'it-pushable'
9
+ import { Uint8ArrayList } from 'uint8arraylist'
10
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
11
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
12
+ import type { Stream } from '@libp2p/interface/connection'
13
+ import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer'
14
+ import type { Source } from 'it-stream-types'
15
+
16
+ let muxers = 0
17
+ let streams = 0
18
+ const MAX_MESSAGE_SIZE = 1024 * 1024
19
+
20
+ interface DataMessage {
21
+ id: string
22
+ type: 'data'
23
+ direction: 'initiator' | 'recipient'
24
+ chunk: string
25
+ }
26
+
27
+ interface ResetMessage {
28
+ id: string
29
+ type: 'reset'
30
+ direction: 'initiator' | 'recipient'
31
+ }
32
+
33
+ interface CloseMessage {
34
+ id: string
35
+ type: 'close'
36
+ direction: 'initiator' | 'recipient'
37
+ }
38
+
39
+ interface CreateMessage {
40
+ id: string
41
+ type: 'create'
42
+ direction: 'initiator'
43
+ }
44
+
45
+ type StreamMessage = DataMessage | ResetMessage | CloseMessage | CreateMessage
46
+
47
+ class MuxedStream {
48
+ public id: string
49
+ public input: Pushable<Uint8ArrayList>
50
+ public stream: Stream
51
+ public type: 'initiator' | 'recipient'
52
+
53
+ private sinkEnded: boolean
54
+ private sourceEnded: boolean
55
+ private readonly abortController: AbortController
56
+ private readonly resetController: AbortController
57
+ private readonly closeController: AbortController
58
+ private readonly log: Logger
59
+
60
+ constructor (init: { id: string, type: 'initiator' | 'recipient', push: Pushable<StreamMessage>, onEnd: (err?: Error) => void }) {
61
+ const { id, type, push, onEnd } = init
62
+
63
+ this.log = logger(`libp2p:mock-muxer:stream:${id}:${type}`)
64
+
65
+ this.id = id
66
+ this.type = type
67
+ this.abortController = new AbortController()
68
+ this.resetController = new AbortController()
69
+ this.closeController = new AbortController()
70
+
71
+ this.sourceEnded = false
72
+ this.sinkEnded = false
73
+
74
+ let endErr: Error | undefined
75
+
76
+ const onSourceEnd = (err?: Error): void => {
77
+ if (this.sourceEnded) {
78
+ return
79
+ }
80
+
81
+ this.log('onSourceEnd sink ended? %s', this.sinkEnded)
82
+
83
+ this.sourceEnded = true
84
+
85
+ if (err != null && endErr == null) {
86
+ endErr = err
87
+ }
88
+
89
+ if (this.sinkEnded) {
90
+ this.stream.stat.timeline.close = Date.now()
91
+
92
+ if (onEnd != null) {
93
+ onEnd(endErr)
94
+ }
95
+ }
96
+ }
97
+
98
+ const onSinkEnd = (err?: Error): void => {
99
+ if (this.sinkEnded) {
100
+ return
101
+ }
102
+
103
+ this.log('onSinkEnd source ended? %s', this.sourceEnded)
104
+
105
+ this.sinkEnded = true
106
+
107
+ if (err != null && endErr == null) {
108
+ endErr = err
109
+ }
110
+
111
+ if (this.sourceEnded) {
112
+ this.stream.stat.timeline.close = Date.now()
113
+
114
+ if (onEnd != null) {
115
+ onEnd(endErr)
116
+ }
117
+ }
118
+ }
119
+
120
+ this.input = pushable({
121
+ onEnd: onSourceEnd
122
+ })
123
+
124
+ this.stream = {
125
+ id,
126
+ sink: async (source) => {
127
+ if (this.sinkEnded) {
128
+ throw new CodeError('stream closed for writing', 'ERR_SINK_ENDED')
129
+ }
130
+
131
+ const signal = anySignal([
132
+ this.abortController.signal,
133
+ this.resetController.signal,
134
+ this.closeController.signal
135
+ ])
136
+
137
+ source = abortableSource(source, signal)
138
+
139
+ try {
140
+ if (this.type === 'initiator') {
141
+ // If initiator, open a new stream
142
+ const createMsg: CreateMessage = {
143
+ id: this.id,
144
+ type: 'create',
145
+ direction: this.type
146
+ }
147
+ push.push(createMsg)
148
+ }
149
+
150
+ const list = new Uint8ArrayList()
151
+
152
+ for await (const chunk of source) {
153
+ list.append(chunk)
154
+
155
+ while (list.length > 0) {
156
+ const available = Math.min(list.length, MAX_MESSAGE_SIZE)
157
+ const dataMsg: DataMessage = {
158
+ id,
159
+ type: 'data',
160
+ chunk: uint8ArrayToString(list.subarray(0, available), 'base64pad'),
161
+ direction: this.type
162
+ }
163
+
164
+ push.push(dataMsg)
165
+ list.consume(available)
166
+ }
167
+ }
168
+ } catch (err: any) {
169
+ if (err.type === 'aborted' && err.message === 'The operation was aborted') {
170
+ if (this.closeController.signal.aborted) {
171
+ return
172
+ }
173
+
174
+ if (this.resetController.signal.aborted) {
175
+ err.message = 'stream reset'
176
+ err.code = 'ERR_STREAM_RESET'
177
+ }
178
+
179
+ if (this.abortController.signal.aborted) {
180
+ err.message = 'stream aborted'
181
+ err.code = 'ERR_STREAM_ABORT'
182
+ }
183
+ }
184
+
185
+ // Send no more data if this stream was remotely reset
186
+ if (err.code !== 'ERR_STREAM_RESET') {
187
+ const resetMsg: ResetMessage = {
188
+ id,
189
+ type: 'reset',
190
+ direction: this.type
191
+ }
192
+ push.push(resetMsg)
193
+ }
194
+
195
+ this.log('sink erred', err)
196
+
197
+ this.input.end(err)
198
+ onSinkEnd(err)
199
+ return
200
+ } finally {
201
+ signal.clear()
202
+ }
203
+
204
+ this.log('sink ended')
205
+
206
+ onSinkEnd()
207
+
208
+ const closeMsg: CloseMessage = {
209
+ id,
210
+ type: 'close',
211
+ direction: this.type
212
+ }
213
+ push.push(closeMsg)
214
+ },
215
+ source: this.input,
216
+
217
+ // Close for reading
218
+ close: () => {
219
+ this.stream.closeRead()
220
+ this.stream.closeWrite()
221
+ },
222
+
223
+ closeRead: () => {
224
+ this.input.end()
225
+ },
226
+
227
+ closeWrite: () => {
228
+ this.closeController.abort()
229
+
230
+ const closeMsg: CloseMessage = {
231
+ id,
232
+ type: 'close',
233
+ direction: this.type
234
+ }
235
+ push.push(closeMsg)
236
+ onSinkEnd()
237
+ },
238
+
239
+ // Close for reading and writing (local error)
240
+ abort: (err: Error) => {
241
+ // End the source with the passed error
242
+ this.input.end(err)
243
+ this.abortController.abort()
244
+ onSinkEnd(err)
245
+ },
246
+
247
+ // Close immediately for reading and writing (remote error)
248
+ reset: () => {
249
+ const err = new CodeError('stream reset', 'ERR_STREAM_RESET')
250
+ this.resetController.abort()
251
+ this.input.end(err)
252
+ onSinkEnd(err)
253
+ },
254
+ stat: {
255
+ direction: type === 'initiator' ? 'outbound' : 'inbound',
256
+ timeline: {
257
+ open: Date.now()
258
+ }
259
+ },
260
+ metadata: {}
261
+ }
262
+ }
263
+ }
264
+
265
+ class MockMuxer implements StreamMuxer {
266
+ public source: AsyncGenerator<Uint8Array>
267
+ public input: Pushable<Uint8Array>
268
+ public streamInput: Pushable<StreamMessage>
269
+ public name: string
270
+ public protocol: string = '/mock-muxer/1.0.0'
271
+
272
+ private readonly closeController: AbortController
273
+ private readonly registryInitiatorStreams: Map<string, MuxedStream>
274
+ private readonly registryRecipientStreams: Map<string, MuxedStream>
275
+ private readonly options: StreamMuxerInit
276
+
277
+ private readonly log: Logger
278
+
279
+ constructor (init?: StreamMuxerInit) {
280
+ this.name = `muxer:${muxers++}`
281
+ this.log = logger(`libp2p:mock-muxer:${this.name}`)
282
+ this.registryInitiatorStreams = new Map()
283
+ this.registryRecipientStreams = new Map()
284
+ this.log('create muxer')
285
+ this.options = init ?? { direction: 'inbound' }
286
+ this.closeController = new AbortController()
287
+ // receives data from the muxer at the other end of the stream
288
+ this.source = this.input = pushable({
289
+ onEnd: (err) => {
290
+ this.close(err)
291
+ }
292
+ })
293
+
294
+ // receives messages from all of the muxed streams
295
+ this.streamInput = pushable<StreamMessage>({
296
+ objectMode: true
297
+ })
298
+ }
299
+
300
+ // receive incoming messages
301
+ async sink (source: Source<Uint8ArrayList | Uint8Array>): Promise<void> {
302
+ try {
303
+ await pipe(
304
+ abortableSource(source, this.closeController.signal),
305
+ (source) => map(source, buf => uint8ArrayToString(buf.subarray())),
306
+ ndjson.parse<StreamMessage>,
307
+ async (source) => {
308
+ for await (const message of source) {
309
+ this.log.trace('-> %s %s %s', message.type, message.direction, message.id)
310
+ this.handleMessage(message)
311
+ }
312
+ }
313
+ )
314
+
315
+ this.log('muxed stream ended')
316
+ this.input.end()
317
+ } catch (err: any) {
318
+ this.log('muxed stream errored', err)
319
+ this.input.end(err)
320
+ }
321
+ }
322
+
323
+ handleMessage (message: StreamMessage): void {
324
+ let muxedStream: MuxedStream | undefined
325
+
326
+ const registry = message.direction === 'initiator' ? this.registryRecipientStreams : this.registryInitiatorStreams
327
+
328
+ if (message.type === 'create') {
329
+ if (registry.has(message.id)) {
330
+ throw new Error(`Already had stream for ${message.id}`)
331
+ }
332
+
333
+ muxedStream = this.createStream(message.id, 'recipient')
334
+ registry.set(muxedStream.stream.id, muxedStream)
335
+
336
+ if (this.options.onIncomingStream != null) {
337
+ this.options.onIncomingStream(muxedStream.stream)
338
+ }
339
+ }
340
+
341
+ muxedStream = registry.get(message.id)
342
+
343
+ if (muxedStream == null) {
344
+ this.log.error(`No stream found for ${message.id}`)
345
+
346
+ return
347
+ }
348
+
349
+ if (message.type === 'data') {
350
+ muxedStream.input.push(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
351
+ } else if (message.type === 'reset') {
352
+ this.log('-> reset stream %s %s', muxedStream.type, muxedStream.stream.id)
353
+ muxedStream.stream.reset()
354
+ } else if (message.type === 'close') {
355
+ this.log('-> closing stream %s %s', muxedStream.type, muxedStream.stream.id)
356
+ muxedStream.stream.closeRead()
357
+ }
358
+ }
359
+
360
+ get streams (): Stream[] {
361
+ return Array.from(this.registryRecipientStreams.values())
362
+ .concat(Array.from(this.registryInitiatorStreams.values()))
363
+ .map(({ stream }) => stream)
364
+ }
365
+
366
+ newStream (name?: string): Stream {
367
+ if (this.closeController.signal.aborted) {
368
+ throw new Error('Muxer already closed')
369
+ }
370
+ this.log('newStream %s', name)
371
+ const storedStream = this.createStream(name, 'initiator')
372
+ this.registryInitiatorStreams.set(storedStream.stream.id, storedStream)
373
+
374
+ return storedStream.stream
375
+ }
376
+
377
+ createStream (name?: string, type: 'initiator' | 'recipient' = 'initiator'): MuxedStream {
378
+ const id = name ?? `${this.name}:stream:${streams++}`
379
+
380
+ this.log('createStream %s %s', type, id)
381
+
382
+ const muxedStream: MuxedStream = new MuxedStream({
383
+ id,
384
+ type,
385
+ push: this.streamInput,
386
+ onEnd: () => {
387
+ this.log('stream ended %s %s', type, id)
388
+
389
+ if (type === 'initiator') {
390
+ this.registryInitiatorStreams.delete(id)
391
+ } else {
392
+ this.registryRecipientStreams.delete(id)
393
+ }
394
+
395
+ if (this.options.onStreamEnd != null) {
396
+ this.options.onStreamEnd(muxedStream.stream)
397
+ }
398
+ }
399
+ })
400
+
401
+ return muxedStream
402
+ }
403
+
404
+ close (err?: Error): void {
405
+ if (this.closeController.signal.aborted) return
406
+ this.log('closing muxed streams')
407
+
408
+ if (err == null) {
409
+ this.streams.forEach(s => {
410
+ s.close()
411
+ })
412
+ } else {
413
+ this.streams.forEach(s => {
414
+ s.abort(err)
415
+ })
416
+ }
417
+ this.closeController.abort()
418
+ this.input.end(err)
419
+ }
420
+ }
421
+
422
+ class MockMuxerFactory implements StreamMuxerFactory {
423
+ public protocol: string = '/mock-muxer/1.0.0'
424
+
425
+ createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
426
+ const mockMuxer = new MockMuxer(init)
427
+
428
+ void Promise.resolve().then(async () => {
429
+ void pipe(
430
+ mockMuxer.streamInput,
431
+ ndjson.stringify,
432
+ (source) => map(source, str => new Uint8ArrayList(uint8ArrayFromString(str))),
433
+ async (source) => {
434
+ for await (const buf of source) {
435
+ mockMuxer.input.push(buf.subarray())
436
+ }
437
+ }
438
+ )
439
+ })
440
+
441
+ return mockMuxer
442
+ }
443
+ }
444
+
445
+ export function mockMuxer (): MockMuxerFactory {
446
+ return new MockMuxerFactory()
447
+ }
@@ -0,0 +1,60 @@
1
+ import { EventEmitter } from '@libp2p/interface/events'
2
+ import { peerDiscovery } from '@libp2p/interface/peer-discovery'
3
+ import * as PeerIdFactory from '@libp2p/peer-id-factory'
4
+ import { multiaddr } from '@multiformats/multiaddr'
5
+ import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface/peer-discovery'
6
+ import type { PeerInfo } from '@libp2p/interface/peer-info'
7
+
8
+ interface MockDiscoveryInit {
9
+ discoveryDelay?: number
10
+ }
11
+
12
+ /**
13
+ * Emits 'peer' events on discovery.
14
+ */
15
+ export class MockDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery {
16
+ public readonly options: MockDiscoveryInit
17
+ private _isRunning: boolean
18
+ private _timer: any
19
+
20
+ constructor (init = {}) {
21
+ super()
22
+
23
+ this.options = init
24
+ this._isRunning = false
25
+ }
26
+
27
+ readonly [peerDiscovery] = this
28
+
29
+ start (): void {
30
+ this._isRunning = true
31
+ this._discoverPeer()
32
+ }
33
+
34
+ stop (): void {
35
+ clearTimeout(this._timer)
36
+ this._isRunning = false
37
+ }
38
+
39
+ isStarted (): boolean {
40
+ return this._isRunning
41
+ }
42
+
43
+ _discoverPeer (): void {
44
+ if (!this._isRunning) return
45
+
46
+ PeerIdFactory.createEd25519PeerId()
47
+ .then(peerId => {
48
+ this._timer = setTimeout(() => {
49
+ this.safeDispatchEvent<PeerInfo>('peer', {
50
+ detail: {
51
+ id: peerId,
52
+ multiaddrs: [multiaddr('/ip4/127.0.0.1/tcp/8000')],
53
+ protocols: []
54
+ }
55
+ })
56
+ }, this.options.discoveryDelay ?? 1000)
57
+ })
58
+ .catch(() => {})
59
+ }
60
+ }
@@ -0,0 +1,88 @@
1
+ import merge from 'merge-options'
2
+ import type { Connection } from '@libp2p/interface/connection'
3
+ import type { PeerId } from '@libp2p/interface/peer-id'
4
+ import type { Topology } from '@libp2p/interface/topology'
5
+ import type { IncomingStreamData, Registrar, StreamHandler, StreamHandlerOptions, StreamHandlerRecord } from '@libp2p/interface-internal/registrar'
6
+
7
+ export class MockRegistrar implements Registrar {
8
+ private readonly topologies = new Map<string, Array<{ id: string, topology: Topology }>>()
9
+ private readonly handlers = new Map<string, StreamHandlerRecord>()
10
+
11
+ getProtocols (): string[] {
12
+ return Array.from(this.handlers.keys()).sort()
13
+ }
14
+
15
+ async handle (protocol: string, handler: StreamHandler, opts?: StreamHandlerOptions): Promise<void> {
16
+ const options = merge.bind({ ignoreUndefined: true })({
17
+ maxInboundStreams: 1,
18
+ maxOutboundStreams: 1
19
+ }, opts)
20
+
21
+ if (this.handlers.has(protocol)) {
22
+ throw new Error(`Handler already registered for protocol ${protocol}`)
23
+ }
24
+
25
+ this.handlers.set(protocol, {
26
+ handler,
27
+ options
28
+ })
29
+ }
30
+
31
+ async unhandle (protocol: string): Promise<void> {
32
+ this.handlers.delete(protocol)
33
+ }
34
+
35
+ getHandler (protocol: string): StreamHandlerRecord {
36
+ const handler = this.handlers.get(protocol)
37
+
38
+ if (handler == null) {
39
+ throw new Error(`No handler registered for protocol ${protocol}`)
40
+ }
41
+
42
+ return handler
43
+ }
44
+
45
+ async register (protocol: string, topology: Topology): Promise<string> {
46
+ const id = `topology-id-${Math.random()}`
47
+ let topologies = this.topologies.get(protocol)
48
+
49
+ if (topologies == null) {
50
+ topologies = []
51
+ }
52
+
53
+ topologies.push({
54
+ id,
55
+ topology
56
+ })
57
+
58
+ this.topologies.set(protocol, topologies)
59
+
60
+ return id
61
+ }
62
+
63
+ unregister (id: string | string[]): void {
64
+ if (!Array.isArray(id)) {
65
+ id = [id]
66
+ }
67
+
68
+ id.forEach(id => this.topologies.delete(id))
69
+ }
70
+
71
+ getTopologies (protocol: string): Topology[] {
72
+ return (this.topologies.get(protocol) ?? []).map(t => t.topology)
73
+ }
74
+ }
75
+
76
+ export function mockRegistrar (): Registrar {
77
+ return new MockRegistrar()
78
+ }
79
+
80
+ export async function mockIncomingStreamEvent (protocol: string, conn: Connection, remotePeer: PeerId): Promise<IncomingStreamData> {
81
+ return {
82
+ ...await conn.newStream([protocol]),
83
+ // @ts-expect-error incomplete implementation
84
+ connection: {
85
+ remotePeer
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,49 @@
1
+ import { mockConnection } from './connection.js'
2
+ import type { Libp2pEvents } from '@libp2p/interface'
3
+ import type { Connection, MultiaddrConnection } from '@libp2p/interface/connection'
4
+ import type { EventEmitter } from '@libp2p/interface/events'
5
+ import type { Registrar } from '@libp2p/interface-internal/registrar'
6
+ import type { Upgrader, UpgraderOptions } from '@libp2p/interface-internal/upgrader'
7
+
8
+ export interface MockUpgraderInit {
9
+ registrar?: Registrar
10
+ events?: EventEmitter<Libp2pEvents>
11
+ }
12
+
13
+ class MockUpgrader implements Upgrader {
14
+ private readonly registrar?: Registrar
15
+ private readonly events?: EventEmitter<Libp2pEvents>
16
+
17
+ constructor (init: MockUpgraderInit) {
18
+ this.registrar = init.registrar
19
+ this.events = init.events
20
+ }
21
+
22
+ async upgradeOutbound (multiaddrConnection: MultiaddrConnection, opts: UpgraderOptions = {}): Promise<Connection> {
23
+ const connection = mockConnection(multiaddrConnection, {
24
+ direction: 'outbound',
25
+ registrar: this.registrar,
26
+ ...opts
27
+ })
28
+
29
+ this.events?.safeDispatchEvent('connection:open', { detail: connection })
30
+
31
+ return connection
32
+ }
33
+
34
+ async upgradeInbound (multiaddrConnection: MultiaddrConnection, opts: UpgraderOptions = {}): Promise<Connection> {
35
+ const connection = mockConnection(multiaddrConnection, {
36
+ direction: 'inbound',
37
+ registrar: this.registrar,
38
+ ...opts
39
+ })
40
+
41
+ this.events?.safeDispatchEvent('connection:open', { detail: connection })
42
+
43
+ return connection
44
+ }
45
+ }
46
+
47
+ export function mockUpgrader (init: MockUpgraderInit = {}): Upgrader {
48
+ return new MockUpgrader(init)
49
+ }