@libp2p/interface-compliance-tests 1.1.6 → 1.1.10

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 (92) hide show
  1. package/dist/src/mocks/connection.d.ts +13 -2
  2. package/dist/src/mocks/connection.d.ts.map +1 -1
  3. package/dist/src/mocks/connection.js +56 -24
  4. package/dist/src/mocks/connection.js.map +1 -1
  5. package/dist/src/mocks/multiaddr-connection.d.ts +2 -1
  6. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -1
  7. package/dist/src/mocks/multiaddr-connection.js +2 -2
  8. package/dist/src/mocks/multiaddr-connection.js.map +1 -1
  9. package/dist/src/mocks/muxer.d.ts +2 -2
  10. package/dist/src/mocks/muxer.d.ts.map +1 -1
  11. package/dist/src/mocks/muxer.js +175 -26
  12. package/dist/src/mocks/muxer.js.map +1 -1
  13. package/dist/src/mocks/registrar.d.ts +11 -2
  14. package/dist/src/mocks/registrar.d.ts.map +1 -1
  15. package/dist/src/mocks/registrar.js +47 -5
  16. package/dist/src/mocks/registrar.js.map +1 -1
  17. package/dist/src/mocks/upgrader.d.ts.map +1 -1
  18. package/dist/src/mocks/upgrader.js +8 -4
  19. package/dist/src/mocks/upgrader.js.map +1 -1
  20. package/dist/src/pubsub/api.d.ts +3 -2
  21. package/dist/src/pubsub/api.d.ts.map +1 -1
  22. package/dist/src/pubsub/api.js +23 -13
  23. package/dist/src/pubsub/api.js.map +1 -1
  24. package/dist/src/pubsub/connection-handlers.d.ts +3 -2
  25. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -1
  26. package/dist/src/pubsub/connection-handlers.js +165 -69
  27. package/dist/src/pubsub/connection-handlers.js.map +1 -1
  28. package/dist/src/pubsub/emit-self.d.ts +3 -2
  29. package/dist/src/pubsub/emit-self.d.ts.map +1 -1
  30. package/dist/src/pubsub/emit-self.js +15 -4
  31. package/dist/src/pubsub/emit-self.js.map +1 -1
  32. package/dist/src/pubsub/index.d.ts +6 -3
  33. package/dist/src/pubsub/index.d.ts.map +1 -1
  34. package/dist/src/pubsub/index.js.map +1 -1
  35. package/dist/src/pubsub/messages.d.ts +3 -2
  36. package/dist/src/pubsub/messages.d.ts.map +1 -1
  37. package/dist/src/pubsub/messages.js +42 -39
  38. package/dist/src/pubsub/messages.js.map +1 -1
  39. package/dist/src/pubsub/multiple-nodes.d.ts +3 -2
  40. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -1
  41. package/dist/src/pubsub/multiple-nodes.js +163 -65
  42. package/dist/src/pubsub/multiple-nodes.js.map +1 -1
  43. package/dist/src/pubsub/two-nodes.d.ts +3 -2
  44. package/dist/src/pubsub/two-nodes.d.ts.map +1 -1
  45. package/dist/src/pubsub/two-nodes.js +80 -38
  46. package/dist/src/pubsub/two-nodes.js.map +1 -1
  47. package/dist/src/stream-muxer/base-test.js +1 -1
  48. package/dist/src/stream-muxer/base-test.js.map +1 -1
  49. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  50. package/dist/src/stream-muxer/close-test.js +12 -10
  51. package/dist/src/stream-muxer/close-test.js.map +1 -1
  52. package/dist/src/transport/dial-test.d.ts.map +1 -1
  53. package/dist/src/transport/dial-test.js +2 -1
  54. package/dist/src/transport/dial-test.js.map +1 -1
  55. package/dist/src/transport/filter-test.js +1 -1
  56. package/dist/src/transport/filter-test.js.map +1 -1
  57. package/dist/src/transport/listen-test.d.ts.map +1 -1
  58. package/dist/src/transport/listen-test.js +2 -1
  59. package/dist/src/transport/listen-test.js.map +1 -1
  60. package/dist/src/utils/is-valid-tick.d.ts +6 -0
  61. package/dist/src/utils/is-valid-tick.d.ts.map +1 -0
  62. package/dist/src/utils/is-valid-tick.js +15 -0
  63. package/dist/src/utils/is-valid-tick.js.map +1 -0
  64. package/package.json +7 -5
  65. package/src/mocks/connection.ts +80 -24
  66. package/src/mocks/multiaddr-connection.ts +3 -2
  67. package/src/mocks/muxer.ts +230 -28
  68. package/src/mocks/registrar.ts +65 -7
  69. package/src/mocks/upgrader.ts +8 -5
  70. package/src/pubsub/api.ts +29 -15
  71. package/src/pubsub/connection-handlers.ts +186 -75
  72. package/src/pubsub/emit-self.ts +19 -7
  73. package/src/pubsub/index.ts +6 -3
  74. package/src/pubsub/messages.ts +59 -44
  75. package/src/pubsub/multiple-nodes.ts +189 -76
  76. package/src/pubsub/two-nodes.ts +94 -46
  77. package/src/stream-muxer/base-test.ts +1 -1
  78. package/src/stream-muxer/close-test.ts +13 -12
  79. package/src/transport/dial-test.ts +2 -1
  80. package/src/transport/filter-test.ts +1 -1
  81. package/src/transport/listen-test.ts +2 -1
  82. package/src/utils/is-valid-tick.ts +18 -0
  83. package/dist/src/pubsub/utils.d.ts +0 -3
  84. package/dist/src/pubsub/utils.d.ts.map +0 -1
  85. package/dist/src/pubsub/utils.js +0 -11
  86. package/dist/src/pubsub/utils.js.map +0 -1
  87. package/dist/src/transport/utils/index.d.ts +0 -15
  88. package/dist/src/transport/utils/index.d.ts.map +0 -1
  89. package/dist/src/transport/utils/index.js +0 -137
  90. package/dist/src/transport/utils/index.js.map +0 -1
  91. package/src/pubsub/utils.ts +0 -13
  92. package/src/transport/utils/index.ts +0 -172
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libp2p/interface-compliance-tests",
3
- "version": "1.1.6",
3
+ "version": "1.1.10",
4
4
  "description": "Compliance tests for JS libp2p interfaces",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "homepage": "https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interface-compliance-tests#readme",
