@libp2p/interface-compliance-tests 3.0.7-ea8a0637 → 3.0.7-eabf6f36
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.
- package/README.md +9 -0
- package/dist/src/connection/index.d.ts +5 -0
- package/dist/src/connection/index.d.ts.map +1 -0
- package/dist/src/connection/index.js +150 -0
- package/dist/src/connection/index.js.map +1 -0
- package/dist/src/connection-encryption/index.d.ts +5 -0
- package/dist/src/connection-encryption/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/index.js +71 -0
- package/dist/src/connection-encryption/index.js.map +1 -0
- package/dist/src/connection-encryption/utils/index.d.ts +3 -0
- package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/utils/index.js +19 -0
- package/dist/src/connection-encryption/utils/index.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/is-valid-tick.d.ts.map +1 -1
- package/dist/src/is-valid-tick.js.map +1 -1
- package/dist/src/mocks/connection-encrypter.d.ts +3 -0
- package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
- package/dist/src/mocks/connection-encrypter.js +98 -0
- package/dist/src/mocks/connection-encrypter.js.map +1 -0
- package/dist/src/mocks/connection-gater.d.ts +3 -0
- package/dist/src/mocks/connection-gater.d.ts.map +1 -0
- package/dist/src/mocks/connection-gater.js +17 -0
- package/dist/src/mocks/connection-gater.js.map +1 -0
- package/dist/src/mocks/connection-manager.d.ts +29 -0
- package/dist/src/mocks/connection-manager.d.ts.map +1 -0
- package/dist/src/mocks/connection-manager.js +145 -0
- package/dist/src/mocks/connection-manager.js.map +1 -0
- package/dist/src/mocks/connection.d.ts +32 -0
- package/dist/src/mocks/connection.d.ts.map +1 -0
- package/dist/src/mocks/connection.js +167 -0
- package/dist/src/mocks/connection.js.map +1 -0
- package/dist/src/mocks/duplex.d.ts +3 -0
- package/dist/src/mocks/duplex.d.ts.map +1 -0
- package/dist/src/mocks/duplex.js +9 -0
- package/dist/src/mocks/duplex.js.map +1 -0
- package/dist/src/mocks/index.d.ts +13 -0
- package/dist/src/mocks/index.d.ts.map +1 -0
- package/dist/src/mocks/index.js +11 -0
- package/dist/src/mocks/index.js.map +1 -0
- package/dist/src/mocks/metrics.d.ts +3 -0
- package/dist/src/mocks/metrics.d.ts.map +1 -0
- package/dist/src/mocks/metrics.js +115 -0
- package/dist/src/mocks/metrics.js.map +1 -0
- package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
- package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
- package/dist/src/mocks/multiaddr-connection.js +60 -0
- package/dist/src/mocks/multiaddr-connection.js.map +1 -0
- package/dist/src/mocks/muxer.d.ts +36 -0
- package/dist/src/mocks/muxer.d.ts.map +1 -0
- package/dist/src/mocks/muxer.js +213 -0
- package/dist/src/mocks/muxer.js.map +1 -0
- package/dist/src/mocks/peer-discovery.d.ts +22 -0
- package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
- package/dist/src/mocks/peer-discovery.js +47 -0
- package/dist/src/mocks/peer-discovery.js.map +1 -0
- package/dist/src/mocks/registrar.d.ts +18 -0
- package/dist/src/mocks/registrar.d.ts.map +1 -0
- package/dist/src/mocks/registrar.js +66 -0
- package/dist/src/mocks/registrar.js.map +1 -0
- package/dist/src/mocks/upgrader.d.ts +10 -0
- package/dist/src/mocks/upgrader.d.ts.map +1 -0
- package/dist/src/mocks/upgrader.js +31 -0
- package/dist/src/mocks/upgrader.js.map +1 -0
- package/dist/src/peer-discovery/index.d.ts +5 -0
- package/dist/src/peer-discovery/index.d.ts.map +1 -0
- package/dist/src/peer-discovery/index.js +66 -0
- package/dist/src/peer-discovery/index.js.map +1 -0
- package/dist/src/pubsub/api.d.ts +6 -0
- package/dist/src/pubsub/api.d.ts.map +1 -0
- package/dist/src/pubsub/api.js +87 -0
- package/dist/src/pubsub/api.js.map +1 -0
- package/dist/src/pubsub/connection-handlers.d.ts +6 -0
- package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
- package/dist/src/pubsub/connection-handlers.js +329 -0
- package/dist/src/pubsub/connection-handlers.js.map +1 -0
- package/dist/src/pubsub/emit-self.d.ts +6 -0
- package/dist/src/pubsub/emit-self.d.ts.map +1 -0
- package/dist/src/pubsub/emit-self.js +80 -0
- package/dist/src/pubsub/emit-self.js.map +1 -0
- package/dist/src/pubsub/index.d.ts +18 -0
- package/dist/src/pubsub/index.d.ts.map +1 -0
- package/dist/src/pubsub/index.js +17 -0
- package/dist/src/pubsub/index.js.map +1 -0
- package/dist/src/pubsub/messages.d.ts +6 -0
- package/dist/src/pubsub/messages.d.ts.map +1 -0
- package/dist/src/pubsub/messages.js +48 -0
- package/dist/src/pubsub/messages.js.map +1 -0
- package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
- package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
- package/dist/src/pubsub/multiple-nodes.js +350 -0
- package/dist/src/pubsub/multiple-nodes.js.map +1 -0
- package/dist/src/pubsub/two-nodes.d.ts +6 -0
- package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
- package/dist/src/pubsub/two-nodes.js +217 -0
- package/dist/src/pubsub/two-nodes.js.map +1 -0
- package/dist/src/pubsub/utils.d.ts +6 -0
- package/dist/src/pubsub/utils.d.ts.map +1 -0
- package/dist/src/pubsub/utils.js +22 -0
- package/dist/src/pubsub/utils.js.map +1 -0
- package/dist/src/stream-muxer/base-test.d.ts +5 -0
- package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/base-test.js +153 -0
- package/dist/src/stream-muxer/base-test.js.map +1 -0
- package/dist/src/stream-muxer/close-test.d.ts +5 -0
- package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/close-test.js +357 -0
- package/dist/src/stream-muxer/close-test.js.map +1 -0
- package/dist/src/stream-muxer/fixtures/pb/message.d.ts +13 -0
- package/dist/src/stream-muxer/fixtures/pb/message.d.ts.map +1 -0
- package/dist/src/stream-muxer/fixtures/pb/message.js +67 -0
- package/dist/src/stream-muxer/fixtures/pb/message.js.map +1 -0
- package/dist/src/stream-muxer/index.d.ts +5 -0
- package/dist/src/stream-muxer/index.d.ts.map +1 -0
- package/dist/src/stream-muxer/index.js +13 -0
- package/dist/src/stream-muxer/index.js.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.js +11 -0
- package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
- package/dist/src/stream-muxer/spawner.d.ts +4 -0
- package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
- package/dist/src/stream-muxer/spawner.js +37 -0
- package/dist/src/stream-muxer/spawner.js.map +1 -0
- package/dist/src/stream-muxer/stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/stress-test.js +23 -0
- package/dist/src/stream-muxer/stress-test.js.map +1 -0
- package/dist/src/transport/dial-test.d.ts +5 -0
- package/dist/src/transport/dial-test.d.ts.map +1 -0
- package/dist/src/transport/dial-test.js +98 -0
- package/dist/src/transport/dial-test.js.map +1 -0
- package/dist/src/transport/filter-test.d.ts +5 -0
- package/dist/src/transport/filter-test.d.ts.map +1 -0
- package/dist/src/transport/filter-test.js +18 -0
- package/dist/src/transport/filter-test.js.map +1 -0
- package/dist/src/transport/index.d.ts +15 -0
- package/dist/src/transport/index.d.ts.map +1 -0
- package/dist/src/transport/index.js +11 -0
- package/dist/src/transport/index.js.map +1 -0
- package/dist/src/transport/listen-test.d.ts +5 -0
- package/dist/src/transport/listen-test.d.ts.map +1 -0
- package/dist/src/transport/listen-test.js +152 -0
- package/dist/src/transport/listen-test.js.map +1 -0
- package/package.json +72 -5
- package/src/connection/index.ts +182 -0
- package/src/connection-encryption/index.ts +97 -0
- package/src/connection-encryption/utils/index.ts +24 -0
- package/src/index.ts +0 -1
- package/src/is-valid-tick.ts +0 -1
- package/src/mocks/connection-encrypter.ts +113 -0
- package/src/mocks/connection-gater.ts +18 -0
- package/src/mocks/connection-manager.ts +211 -0
- package/src/mocks/connection.ts +226 -0
- package/src/mocks/duplex.ts +10 -0
- package/src/mocks/index.ts +12 -0
- package/src/mocks/metrics.ts +162 -0
- package/src/mocks/multiaddr-connection.ts +76 -0
- package/src/mocks/muxer.ts +303 -0
- package/src/mocks/peer-discovery.ts +60 -0
- package/src/mocks/registrar.ts +88 -0
- package/src/mocks/upgrader.ts +49 -0
- package/src/peer-discovery/index.ts +90 -0
- package/src/pubsub/api.ts +114 -0
- package/src/pubsub/connection-handlers.ts +413 -0
- package/src/pubsub/emit-self.ts +99 -0
- package/src/pubsub/index.ts +34 -0
- package/src/pubsub/messages.ts +59 -0
- package/src/pubsub/multiple-nodes.ts +440 -0
- package/src/pubsub/two-nodes.ts +273 -0
- package/src/pubsub/utils.ts +29 -0
- package/src/stream-muxer/base-test.ts +196 -0
- package/src/stream-muxer/close-test.ts +442 -0
- package/src/stream-muxer/fixtures/pb/message.proto +7 -0
- package/src/stream-muxer/fixtures/pb/message.ts +87 -0
- package/src/stream-muxer/index.ts +15 -0
- package/src/stream-muxer/mega-stress-test.ts +14 -0
- package/src/stream-muxer/spawner.ts +55 -0
- package/src/stream-muxer/stress-test.ts +27 -0
- package/src/transport/dial-test.ts +124 -0
- package/src/transport/filter-test.ts +25 -0
- package/src/transport/index.ts +25 -0
- package/src/transport/listen-test.ts +191 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { start, stop } from '@libp2p/interface/startable'
|
|
2
|
+
import { expect } from 'aegir/chai'
|
|
3
|
+
import pDefer from 'p-defer'
|
|
4
|
+
import { pEvent } from 'p-event'
|
|
5
|
+
import pWaitFor from 'p-wait-for'
|
|
6
|
+
import sinon from 'sinon'
|
|
7
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
8
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
9
|
+
import { mockNetwork } from '../mocks/index.js'
|
|
10
|
+
import { createComponents } from './utils.js'
|
|
11
|
+
import type { PubSubArgs } from './index.js'
|
|
12
|
+
import type { TestSetup } from '../index.js'
|
|
13
|
+
import type { MockNetworkComponents } from '../mocks/index.js'
|
|
14
|
+
import type { Message, PubSub } from '@libp2p/interface/pubsub'
|
|
15
|
+
|
|
16
|
+
export default (common: TestSetup<PubSub, PubSubArgs>): void => {
|
|
17
|
+
describe('pubsub connection handlers', () => {
|
|
18
|
+
let psA: PubSub
|
|
19
|
+
let psB: PubSub
|
|
20
|
+
let componentsA: MockNetworkComponents
|
|
21
|
+
let componentsB: MockNetworkComponents
|
|
22
|
+
|
|
23
|
+
describe('nodes send state on connection', () => {
|
|
24
|
+
// Create pubsub nodes and connect them
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
mockNetwork.reset()
|
|
27
|
+
|
|
28
|
+
componentsA = await createComponents()
|
|
29
|
+
componentsB = await createComponents()
|
|
30
|
+
|
|
31
|
+
psA = componentsA.pubsub = await common.setup({
|
|
32
|
+
components: componentsA,
|
|
33
|
+
init: {}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
psB = componentsB.pubsub = await common.setup({
|
|
37
|
+
components: componentsB,
|
|
38
|
+
init: {}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Start pubsub
|
|
42
|
+
await start(...Object.values(componentsA), ...Object.values(componentsB))
|
|
43
|
+
|
|
44
|
+
expect(psA.getPeers()).to.be.empty()
|
|
45
|
+
expect(psB.getPeers()).to.be.empty()
|
|
46
|
+
|
|
47
|
+
// Make subscriptions prior to nodes connected
|
|
48
|
+
psA.subscribe('Za')
|
|
49
|
+
psB.subscribe('Zb')
|
|
50
|
+
|
|
51
|
+
expect(psA.getPeers()).to.be.empty()
|
|
52
|
+
expect(psA.getTopics()).to.deep.equal(['Za'])
|
|
53
|
+
expect(psB.getPeers()).to.be.empty()
|
|
54
|
+
expect(psB.getTopics()).to.deep.equal(['Zb'])
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
afterEach(async () => {
|
|
58
|
+
sinon.restore()
|
|
59
|
+
await stop(...Object.values(componentsA), ...Object.values(componentsB))
|
|
60
|
+
await common.teardown()
|
|
61
|
+
mockNetwork.reset()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('existing subscriptions are sent upon peer connection', async function () {
|
|
65
|
+
const subscriptionsChanged = Promise.all([
|
|
66
|
+
pEvent(psA, 'subscription-change'),
|
|
67
|
+
pEvent(psB, 'subscription-change')
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
71
|
+
|
|
72
|
+
await subscriptionsChanged
|
|
73
|
+
|
|
74
|
+
expect(psA.getPeers()).to.have.lengthOf(1)
|
|
75
|
+
expect(psB.getPeers()).to.have.lengthOf(1)
|
|
76
|
+
|
|
77
|
+
expect(psA.getTopics()).to.deep.equal(['Za'])
|
|
78
|
+
expect(psB.getTopics()).to.deep.equal(['Zb'])
|
|
79
|
+
|
|
80
|
+
expect(psA.getSubscribers('Zb').map(p => p.toString())).to.deep.equal([componentsB.peerId.toString()])
|
|
81
|
+
expect(psB.getSubscribers('Za').map(p => p.toString())).to.deep.equal([componentsA.peerId.toString()])
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('pubsub started before connect', () => {
|
|
86
|
+
let psA: PubSub
|
|
87
|
+
let psB: PubSub
|
|
88
|
+
let componentsA: MockNetworkComponents
|
|
89
|
+
let componentsB: MockNetworkComponents
|
|
90
|
+
|
|
91
|
+
// Create pubsub nodes and start them
|
|
92
|
+
beforeEach(async () => {
|
|
93
|
+
mockNetwork.reset()
|
|
94
|
+
componentsA = await createComponents()
|
|
95
|
+
componentsB = await createComponents()
|
|
96
|
+
|
|
97
|
+
psA = componentsA.pubsub = await common.setup({
|
|
98
|
+
components: componentsA,
|
|
99
|
+
init: {}
|
|
100
|
+
})
|
|
101
|
+
psB = componentsB.pubsub = await common.setup({
|
|
102
|
+
components: componentsB,
|
|
103
|
+
init: {}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
await start(...Object.values(componentsA), ...Object.values(componentsB))
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
afterEach(async () => {
|
|
110
|
+
sinon.restore()
|
|
111
|
+
await stop(...Object.values(componentsA), ...Object.values(componentsB))
|
|
112
|
+
await common.teardown()
|
|
113
|
+
mockNetwork.reset()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should get notified of connected peers on dial', async () => {
|
|
117
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
118
|
+
|
|
119
|
+
return Promise.all([
|
|
120
|
+
pWaitFor(() => psA.getPeers().length === 1),
|
|
121
|
+
pWaitFor(() => psB.getPeers().length === 1)
|
|
122
|
+
])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should receive pubsub messages', async () => {
|
|
126
|
+
const defer = pDefer()
|
|
127
|
+
const topic = 'test-topic'
|
|
128
|
+
const data = uint8ArrayFromString('hey!')
|
|
129
|
+
|
|
130
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
131
|
+
|
|
132
|
+
let subscribedTopics = psA.getTopics()
|
|
133
|
+
expect(subscribedTopics).to.not.include(topic)
|
|
134
|
+
|
|
135
|
+
psA.subscribe(topic)
|
|
136
|
+
psA.addEventListener('message', (evt) => {
|
|
137
|
+
if (evt.detail.topic === topic) {
|
|
138
|
+
const msg = evt.detail
|
|
139
|
+
expect(msg.data).to.equalBytes(data)
|
|
140
|
+
defer.resolve()
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
psA.subscribe(topic)
|
|
144
|
+
|
|
145
|
+
subscribedTopics = psA.getTopics()
|
|
146
|
+
expect(subscribedTopics).to.include(topic)
|
|
147
|
+
|
|
148
|
+
// wait for psB to know about psA subscription
|
|
149
|
+
await pWaitFor(() => {
|
|
150
|
+
const subscribedPeers = psB.getSubscribers(topic)
|
|
151
|
+
return subscribedPeers.map(p => p.toString()).includes(componentsA.peerId.toString()) // eslint-disable-line max-nested-callbacks
|
|
152
|
+
})
|
|
153
|
+
await psB.publish(topic, data)
|
|
154
|
+
|
|
155
|
+
await defer.promise
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
describe('pubsub started after connect', () => {
|
|
160
|
+
let psA: PubSub
|
|
161
|
+
let psB: PubSub
|
|
162
|
+
let componentsA: MockNetworkComponents
|
|
163
|
+
let componentsB: MockNetworkComponents
|
|
164
|
+
|
|
165
|
+
// Create pubsub nodes
|
|
166
|
+
beforeEach(async () => {
|
|
167
|
+
mockNetwork.reset()
|
|
168
|
+
componentsA = await createComponents()
|
|
169
|
+
componentsB = await createComponents()
|
|
170
|
+
|
|
171
|
+
psA = componentsA.pubsub = await common.setup({
|
|
172
|
+
components: componentsA,
|
|
173
|
+
init: {}
|
|
174
|
+
})
|
|
175
|
+
psB = componentsB.pubsub = await common.setup({
|
|
176
|
+
components: componentsB,
|
|
177
|
+
init: {}
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
afterEach(async () => {
|
|
182
|
+
sinon.restore()
|
|
183
|
+
await stop(...Object.values(componentsA), ...Object.values(componentsB))
|
|
184
|
+
await common.teardown()
|
|
185
|
+
mockNetwork.reset()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should get notified of connected peers after starting', async () => {
|
|
189
|
+
await start(...Object.values(componentsA), ...Object.values(componentsB))
|
|
190
|
+
|
|
191
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
192
|
+
|
|
193
|
+
return Promise.all([
|
|
194
|
+
pWaitFor(() => psA.getPeers().length === 1),
|
|
195
|
+
pWaitFor(() => psB.getPeers().length === 1)
|
|
196
|
+
])
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('should receive pubsub messages', async () => {
|
|
200
|
+
const defer = pDefer()
|
|
201
|
+
const topic = 'test-topic'
|
|
202
|
+
const data = uint8ArrayFromString('hey!')
|
|
203
|
+
|
|
204
|
+
await start(...Object.values(componentsA), ...Object.values(componentsB))
|
|
205
|
+
|
|
206
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
207
|
+
|
|
208
|
+
await Promise.all([
|
|
209
|
+
pWaitFor(() => psA.getPeers().length === 1),
|
|
210
|
+
pWaitFor(() => psB.getPeers().length === 1)
|
|
211
|
+
])
|
|
212
|
+
|
|
213
|
+
let subscribedTopics = psA.getTopics()
|
|
214
|
+
expect(subscribedTopics).to.not.include(topic)
|
|
215
|
+
|
|
216
|
+
psA.subscribe(topic)
|
|
217
|
+
psA.addEventListener('message', (evt) => {
|
|
218
|
+
if (evt.detail.topic === topic) {
|
|
219
|
+
const msg = evt.detail
|
|
220
|
+
expect(msg.data).to.equalBytes(data)
|
|
221
|
+
defer.resolve()
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
psA.subscribe(topic)
|
|
225
|
+
|
|
226
|
+
subscribedTopics = psA.getTopics()
|
|
227
|
+
expect(subscribedTopics).to.include(topic)
|
|
228
|
+
|
|
229
|
+
// wait for psB to know about psA subscription
|
|
230
|
+
await pWaitFor(() => {
|
|
231
|
+
const subscribedPeers = psB.getSubscribers(topic)
|
|
232
|
+
return subscribedPeers.map(p => p.toString()).includes(componentsA.peerId.toString()) // eslint-disable-line max-nested-callbacks
|
|
233
|
+
})
|
|
234
|
+
await psB.publish(topic, data)
|
|
235
|
+
|
|
236
|
+
await defer.promise
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
describe('pubsub with intermittent connections', () => {
|
|
241
|
+
let psA: PubSub
|
|
242
|
+
let psB: PubSub
|
|
243
|
+
let componentsA: MockNetworkComponents
|
|
244
|
+
let componentsB: MockNetworkComponents
|
|
245
|
+
|
|
246
|
+
// Create pubsub nodes and start them
|
|
247
|
+
beforeEach(async () => {
|
|
248
|
+
mockNetwork.reset()
|
|
249
|
+
componentsA = await createComponents()
|
|
250
|
+
componentsB = await createComponents()
|
|
251
|
+
|
|
252
|
+
psA = componentsA.pubsub = await common.setup({
|
|
253
|
+
components: componentsA,
|
|
254
|
+
init: {}
|
|
255
|
+
})
|
|
256
|
+
psB = componentsB.pubsub = await common.setup({
|
|
257
|
+
components: componentsB,
|
|
258
|
+
init: {}
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
await start(...Object.values(componentsA), ...Object.values(componentsB))
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
afterEach(async () => {
|
|
265
|
+
sinon.restore()
|
|
266
|
+
await stop(...Object.values(componentsA), ...Object.values(componentsB))
|
|
267
|
+
await common.teardown()
|
|
268
|
+
mockNetwork.reset()
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it.skip('should receive pubsub messages after a node restart', async function () {
|
|
272
|
+
const topic = 'test-topic'
|
|
273
|
+
const data = uint8ArrayFromString('hey!')
|
|
274
|
+
|
|
275
|
+
let counter = 0
|
|
276
|
+
const defer1 = pDefer()
|
|
277
|
+
const defer2 = pDefer()
|
|
278
|
+
|
|
279
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
280
|
+
|
|
281
|
+
let subscribedTopics = psA.getTopics()
|
|
282
|
+
expect(subscribedTopics).to.not.include(topic)
|
|
283
|
+
|
|
284
|
+
psA.subscribe(topic)
|
|
285
|
+
psA.addEventListener('message', (evt) => {
|
|
286
|
+
if (evt.detail.topic === topic) {
|
|
287
|
+
const msg = evt.detail
|
|
288
|
+
expect(msg.data).to.equalBytes(data)
|
|
289
|
+
counter++
|
|
290
|
+
counter === 1 ? defer1.resolve() : defer2.resolve()
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
psA.subscribe(topic)
|
|
294
|
+
|
|
295
|
+
subscribedTopics = psA.getTopics()
|
|
296
|
+
expect(subscribedTopics).to.include(topic)
|
|
297
|
+
|
|
298
|
+
// wait for psB to know about psA subscription
|
|
299
|
+
await pWaitFor(() => {
|
|
300
|
+
const subscribedPeers = psB.getSubscribers(topic)
|
|
301
|
+
return subscribedPeers.map(p => p.toString()).includes(componentsA.peerId.toString()) // eslint-disable-line max-nested-callbacks
|
|
302
|
+
})
|
|
303
|
+
await psB.publish(topic, data)
|
|
304
|
+
|
|
305
|
+
await defer1.promise
|
|
306
|
+
|
|
307
|
+
await stop(psB)
|
|
308
|
+
await pWaitFor(() => {
|
|
309
|
+
// @ts-expect-error protected fields
|
|
310
|
+
const aHasConnectionToB = psA._libp2p.connectionManager.get(psB.peerId)
|
|
311
|
+
// @ts-expect-error protected fields
|
|
312
|
+
const bHasConnectionToA = psB._libp2p.connectionManager.get(psA.peerId)
|
|
313
|
+
|
|
314
|
+
return aHasConnectionToB != null && bHasConnectionToA != null
|
|
315
|
+
})
|
|
316
|
+
await start(psB)
|
|
317
|
+
|
|
318
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
319
|
+
|
|
320
|
+
// wait for remoteLibp2p to know about libp2p subscription
|
|
321
|
+
await pWaitFor(() => {
|
|
322
|
+
const subscribedPeers = psB.getSubscribers(topic)
|
|
323
|
+
return subscribedPeers.toString().includes(componentsA.peerId.toString())
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
await psB.publish(topic, data)
|
|
327
|
+
|
|
328
|
+
await defer2.promise
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it.skip('should handle quick reconnects with a delayed disconnect', async () => {
|
|
332
|
+
// Subscribe on both
|
|
333
|
+
let aReceivedFirstMessageFromB = false
|
|
334
|
+
let aReceivedSecondMessageFromB = false
|
|
335
|
+
let bReceivedFirstMessageFromA = false
|
|
336
|
+
let bReceivedSecondMessageFromA = false
|
|
337
|
+
const topic = 'reconnect-channel'
|
|
338
|
+
|
|
339
|
+
const handlerSpyA = (evt: CustomEvent<Message>): void => {
|
|
340
|
+
if (evt.detail.topic !== topic) {
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const message = evt.detail
|
|
345
|
+
const data = uint8ArrayToString(message.data)
|
|
346
|
+
|
|
347
|
+
if (data === 'message-from-b-1') {
|
|
348
|
+
aReceivedFirstMessageFromB = true
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (data === 'message-from-b-2') {
|
|
352
|
+
aReceivedSecondMessageFromB = true
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const handlerSpyB = (evt: CustomEvent<Message>): void => {
|
|
356
|
+
if (evt.detail.topic !== topic) {
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const message = evt.detail
|
|
361
|
+
const data = uint8ArrayToString(message.data)
|
|
362
|
+
|
|
363
|
+
if (data === 'message-from-a-1') {
|
|
364
|
+
bReceivedFirstMessageFromA = true
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (data === 'message-from-a-2') {
|
|
368
|
+
bReceivedSecondMessageFromA = true
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
psA.addEventListener('message', handlerSpyA)
|
|
373
|
+
psB.addEventListener('message', handlerSpyB)
|
|
374
|
+
psA.subscribe(topic)
|
|
375
|
+
psB.subscribe(topic)
|
|
376
|
+
|
|
377
|
+
// Create two connections to the remote peer
|
|
378
|
+
// @ts-expect-error protected fields
|
|
379
|
+
const originalConnection = await psA._libp2p.dialer.connectToPeer(psB.peerId)
|
|
380
|
+
|
|
381
|
+
// second connection
|
|
382
|
+
await componentsA.connectionManager.openConnection(componentsB.peerId)
|
|
383
|
+
|
|
384
|
+
// Wait for subscriptions to occur
|
|
385
|
+
await pWaitFor(() => {
|
|
386
|
+
return psA.getSubscribers(topic).map(p => p.toString()).includes(componentsB.peerId.toString()) &&
|
|
387
|
+
psB.getSubscribers(topic).map(p => p.toString()).includes(componentsA.peerId.toString())
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
// Verify messages go both ways
|
|
391
|
+
await psA.publish(topic, uint8ArrayFromString('message-from-a-1'))
|
|
392
|
+
await psB.publish(topic, uint8ArrayFromString('message-from-b-1'))
|
|
393
|
+
await pWaitFor(() => {
|
|
394
|
+
return aReceivedFirstMessageFromB && bReceivedFirstMessageFromA
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
// Disconnect the first connection (this acts as a delayed reconnect)
|
|
398
|
+
// @ts-expect-error protected fields
|
|
399
|
+
const psAConnUpdateSpy = sinon.spy(psA._libp2p.connectionManager.connections, 'set')
|
|
400
|
+
|
|
401
|
+
await originalConnection.close()
|
|
402
|
+
await pWaitFor(() => psAConnUpdateSpy.callCount === 1)
|
|
403
|
+
|
|
404
|
+
// Verify messages go both ways after the disconnect
|
|
405
|
+
await psA.publish(topic, uint8ArrayFromString('message-from-a-2'))
|
|
406
|
+
await psB.publish(topic, uint8ArrayFromString('message-from-b-2'))
|
|
407
|
+
await pWaitFor(() => {
|
|
408
|
+
return aReceivedSecondMessageFromB && bReceivedSecondMessageFromA
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
})
|
|
412
|
+
})
|
|
413
|
+
}
|