chore: refactor connection manager and registrar

This commit is contained in:
Vasco Santos
2020-04-18 17:06:56 +02:00
parent 67fda7e106
commit 31d8a929ea
18 changed files with 424 additions and 411 deletions

View File

@ -0,0 +1,88 @@
'use strict'
/* eslint-env mocha */
const chai = require('chai')
chai.use(require('dirty-chai'))
chai.use(require('chai-as-promised'))
const { expect } = chai
const sinon = require('sinon')
const multiaddr = require('multiaddr')
const peerUtils = require('../utils/creators/peer')
const mockConnection = require('../utils/mockConnection')
const baseOptions = require('../utils/base-options.browser')
const listenMultiaddr = multiaddr('/ip4/127.0.0.1/tcp/15002/ws')
describe('Connection Manager', () => {
let libp2p
beforeEach(async () => {
[libp2p] = await peerUtils.createPeer({
config: {
addresses: {
listen: [listenMultiaddr]
},
modules: baseOptions.modules
}
})
})
afterEach(() => libp2p.stop())
it('should filter connections on disconnect, removing the closed one', async () => {
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
const conn1 = await mockConnection({ localPeer, remotePeer })
const conn2 = await mockConnection({ localPeer, remotePeer })
const id = remotePeer.toB58String()
// Add connection to the connectionManager
libp2p.connectionManager.onConnect(conn1)
libp2p.connectionManager.onConnect(conn2)
expect(libp2p.connectionManager.connections.get(id).length).to.eql(2)
conn2._stat.status = 'closed'
libp2p.connectionManager.onDisconnect(conn2)
const peerConnections = libp2p.connectionManager.connections.get(id)
expect(peerConnections.length).to.eql(1)
expect(peerConnections[0]._stat.status).to.eql('open')
})
it('should add connection on dial and remove on node stop', async () => {
const [remoteLibp2p] = await peerUtils.createPeer({
config: {
addresses: {
listen: [multiaddr('/ip4/127.0.0.1/tcp/15003/ws')]
},
modules: baseOptions.modules
}
})
// Spy on emit for easy verification
sinon.spy(libp2p.connectionManager, 'emit')
sinon.spy(remoteLibp2p.connectionManager, 'emit')
libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.addresses.listen)
await libp2p.dial(remoteLibp2p.peerId)
// check connect event
expect(libp2p.connectionManager.emit.callCount).to.equal(1)
const [event, connection] = libp2p.connectionManager.emit.getCall(0).args
expect(event).to.equal('peer:connect')
expect(connection.remotePeer.isEqual(remoteLibp2p.peerId)).to.equal(true)
const libp2pConn = libp2p.connectionManager.get(remoteLibp2p.peerId)
expect(libp2pConn).to.exist()
const remoteConn = remoteLibp2p.connectionManager.get(libp2p.peerId)
expect(remoteConn).to.exist()
await remoteLibp2p.stop()
expect(remoteLibp2p.connectionManager.size).to.eql(0)
})
})

View File

