@libp2p/interface-compliance-tests 0.0.0 → 0.2.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 (177) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +25 -0
  3. package/dist/src/connection/connection.d.ts +5 -0
  4. package/dist/src/connection/connection.d.ts.map +1 -0
  5. package/dist/src/connection/connection.js +148 -0
  6. package/dist/src/connection/connection.js.map +1 -0
  7. package/dist/src/connection/index.d.ts +5 -0
  8. package/dist/src/connection/index.d.ts.map +1 -0
  9. package/dist/src/connection/index.js +5 -0
  10. package/dist/src/connection/index.js.map +1 -0
  11. package/dist/src/crypto/index.d.ts +5 -0
  12. package/dist/src/crypto/index.d.ts.map +1 -0
  13. package/dist/src/crypto/index.js +78 -0
  14. package/dist/src/crypto/index.js.map +1 -0
  15. package/dist/src/index.d.ts +5 -0
  16. package/dist/src/index.d.ts.map +1 -0
  17. package/dist/src/index.js +2 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/peer-discovery/index.d.ts +6 -0
  20. package/dist/src/peer-discovery/index.d.ts.map +1 -0
  21. package/dist/src/peer-discovery/index.js +63 -0
  22. package/dist/src/peer-discovery/index.js.map +1 -0
  23. package/dist/src/pubsub/api.d.ts +6 -0
  24. package/dist/src/pubsub/api.d.ts.map +1 -0
  25. package/dist/src/pubsub/api.js +65 -0
  26. package/dist/src/pubsub/api.js.map +1 -0
  27. package/dist/src/pubsub/connection-handlers.d.ts +6 -0
  28. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
  29. package/dist/src/pubsub/connection-handlers.js +279 -0
  30. package/dist/src/pubsub/connection-handlers.js.map +1 -0
  31. package/dist/src/pubsub/emit-self.d.ts +6 -0
  32. package/dist/src/pubsub/emit-self.d.ts.map +1 -0
  33. package/dist/src/pubsub/emit-self.js +51 -0
  34. package/dist/src/pubsub/emit-self.js.map +1 -0
  35. package/dist/src/pubsub/index.d.ts +6 -0
  36. package/dist/src/pubsub/index.d.ts.map +1 -0
  37. package/dist/src/pubsub/index.js +17 -0
  38. package/dist/src/pubsub/index.js.map +1 -0
  39. package/dist/src/pubsub/messages.d.ts +6 -0
  40. package/dist/src/pubsub/messages.d.ts.map +1 -0
  41. package/dist/src/pubsub/messages.js +93 -0
  42. package/dist/src/pubsub/messages.js.map +1 -0
  43. package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
  44. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
  45. package/dist/src/pubsub/multiple-nodes.js +283 -0
  46. package/dist/src/pubsub/multiple-nodes.js.map +1 -0
  47. package/dist/src/pubsub/two-nodes.d.ts +6 -0
  48. package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
  49. package/dist/src/pubsub/two-nodes.js +127 -0
  50. package/dist/src/pubsub/two-nodes.js.map +1 -0
  51. package/dist/src/pubsub/utils.d.ts +3 -0
  52. package/dist/src/pubsub/utils.d.ts.map +1 -0
  53. package/dist/src/pubsub/utils.js +11 -0
  54. package/dist/src/pubsub/utils.js.map +1 -0
  55. package/dist/src/record/index.d.ts +5 -0
  56. package/dist/src/record/index.d.ts.map +1 -0
  57. package/dist/src/record/index.js +25 -0
  58. package/dist/src/record/index.js.map +1 -0
  59. package/dist/src/stream-muxer/base-test.d.ts +5 -0
  60. package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
  61. package/dist/src/stream-muxer/base-test.js +123 -0
  62. package/dist/src/stream-muxer/base-test.js.map +1 -0
  63. package/dist/src/stream-muxer/close-test.d.ts +5 -0
  64. package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
  65. package/dist/src/stream-muxer/close-test.js +103 -0
  66. package/dist/src/stream-muxer/close-test.js.map +1 -0
  67. package/dist/src/stream-muxer/index.d.ts +5 -0
  68. package/dist/src/stream-muxer/index.d.ts.map +1 -0
  69. package/dist/src/stream-muxer/index.js +13 -0
  70. package/dist/src/stream-muxer/index.js.map +1 -0
  71. package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
  72. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  73. package/dist/src/stream-muxer/mega-stress-test.js +8 -0
  74. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  75. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  76. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  77. package/dist/src/stream-muxer/spawner.js +32 -0
  78. package/dist/src/stream-muxer/spawner.js.map +1 -0
  79. package/dist/src/stream-muxer/stress-test.d.ts +5 -0
  80. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
  81. package/dist/src/stream-muxer/stress-test.js +21 -0
  82. package/dist/src/stream-muxer/stress-test.js.map +1 -0
  83. package/dist/src/topology/multicodec-topology.d.ts +5 -0
  84. package/dist/src/topology/multicodec-topology.d.ts.map +1 -0
  85. package/dist/src/topology/multicodec-topology.js +109 -0
  86. package/dist/src/topology/multicodec-topology.js.map +1 -0
  87. package/dist/src/topology/topology.d.ts +5 -0
  88. package/dist/src/topology/topology.d.ts.map +1 -0
  89. package/dist/src/topology/topology.js +29 -0
  90. package/dist/src/topology/topology.js.map +1 -0
  91. package/dist/src/transport/dial-test.d.ts +5 -0
  92. package/dist/src/transport/dial-test.d.ts.map +1 -0
  93. package/dist/src/transport/dial-test.js +78 -0
  94. package/dist/src/transport/dial-test.js.map +1 -0
  95. package/dist/src/transport/filter-test.d.ts +5 -0
  96. package/dist/src/transport/filter-test.d.ts.map +1 -0
  97. package/dist/src/transport/filter-test.js +19 -0
  98. package/dist/src/transport/filter-test.js.map +1 -0
  99. package/dist/src/transport/index.d.ts +18 -0
  100. package/dist/src/transport/index.d.ts.map +1 -0
  101. package/dist/src/transport/index.js +11 -0
  102. package/dist/src/transport/index.js.map +1 -0
  103. package/dist/src/transport/listen-test.d.ts +5 -0
  104. package/dist/src/transport/listen-test.d.ts.map +1 -0
  105. package/dist/src/transport/listen-test.js +116 -0
  106. package/dist/src/transport/listen-test.js.map +1 -0
  107. package/dist/src/transport/utils/index.d.ts +8 -0
  108. package/dist/src/transport/utils/index.d.ts.map +1 -0
  109. package/dist/src/transport/utils/index.js +104 -0
  110. package/dist/src/transport/utils/index.js.map +1 -0
  111. package/dist/src/utils/peers.d.ts +7 -0
  112. package/dist/src/utils/peers.d.ts.map +1 -0
  113. package/dist/src/utils/peers.js +26 -0
  114. package/dist/src/utils/peers.js.map +1 -0
  115. package/dist/test/connection/index.spec.d.ts +2 -0
  116. package/dist/test/connection/index.spec.d.ts.map +1 -0
  117. package/dist/test/connection/index.spec.js +71 -0
  118. package/dist/test/connection/index.spec.js.map +1 -0
  119. package/dist/test/crypto/index.spec.d.ts +2 -0
  120. package/dist/test/crypto/index.spec.d.ts.map +1 -0
  121. package/dist/test/crypto/index.spec.js +11 -0
  122. package/dist/test/crypto/index.spec.js.map +1 -0
  123. package/dist/test/crypto/mock-crypto.d.ts +4 -0
  124. package/dist/test/crypto/mock-crypto.d.ts.map +1 -0
  125. package/dist/test/crypto/mock-crypto.js +93 -0
  126. package/dist/test/crypto/mock-crypto.js.map +1 -0
  127. package/dist/test/peer-discovery/index.spec.d.ts +2 -0
  128. package/dist/test/peer-discovery/index.spec.d.ts.map +1 -0
  129. package/dist/test/peer-discovery/index.spec.js +18 -0
  130. package/dist/test/peer-discovery/index.spec.js.map +1 -0
  131. package/dist/test/peer-discovery/mock-discovery.d.ts +20 -0
  132. package/dist/test/peer-discovery/mock-discovery.d.ts.map +1 -0
  133. package/dist/test/peer-discovery/mock-discovery.js +39 -0
  134. package/dist/test/peer-discovery/mock-discovery.js.map +1 -0
  135. package/dist/test/topology/mock-peer-store.d.ts +12 -0
  136. package/dist/test/topology/mock-peer-store.d.ts.map +1 -0
  137. package/dist/test/topology/mock-peer-store.js +18 -0
  138. package/dist/test/topology/mock-peer-store.js.map +1 -0
  139. package/dist/test/topology/multicodec-topology.spec.d.ts +2 -0
  140. package/dist/test/topology/multicodec-topology.spec.d.ts.map +1 -0
  141. package/dist/test/topology/multicodec-topology.spec.js +45 -0
  142. package/dist/test/topology/multicodec-topology.spec.js.map +1 -0
  143. package/dist/test/topology/topology.spec.d.ts +2 -0
  144. package/dist/test/topology/topology.spec.d.ts.map +1 -0
  145. package/dist/test/topology/topology.spec.js +21 -0
  146. package/dist/test/topology/topology.spec.js.map +1 -0
  147. package/dist/tsconfig.tsbuildinfo +1 -0
  148. package/package.json +120 -4
  149. package/src/connection/README.md +256 -0
  150. package/src/connection/connection.ts +178 -0
  151. package/src/connection/index.ts +7 -0
  152. package/src/crypto/index.ts +104 -0
  153. package/src/index.ts +5 -0
  154. package/src/peer-discovery/index.ts +87 -0
  155. package/src/pubsub/api.ts +89 -0
  156. package/src/pubsub/connection-handlers.ts +356 -0
  157. package/src/pubsub/emit-self.ts +67 -0
  158. package/src/pubsub/index.ts +20 -0
  159. package/src/pubsub/messages.ts +111 -0
  160. package/src/pubsub/multiple-nodes.ts +353 -0
  161. package/src/pubsub/two-nodes.ts +175 -0
  162. package/src/pubsub/utils.ts +13 -0
  163. package/src/record/index.ts +32 -0
  164. package/src/stream-muxer/base-test.ts +154 -0
  165. package/src/stream-muxer/close-test.ts +124 -0
  166. package/src/stream-muxer/index.ts +15 -0
  167. package/src/stream-muxer/mega-stress-test.ts +11 -0
  168. package/src/stream-muxer/spawner.ts +52 -0
  169. package/src/stream-muxer/stress-test.ts +24 -0
  170. package/src/topology/multicodec-topology.ts +136 -0
  171. package/src/topology/topology.ts +38 -0
  172. package/src/transport/dial-test.ts +98 -0
  173. package/src/transport/filter-test.ts +26 -0
  174. package/src/transport/index.ts +29 -0
  175. package/src/transport/listen-test.ts +152 -0
  176. package/src/transport/utils/index.ts +123 -0
  177. package/src/utils/peers.ts +25 -0
