@libp2p/interface-compliance-tests 3.0.7 → 4.0.0

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 (184) 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 +150 -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 +19 -0
  13. package/dist/src/connection-encryption/utils/index.js.map +1 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/is-valid-tick.d.ts.map +1 -1
  16. package/dist/src/is-valid-tick.js.map +1 -1
  17. package/dist/src/mocks/connection-encrypter.d.ts +3 -0
  18. package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
  19. package/dist/src/mocks/connection-encrypter.js +98 -0
  20. package/dist/src/mocks/connection-encrypter.js.map +1 -0
  21. package/dist/src/mocks/connection-gater.d.ts +3 -0
  22. package/dist/src/mocks/connection-gater.d.ts.map +1 -0
  23. package/dist/src/mocks/connection-gater.js +17 -0
  24. package/dist/src/mocks/connection-gater.js.map +1 -0
  25. package/dist/src/mocks/connection-manager.d.ts +29 -0
  26. package/dist/src/mocks/connection-manager.d.ts.map +1 -0
  27. package/dist/src/mocks/connection-manager.js +145 -0
  28. package/dist/src/mocks/connection-manager.js.map +1 -0
  29. package/dist/src/mocks/connection.d.ts +32 -0
  30. package/dist/src/mocks/connection.d.ts.map +1 -0
  31. package/dist/src/mocks/connection.js +167 -0
  32. package/dist/src/mocks/connection.js.map +1 -0
  33. package/dist/src/mocks/duplex.d.ts +3 -0
  34. package/dist/src/mocks/duplex.d.ts.map +1 -0
  35. package/dist/src/mocks/duplex.js +9 -0
  36. package/dist/src/mocks/duplex.js.map +1 -0
  37. package/dist/src/mocks/index.d.ts +13 -0
  38. package/dist/src/mocks/index.d.ts.map +1 -0
  39. package/dist/src/mocks/index.js +11 -0
  40. package/dist/src/mocks/index.js.map +1 -0
  41. package/dist/src/mocks/metrics.d.ts +3 -0
  42. package/dist/src/mocks/metrics.d.ts.map +1 -0
  43. package/dist/src/mocks/metrics.js +115 -0
  44. package/dist/src/mocks/metrics.js.map +1 -0
  45. package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
  46. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
  47. package/dist/src/mocks/multiaddr-connection.js +60 -0
  48. package/dist/src/mocks/multiaddr-connection.js.map +1 -0
  49. package/dist/src/mocks/muxer.d.ts +36 -0
  50. package/dist/src/mocks/muxer.d.ts.map +1 -0
  51. package/dist/src/mocks/muxer.js +213 -0
  52. package/dist/src/mocks/muxer.js.map +1 -0
  53. package/dist/src/mocks/peer-discovery.d.ts +22 -0
  54. package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
  55. package/dist/src/mocks/peer-discovery.js +47 -0
  56. package/dist/src/mocks/peer-discovery.js.map +1 -0
  57. package/dist/src/mocks/registrar.d.ts +18 -0
  58. package/dist/src/mocks/registrar.d.ts.map +1 -0
  59. package/dist/src/mocks/registrar.js +66 -0
  60. package/dist/src/mocks/registrar.js.map +1 -0
  61. package/dist/src/mocks/upgrader.d.ts +10 -0
  62. package/dist/src/mocks/upgrader.d.ts.map +1 -0
  63. package/dist/src/mocks/upgrader.js +31 -0
  64. package/dist/src/mocks/upgrader.js.map +1 -0
  65. package/dist/src/peer-discovery/index.d.ts +5 -0
  66. package/dist/src/peer-discovery/index.d.ts.map +1 -0
  67. package/dist/src/peer-discovery/index.js +66 -0
  68. package/dist/src/peer-discovery/index.js.map +1 -0
  69. package/dist/src/pubsub/api.d.ts +6 -0
  70. package/dist/src/pubsub/api.d.ts.map +1 -0
  71. package/dist/src/pubsub/api.js +87 -0
  72. package/dist/src/pubsub/api.js.map +1 -0
  73. package/dist/src/pubsub/connection-handlers.d.ts +6 -0
  74. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
  75. package/dist/src/pubsub/connection-handlers.js +329 -0
  76. package/dist/src/pubsub/connection-handlers.js.map +1 -0
  77. package/dist/src/pubsub/emit-self.d.ts +6 -0
  78. package/dist/src/pubsub/emit-self.d.ts.map +1 -0
  79. package/dist/src/pubsub/emit-self.js +80 -0
  80. package/dist/src/pubsub/emit-self.js.map +1 -0
  81. package/dist/src/pubsub/index.d.ts +18 -0
  82. package/dist/src/pubsub/index.d.ts.map +1 -0
  83. package/dist/src/pubsub/index.js +17 -0
  84. package/dist/src/pubsub/index.js.map +1 -0
  85. package/dist/src/pubsub/messages.d.ts +6 -0
  86. package/dist/src/pubsub/messages.d.ts.map +1 -0
  87. package/dist/src/pubsub/messages.js +48 -0
  88. package/dist/src/pubsub/messages.js.map +1 -0
  89. package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
  90. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
  91. package/dist/src/pubsub/multiple-nodes.js +350 -0
  92. package/dist/src/pubsub/multiple-nodes.js.map +1 -0
  93. package/dist/src/pubsub/two-nodes.d.ts +6 -0
  94. package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
  95. package/dist/src/pubsub/two-nodes.js +217 -0
  96. package/dist/src/pubsub/two-nodes.js.map +1 -0
  97. package/dist/src/pubsub/utils.d.ts +6 -0
  98. package/dist/src/pubsub/utils.d.ts.map +1 -0
  99. package/dist/src/pubsub/utils.js +22 -0
  100. package/dist/src/pubsub/utils.js.map +1 -0
  101. package/dist/src/stream-muxer/base-test.d.ts +5 -0
  102. package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
  103. package/dist/src/stream-muxer/base-test.js +153 -0
  104. package/dist/src/stream-muxer/base-test.js.map +1 -0
  105. package/dist/src/stream-muxer/close-test.d.ts +5 -0
  106. package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
  107. package/dist/src/stream-muxer/close-test.js +357 -0
  108. package/dist/src/stream-muxer/close-test.js.map +1 -0
  109. package/dist/src/stream-muxer/fixtures/pb/message.d.ts +13 -0
  110. package/dist/src/stream-muxer/fixtures/pb/message.d.ts.map +1 -0
  111. package/dist/src/stream-muxer/fixtures/pb/message.js +67 -0
  112. package/dist/src/stream-muxer/fixtures/pb/message.js.map +1 -0
  113. package/dist/src/stream-muxer/index.d.ts +5 -0
  114. package/dist/src/stream-muxer/index.d.ts.map +1 -0
  115. package/dist/src/stream-muxer/index.js +13 -0
  116. package/dist/src/stream-muxer/index.js.map +1 -0
  117. package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
  118. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  119. package/dist/src/stream-muxer/mega-stress-test.js +11 -0
  120. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  121. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  122. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  123. package/dist/src/stream-muxer/spawner.js +37 -0
  124. package/dist/src/stream-muxer/spawner.js.map +1 -0
  125. package/dist/src/stream-muxer/stress-test.d.ts +5 -0
  126. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
  127. package/dist/src/stream-muxer/stress-test.js +23 -0
  128. package/dist/src/stream-muxer/stress-test.js.map +1 -0
  129. package/dist/src/transport/dial-test.d.ts +5 -0
  130. package/dist/src/transport/dial-test.d.ts.map +1 -0
  131. package/dist/src/transport/dial-test.js +98 -0
  132. package/dist/src/transport/dial-test.js.map +1 -0
  133. package/dist/src/transport/filter-test.d.ts +5 -0
  134. package/dist/src/transport/filter-test.d.ts.map +1 -0
  135. package/dist/src/transport/filter-test.js +18 -0
  136. package/dist/src/transport/filter-test.js.map +1 -0
  137. package/dist/src/transport/index.d.ts +15 -0
  138. package/dist/src/transport/index.d.ts.map +1 -0
  139. package/dist/src/transport/index.js +11 -0
  140. package/dist/src/transport/index.js.map +1 -0
  141. package/dist/src/transport/listen-test.d.ts +5 -0
  142. package/dist/src/transport/listen-test.d.ts.map +1 -0
  143. package/dist/src/transport/listen-test.js +152 -0
  144. package/dist/src/transport/listen-test.js.map +1 -0
  145. package/package.json +74 -97
  146. package/src/connection/index.ts +182 -0
  147. package/src/connection-encryption/index.ts +97 -0
  148. package/src/connection-encryption/utils/index.ts +24 -0
  149. package/src/index.ts +0 -1
  150. package/src/is-valid-tick.ts +0 -1
  151. package/src/mocks/connection-encrypter.ts +113 -0
  152. package/src/mocks/connection-gater.ts +18 -0
  153. package/src/mocks/connection-manager.ts +211 -0
  154. package/src/mocks/connection.ts +226 -0
  155. package/src/mocks/duplex.ts +10 -0
  156. package/src/mocks/index.ts +12 -0
  157. package/src/mocks/metrics.ts +162 -0
  158. package/src/mocks/multiaddr-connection.ts +76 -0
  159. package/src/mocks/muxer.ts +303 -0
  160. package/src/mocks/peer-discovery.ts +60 -0
  161. package/src/mocks/registrar.ts +88 -0
  162. package/src/mocks/upgrader.ts +49 -0
  163. package/src/peer-discovery/index.ts +90 -0
  164. package/src/pubsub/api.ts +114 -0
  165. package/src/pubsub/connection-handlers.ts +413 -0
  166. package/src/pubsub/emit-self.ts +99 -0
  167. package/src/pubsub/index.ts +34 -0
  168. package/src/pubsub/messages.ts +59 -0
  169. package/src/pubsub/multiple-nodes.ts +440 -0
  170. package/src/pubsub/two-nodes.ts +273 -0
  171. package/src/pubsub/utils.ts +29 -0
  172. package/src/stream-muxer/base-test.ts +196 -0
  173. package/src/stream-muxer/close-test.ts +442 -0
  174. package/src/stream-muxer/fixtures/pb/message.proto +7 -0
  175. package/src/stream-muxer/fixtures/pb/message.ts +87 -0
  176. package/src/stream-muxer/index.ts +15 -0
  177. package/src/stream-muxer/mega-stress-test.ts +14 -0
  178. package/src/stream-muxer/spawner.ts +55 -0
  179. package/src/stream-muxer/stress-test.ts +27 -0
  180. package/src/transport/dial-test.ts +124 -0
  181. package/src/transport/filter-test.ts +25 -0
  182. package/src/transport/index.ts +25 -0
  183. package/src/transport/listen-test.ts +191 -0
  184. package/dist/typedoc-urls.json +0 -3