@ -7,7 +7,7 @@ chai.use(require('chai-as-promised'))
const { expect } = chai
const sinon = require('sinon')
const { createPeer } = require('../utils/creators/peer')
const peerUtils = require('../utils/creators/peer')
const mockConnection = require('../utils/mockConnection')
const baseOptions = require('../utils/base-options.browser')
@ -20,7 +20,7 @@ describe('Connection Manager', () => {
})
it('should be able to create without metrics', async () => {
[libp2p] = await createPeer({
[libp2p] = await peerUtils.createPeer({
config: {
modules: baseOptions.modules
},
@ -35,7 +35,7 @@ describe('Connection Manager', () => {
})
it('should be able to create with metrics', async () => {
[libp2p] = await createPeer({
[libp2p] = await peerUtils.createPeer({
config: {
modules: baseOptions.modules,
metrics: {
@ -49,12 +49,12 @@ describe('Connection Manager', () => {
await libp2p.start()
expect(spy).to.have.property('callCount', 1)
expect(libp2p.connectionManager._metrics).to.exist()
expect(libp2p.connectionManager._libp2p.metrics).to.exist()
})
it('should close lowest value peer connection when the maximum has been reached', async () => {
const max = 5
;[libp2p] = await createPeer({
;[libp2p] = await peerUtils.createPeer({
config: {
modules: baseOptions.modules,
connectionManager: {
@ -92,7 +92,7 @@ describe('Connection Manager', () => {
it('should close connection when the maximum has been reached even without peer values', async () => {
const max = 5
;[libp2p] = await createPeer({
;[libp2p] = await peerUtils.createPeer({
config: {
modules: baseOptions.modules,
connectionManager: {
@ -110,7 +110,7 @@ describe('Connection Manager', () => {
const spy = sinon.spy()
await Promise.all([...new Array(max + 1)].map(async () => {
const connection = await mockConnection()
sinon.stub(connection, 'close').callsFake(() => spy())
sinon.stub(connection, 'close').callsFake(() => spy()) // eslint-disable-line
libp2p.connectionManager.onConnect(connection)
}))
@ -119,7 +119,7 @@ describe('Connection Manager', () => {
})
it('should fail if the connection manager has mismatched connection limit options', async () => {
await expect(createPeer({
await expect(peerUtils.createPeer({
config: {
modules: baseOptions.modules,
connectionManager: {

View File

@ -374,8 +374,8 @@ describe('Dialing (direct, TCP)', () => {
}
// 1 connection, because we know the peer in the multiaddr
expect(libp2p.connectionManager._connections.size).to.equal(1)
expect(remoteLibp2p.connectionManager._connections.size).to.equal(1)
expect(libp2p.connectionManager.size).to.equal(1)
expect(remoteLibp2p.connectionManager.size).to.equal(1)
})
it('should coalesce parallel dials to the same error on failure', async () => {
@ -409,8 +409,8 @@ describe('Dialing (direct, TCP)', () => {
}
// 1 connection, because we know the peer in the multiaddr
expect(libp2p.connectionManager._connections.size).to.equal(0)
expect(remoteLibp2p.connectionManager._connections.size).to.equal(0)
expect(libp2p.connectionManager.size).to.equal(0)
expect(remoteLibp2p.connectionManager.size).to.equal(0)
})
})
})

View File

@ -121,7 +121,7 @@ describe('Dialing (via relay, TCP)', () => {
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
// We should not be connected to the relay, because we weren't before the dial
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerId)
const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId)
expect(srcToRelayConn).to.not.exist()
})
@ -138,7 +138,7 @@ describe('Dialing (via relay, TCP)', () => {
.to.eventually.be.rejectedWith(AggregateError)
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerId)
const srcToRelayConn = srcLibp2p.connectionManager.get(relayLibp2p.peerId)
expect(srcToRelayConn).to.exist()
expect(srcToRelayConn.stat.status).to.equal('open')
})
@ -164,7 +164,7 @@ describe('Dialing (via relay, TCP)', () => {
.to.eventually.be.rejectedWith(AggregateError)
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
const dstToRelayConn = dstLibp2p.registrar.getConnection(relayLibp2p.peerId)
const dstToRelayConn = dstLibp2p.connectionManager.get(relayLibp2p.peerId)
expect(dstToRelayConn).to.exist()
expect(dstToRelayConn.stat.status).to.equal('open')
})

View File

@ -7,6 +7,7 @@ chai.use(require('chai-as-promised'))
const { expect } = chai
const sinon = require('sinon')
const { EventEmitter } = require('events')
const delay = require('delay')
const PeerId = require('peer-id')
const duplexPair = require('it-pair/duplex')
@ -48,14 +49,13 @@ describe('Identify', () => {
listen: []
},
protocols,
registrar: {
peerStore: {
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
connectionManager: new EventEmitter(),
peerStore: {
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
}
})
@ -64,7 +64,8 @@ describe('Identify', () => {
addresses: {
listen: []
},
protocols
protocols,
connectionManager: new EventEmitter()
})
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
@ -74,8 +75,8 @@ describe('Identify', () => {
const [local, remote] = duplexPair()
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY })
sinon.spy(localIdentify.registrar.peerStore.addressBook, 'set')
sinon.spy(localIdentify.registrar.peerStore.protoBook, 'set')
sinon.spy(localIdentify.peerStore.addressBook, 'set')
sinon.spy(localIdentify.peerStore.protoBook, 'set')
// Run identify
await Promise.all([
@ -87,10 +88,10 @@ describe('Identify', () => {
})
])
expect(localIdentify.registrar.peerStore.addressBook.set.callCount).to.equal(1)
expect(localIdentify.registrar.peerStore.protoBook.set.callCount).to.equal(1)
expect(localIdentify.peerStore.addressBook.set.callCount).to.equal(1)
expect(localIdentify.peerStore.protoBook.set.callCount).to.equal(1)
// Validate the remote peer gets updated in the peer store
const call = localIdentify.registrar.peerStore.addressBook.set.firstCall
const call = localIdentify.peerStore.addressBook.set.firstCall
expect(call.args[0].id.bytes).to.equal(remotePeer.bytes)
})
@ -101,14 +102,13 @@ describe('Identify', () => {
listen: []
},
protocols,
registrar: {
peerStore: {
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
connectionManager: new EventEmitter(),
peerStore: {
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
}
})
@ -117,7 +117,8 @@ describe('Identify', () => {
addresses: {
listen: []
},
protocols
protocols,
connectionManager: new EventEmitter()
})
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
@ -145,12 +146,15 @@ describe('Identify', () => {
describe('push', () => {
it('should be able to push identify updates to another peer', async () => {
const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
const connectionManager = new EventEmitter()
connectionManager.getConnection = () => {}
const localIdentify = new IdentifyService({
peerId: localPeer,
addresses: {
listen: [listeningAddr]
},
registrar: { getConnection: () => {} },
connectionManager,
protocols: new Map([
[multicodecs.IDENTIFY],
[multicodecs.IDENTIFY_PUSH],
@ -162,14 +166,13 @@ describe('Identify', () => {
addresses: {
listen: []
},
registrar: {
peerStore: {
addressBook: {
set: () => {}
},
protoBook: {
set: () => { }
}
connectionManager,
peerStore: {
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
}
})
@ -182,8 +185,8 @@ describe('Identify', () => {
const [local, remote] = duplexPair()
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH })
sinon.spy(remoteIdentify.registrar.peerStore.addressBook, 'set')
sinon.spy(remoteIdentify.registrar.peerStore.protoBook, 'set')
sinon.spy(remoteIdentify.peerStore.addressBook, 'set')
sinon.spy(remoteIdentify.peerStore.protoBook, 'set')
// Run identify
await Promise.all([
@ -195,12 +198,12 @@ describe('Identify', () => {
})
])
expect(remoteIdentify.registrar.peerStore.addressBook.set.callCount).to.equal(1)
expect(remoteIdentify.registrar.peerStore.protoBook.set.callCount).to.equal(1)
const [peerId, multiaddrs] = remoteIdentify.registrar.peerStore.addressBook.set.firstCall.args
expect(remoteIdentify.peerStore.addressBook.set.callCount).to.equal(1)
expect(remoteIdentify.peerStore.protoBook.set.callCount).to.equal(1)
const [peerId, multiaddrs] = remoteIdentify.peerStore.addressBook.set.firstCall.args
expect(peerId.bytes).to.eql(localPeer.bytes)
expect(multiaddrs).to.eql([listeningAddr])
const [peerId2, protocols] = remoteIdentify.registrar.peerStore.protoBook.set.firstCall.args
const [peerId2, protocols] = remoteIdentify.peerStore.protoBook.set.firstCall.args
expect(peerId2.bytes).to.eql(localPeer.bytes)
expect(protocols).to.eql(Array.from(localProtocols))
})

View File

@ -7,6 +7,8 @@ chai.use(require('chai-as-promised'))
const { expect } = chai
const sinon = require('sinon')
const { EventEmitter } = require('events')
const { randomBytes } = require('libp2p-crypto')
const duplexPair = require('it-pair/duplex')
const pipe = require('it-pipe')
@ -35,7 +37,8 @@ describe('Metrics', () => {
const [local, remote] = duplexPair()
const metrics = new Metrics({
computeThrottleMaxQueueSize: 1, // compute after every message
movingAverageIntervals: [10, 100, 1000]
movingAverageIntervals: [10, 100, 1000],
connectionManager: new EventEmitter()
})
metrics.trackStream({
@ -70,7 +73,8 @@ describe('Metrics', () => {
const [local, remote] = duplexPair()
const metrics = new Metrics({
computeThrottleMaxQueueSize: 1, // compute after every message
movingAverageIntervals: [10, 100, 1000]
movingAverageIntervals: [10, 100, 1000],
connectionManager: new EventEmitter()
})
metrics.trackStream({
@ -118,7 +122,8 @@ describe('Metrics', () => {
const [local2, remote2] = duplexPair()
const metrics = new Metrics({
computeThrottleMaxQueueSize: 1, // compute after every message
movingAverageIntervals: [10, 100, 1000]
movingAverageIntervals: [10, 100, 1000],
connectionManager: new EventEmitter()
})
const protocol = '/echo/1.0.0'
metrics.start()
@ -173,7 +178,8 @@ describe('Metrics', () => {
const [local, remote] = duplexPair()
const metrics = new Metrics({
computeThrottleMaxQueueSize: 1, // compute after every message
movingAverageIntervals: [10, 100, 1000]
movingAverageIntervals: [10, 100, 1000],
connectionManager: new EventEmitter()
})
metrics.start()
@ -228,7 +234,8 @@ describe('Metrics', () => {
}))
const metrics = new Metrics({
maxOldPeersRetention: 5 // Only keep track of 5
maxOldPeersRetention: 5, // Only keep track of 5
connectionManager: new EventEmitter()
})
// Clone so trackedPeers isn't modified

View File

@ -1,72 +0,0 @@
'use strict'
/* eslint-env mocha */
const chai = require('chai')
chai.use(require('dirty-chai'))
const { expect } = chai
const sinon = require('sinon')
const mergeOptions = require('merge-options')
const multiaddr = require('multiaddr')
const Libp2p = require('../../src')
const baseOptions = require('../utils/base-options')
const peerUtils = require('../utils/creators/peer')
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
describe('registrar on dial', () => {
let peerId
let remotePeerId
let libp2p
let remoteLibp2p
let remoteAddr
before(async () => {
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
remoteLibp2p = new Libp2p(mergeOptions(baseOptions, {
peerId: remotePeerId
}))
await remoteLibp2p.transportManager.listen([listenAddr])
remoteAddr = remoteLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${remotePeerId.toB58String()}`)
})
after(async () => {
sinon.restore()
await remoteLibp2p.stop()
libp2p && await libp2p.stop()
})
it('should inform registrar of a new connection', async () => {
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerId
}))
sinon.spy(remoteLibp2p.registrar, 'onConnect')
await libp2p.dial(remoteAddr)
expect(remoteLibp2p.registrar.onConnect.callCount).to.equal(1)
const libp2pConn = libp2p.registrar.getConnection(remotePeerId)
expect(libp2pConn).to.exist()
const remoteConn = remoteLibp2p.registrar.getConnection(peerId)
expect(remoteConn).to.exist()
})
it('should be closed on libp2p stop', async () => {
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerId
}))
await libp2p.dial(remoteAddr)
expect(libp2p.connections.size).to.equal(1)
sinon.spy(libp2p.registrar, 'close')
await libp2p.stop()
expect(libp2p.registrar.close.callCount).to.equal(1)
expect(libp2p.connections.size).to.equal(0)
})
})

View File

@ -6,21 +6,26 @@ chai.use(require('dirty-chai'))
const { expect } = chai
const pDefer = require('p-defer')
const { EventEmitter } = require('events')
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 createMockConnection = require('../utils/mockConnection')
const peerUtils = require('../utils/creators/peer')
const baseOptions = require('../utils/base-options.browser')
const multicodec = '/test/1.0.0'
describe('registrar', () => {
let peerStore, registrar
let peerStore
let registrar
describe('errors', () => {
beforeEach(() => {
peerStore = new PeerStore()
registrar = new Registrar({ peerStore })
registrar = new Registrar({ peerStore, connectionManager: new EventEmitter() })
})
it('should fail to register a protocol if no multicodec is provided', () => {
@ -36,11 +41,19 @@ describe('registrar', () => {
})
describe('registration', () => {
beforeEach(() => {
peerStore = new PeerStore()
registrar = new Registrar({ peerStore })
let libp2p
beforeEach(async () => {
[libp2p] = await peerUtils.createPeer({
config: {
modules: baseOptions.modules
},
started: false
})
})
afterEach(() => libp2p.stop())
it('should be able to register a protocol', () => {
const topologyProps = new Topology({
multicodecs: multicodec,
@ -50,7 +63,7 @@ describe('registrar', () => {
}
})
const identifier = registrar.register(topologyProps)
const identifier = libp2p.registrar.register(topologyProps)
expect(identifier).to.exist()
})
@ -64,14 +77,14 @@ describe('registrar', () => {
}
})
const identifier = registrar.register(topologyProps)
const success = registrar.unregister(identifier)
const identifier = libp2p.registrar.register(topologyProps)
const success = libp2p.registrar.unregister(identifier)
expect(success).to.eql(true)
})
it('should fail to unregister if no register was made', () => {
const success = registrar.unregister('bad-identifier')
const success = libp2p.registrar.unregister('bad-identifier')
expect(success).to.eql(false)
})
@ -85,10 +98,10 @@ describe('registrar', () => {
const remotePeerId = conn.remotePeer
// Add connected peer with protocol to peerStore and registrar
peerStore.protoBook.add(remotePeerId, [multicodec])
libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
registrar.onConnect(remotePeerId, conn)
expect(registrar.connections.size).to.eql(1)
libp2p.connectionManager.onConnect(conn)
expect(libp2p.connectionManager.size).to.eql(1)
const topologyProps = new Topology({
multicodecs: multicodec,
@ -108,14 +121,16 @@ describe('registrar', () => {
})
// Register protocol
const identifier = registrar.register(topologyProps)
const topology = registrar.topologies.get(identifier)
const identifier = libp2p.registrar.register(topologyProps)
const topology = libp2p.registrar.topologies.get(identifier)
// Topology created
expect(topology).to.exist()
registrar.onDisconnect(remotePeerId)
expect(registrar.connections.size).to.eql(0)
await conn.close()
libp2p.connectionManager.onDisconnect(conn)
expect(libp2p.connectionManager.size).to.eql(0)
// Wait for handlers to be called
return Promise.all([
@ -141,68 +156,30 @@ describe('registrar', () => {
})
// Register protocol
const identifier = registrar.register(topologyProps)
const topology = registrar.topologies.get(identifier)
const identifier = libp2p.registrar.register(topologyProps)
const topology = libp2p.registrar.topologies.get(identifier)
// Topology created
expect(topology).to.exist()
expect(registrar.connections.size).to.eql(0)
expect(libp2p.connectionManager.size).to.eql(0)
// Setup connections before registrar
const conn = await createMockConnection()
const remotePeerId = conn.remotePeer
// Add connected peer to peerStore and registrar
peerStore.protoBook.set(remotePeerId, [])
registrar.onConnect(remotePeerId, conn)
libp2p.peerStore.protoBook.set(remotePeerId, [])
libp2p.connectionManager.onConnect(conn)
// Add protocol to peer and update it
peerStore.protoBook.add(remotePeerId, [multicodec])
libp2p.peerStore.protoBook.add(remotePeerId, [multicodec])
await onConnectDefer.promise
// Remove protocol to peer and update it
peerStore.protoBook.set(remotePeerId, [])
libp2p.peerStore.protoBook.set(remotePeerId, [])
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.createPeerId({ number: 2 })
const conn1 = await createMockConnection({ localPeer, remotePeer })
const conn2 = await createMockConnection({ localPeer, remotePeer })
const id = remotePeer.toB58String()
// Add connection to registrar
registrar.onConnect(remotePeer, conn1)
registrar.onConnect(remotePeer, conn2)
expect(registrar.connections.get(id).length).to.eql(2)
conn2._stat.status = 'closed'
registrar.onDisconnect(remotePeer, conn2)
const peerConnections = registrar.connections.get(id)
expect(peerConnections.length).to.eql(1)
expect(peerConnections[0]._stat.status).to.eql('open')
})
})
})

View File

@ -1,51 +0,0 @@
'use strict'
const { Connection } = require('libp2p-interfaces/src/connection')
const multiaddr = require('multiaddr')
const pair = require('it-pair')
const peerUtils = require('../utils/creators/peer')
module.exports.createMockConnection = async (properties = {}) => {
const localAddr = multiaddr('/ip4/127.0.0.1/tcp/8080')
const remoteAddr = multiaddr('/ip4/127.0.0.1/tcp/8081')
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
const openStreams = []
let streamId = 0
return new Connection({
localPeer: localPeer,
remotePeer: remotePeer,
localAddr,
remoteAddr,
stat: {
timeline: {
open: Date.now() - 10,
upgraded: Date.now()
},
direction: 'outbound',
encryption: '/secio/1.0.0',
multiplexer: '/mplex/6.7.0',
status: 'open'
},
newStream: (protocols) => {
const id = streamId++
const stream = pair()
stream.close = () => stream.sink([])
stream.id = id
openStreams.push(stream)
return {
stream,
protocol: protocols[0]
}
},
close: () => { },
getStreams: () => openStreams,
...properties
})
}

View File

@ -421,24 +421,24 @@ describe('libp2p.upgrader', () => {
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer })
// Spy on emit for easy verification
sinon.spy(libp2p, 'emit')
sinon.spy(libp2p.connectionManager, 'emit')
// Upgrade and check the connect event
const connections = await Promise.all([
libp2p.upgrader.upgradeOutbound(outbound),
remoteUpgrader.upgradeInbound(inbound)
])
expect(libp2p.emit.callCount).to.equal(1)
expect(libp2p.connectionManager.emit.callCount).to.equal(1)
let [event, peerId] = libp2p.emit.getCall(0).args
let [event, connection] = libp2p.connectionManager.emit.getCall(0).args
expect(event).to.equal('peer:connect')
expect(peerId.isEqual(remotePeer)).to.equal(true)
expect(connection.remotePeer.isEqual(remotePeer)).to.equal(true)
// Close and check the disconnect event
await Promise.all(connections.map(conn => conn.close()))
expect(libp2p.emit.callCount).to.equal(2)
;([event, peerId] = libp2p.emit.getCall(1).args)
expect(libp2p.connectionManager.emit.callCount).to.equal(2)
;([event, connection] = libp2p.connectionManager.emit.getCall(1).args)
expect(event).to.equal('peer:disconnect')
expect(peerId.isEqual(remotePeer)).to.equal(true)
expect(connection.remotePeer.isEqual(remotePeer)).to.equal(true)
})
})