@@ -0,0 +1,89 @@
1
+ import { expect } from 'aegir/utils/chai.js'
2
+ import sinon from 'sinon'
3
+ import pDefer from 'p-defer'
4
+ import pWaitFor from 'p-wait-for'
5
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
6
+ import type { TestSetup } from '../index.js'
7
+ import type { PubSub, Message } from '@libp2p/interfaces/pubsub'
8
+ import type { Startable } from '@libp2p/interfaces'
9
+
10
+ const topic = 'foo'
11
+ const data = uint8ArrayFromString('bar')
12
+
13
+ export default (common: TestSetup<PubSub & Startable>) => {
14
+ describe('pubsub api', () => {
15
+ let pubsub: PubSub & Startable
16
+
17
+ // Create pubsub router
18
+ beforeEach(async () => {
19
+ pubsub = await common.setup()
20
+ })
21
+
22
+ afterEach(async () => {
23
+ sinon.restore()
24
+ await pubsub.stop()
25
+ await common.teardown()
26
+ })
27
+
28
+ it('can start correctly', async () => {
29
+ sinon.spy(pubsub.registrar, 'register')
30
+
31
+ await pubsub.start()
32
+
33
+ expect(pubsub.started).to.eql(true)
34
+ expect(pubsub.registrar.register).to.have.property('callCount', 1)
35
+ })
36
+
37
+ it('can stop correctly', async () => {
38
+ sinon.spy(pubsub.registrar, 'unregister')
39
+
40
+ await pubsub.start()
41
+ await pubsub.stop()
42
+
43
+ expect(pubsub.started).to.eql(false)
44
+ expect(pubsub.registrar.unregister).to.have.property('callCount', 1)
45
+ })
46
+
47
+ it('can subscribe and unsubscribe correctly', async () => {
48
+ const handler = () => {
49
+ throw new Error('a message should not be received')
50
+ }
51
+
52
+ await pubsub.start()
53
+ pubsub.subscribe(topic)
54
+ pubsub.on('topic', handler)
55
+
56
+ await pWaitFor(() => {
57
+ const topics = pubsub.getTopics()
58
+ return topics.length === 1 && topics[0] === topic
59
+ })
60
+
61
+ pubsub.unsubscribe(topic)
62
+
63
+ await pWaitFor(() => pubsub.getTopics().length === 0)
64
+
65
+ // Publish to guarantee the handler is not called
66
+ await pubsub.publish(topic, data)
67
+
68
+ await pubsub.stop()
69
+ })
70
+
71
+ it('can subscribe and publish correctly', async () => {
72
+ const defer = pDefer()
73
+
74
+ const handler = (msg: Message) => {
75
+ expect(msg).to.not.eql(undefined)
76
+ defer.resolve()
77
+ }
78
+
79
+ await pubsub.start()
80
+
81
+ pubsub.subscribe(topic)
82
+ pubsub.on(topic, handler)
83
+ await pubsub.publish(topic, data)
84
+ await defer.promise
85
+
86
+ await pubsub.stop()
87
+ })
88
+ })
89
+ }
@@ -0,0 +1,356 @@
1
+ import { expect } from 'aegir/utils/chai.js'
2
+ import sinon from 'sinon'
3
+ import pDefer from 'p-defer'
4
+ import pWaitFor from 'p-wait-for'
5
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
6
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
7
+ import { expectSet } from './utils.js'
8
+ import type { TestSetup } from '../index.js'
9
+ import type { PubSub, Message } from '@libp2p/interfaces/pubsub'
10
+ import type { Startable } from '@libp2p/interfaces'
11
+
12
+ export default (common: TestSetup<PubSub & Startable>) => {
13
+ describe('pubsub connection handlers', () => {
14
+ let psA: PubSub & Startable
15
+ let psB: PubSub & Startable
16
+
17
+ describe('nodes send state on connection', () => {
18
+ // Create pubsub nodes and connect them
19
+ before(async () => {
20
+ psA = await common.setup()
21
+ psB = await common.setup()
22
+
23
+ expect(psA.peers.size).to.be.eql(0)
24
+ expect(psB.peers.size).to.be.eql(0)
25
+
26
+ // Start pubsub
27
+ await psA.start()
28
+ await psB.start()
29
+ })
30
+
31
+ // Make subscriptions prior to nodes connected
32
+ before(() => {
33
+ psA.subscribe('Za')
34
+ psB.subscribe('Zb')
35
+
36
+ expect(psA.peers.size).to.equal(0)
37
+ expectSet(psA.subscriptions, ['Za'])
38
+ expect(psB.peers.size).to.equal(0)
39
+ expectSet(psB.subscriptions, ['Zb'])
40
+ })
41
+
42
+ after(async () => {
43
+ sinon.restore()
44
+ await common.teardown()
45
+ })
46
+
47
+ it('existing subscriptions are sent upon peer connection', async function () {
48
+ await Promise.all([
49
+ // @ts-expect-error protected fields
50
+ psA._libp2p.dial(psB.peerId),
51
+ new Promise((resolve) => psA.once('pubsub:subscription-change', resolve)),
52
+ new Promise((resolve) => psB.once('pubsub:subscription-change', resolve))
53
+ ])
54
+
55
+ expect(psA.peers.size).to.equal(1)
56
+ expect(psB.peers.size).to.equal(1)
57
+
58
+ expectSet(psA.subscriptions, ['Za'])
59
+
60
+ expectSet(psB.topics.get('Za'), [psA.peerId.toString()])
61
+
62
+ expectSet(psB.subscriptions, ['Zb'])
63
+
64
+ expectSet(psA.topics.get('Zb'), [psB.peerId.toString()])
65
+ })
66
+ })
67
+
68
+ describe('pubsub started before connect', () => {
69
+ // Create pubsub nodes and start them
70
+ beforeEach(async () => {
71
+ psA = await common.setup()
72
+ psB = await common.setup()
73
+
74
+ await psA.start()
75
+ await psB.start()
76
+ })
77
+
78
+ afterEach(async () => {
79
+ sinon.restore()
80
+
81
+ await common.teardown()
82
+ })
83
+
84
+ it('should get notified of connected peers on dial', async () => {
85
+ // @ts-expect-error protected fields
86
+ const connection = await psA._libp2p.dial(psB.peerId)
87
+ expect(connection).to.exist()
88
+
89
+ return await Promise.all([
90
+ pWaitFor(() => psA.peers.size === 1),
91
+ pWaitFor(() => psB.peers.size === 1)
92
+ ])
93
+ })
94
+
95
+ it('should receive pubsub messages', async () => {
96
+ const defer = pDefer()
97
+ const topic = 'test-topic'
98
+ const data = uint8ArrayFromString('hey!')
99
+
100
+ // @ts-expect-error protected fields
101
+ await psA._libp2p.dial(psB.peerId)
102
+
103
+ let subscribedTopics = psA.getTopics()
104
+ expect(subscribedTopics).to.not.include(topic)
105
+
106
+ psA.on(topic, (msg) => {
107
+ expect(msg.data).to.equalBytes(data)
108
+ defer.resolve()
109
+ })
110
+ psA.subscribe(topic)
111
+
112
+ subscribedTopics = psA.getTopics()
113
+ expect(subscribedTopics).to.include(topic)
114
+
115
+ // wait for psB to know about psA subscription
116
+ await pWaitFor(() => {
117
+ const subscribedPeers = psB.getSubscribers(topic)
118
+ return subscribedPeers.includes(psA.peerId.toString())
119
+ })
120
+ void psB.publish(topic, data)
121
+
122
+ await defer.promise
123
+ })
124
+ })
125
+
126
+ describe('pubsub started after connect', () => {
127
+ // Create pubsub nodes
128
+ beforeEach(async () => {
129
+ psA = await common.setup()
130
+ psB = await common.setup()
131
+ })
132
+
133
+ afterEach(async () => {
134
+ sinon.restore()
135
+
136
+ await psA.stop()
137
+ await psB.stop()
138
+
139
+ await common.teardown()
140
+ })
141
+
142
+ it('should get notified of connected peers after starting', async () => {
143
+ // @ts-expect-error protected fields
144
+ const connection = await psA._libp2p.dial(psB.peerId)
145
+ expect(connection).to.exist()
146
+ expect(psA.peers.size).to.be.eql(0)
147
+ expect(psB.peers.size).to.be.eql(0)
148
+
149
+ await psA.start()
150
+ await psB.start()
151
+
152
+ return await Promise.all([
153
+ pWaitFor(() => psA.peers.size === 1),
154
+ pWaitFor(() => psB.peers.size === 1)
155
+ ])
156
+ })
157
+
158
+ it('should receive pubsub messages', async () => {
159
+ const defer = pDefer()
160
+ const topic = 'test-topic'
161
+ const data = uint8ArrayFromString('hey!')
162
+
163
+ // @ts-expect-error protected fields
164
+ await psA._libp2p.dial(psB.peerId)
165
+
166
+ await psA.start()
167
+ await psB.start()
168
+
169
+ await Promise.all([
170
+ pWaitFor(() => psA.peers.size === 1),
171
+ pWaitFor(() => psB.peers.size === 1)
172
+ ])
173
+
174
+ let subscribedTopics = psA.getTopics()
175
+ expect(subscribedTopics).to.not.include(topic)
176
+
177
+ psA.on(topic, (msg) => {
178
+ expect(msg.data).to.equalBytes(data)
179
+ defer.resolve()
180
+ })
181
+ psA.subscribe(topic)
182
+
183
+ subscribedTopics = psA.getTopics()
184
+ expect(subscribedTopics).to.include(topic)
185
+
186
+ // wait for psB to know about psA subscription
187
+ await pWaitFor(() => {
188
+ const subscribedPeers = psB.getSubscribers(topic)
189
+ return subscribedPeers.includes(psA.peerId.toString())
190
+ })
191
+ void psB.publish(topic, data)
192
+
193
+ await defer.promise
194
+ })
195
+ })
196
+
197
+ describe('pubsub with intermittent connections', () => {
198
+ // Create pubsub nodes and start them
199
+ beforeEach(async () => {
200
+ psA = await common.setup()
201
+ psB = await common.setup()
202
+
203
+ await psA.start()
204
+ await psB.start()
205
+ })
206
+
207
+ afterEach(async () => {
208
+ sinon.restore()
209
+
210
+ await psA.stop()
211
+ await psB.stop()
212
+
213
+ await common.teardown()
214
+ })
215
+
216
+ it('should receive pubsub messages after a node restart', async function () {
217
+ const topic = 'test-topic'
218
+ const data = uint8ArrayFromString('hey!')
219
+ const psAid = psA.peerId.toString()
220
+
221
+ let counter = 0
222
+ const defer1 = pDefer()
223
+ const defer2 = pDefer()
224
+
225
+ // @ts-expect-error protected fields
226
+ await psA._libp2p.dial(psB.peerId)
227
+
228
+ let subscribedTopics = psA.getTopics()
229
+ expect(subscribedTopics).to.not.include(topic)
230
+
231
+ psA.on(topic, (msg) => {
232
+ expect(msg.data).to.equalBytes(data)
233
+ counter++
234
+ counter === 1 ? defer1.resolve() : defer2.resolve()
235
+ })
236
+ psA.subscribe(topic)
237
+
238
+ subscribedTopics = psA.getTopics()
239
+ expect(subscribedTopics).to.include(topic)
240
+
241
+ // wait for psB to know about psA subscription
242
+ await pWaitFor(() => {
243
+ const subscribedPeers = psB.getSubscribers(topic)
244
+ return subscribedPeers.includes(psAid)
245
+ })
246
+ void psB.publish(topic, data)
247
+
248
+ await defer1.promise
249
+
250
+ await psB.stop()
251
+ // @ts-expect-error protected fields
252
+ await psB._libp2p.stop()
253
+ await pWaitFor(() => {
254
+ // @ts-expect-error protected fields
255
+ const aHasConnectionToB = psA._libp2p.connectionManager.get(psB.peerId)
256
+ // @ts-expect-error protected fields
257
+ const bHasConnectionToA = psB._libp2p.connectionManager.get(psA.peerId)
258
+
259
+ return aHasConnectionToB != null && bHasConnectionToA != null
260
+ })
261
+ // @ts-expect-error protected fields
262
+ await psB._libp2p.start()
263
+ await psB.start()
264
+
265
+ // @ts-expect-error protected fields
266
+ psA._libp2p.peerStore.addressBook.set(psB.peerId, psB._libp2p.multiaddrs)
267
+ // @ts-expect-error protected fields
268
+ await psA._libp2p.dial(psB.peerId)
269
+
270
+ // wait for remoteLibp2p to know about libp2p subscription
271
+ await pWaitFor(() => {
272
+ const subscribedPeers = psB.getSubscribers(topic)
273
+ return subscribedPeers.includes(psAid)
274
+ })
275
+
276
+ void psB.publish(topic, data)
277
+
278
+ await defer2.promise
279
+ })
280
+
281
+ it('should handle quick reconnects with a delayed disconnect', async () => {
282
+ // Subscribe on both
283
+ let aReceivedFirstMessageFromB = false
284
+ let aReceivedSecondMessageFromB = false
285
+ let bReceivedFirstMessageFromA = false
286
+ let bReceivedSecondMessageFromA = false
287
+
288
+ const handlerSpyA = (message: Message) => {
289
+ const data = uint8ArrayToString(message.data)
290
+
291
+ if (data === 'message-from-b-1') {
292
+ aReceivedFirstMessageFromB = true
293
+ }
294
+
295
+ if (data === 'message-from-b-2') {
296
+ aReceivedSecondMessageFromB = true
297
+ }
298
+ }
299
+ const handlerSpyB = (message: Message) => {
300
+ const data = uint8ArrayToString(message.data)
301
+
302
+ if (data === 'message-from-a-1') {
303
+ bReceivedFirstMessageFromA = true
304
+ }
305
+
306
+ if (data === 'message-from-a-2') {
307
+ bReceivedSecondMessageFromA = true
308
+ }
309
+ }
310
+
311
+ const topic = 'reconnect-channel'
312
+
313
+ psA.on(topic, handlerSpyA)
314
+ psB.on(topic, handlerSpyB)
315
+ psA.subscribe(topic)
316
+ psB.subscribe(topic)
317
+
318
+ // Create two connections to the remote peer
319
+ // @ts-expect-error protected fields
320
+ const originalConnection = await psA._libp2p.dialer.connectToPeer(psB.peerId)
321
+ // second connection
322
+ // @ts-expect-error protected fields
323
+ await psA._libp2p.dialer.connectToPeer(psB.peerId)
324
+ // @ts-expect-error protected fields
325
+ expect(psA._libp2p.connections.get(psB.peerId.toString())).to.have.length(2)
326
+
327
+ // Wait for subscriptions to occur
328
+ await pWaitFor(() => {
329
+ return psA.getSubscribers(topic).includes(psB.peerId.toString()) &&
330
+ psB.getSubscribers(topic).includes(psA.peerId.toString())
331
+ })
332
+
333
+ // Verify messages go both ways
334
+ void psA.publish(topic, uint8ArrayFromString('message-from-a-1'))
335
+ void psB.publish(topic, uint8ArrayFromString('message-from-b-1'))
336
+ await pWaitFor(() => {
337
+ return aReceivedFirstMessageFromB && bReceivedFirstMessageFromA
338
+ })
339
+
340
+ // Disconnect the first connection (this acts as a delayed reconnect)
341
+ // @ts-expect-error protected fields
342
+ const psAConnUpdateSpy = sinon.spy(psA._libp2p.connectionManager.connections, 'set')
343
+
344
+ await originalConnection.close()
345
+ await pWaitFor(() => psAConnUpdateSpy.callCount === 1)
346
+
347
+ // Verify messages go both ways after the disconnect
348
+ void psA.publish(topic, uint8ArrayFromString('message-from-a-2'))
349
+ void psB.publish(topic, uint8ArrayFromString('message-from-b-2'))
350
+ await pWaitFor(() => {
351
+ return aReceivedSecondMessageFromB && bReceivedSecondMessageFromA
352
+ })
353
+ })
354
+ })
355
+ })
356
+ }
@@ -0,0 +1,67 @@
1
+ import { expect } from 'aegir/utils/chai.js'
2
+ import sinon from 'sinon'
3
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
+ import type { TestSetup } from '../index.js'
5
+ import type { PubSub, PubsubOptions } from '@libp2p/interfaces/pubsub'
6
+ import type { Startable } from '@libp2p/interfaces'
7
+
8
+ const topic = 'foo'
9
+ const data = uint8ArrayFromString('bar')
10
+ const shouldNotHappen = () => expect.fail()
11
+
12
+ export default (common: TestSetup<PubSub & Startable, Partial<PubsubOptions>>) => {
13
+ describe('emit self', () => {
14
+ let pubsub: PubSub & Startable
15
+
16
+ describe('enabled', () => {
17
+ before(async () => {
18
+ pubsub = await common.setup({ emitSelf: true })
19
+ })
20
+
21
+ before(async () => {
22
+ await pubsub.start()
23
+ pubsub.subscribe(topic)
24
+ })
25
+
26
+ after(async () => {
27
+ sinon.restore()
28
+ await pubsub.stop()
29
+ await common.teardown()
30
+ })
31
+
32
+ it('should emit to self on publish', async () => {
33
+ const promise = new Promise((resolve) => pubsub.once(topic, resolve))
34
+
35
+ void pubsub.publish(topic, data)
36
+
37
+ return await promise
38
+ })
39
+ })
40
+
41
+ describe('disabled', () => {
42
+ before(async () => {
43
+ pubsub = await common.setup({ emitSelf: false })
44
+ })
45
+
46
+ before(async () => {
47
+ await pubsub.start()
48
+ pubsub.subscribe(topic)
49
+ })
50
+
51
+ after(async () => {
52
+ sinon.restore()
53
+ await pubsub.stop()
54
+ await common.teardown()
55
+ })
56
+
57
+ it('should not emit to self on publish', async () => {
58
+ pubsub.once(topic, () => shouldNotHappen)
59
+
60
+ void pubsub.publish(topic, data)
61
+
62
+ // Wait 1 second to guarantee that self is not noticed
63
+ return await new Promise((resolve) => setTimeout(resolve, 1000))
64
+ })
65
+ })
66
+ })
67
+ }
@@ -0,0 +1,20 @@
1
+ import apiTest from './api.js'
2
+ import emitSelfTest from './emit-self.js'
3
+ import messagesTest from './messages.js'
4
+ import connectionHandlersTest from './connection-handlers.js'
5
+ import twoNodesTest from './two-nodes.js'
6
+ import multipleNodesTest from './multiple-nodes.js'
7
+ import type { TestSetup } from '../index.js'
8
+ import type { PubSub } from '@libp2p/interfaces/pubsub'
9
+ import type { Startable } from '@libp2p/interfaces'
10
+
11
+ export default (common: TestSetup<PubSub & Startable>) => {
12
+ describe('interface-pubsub compliance tests', () => {
13
+ apiTest(common)
14
+ emitSelfTest(common)
15
+ messagesTest(common)
16
+ connectionHandlersTest(common)
17
+ twoNodesTest(common)
18
+ multipleNodesTest(common)
19
+ })
20
+ }
@@ -0,0 +1,111 @@
1
+ import { expect } from 'aegir/utils/chai.js'
2
+ import sinon from 'sinon'
3
+ import * as PeerIdFactory from '@libp2p/peer-id-factory'
4
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
5
+ import * as utils from '@libp2p/pubsub/utils'
6
+ import { PeerStreams } from '@libp2p/pubsub/peer-streams'
7
+ import type { TestSetup } from '../index.js'
8
+ import type { PubSub } from '@libp2p/interfaces/pubsub'
9
+ import type { Startable } from '@libp2p/interfaces'
10
+
11
+ const topic = 'foo'
12
+ const data = uint8ArrayFromString('bar')
13
+
14
+ export default (common: TestSetup<PubSub & Startable>) => {
15
+ describe('messages', () => {
16
+ let pubsub: PubSub & Startable
17
+
18
+ // Create pubsub router
19
+ beforeEach(async () => {
20
+ pubsub = await common.setup()
21
+ await pubsub.start()
22
+ })
23
+
24
+ afterEach(async () => {
25
+ sinon.restore()
26
+ await pubsub.stop()
27
+ await common.teardown()
28
+ })
29
+
30
+ it('should emit normalized signed messages on publish', async () => {
31
+ pubsub.globalSignaturePolicy = 'StrictSign'
32
+ // @ts-expect-error protected field
33
+ sinon.spy(pubsub, '_emitMessage')
34
+
35
+ await pubsub.publish(topic, data)
36
+ // @ts-expect-error protected field
37
+ expect(pubsub._emitMessage.callCount).to.eql(1)
38
+ // @ts-expect-error protected field
39
+ const [messageToEmit] = pubsub._emitMessage.getCall(0).args
40
+
41
+ expect(messageToEmit.seqno).to.not.eql(undefined)
42
+ expect(messageToEmit.key).to.not.eql(undefined)
43
+ expect(messageToEmit.signature).to.not.eql(undefined)
44
+ })
45
+
46
+ it('should drop unsigned messages', async () => {
47
+ // @ts-expect-error protected field
48
+ sinon.spy(pubsub, '_emitMessage')
49
+ // @ts-expect-error protected field
50
+ sinon.spy(pubsub, '_publish')
51
+ sinon.spy(pubsub, 'validate')
52
+
53
+ const peerStream = new PeerStreams({
54
+ id: await PeerIdFactory.createEd25519PeerId(),
55
+ protocol: 'test'
56
+ })
57
+ const rpc = {
58
+ subscriptions: [],
59
+ msgs: [{
60
+ receivedFrom: peerStream.id.toString(),
61
+ from: peerStream.id.toBytes(),
62
+ data,
63
+ seqno: utils.randomSeqno(),
64
+ topicIDs: [topic]
65
+ }]
66
+ }
67
+
68
+ pubsub.subscribe(topic)
69
+ // @ts-expect-error protected field
70
+ await pubsub._processRpc(peerStream.id.toString(), peerStream, rpc)
71
+
72
+ expect(pubsub.validate).to.have.property('callCount', 1)
73
+ // @ts-expect-error protected field
74
+ expect(pubsub._emitMessage).to.have.property('called', false)
75
+ // @ts-expect-error protected field
76
+ expect(pubsub._publish).to.have.property('called', false)
77
+ })
78
+
79
+ it('should not drop unsigned messages if strict signing is disabled', async () => {
80
+ pubsub.globalSignaturePolicy = 'StrictNoSign'
81
+ // @ts-expect-error protected field
82
+ sinon.spy(pubsub, '_emitMessage')
83
+ // @ts-expect-error protected field
84
+ sinon.spy(pubsub, '_publish')
85
+ sinon.spy(pubsub, 'validate')
86
+
87
+ const peerStream = new PeerStreams({
88
+ id: await PeerIdFactory.createEd25519PeerId(),
89
+ protocol: 'test'
90
+ })
91
+
92
+ const rpc = {
93
+ subscriptions: [],
94
+ msgs: [{
95
+ data,
96
+ topicIDs: [topic]
97
+ }]
98
+ }
99
+
100
+ pubsub.subscribe(topic)
101
+ // @ts-expect-error protected field
102
+ await pubsub._processRpc(peerStream.id.toString(), peerStream, rpc)
103
+
104
+ expect(pubsub.validate).to.have.property('callCount', 1)
105
+ // @ts-expect-error protected field
106
+ expect(pubsub._emitMessage).to.have.property('called', 1)
107
+ // @ts-expect-error protected field
108
+ expect(pubsub._publish).to.have.property('called', 1)
109
+ })
110
+ })
111
+ }