'use strict' /* eslint-env mocha */ const chai = require('chai') chai.use(require('dirty-chai')) const { expect } = chai const pDefer = require('p-defer') const PeerInfo = require('peer-info') const Topology = require('libp2p-interfaces/src/topology/multicodec-topology') const PeerStore = require('../../src/peer-store') const Registrar = require('../../src/registrar') const { createMockConnection } = require('./utils') const peerUtils = require('../utils/creators/peer') const multicodec = '/test/1.0.0' describe('registrar', () => { let peerStore, registrar describe('errors', () => { beforeEach(() => { peerStore = new PeerStore() registrar = new Registrar({ peerStore }) }) it('should fail to register a protocol if no multicodec is provided', () => { expect(() => registrar.register()).to.throw() }) it('should fail to register a protocol if an invalid topology is provided', () => { const fakeTopology = { random: 1 } expect(() => registrar.register(fakeTopology)).to.throw() }) }) describe('registration', () => { beforeEach(() => { peerStore = new PeerStore() registrar = new Registrar({ peerStore }) }) it('should be able to register a protocol', () => { const topologyProps = new Topology({ multicodecs: multicodec, handlers: { onConnect: () => { }, onDisconnect: () => { } } }) const identifier = registrar.register(topologyProps) expect(identifier).to.exist() }) it('should be able to unregister a protocol', () => { const topologyProps = new Topology({ multicodecs: multicodec, handlers: { onConnect: () => { }, onDisconnect: () => { } } }) const identifier = registrar.register(topologyProps) const success = registrar.unregister(identifier) expect(success).to.eql(true) }) it('should fail to unregister if no register was made', () => { const success = registrar.unregister('bad-identifier') expect(success).to.eql(false) }) it('should call onConnect handler for connected peers after register', async () => { const onConnectDefer = pDefer() const onDisconnectDefer = pDefer() // Setup connections before registrar const conn = await createMockConnection() const remotePeerInfo = await PeerInfo.create(conn.remotePeer) // Add protocol to peer remotePeerInfo.protocols.add(multicodec) // Add connected peer to peerStore and registrar peerStore.put(remotePeerInfo) registrar.onConnect(remotePeerInfo, conn) expect(registrar.connections.size).to.eql(1) const topologyProps = new Topology({ multicodecs: multicodec, handlers: { onConnect: (peerInfo, connection) => { expect(peerInfo.id.toB58String()).to.eql(remotePeerInfo.id.toB58String()) expect(connection.id).to.eql(conn.id) onConnectDefer.resolve() }, onDisconnect: (peerInfo) => { expect(peerInfo.id.toB58String()).to.eql(remotePeerInfo.id.toB58String()) onDisconnectDefer.resolve() } } }) // Register protocol const identifier = registrar.register(topologyProps) const topology = registrar.topologies.get(identifier) // Topology created expect(topology).to.exist() registrar.onDisconnect(remotePeerInfo) expect(registrar.connections.size).to.eql(0) // Wait for handlers to be called return Promise.all([ onConnectDefer.promise, onDisconnectDefer.promise ]) }) it('should call onConnect handler after register, once a peer is connected and protocols are updated', async () => { const onConnectDefer = pDefer() const onDisconnectDefer = pDefer() const topologyProps = new Topology({ multicodecs: multicodec, handlers: { onConnect: () => { onConnectDefer.resolve() }, onDisconnect: () => { onDisconnectDefer.resolve() } } }) // Register protocol const identifier = registrar.register(topologyProps) const topology = registrar.topologies.get(identifier) // Topology created expect(topology).to.exist() expect(registrar.connections.size).to.eql(0) // Setup connections before registrar const conn = await createMockConnection() const peerInfo = await PeerInfo.create(conn.remotePeer) // Add connected peer to peerStore and registrar peerStore.put(peerInfo) registrar.onConnect(peerInfo, conn) // Add protocol to peer and update it peerInfo.protocols.add(multicodec) peerStore.put(peerInfo) await onConnectDefer.promise // Remove protocol to peer and update it peerInfo.protocols.delete(multicodec) peerStore.replace(peerInfo) await onDisconnectDefer.promise }) it('should filter connections on disconnect, removing the closed one', async () => { const onDisconnectDefer = pDefer() const topologyProps = new Topology({ multicodecs: multicodec, handlers: { onConnect: () => {}, onDisconnect: () => { onDisconnectDefer.resolve() } } }) // Register protocol registrar.register(topologyProps) // Setup connections before registrar const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 }) const conn1 = await createMockConnection({ localPeer: localPeer.id, remotePeer: remotePeer.id }) const conn2 = await createMockConnection({ localPeer: localPeer.id, remotePeer: remotePeer.id }) const peerInfo = await PeerInfo.create(remotePeer.id) const id = peerInfo.id.toB58String() // Add connection to registrar peerStore.put(peerInfo) registrar.onConnect(peerInfo, conn1) registrar.onConnect(peerInfo, conn2) expect(registrar.connections.get(id).length).to.eql(2) conn2._stat.status = 'closed' registrar.onDisconnect(peerInfo, conn2) const peerConnections = registrar.connections.get(id) expect(peerConnections.length).to.eql(1) expect(peerConnections[0]._stat.status).to.eql('open') }) }) })