@@ -0,0 +1,303 @@
1
+ import { AbstractStream, type AbstractStreamInit } from '@libp2p/interface/stream-muxer/stream'
2
+ import { type Logger, logger } from '@libp2p/logger'
3
+ import { abortableSource } from 'abortable-iterator'
4
+ import map from 'it-map'
5
+ import * as ndjson from 'it-ndjson'
6
+ import { pipe } from 'it-pipe'
7
+ import { type Pushable, pushable } from 'it-pushable'
8
+ import { Uint8ArrayList } from 'uint8arraylist'
9
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
10
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
11
+ import type { AbortOptions } from '@libp2p/interface'
12
+ import type { Direction, 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
+
19
+ interface DataMessage {
20
+ id: string
21
+ type: 'data'
22
+ direction: Direction
23
+ chunk: string
24
+ }
25
+
26
+ interface ResetMessage {
27
+ id: string
28
+ type: 'reset'
29
+ direction: Direction
30
+ }
31
+
32
+ interface CloseMessage {
33
+ id: string
34
+ type: 'close'
35
+ direction: Direction
36
+ }
37
+
38
+ interface CreateMessage {
39
+ id: string
40
+ type: 'create'
41
+ direction: 'outbound'
42
+ }
43
+
44
+ type StreamMessage = DataMessage | ResetMessage | CloseMessage | CreateMessage
45
+
46
+ export interface MockMuxedStreamInit extends AbstractStreamInit {
47
+ push: Pushable<StreamMessage>
48
+ }
49
+
50
+ class MuxedStream extends AbstractStream {
51
+ private readonly push: Pushable<StreamMessage>
52
+
53
+ constructor (init: MockMuxedStreamInit) {
54
+ super(init)
55
+
56
+ this.push = init.push
57
+ }
58
+
59
+ sendNewStream (): void {
60
+ // If initiator, open a new stream
61
+ const createMsg: CreateMessage = {
62
+ id: this.id,
63
+ type: 'create',
64
+ direction: 'outbound'
65
+ }
66
+ this.push.push(createMsg)
67
+ }
68
+
69
+ sendData (data: Uint8ArrayList): void {
70
+ const dataMsg: DataMessage = {
71
+ id: this.id,
72
+ type: 'data',
73
+ chunk: uint8ArrayToString(data.subarray(), 'base64pad'),
74
+ direction: this.direction
75
+ }
76
+ this.push.push(dataMsg)
77
+ }
78
+
79
+ sendReset (): void {
80
+ const resetMsg: ResetMessage = {
81
+ id: this.id,
82
+ type: 'reset',
83
+ direction: this.direction
84
+ }
85
+ this.push.push(resetMsg)
86
+ }
87
+
88
+ sendCloseWrite (): void {
89
+ const closeMsg: CloseMessage = {
90
+ id: this.id,
91
+ type: 'close',
92
+ direction: this.direction
93
+ }
94
+ this.push.push(closeMsg)
95
+ }
96
+
97
+ sendCloseRead (): void {
98
+ // does not support close read, only close write
99
+ }
100
+ }
101
+
102
+ class MockMuxer implements StreamMuxer {
103
+ public source: AsyncGenerator<Uint8Array>
104
+ public input: Pushable<Uint8Array>
105
+ public streamInput: Pushable<StreamMessage>
106
+ public name: string
107
+ public protocol: string = '/mock-muxer/1.0.0'
108
+
109
+ private readonly closeController: AbortController
110
+ private readonly registryInitiatorStreams: Map<string, MuxedStream>
111
+ private readonly registryRecipientStreams: Map<string, MuxedStream>
112
+ private readonly options: StreamMuxerInit
113
+
114
+ private readonly log: Logger
115
+
116
+ constructor (init?: StreamMuxerInit) {
117
+ this.name = `muxer:${muxers++}`
118
+ this.log = logger(`libp2p:mock-muxer:${this.name}`)
119
+ this.registryInitiatorStreams = new Map()
120
+ this.registryRecipientStreams = new Map()
121
+ this.log('create muxer')
122
+ this.options = init ?? { direction: 'inbound' }
123
+ this.closeController = new AbortController()
124
+ // receives data from the muxer at the other end of the stream
125
+ this.source = this.input = pushable({
126
+ onEnd: () => {
127
+ for (const stream of this.registryInitiatorStreams.values()) {
128
+ stream.destroy()
129
+ }
130
+
131
+ for (const stream of this.registryRecipientStreams.values()) {
132
+ stream.destroy()
133
+ }
134
+ }
135
+ })
136
+
137
+ // receives messages from all of the muxed streams
138
+ this.streamInput = pushable<StreamMessage>({
139
+ objectMode: true
140
+ })
141
+ }
142
+
143
+ // receive incoming messages
144
+ async sink (source: Source<Uint8ArrayList | Uint8Array>): Promise<void> {
145
+ try {
146
+ await pipe(
147
+ abortableSource(source, this.closeController.signal),
148
+ (source) => map(source, buf => uint8ArrayToString(buf.subarray())),
149
+ ndjson.parse<StreamMessage>,
150
+ async (source) => {
151
+ for await (const message of source) {
152
+ this.log.trace('-> %s %s %s', message.type, message.direction, message.id)
153
+ this.handleMessage(message)
154
+ }
155
+ }
156
+ )
157
+
158
+ this.log('muxed stream ended')
159
+ this.input.end()
160
+ } catch (err: any) {
161
+ this.log('muxed stream errored', err)
162
+ this.input.end(err)
163
+ }
164
+ }
165
+
166
+ handleMessage (message: StreamMessage): void {
167
+ let muxedStream: MuxedStream | undefined
168
+
169
+ const registry = message.direction === 'outbound' ? this.registryRecipientStreams : this.registryInitiatorStreams
170
+
171
+ if (message.type === 'create') {
172
+ if (registry.has(message.id)) {
173
+ throw new Error(`Already had stream for ${message.id}`)
174
+ }
175
+
176
+ muxedStream = this.createStream(message.id, 'inbound')
177
+ registry.set(muxedStream.id, muxedStream)
178
+
179
+ if (this.options.onIncomingStream != null) {
180
+ this.options.onIncomingStream(muxedStream)
181
+ }
182
+ }
183
+
184
+ muxedStream = registry.get(message.id)
185
+
186
+ if (muxedStream == null) {
187
+ this.log.error(`No stream found for ${message.id}`)
188
+
189
+ return
190
+ }
191
+
192
+ if (message.type === 'data') {
193
+ muxedStream.sourcePush(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
194
+ } else if (message.type === 'reset') {
195
+ this.log('-> reset stream %s %s', muxedStream.direction, muxedStream.id)
196
+ muxedStream.reset()
197
+ } else if (message.type === 'close') {
198
+ this.log('-> closing stream %s %s', muxedStream.direction, muxedStream.id)
199
+ muxedStream.remoteCloseWrite()
200
+ }
201
+ }
202
+
203
+ get streams (): Stream[] {
204
+ return Array.from(this.registryRecipientStreams.values())
205
+ .concat(Array.from(this.registryInitiatorStreams.values()))
206
+ }
207
+
208
+ newStream (name?: string): Stream {
209
+ if (this.closeController.signal.aborted) {
210
+ throw new Error('Muxer already closed')
211
+ }
212
+ this.log('newStream %s', name)
213
+ const storedStream = this.createStream(name, 'outbound')
214
+ this.registryInitiatorStreams.set(storedStream.id, storedStream)
215
+
216
+ return storedStream
217
+ }
218
+
219
+ createStream (name?: string, direction: Direction = 'outbound'): MuxedStream {
220
+ const id = name ?? `${streams++}`
221
+
222
+ this.log('createStream %s %s', direction, id)
223
+
224
+ const muxedStream: MuxedStream = new MuxedStream({
225
+ id,
226
+ direction,
227
+ push: this.streamInput,
228
+ onEnd: () => {
229
+ this.log('stream ended')
230
+
231
+ if (direction === 'outbound') {
232
+ this.registryInitiatorStreams.delete(muxedStream.id)
233
+ } else {
234
+ this.registryRecipientStreams.delete(muxedStream.id)
235
+ }
236
+
237
+ if (this.options.onStreamEnd != null) {
238
+ this.options.onStreamEnd(muxedStream)
239
+ }
240
+ },
241
+ log: logger(`libp2p:mock-muxer:stream:${direction}:${id}`)
242
+ })
243
+
244
+ return muxedStream
245
+ }
246
+
247
+ async close (options?: AbortOptions): Promise<void> {
248
+ if (this.closeController.signal.aborted) {
249
+ return
250
+ }
251
+
252
+ this.log('closing muxed streams')
253
+
254
+ await Promise.all(
255
+ this.streams.map(async s => s.close())
256
+ )
257
+
258
+ this.closeController.abort()
259
+ this.input.end()
260
+ }
261
+
262
+ abort (err: Error): void {
263
+ if (this.closeController.signal.aborted) {
264
+ return
265
+ }
266
+
267
+ this.log('aborting muxed streams')
268
+
269
+ this.streams.forEach(s => {
270
+ s.abort(err)
271
+ })
272
+
273
+ this.closeController.abort(err)
274
+ this.input.end(err)
275
+ }
276
+ }
277
+
278
+ class MockMuxerFactory implements StreamMuxerFactory {
279
+ public protocol: string = '/mock-muxer/1.0.0'
280
+
281
+ createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
282
+ const mockMuxer = new MockMuxer(init)
283
+
284
+ void Promise.resolve().then(async () => {
285
+ void pipe(
286
+ mockMuxer.streamInput,
287
+ ndjson.stringify,
288
+ (source) => map(source, str => new Uint8ArrayList(uint8ArrayFromString(str))),
289
+ async (source) => {
290
+ for await (const buf of source) {
291
+ mockMuxer.input.push(buf.subarray())
292
+ }
293
+ }
294
+ )
295
+ })
296
+
297
+ return mockMuxer
298
+ }
299
+ }
300
+
301
+ export function mockMuxer (): MockMuxerFactory {
302
+ return new MockMuxerFactory()
303
+ }
@@ -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 { Upgrader, UpgraderOptions } from '@libp2p/interface/transport'
6
+ import type { Registrar } from '@libp2p/interface-internal/registrar'
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
+ }
@@ -0,0 +1,90 @@
1
+ import { start, stop } from '@libp2p/interface/startable'
2
+ import { isMultiaddr } from '@multiformats/multiaddr'
3
+ import { expect } from 'aegir/chai'
4
+ import delay from 'delay'
5
+ import pDefer from 'p-defer'
6
+ import type { TestSetup } from '../index.js'
7
+ import type { PeerDiscovery } from '@libp2p/interface/peer-discovery'
8
+
9
+ export default (common: TestSetup<PeerDiscovery>): void => {
10
+ describe('interface-peer-discovery compliance tests', () => {
11
+ let discovery: PeerDiscovery
12
+
13
+ beforeEach(async () => {
14
+ discovery = await common.setup()
15
+ })
16
+
17
+ afterEach('ensure discovery was stopped', async () => {
18
+ await stop(discovery)
19
+
20
+ await common.teardown()
21
+ })
22
+
23
+ it('can start the service', async () => {
24
+ await start(discovery)
25
+ })
26
+
27
+ it('can start and stop the service', async () => {
28
+ await start(discovery)
29
+ await stop(discovery)
30
+ })
31
+
32
+ it('should not fail to stop the service if it was not started', async () => {
33
+ await stop(discovery)
34
+ })
35
+
36
+ it('should not fail to start the service if it is already started', async () => {
37
+ await start(discovery)
38
+ await start(discovery)
39
+ })
40
+
41
+ it('should emit a peer event after start', async () => {
42
+ const defer = pDefer()
43
+
44
+ discovery.addEventListener('peer', (evt) => {
45
+ const { id, multiaddrs } = evt.detail
46
+ expect(id).to.exist()
47
+ expect(id)
48
+ .to.have.property('type')
49
+ .that.is.oneOf(['RSA', 'Ed25519', 'secp256k1'])
50
+ expect(multiaddrs).to.exist()
51
+
52
+ multiaddrs.forEach((m) => expect(isMultiaddr(m)).to.eql(true))
53
+
54
+ defer.resolve()
55
+ })
56
+
57
+ await start(discovery)
58
+
59
+ await defer.promise
60
+ })
61
+
62
+ it('should not receive a peer event before start', async () => {
63
+ discovery.addEventListener('peer', () => {
64
+ throw new Error('should not receive a peer event before start')
65
+ })
66
+
67
+ await delay(2000)
68
+ })
69
+
70
+ it('should not receive a peer event after stop', async () => {
71
+ const deferStart = pDefer()
72
+
73
+ discovery.addEventListener('peer', () => {
74
+ deferStart.resolve()
75
+ })
76
+
77
+ await start(discovery)
78
+
79
+ await deferStart.promise
80
+
81
+ await stop(discovery)
82
+
83
+ discovery.addEventListener('peer', () => {
84
+ throw new Error('should not receive a peer event after stop')
85
+ })
86
+
87
+ await delay(2000)
88
+ })
89
+ })
90
+ }
@@ -0,0 +1,114 @@
1
+ import { isStartable, start, stop } from '@libp2p/interface/startable'
2
+ import { expect } from 'aegir/chai'
3
+ import delay from 'delay'
4
+ import pDefer from 'p-defer'
5
+ import pWaitFor from 'p-wait-for'
6
+ import sinon from 'sinon'
7
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
8
+ import { mockNetwork } from '../mocks/index.js'
9
+ import { createComponents } from './utils.js'
10
+ import type { PubSubArgs, PubSubComponents } from './index.js'
11
+ import type { TestSetup } from '../index.js'
12
+ import type { PubSub } from '@libp2p/interface/pubsub'
13
+
14
+ const topic = 'foo'
15
+ const data = uint8ArrayFromString('bar')
16
+
17
+ export default (common: TestSetup<PubSub, PubSubArgs>): void => {
18
+ describe('pubsub api', () => {
19
+ let pubsub: PubSub
20
+ let components: PubSubComponents
21
+
22
+ // Create pubsub router
23
+ beforeEach(async () => {
24
+ mockNetwork.reset()
25
+ components = await createComponents()
26
+
27
+ pubsub = components.pubsub = await common.setup({
28
+ components,
29
+ init: {
30
+ emitSelf: true
31
+ }
32
+ })
33
+ })
34
+
35
+ afterEach(async () => {
36
+ sinon.restore()
37
+ await stop(...Object.values(components))
38
+ await common.teardown()
39
+ mockNetwork.reset()
40
+ })
41
+
42
+ it('can start correctly', async () => {
43
+ if (!isStartable(pubsub)) {
44
+ return
45
+ }
46
+
47
+ sinon.spy(components.registrar, 'register')
48
+
49
+ await start(...Object.values(components))
50
+
51
+ expect(pubsub.isStarted()).to.equal(true)
52
+ expect(components.registrar.register).to.have.property('callCount', 1)
53
+ })
54
+
55
+ it('can stop correctly', async () => {
56
+ if (!isStartable(pubsub)) {
57
+ return
58
+ }
59
+
60
+ sinon.spy(components.registrar, 'unregister')
61
+
62
+ await start(...Object.values(components))
63
+ await stop(...Object.values(components))
64
+
65
+ expect(pubsub.isStarted()).to.equal(false)
66
+ expect(components.registrar.unregister).to.have.property('callCount', 1)
67
+ })
68
+
69
+ it('can subscribe and unsubscribe correctly', async () => {
70
+ const handler = (): void => {
71
+ throw new Error('a message should not be received')
72
+ }
73
+
74
+ await start(...Object.values(components))
75
+ pubsub.subscribe(topic)
76
+ pubsub.addEventListener('message', handler)
77
+
78
+ await pWaitFor(() => {
79
+ const topics = pubsub.getTopics()
80
+ return topics.length === 1 && topics[0] === topic
81
+ })
82
+
83
+ pubsub.removeEventListener('message', handler)
84
+ pubsub.unsubscribe(topic)
85
+
86
+ await pWaitFor(() => pubsub.getTopics().length === 0)
87
+
88
+ // Publish to guarantee the handler is not called
89
+ await pubsub.publish(topic, data)
90
+
91
+ // handlers are called async
92
+ await delay(100)
93
+
94
+ await stop(...Object.values(components))
95
+ })
96
+
97
+ it('can subscribe and publish correctly', async () => {
98
+ const defer = pDefer()
99
+
100
+ await start(...Object.values(components))
101
+
102
+ pubsub.subscribe(topic)
103
+ pubsub.addEventListener('message', (evt) => {
104
+ expect(evt).to.have.nested.property('detail.topic', topic)
105
+ expect(evt).to.have.deep.nested.property('detail.data', data)
106
+ defer.resolve()
107
+ })
108
+ await pubsub.publish(topic, data)
109
+ await defer.promise
110
+
111
+ await stop(...Object.values(components))
112
+ })
113
+ })
114
+ }