@@ -56,6 +56,10 @@
56
56
  "import": "./dist/src/crypto/index.js",
57
57
  "types": "./dist/src/crypto/index.d.ts"
58
58
  },
59
+ "./mocks": {
60
+ "import": "./dist/src/mocks/index.js",
61
+ "types": "./dist/src/mocks/index.d.ts"
62
+ },
59
63
  "./peer-discovery": {
60
64
  "import": "./dist/src/peer-discovery/index.js",
61
65
  "types": "./dist/src/peer-discovery/index.d.ts"
@@ -88,10 +92,6 @@
88
92
  "import": "./dist/src/transport/utils/index.js",
89
93
  "types": "./dist/src/transport/utils/index.d.ts"
90
94
  },
91
- "./mocks": {
92
- "import": "./dist/src/mocks/index.js",
93
- "types": "./dist/src/mocks/index.d.ts"
94
- },
95
95
  "./utils/peers": {
96
96
  "import": "./dist/src/utils/peers.js",
97
97
  "types": "./dist/src/utils/peers.d.ts"
@@ -200,6 +200,8 @@
200
200
  "dependencies": {
201
201
  "@libp2p/crypto": "^0.22.2",
202
202
  "@libp2p/interfaces": "^1.0.0",
203
+ "@libp2p/logger": "^1.0.3",
204
+ "@libp2p/multistream-select": "^1.0.0",
203
205
  "@libp2p/peer-id": "^1.0.0",
204
206
  "@libp2p/peer-id-factory": "^1.0.0",
205
207
  "@libp2p/pubsub": "^1.1.0",
@@ -1,5 +1,4 @@
1
1
  import { peerIdFromString } from '@libp2p/peer-id'
2
- import { createEd25519PeerId } from '@libp2p/peer-id-factory'
3
2
  import { pipe } from 'it-pipe'
4
3
  import { duplexPair } from 'it-pair/duplex'
5
4
  import type { MultiaddrConnection } from '@libp2p/interfaces/transport'
@@ -7,20 +6,68 @@ import type { Connection, Stream, Metadata, ProtocolStream } from '@libp2p/inter
7
6
  import type { Muxer } from '@libp2p/interfaces/stream-muxer'
8
7
  import type { Duplex } from 'it-stream-types'
9
8
  import { mockMuxer } from './muxer.js'
9
+ import type { PeerId } from '@libp2p/interfaces/src/peer-id'
10
+ import { mockMultiaddrConnection } from './multiaddr-connection.js'
11
+ import type { Registrar } from '@libp2p/interfaces/registrar'
12
+ import { mockRegistrar } from './registrar.js'
13
+ import { Dialer, Listener } from '@libp2p/multistream-select'
14
+ import { logger } from '@libp2p/logger'
15
+ import { CustomEvent } from '@libp2p/interfaces'
10
16
 
11
- export async function mockConnection (maConn: MultiaddrConnection, direction: 'inbound' | 'outbound' = 'inbound', muxer: Muxer = mockMuxer()): Promise<Connection> {
17
+ const log = logger('libp2p:mock-connection')
18
+
19
+ export interface MockConnectionOptions {
20
+ direction?: 'inbound' | 'outbound'
21
+ muxer?: Muxer
22
+ registrar?: Registrar
23
+ }
24
+
25
+ export function mockConnection (maConn: MultiaddrConnection, opts: MockConnectionOptions = {}): Connection {
12
26
  const remoteAddr = maConn.remoteAddr
13
27
  const remotePeerIdStr = remoteAddr.getPeerId()
14
- const remotePeer = remotePeerIdStr != null ? peerIdFromString(remotePeerIdStr) : await createEd25519PeerId()
28
+
29
+ if (remotePeerIdStr == null) {
30
+ throw new Error('Remote multiaddr must contain a peer id')
31
+ }
32
+
33
+ const remotePeer = peerIdFromString(remotePeerIdStr)
15
34
  const registry = new Map()
16
35
  const streams: Stream[] = []
17
- let streamId = 0
36
+ const direction = opts.direction ?? 'inbound'
37
+ const registrar = opts.registrar ?? mockRegistrar()
38
+
39
+ const muxer = opts.muxer ?? mockMuxer({
40
+ onStream: (muxedStream) => {
41
+ const mss = new Listener(muxedStream)
42
+ try {
43
+ mss.handle(registrar.getProtocols())
44
+ .then(({ stream, protocol }) => {
45
+ log('%s: incoming stream opened on %s', direction, protocol)
46
+ muxedStream = { ...muxedStream, ...stream }
47
+
48
+ connection.addStream(muxedStream, { protocol, metadata: {} })
49
+ const handler = registrar.getHandler(protocol)
50
+
51
+ handler(new CustomEvent('incomingStream', {
52
+ detail: { connection, stream: muxedStream, protocol }
53
+ }))
54
+ }).catch(err => {
55
+ log.error(err)
56
+ })
57
+ } catch (err: any) {
58
+ log.error(err)
59
+ }
60
+ },
61
+ onStreamEnd: (stream) => {
62
+ connection.removeStream(stream.id)
63
+ }
64
+ })
18
65
 
19
66
  void pipe(
20
67
  maConn, muxer, maConn
21
68
  )
22
69
 
23
- return {
70
+ const connection: Connection = {
24
71
  id: 'mock-connection',
25
72
  remoteAddr,
26
73
  remotePeer,
@@ -43,11 +90,17 @@ export async function mockConnection (maConn: MultiaddrConnection, direction: 'i
43
90
  throw new Error('protocols must have a length')
44
91
  }
45
92
 
46
- const id = `${streamId++}`
93
+ const id = `${Math.random()}`
47
94
  const stream: Stream = muxer.newStream(id)
95
+ const mss = new Dialer(stream)
96
+ const result = await mss.select(protocols)
97
+
48
98
  const streamData: ProtocolStream = {
49
- protocol: protocols[0],
50
- stream
99
+ protocol: result.protocol,
100
+ stream: {
101
+ ...stream,
102
+ ...result.stream
103
+ }
51
104
  }
52
105
 
53
106
  registry.set(id, streamData)
@@ -64,6 +117,8 @@ export async function mockConnection (maConn: MultiaddrConnection, direction: 'i
64
117
  await maConn.close()
65
118
  }
66
119
  }
120
+
121
+ return connection
67
122
  }
68
123
 
69
124
  export function mockStream (stream: Duplex<Uint8Array>): Stream {
@@ -79,23 +134,24 @@ export function mockStream (stream: Duplex<Uint8Array>): Stream {
79
134
  }
80
135
  }
81
136
 
82
- export function connectionPair (): [ Connection, Connection ] {
83
- const [d0, d1] = duplexPair<Uint8Array>()
137
+ export interface Peer {
138
+ peerId: PeerId
139
+ registrar: Registrar
140
+ }
141
+
142
+ export function connectionPair (a: Peer, b: Peer): [ Connection, Connection ] {
143
+ const [peerBtoPeerA, peerAtoPeerB] = duplexPair<Uint8Array>()
84
144
 
85
145
  return [
86
- // @ts-expect-error not a complete implementation
87
- {
88
- newStream: async (multicodecs: string[]) => await Promise.resolve({
89
- stream: mockStream(d0),
90
- protocol: multicodecs[0]
91
- })
92
- },
93
- // @ts-expect-error not a complete implementation
94
- {
95
- newStream: async (multicodecs: string[]) => await Promise.resolve({
96
- stream: mockStream(d1),
97
- protocol: multicodecs[0]
98
- })
99
- }
146
+ mockConnection(
147
+ mockMultiaddrConnection(peerAtoPeerB, b.peerId), {
148
+ registrar: a.registrar
149
+ }
150
+ ),
151
+ mockConnection(
152
+ mockMultiaddrConnection(peerBtoPeerA, a.peerId), {
153
+ registrar: b.registrar
154
+ }
155
+ )
100
156
  ]
101
157
  }
@@ -1,8 +1,9 @@
1
1
  import { Multiaddr } from '@multiformats/multiaddr'
2
2
  import type { MultiaddrConnection } from '@libp2p/interfaces/transport'
3
3
  import type { Duplex } from 'it-stream-types'
4
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
4
5
 
5
- export function mockMultiaddrConnection (source: Duplex<Uint8Array> & Partial<MultiaddrConnection>): MultiaddrConnection {
6
+ export function mockMultiaddrConnection (source: Duplex<Uint8Array> & Partial<MultiaddrConnection>, peerId: PeerId): MultiaddrConnection {
6
7
  const maConn: MultiaddrConnection = {
7
8
  async close () {
8
9
 
@@ -10,7 +11,7 @@ export function mockMultiaddrConnection (source: Duplex<Uint8Array> & Partial<Mu
10
11
  timeline: {
11
12
  open: Date.now()
12
13
  },
13
- remoteAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4001'),
14
+ remoteAddr: new Multiaddr(`/ip4/127.0.0.1/tcp/4001/p2p/${peerId.toString()}`),
14
15
  ...source
15
16
  }
16
17
 
@@ -1,41 +1,243 @@
1
- import { pair } from 'it-pair'
2
- import { pushable } from 'it-pushable'
3
- import drain from 'it-drain'
1
+ import { Pushable, pushable } from 'it-pushable'
2
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
3
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
4
+ import { abortableSource } from 'abortable-iterator'
4
5
  import type { Stream } from '@libp2p/interfaces/connection'
5
- import type { Muxer } from '@libp2p/interfaces/stream-muxer'
6
+ import type { Muxer, MuxerOptions } from '@libp2p/interfaces/stream-muxer'
7
+ import type { Source } from 'it-stream-types'
6
8
 
7
- export function mockMuxer (): Muxer {
9
+ interface DataMessage {
10
+ id: string
11
+ type: 'data'
12
+ chunk: string
13
+ }
14
+
15
+ interface ResetMessage {
16
+ id: string
17
+ type: 'reset'
18
+ }
19
+
20
+ interface CloseMessage {
21
+ id: string
22
+ type: 'close'
23
+ }
24
+
25
+ type StreamMessage = DataMessage | ResetMessage | CloseMessage
26
+
27
+ class MuxedStream {
28
+ public id: string
29
+ public input: Pushable<Uint8Array>
30
+ public stream: Stream
31
+
32
+ private sourceClosed: boolean
33
+ private sinkClosed: boolean
34
+ private readonly controller: AbortController
35
+ private readonly onEnd: () => void
36
+
37
+ constructor (opts: { id: string, push: Pushable<StreamMessage>, onEnd: () => void }) {
38
+ const { id, push, onEnd } = opts
39
+
40
+ this.id = id
41
+ this.controller = new AbortController()
42
+ this.onEnd = onEnd
43
+ this.sourceClosed = false
44
+ this.sinkClosed = false
45
+ this.input = pushable<Uint8Array>({
46
+ onEnd: () => {
47
+ this.sourceClosed = true
48
+ this.maybeEndStream()
49
+ }
50
+ })
51
+ this.stream = {
52
+ id,
53
+ sink: async (source) => {
54
+ source = abortableSource(source, this.controller.signal)
55
+
56
+ try {
57
+ for await (const chunk of source) {
58
+ const dataMsg: DataMessage = {
59
+ id,
60
+ type: 'data',
61
+ chunk: uint8ArrayToString(chunk, 'base64')
62
+ }
63
+
64
+ push.push(dataMsg)
65
+ }
66
+
67
+ const closeMsg: CloseMessage = {
68
+ id,
69
+ type: 'close'
70
+ }
71
+
72
+ push.push(closeMsg)
73
+ } catch (err) {
74
+ if (!this.controller.signal.aborted) {
75
+ throw err
76
+ }
77
+ }
78
+
79
+ this.closeSink()
80
+ },
81
+ source: this.input,
82
+ close: () => {
83
+ const closeMsg: CloseMessage = {
84
+ id,
85
+ type: 'close'
86
+ }
87
+ push.push(closeMsg)
88
+
89
+ this.closeSink()
90
+ this.closeSource()
91
+ },
92
+ abort: () => {
93
+ const resetMsg: ResetMessage = {
94
+ id,
95
+ type: 'reset'
96
+ }
97
+ push.push(resetMsg)
98
+
99
+ this.closeSink()
100
+ this.closeSource()
101
+ },
102
+ reset: () => {
103
+ const resetMsg: ResetMessage = {
104
+ id,
105
+ type: 'reset'
106
+ }
107
+ push.push(resetMsg)
108
+
109
+ this.closeSink()
110
+ this.closeSource()
111
+ },
112
+ timeline: {
113
+ open: Date.now()
114
+ }
115
+ }
116
+ }
117
+
118
+ maybeEndStream () {
119
+ if (this.stream.timeline.close != null) {
120
+ // already ended
121
+ return
122
+ }
123
+
124
+ if (this.sinkClosed && this.sourceClosed) {
125
+ this.stream.timeline.close = Date.now()
126
+ this.onEnd()
127
+ }
128
+ }
129
+
130
+ closeSource () {
131
+ this.sourceClosed = true
132
+ this.input.end()
133
+ }
134
+
135
+ closeSink () {
136
+ this.sinkClosed = true
137
+ this.controller.abort()
138
+ this.maybeEndStream()
139
+ }
140
+ }
141
+
142
+ export function mockMuxer (options?: MuxerOptions): Muxer {
8
143
  let streamId = 0
9
- let streams: Stream[] = []
10
- const p = pushable<Uint8Array>()
144
+ const streams = new Map<string, MuxedStream>()
145
+
146
+ // process incoming messages from the other muxer
147
+ const muxerSource = pushable<Uint8Array>({
148
+ onEnd: () => {
149
+ for (const muxedStream of streams.values()) {
150
+ muxedStream.stream.close()
151
+ }
152
+ }
153
+ })
154
+
155
+ // receives messages from all of the muxed streams
156
+ const push = pushable<StreamMessage>()
157
+ void Promise.resolve().then(async () => {
158
+ for await (const message of push) {
159
+ if (message.type === 'data') {
160
+ muxerSource.push(uint8ArrayFromString(JSON.stringify({
161
+ id: message.id,
162
+ type: message.type,
163
+ chunk: message.chunk
164
+ })))
165
+ } else {
166
+ muxerSource.push(uint8ArrayFromString(JSON.stringify({
167
+ id: message.id,
168
+ type: message.type
169
+ })))
170
+ }
171
+ }
172
+ })
173
+
174
+ function createStream (name?: string): MuxedStream {
175
+ const id = name ?? `${streamId++}`
176
+
177
+ const muxedStream: MuxedStream = new MuxedStream({
178
+ id,
179
+ push,
180
+ onEnd: () => {
181
+ streams.delete(id)
182
+
183
+ if (options?.onStreamEnd != null) {
184
+ options?.onStreamEnd(muxedStream.stream)
185
+ }
186
+ }
187
+ })
188
+
189
+ return muxedStream
190
+ }
11
191
 
12
192
  const muxer: Muxer = {
13
- source: p,
14
- sink: async (source) => {
15
- await drain(source)
193
+ // receive incoming messages
194
+ async sink (source: Source<Uint8Array>) {
195
+ for await (const buf of source) {
196
+ const message: StreamMessage = JSON.parse(uint8ArrayToString(buf))
197
+ let muxedStream = streams.get(message.id)
198
+
199
+ if (muxedStream == null) {
200
+ muxedStream = createStream(message.id)
201
+ streams.set(muxedStream.stream.id, muxedStream)
202
+
203
+ if (options?.onStream != null) {
204
+ options.onStream(muxedStream.stream)
205
+ }
206
+ }
207
+
208
+ if (message.type === 'data') {
209
+ muxedStream.input.push(uint8ArrayFromString(message.chunk, 'base64'))
210
+ } else if (message.type === 'reset') {
211
+ muxedStream.closeSink()
212
+ muxedStream.closeSource()
213
+ } else if (message.type === 'close') {
214
+ muxedStream.closeSource()
215
+ }
216
+ }
217
+
218
+ for (const muxedStream of streams.values()) {
219
+ muxedStream.stream.close()
220
+ }
221
+
222
+ muxerSource.end()
16
223
  },
224
+
225
+ source: muxerSource,
226
+
17
227
  get streams () {
18
- return streams
228
+ return Array.from(streams.values()).map(({ stream }) => stream)
19
229
  },
20
- newStream: (name?: string) => {
21
- const echo = pair<Uint8Array>()
22
-
23
- const id = `${streamId++}`
24
- const stream: Stream = {
25
- id,
26
- sink: echo.sink,
27
- source: echo.source,
28
- close: () => {
29
- streams = streams.filter(s => s !== stream)
30
- },
31
- abort: () => {},
32
- reset: () => {},
33
- timeline: {
34
- open: 0
35
- }
230
+
231
+ newStream (name?: string) {
232
+ const storedStream = createStream(name)
233
+
234
+ streams.set(storedStream.stream.id, storedStream)
235
+
236
+ if (options?.onStream != null) {
237
+ options.onStream(storedStream.stream)
36
238
  }
37
239
 
38
- return stream
240
+ return storedStream.stream
39
241
  }
40
242
  }
41
243
 
@@ -1,15 +1,41 @@
1
- import type { Registrar, StreamHandler } from '@libp2p/interfaces/registrar'
1
+ import type { IncomingStreamData, Registrar, StreamHandler } from '@libp2p/interfaces/registrar'
2
+ import type { Connection } from '@libp2p/interfaces/src/connection'
3
+ import type { PeerId } from '@libp2p/interfaces/src/peer-id'
2
4
  import type { Topology } from '@libp2p/interfaces/topology'
5
+ import { connectionPair } from './connection.js'
6
+ import { CustomEvent } from '@libp2p/interfaces'
3
7
 
4
8
  export class MockRegistrar implements Registrar {
5
9
  private readonly topologies: Map<string, { topology: Topology, protocols: string[] }> = new Map()
6
10
  private readonly handlers: Map<string, { handler: StreamHandler, protocols: string[] }> = new Map()
7
11
 
12
+ getProtocols () {
13
+ const protocols = new Set<string>()
14
+
15
+ for (const topology of this.topologies.values()) {
16
+ topology.protocols.forEach(protocol => protocols.add(protocol))
17
+ }
18
+
19
+ for (const handler of this.handlers.values()) {
20
+ handler.protocols.forEach(protocol => protocols.add(protocol))
21
+ }
22
+
23
+ return Array.from(protocols).sort()
24
+ }
25
+
8
26
  async handle (protocols: string | string[], handler: StreamHandler) {
9
27
  if (!Array.isArray(protocols)) {
10
28
  protocols = [protocols]
11
29
  }
12
30
 
31
+ for (const protocol of protocols) {
32
+ for (const { protocols } of this.handlers.values()) {
33
+ if (protocols.includes(protocol)) {
34
+ throw new Error(`Handler already registered for protocol ${protocol}`)
35
+ }
36
+ }
37
+ }
38
+
13
39
  const id = `handler-id-${Math.random()}`
14
40
 
15
41
  this.handlers.set(id, {
@@ -24,16 +50,14 @@ export class MockRegistrar implements Registrar {
24
50
  this.handlers.delete(id)
25
51
  }
26
52
 
27
- getHandlers (protocol: string) {
28
- const output: StreamHandler[] = []
29
-
53
+ getHandler (protocol: string) {
30
54
  for (const { handler, protocols } of this.handlers.values()) {
31
55
  if (protocols.includes(protocol)) {
32
- output.push(handler)
56
+ return handler
33
57
  }
34
58
  }
35
59
 
36
- return output
60
+ throw new Error(`No handler registered for protocol ${protocol}`)
37
61
  }
38
62
 
39
63
  register (protocols: string | string[], topology: Topology) {
@@ -68,10 +92,44 @@ export class MockRegistrar implements Registrar {
68
92
  }
69
93
  }
70
94
 
71
- return output
95
+ if (output.length > 0) {
96
+ return output
97
+ }
98
+
99
+ throw new Error(`No topologies registered for protocol ${protocol}`)
72
100
  }
73
101
  }
74
102
 
75
103
  export function mockRegistrar () {
76
104
  return new MockRegistrar()
77
105
  }
106
+
107
+ export async function mockIncomingStreamEvent (protocol: string, conn: Connection, remotePeer: PeerId): Promise<CustomEvent<IncomingStreamData>> {
108
+ // @ts-expect-error incomplete implementation
109
+ return new CustomEvent('incomingStream', {
110
+ detail: {
111
+ ...await conn.newStream([protocol]),
112
+ connection: {
113
+ remotePeer
114
+ }
115
+ }
116
+ })
117
+ }
118
+
119
+ export interface Peer {
120
+ peerId: PeerId
121
+ registrar: Registrar
122
+ }
123
+
124
+ export async function connectPeers (protocol: string, a: Peer, b: Peer) {
125
+ // Notify peers of connection
126
+ const [aToB, bToA] = connectionPair(a, b)
127
+
128
+ for (const topology of a.registrar.getTopologies(protocol)) {
129
+ await topology.onConnect(b.peerId, aToB)
130
+ }
131
+
132
+ for (const topology of b.registrar.getTopologies(protocol)) {
133
+ await topology.onConnect(a.peerId, bToA)
134
+ }
135
+ }
@@ -1,5 +1,4 @@
1
1
  import { expect } from 'aegir/utils/chai.js'
2
- import { mockMuxer } from './muxer.js'
3
2
  import { mockConnection } from './connection.js'
4
3
  import type { Upgrader, MultiaddrConnection } from '@libp2p/interfaces/transport'
5
4
  import type { Muxer } from '@libp2p/interfaces/stream-muxer'
@@ -16,16 +15,20 @@ export function mockUpgrader (options: MockUpgraderOptions = {}) {
16
15
  return multiaddrConnection
17
16
  }
18
17
 
19
- const muxer = options.muxer ?? mockMuxer()
20
-
21
18
  const upgrader: Upgrader = {
22
19
  async upgradeOutbound (multiaddrConnection) {
23
20
  ensureProps(multiaddrConnection)
24
- return await mockConnection(multiaddrConnection, 'outbound', muxer)
21
+ return mockConnection(multiaddrConnection, {
22
+ direction: 'outbound',
23
+ muxer: options.muxer
24
+ })
25
25
  },
26
26
  async upgradeInbound (multiaddrConnection) {
27
27
  ensureProps(multiaddrConnection)
28
- return await mockConnection(multiaddrConnection, 'inbound', muxer)
28
+ return mockConnection(multiaddrConnection, {
29
+ direction: 'inbound',
30
+ muxer: options.muxer
31
+ })
29
32
  }
30
33
  }
31
34