fix: make tests more reliable (#1139)

Try to use only public functions and properties to verify test behaviour
This commit is contained in:
Alex Potsides 2022-01-21 12:58:31 +00:00 committed by GitHub
parent 4c3bf01f35
commit b7e87066a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 192 deletions

View File

@ -592,7 +592,7 @@ const node = await Libp2p.create({
#### Configuring Transport Manager
The Transport Manager is responsible for managing the libp2p transports life cycle. This includes starting listeners for the provided listen addresses, closing these listeners and dialing using the provided transports. By default, if a libp2p node has a list of multiaddrs for listenning on and there are no valid transports for those multiaddrs, libp2p will throw an error on startup and shutdown. However, for some applications it is perfectly acceptable for libp2p nodes to start in dial only mode if all the listen multiaddrs failed. This error tolerance can be enabled as follows:
The Transport Manager is responsible for managing the libp2p transports life cycle. This includes starting listeners for the provided listen addresses, closing these listeners and dialing using the provided transports. By default, if a libp2p node has a list of multiaddrs for listening on and there are no valid transports for those multiaddrs, libp2p will throw an error on startup and shutdown. However, for some applications it is perfectly acceptable for libp2p nodes to start in dial only mode if all the listen multiaddrs failed. This error tolerance can be enabled as follows:
```js
const Libp2p = require('libp2p')

View File

@ -8,6 +8,7 @@ const log = Object.assign(debug('libp2p:auto-relay'), {
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
const { Multiaddr } = require('multiaddr')
const all = require('it-all')
const { relay: multicodec } = require('./multicodec')
const { canHop } = require('./circuit/hop')
@ -149,6 +150,7 @@ class AutoRelay {
* @returns {Promise<void>}
*/
async _addListenRelay (connection, id) {
try {
// Check if already listening on enough relays
if (this._listenRelays.size >= this.maxListeners) {
return
@ -167,8 +169,8 @@ class AutoRelay {
this._listenRelays.add(id)
// Attempt to listen on relay
try {
await this._transportManager.listen([new Multiaddr(listenAddr)])
// Announce multiaddrs will update on listen success by TransportManager event being triggered
} catch (/** @type {any} */ err) {
this._onError(err)
@ -206,13 +208,18 @@ class AutoRelay {
}
const knownHopsToDial = []
const peers = await all(this._peerStore.getPeers())
// Check if we have known hop peers to use and attempt to listen on the already connected
for await (const { id, metadata } of this._peerStore.getPeers()) {
for await (const { id, metadata } of peers) {
const idStr = id.toB58String()
// Continue to next if listening on this or peer to ignore
if (this._listenRelays.has(idStr) || peersToIgnore.includes(idStr)) {
if (this._listenRelays.has(idStr)) {
continue
}
if (peersToIgnore.includes(idStr)) {
continue
}

View File

@ -2,8 +2,7 @@
/* eslint-env mocha */
const { expect } = require('aegir/utils/chai')
const delay = require('delay')
const pDefer = require('p-defer')
const defer = require('p-defer')
const pWaitFor = require('p-wait-for')
const sinon = require('sinon')
const nock = require('nock')
@ -19,11 +18,35 @@ const baseOptions = require('../utils/base-options')
const listenAddr = '/ip4/0.0.0.0/tcp/0'
async function usingAsRelay (node, relay, opts) {
// Wait for peer to be used as a relay
await pWaitFor(() => {
for (const addr of node.multiaddrs) {
if (addr.toString().includes(`${relay.peerId.toB58String()}/p2p-circuit`)) {
return true
}
}
return false
}, opts)
}
async function discoveredRelayConfig (node, relay) {
await pWaitFor(async () => {
const protos = await node.peerStore.protoBook.get(relay.peerId)
const supportsRelay = protos.includes('/libp2p/circuit/relay/0.1.0')
const metadata = await node.peerStore.metadataBook.get(relay.peerId)
const supportsHop = metadata.has('hop_relay')
return supportsRelay && supportsHop
})
}
describe('auto-relay', () => {
describe('basics', () => {
let libp2p
let relayLibp2p
let autoRelay
beforeEach(async () => {
const peerIds = await createPeerId({ number: 2 })
@ -59,10 +82,6 @@ describe('auto-relay', () => {
peerId
})
})
autoRelay = libp2p.relay._autoRelay
expect(autoRelay.maxListeners).to.eql(1)
})
beforeEach(() => {
@ -76,22 +95,15 @@ describe('auto-relay', () => {
})
it('should ask if node supports hop on protocol change (relay protocol) and add to listen multiaddrs', async () => {
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay, '_addListenRelay')
const originalMultiaddrsLength = relayLibp2p.multiaddrs.length
// Discover relay
await libp2p.peerStore.addressBook.add(relayLibp2p.peerId, relayLibp2p.multiaddrs)
await libp2p.dial(relayLibp2p.peerId)
// Wait for peer added as listen relay
await pWaitFor(() => autoRelay._addListenRelay.callCount === 1)
expect(autoRelay._listenRelays.size).to.equal(1)
await discoveredRelayConfig(libp2p, relayLibp2p)
// Wait for listen multiaddr update
await pWaitFor(() => libp2p.multiaddrs.length === originalMultiaddrsLength + 1)
expect(libp2p.multiaddrs[originalMultiaddrsLength].getPeerId()).to.eql(relayLibp2p.peerId.toB58String())
// Wait to start using peer as a relay
await usingAsRelay(libp2p, relayLibp2p)
// Peer has relay multicodec
const knownProtocols = await libp2p.peerStore.protoBook.get(relayLibp2p.peerId)
@ -104,7 +116,6 @@ describe('auto-relay', () => {
let relayLibp2p1
let relayLibp2p2
let relayLibp2p3
let autoRelay1
beforeEach(async () => {
const peerIds = await createPeerId({ number: 4 })
@ -144,10 +155,6 @@ describe('auto-relay', () => {
peerId
})
})
autoRelay1 = relayLibp2p1.relay._autoRelay
expect(autoRelay1.maxListeners).to.eql(1)
})
beforeEach(() => {
@ -161,27 +168,13 @@ describe('auto-relay', () => {
})
it('should ask if node supports hop on protocol change (relay protocol) and add to listen multiaddrs', async () => {
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay1, '_addListenRelay')
// Discover relay
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
await relayLibp2p1.dial(relayLibp2p2.peerId)
await discoveredRelayConfig(relayLibp2p1, relayLibp2p2)
// Wait for peer added as listen relay
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1)
expect(autoRelay1._listenRelays.size).to.equal(1)
// Wait for listen multiaddr update
await Promise.all([
pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1),
pWaitFor(() => relayLibp2p2.multiaddrs.length === originalMultiaddrs2Length + 1)
])
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Peer has relay multicodec
const knownProtocols = await relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
@ -189,52 +182,28 @@ describe('auto-relay', () => {
})
it('should be able to dial a peer from its relayed address previously added', async () => {
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
// Discover relay
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
await discoveredRelayConfig(relayLibp2p1, relayLibp2p2)
// Wait for listen multiaddr update
await Promise.all([
pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1),
pWaitFor(() => relayLibp2p2.multiaddrs.length === originalMultiaddrs2Length + 1)
])
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
// Wait for peer added as listen relay
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Dial from the other through a relay
const relayedMultiaddr2 = new Multiaddr(`${relayLibp2p1.multiaddrs[0]}/p2p/${relayLibp2p1.peerId.toB58String()}/p2p-circuit`)
await libp2p.peerStore.addressBook.add(relayLibp2p2.peerId, [relayedMultiaddr2])
await libp2p.dial(relayLibp2p2.peerId)
})
it('should only add maxListeners relayed addresses', async () => {
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
const originalMultiaddrs2Length = relayLibp2p2.multiaddrs.length
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay1, '_addListenRelay')
sinon.spy(autoRelay1._listenRelays, 'add')
// Discover one relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
expect(relayLibp2p1.connectionManager.size).to.eql(1)
await discoveredRelayConfig(relayLibp2p1, relayLibp2p2)
// Wait for peer added as listen relay
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1 && autoRelay1._listenRelays.add.callCount === 1)
expect(autoRelay1._listenRelays.size).to.equal(1)
// Wait for listen multiaddr update
await Promise.all([
pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1),
pWaitFor(() => relayLibp2p2.multiaddrs.length === originalMultiaddrs2Length + 1)
])
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Relay2 has relay multicodec
const knownProtocols2 = await relayLibp2p1.peerStore.protoBook.get(relayLibp2p2.peerId)
@ -243,14 +212,12 @@ describe('auto-relay', () => {
// Discover an extra relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
await discoveredRelayConfig(relayLibp2p1, relayLibp2p3)
// Wait to guarantee the dialed peer is not added as a listen relay
await delay(300)
expect(autoRelay1._addListenRelay.callCount).to.equal(2)
expect(autoRelay1._listenRelays.add.callCount).to.equal(1)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.eql(2)
await expect(usingAsRelay(relayLibp2p1, relayLibp2p3, {
timeout: 1000
})).to.eventually.be.rejected()
// Relay2 has relay multicodec
const knownProtocols3 = await relayLibp2p1.peerStore.protoBook.get(relayLibp2p3.peerId)
@ -258,19 +225,16 @@ describe('auto-relay', () => {
})
it('should not listen on a relayed address if peer disconnects', async () => {
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
// Spy if identify push is fired on adding/removing listen addr
sinon.spy(relayLibp2p1.identifyService, 'pushToPeerStore')
// Discover one relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
await discoveredRelayConfig(relayLibp2p1, relayLibp2p2)
// Wait for listenning on the relay
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
// Wait for listening on the relay
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Identify push for adding listen relay multiaddr
expect(relayLibp2p1.identifyService.pushToPeerStore.callCount).to.equal(1)
@ -279,107 +243,67 @@ describe('auto-relay', () => {
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
// Wait for removed listening on the relay
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length)
expect(autoRelay1._listenRelays.size).to.equal(0)
await expect(usingAsRelay(relayLibp2p1, relayLibp2p2, {
timeout: 1000
})).to.eventually.be.rejected()
// Identify push for removing listen relay multiaddr
await pWaitFor(() => relayLibp2p1.identifyService.pushToPeerStore.callCount === 2)
})
it('should try to listen on other connected peers relayed address if one used relay disconnects', async () => {
const originalMultiaddrs1Length = relayLibp2p1.multiaddrs.length
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay1, '_addListenRelay')
sinon.spy(relayLibp2p1.transportManager, 'listen')
// Discover one relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
await discoveredRelayConfig(relayLibp2p1, relayLibp2p2)
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Discover an extra relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for both peer to be attempted to added as listen relay
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.equal(2)
// Wait for listen multiaddr update
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1)
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p2.peerId.toB58String())
await discoveredRelayConfig(relayLibp2p1, relayLibp2p3)
// Only one will be used for listening
expect(relayLibp2p1.transportManager.listen.callCount).to.equal(1)
// Spy if relay from listen map was removed
sinon.spy(autoRelay1._listenRelays, 'delete')
await expect(usingAsRelay(relayLibp2p1, relayLibp2p3, {
timeout: 1000
})).to.eventually.be.rejected()
// Disconnect from peer used for relay
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
expect(autoRelay1._listenRelays.delete.callCount).to.equal(1)
expect(autoRelay1._addListenRelay.callCount).to.equal(1)
await relayLibp2p2.stop()
// Wait for other peer connected to be added as listen addr
await pWaitFor(() => relayLibp2p1.transportManager.listen.callCount === 2)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.eql(1)
// Wait for listen multiaddr update
await pWaitFor(() => relayLibp2p1.multiaddrs.length === originalMultiaddrs1Length + 1)
expect(relayLibp2p1.multiaddrs[originalMultiaddrs1Length].getPeerId()).to.eql(relayLibp2p3.peerId.toB58String())
await usingAsRelay(relayLibp2p1, relayLibp2p3)
})
it('should try to listen on stored peers relayed address if one used relay disconnects and there are not enough connected', async () => {
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay1, '_addListenRelay')
sinon.spy(relayLibp2p1.transportManager, 'listen')
// Discover one relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Wait for peer to be used as a relay
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Discover an extra relay and connect to gather its Hop support
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for both peer to be attempted to added as listen relay
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 2)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.equal(2)
// Only one will be used for listening
expect(relayLibp2p1.transportManager.listen.callCount).to.equal(1)
// wait for identify for newly dialled peer
await discoveredRelayConfig(relayLibp2p1, relayLibp2p3)
// Disconnect not used listen relay
await relayLibp2p1.hangUp(relayLibp2p3.peerId)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.equal(1)
// Spy on dial
sinon.spy(autoRelay1, '_tryToListenOnRelay')
sinon.spy(relayLibp2p1, 'dial')
// Remove peer used as relay from peerStore and disconnect it
await relayLibp2p1.peerStore.delete(relayLibp2p2.peerId)
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
expect(autoRelay1._listenRelays.size).to.equal(0)
expect(relayLibp2p1.connectionManager.size).to.equal(0)
await relayLibp2p1.peerStore.delete(relayLibp2p2.peerId)
await pWaitFor(() => relayLibp2p1.connectionManager.size === 0)
// Wait for other peer connected to be added as listen addr
await pWaitFor(() => relayLibp2p1.transportManager.listen.callCount === 2)
await pWaitFor(() => autoRelay1._tryToListenOnRelay.callCount === 1)
await pWaitFor(() => autoRelay1._listenRelays.size === 1)
await pWaitFor(() => relayLibp2p1.connectionManager.size === 1)
await usingAsRelay(relayLibp2p1, relayLibp2p3)
})
it('should not fail when trying to dial unreachable peers to add as hop relay and replaced removed ones', async () => {
const defer = pDefer()
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay1, '_addListenRelay')
sinon.spy(relayLibp2p1.transportManager, 'listen')
const deferred = defer()
// Discover one relay and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p2.peerId, relayLibp2p2.multiaddrs)
@ -389,34 +313,28 @@ describe('auto-relay', () => {
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for both peer to be attempted to added as listen relay
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 2)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.equal(2)
// Wait for peer to be used as a relay
await usingAsRelay(relayLibp2p1, relayLibp2p2)
// Only one will be used for listening
expect(relayLibp2p1.transportManager.listen.callCount).to.equal(1)
// wait for identify for newly dialled peer
await discoveredRelayConfig(relayLibp2p1, relayLibp2p3)
// Disconnect not used listen relay
await relayLibp2p1.hangUp(relayLibp2p3.peerId)
expect(autoRelay1._listenRelays.size).to.equal(1)
expect(relayLibp2p1.connectionManager.size).to.equal(1)
// Stub dial
sinon.stub(relayLibp2p1, 'dial').callsFake(() => {
defer.resolve()
deferred.resolve()
return Promise.reject(new Error('failed to dial'))
})
// Remove peer used as relay from peerStore and disconnect it
await relayLibp2p1.peerStore.delete(relayLibp2p2.peerId)
await relayLibp2p1.hangUp(relayLibp2p2.peerId)
expect(autoRelay1._listenRelays.size).to.equal(0)
await relayLibp2p1.peerStore.delete(relayLibp2p2.peerId)
expect(relayLibp2p1.connectionManager.size).to.equal(0)
// Wait for failed dial
await defer.promise
await deferred.promise
})
})
@ -424,8 +342,6 @@ describe('auto-relay', () => {
let relayLibp2p1
let relayLibp2p2
let relayLibp2p3
let autoRelay1
let autoRelay2
beforeEach(async () => {
const peerIds = await createPeerId({ number: 3 })
@ -458,9 +374,6 @@ describe('auto-relay', () => {
peerId
})
})
autoRelay1 = relayLibp2p1.relay._autoRelay
autoRelay2 = relayLibp2p2.relay._autoRelay
})
beforeEach(() => {
@ -474,25 +387,15 @@ describe('auto-relay', () => {
})
it('should not add listener to a already relayed connection', async () => {
// Spy if a connected peer is being added as listen relay
sinon.spy(autoRelay1, '_addListenRelay')
sinon.spy(autoRelay2, '_addListenRelay')
// Relay 1 discovers Relay 3 and connect
await relayLibp2p1.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p1.dial(relayLibp2p3.peerId)
// Wait for peer added as listen relay
await pWaitFor(() => autoRelay1._addListenRelay.callCount === 1)
expect(autoRelay1._listenRelays.size).to.equal(1)
await usingAsRelay(relayLibp2p1, relayLibp2p3)
// Relay 2 discovers Relay 3 and connect
await relayLibp2p2.peerStore.addressBook.add(relayLibp2p3.peerId, relayLibp2p3.multiaddrs)
await relayLibp2p2.dial(relayLibp2p3.peerId)
// Wait for peer added as listen relay
await pWaitFor(() => autoRelay2._addListenRelay.callCount === 1)
await pWaitFor(() => autoRelay2._listenRelays.size === 1)
await usingAsRelay(relayLibp2p2, relayLibp2p3)
// Relay 1 discovers Relay 2 relayed multiaddr via Relay 3
const ma2RelayedBy3 = relayLibp2p2.multiaddrs[relayLibp2p2.multiaddrs.length - 1]
@ -500,8 +403,9 @@ describe('auto-relay', () => {
await relayLibp2p1.dial(relayLibp2p2.peerId)
// Peer not added as listen relay
expect(autoRelay1._addListenRelay.callCount).to.equal(1)
expect(autoRelay1._listenRelays.size).to.equal(1)
await expect(usingAsRelay(relayLibp2p1, relayLibp2p2, {
timeout: 1000
})).to.eventually.be.rejected()
})
})