test: add tests for DialRequest

This commit is contained in:
Jacob Heun 2019-12-06 14:26:21 +01:00
parent 3b06283ad8
commit c7dcfe5e48
No known key found for this signature in database
GPG Key ID: CA5A94C15809879F
2 changed files with 181 additions and 2 deletions

View File

@ -14,7 +14,7 @@ class DialRequest {
*
* @param {object} options
* @param {Multiaddr[]} options.addrs
* @param {TransportManager} options.transportManager
* @param {function(Multiaddr):Promise<Connection>} options.dialAction
* @param {Dialer} options.dialer
*/
constructor ({
@ -31,7 +31,6 @@ class DialRequest {
* @async
* @param {object} options
* @param {AbortSignal} options.signal An AbortController signal
* @param {number} options.timeout The max dial time for each request
* @returns {Connection}
*/
async run (options) {

View File

@ -0,0 +1,180 @@
'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 { AbortError } = require('libp2p-interfaces/src/transport/errors')
const AbortController = require('abort-controller')
const AggregateError = require('aggregate-error')
const pDefer = require('p-defer')
const delay = require('delay')
const { DialRequest } = require('../../src/dialer/dial-request')
const createMockConnection = require('../utils/mockConnection')
describe('Dial Request', () => {
it('should end when a single multiaddr dials succeeds', async () => {
const mockConnection = await createMockConnection()
const actions = {
[1]: () => Promise.reject(),
[2]: () => Promise.resolve(mockConnection),
[3]: () => Promise.reject()
}
const dialAction = (num) => actions[num]()
const tokens = ['a', 'b']
const controller = new AbortController()
const dialer = {
getTokens: () => [...tokens],
releaseToken: () => {}
}
const dialRequest = new DialRequest({
addrs: Object.keys(actions),
dialer,
dialAction
})
sinon.spy(actions, 1)
sinon.spy(actions, 2)
sinon.spy(actions, 3)
sinon.spy(dialer, 'releaseToken')
const result = await dialRequest.run({ signal: controller.signal })
expect(result).to.equal(mockConnection)
expect(actions[1]).to.have.property('callCount', 1)
expect(actions[2]).to.have.property('callCount', 1)
expect(actions[3]).to.have.property('callCount', 0)
expect(dialer.releaseToken).to.have.property('callCount', tokens.length)
})
it('should throw an AggregateError if all dials fail', async () => {
const actions = {
[1]: () => Promise.reject(),
[2]: () => Promise.reject(),
[3]: () => Promise.reject()
}
const dialAction = (num) => actions[num]()
const addrs = Object.keys(actions)
const tokens = ['a', 'b']
const controller = new AbortController()
const dialer = {
getTokens: () => [...tokens],
releaseToken: () => {}
}
const dialRequest = new DialRequest({
addrs,
dialer,
dialAction
})
sinon.spy(actions, 1)
sinon.spy(actions, 2)
sinon.spy(actions, 3)
sinon.spy(dialer, 'getTokens')
sinon.spy(dialer, 'releaseToken')
try {
await dialRequest.run({ signal: controller.signal })
expect.fail('Should have thrown')
} catch (err) {
expect(err).to.be.an.instanceof(AggregateError)
}
expect(actions[1]).to.have.property('callCount', 1)
expect(actions[2]).to.have.property('callCount', 1)
expect(actions[3]).to.have.property('callCount', 1)
expect(dialer.getTokens.calledWith(addrs.length)).to.equal(true)
expect(dialer.releaseToken).to.have.property('callCount', tokens.length)
})
it('should handle a large number of addrs', async () => {
const reject = sinon.stub().callsFake(() => Promise.reject())
const actions = {}
const addrs = [...new Array(25)].map((_, index) => index + 1)
addrs.forEach(addr => {
actions[addr] = reject
})
const dialAction = (addr) => actions[addr]()
const tokens = ['a', 'b']
const controller = new AbortController()
const dialer = {
getTokens: () => [...tokens],
releaseToken: () => {}
}
const dialRequest = new DialRequest({
addrs,
dialer,
dialAction
})
sinon.spy(dialer, 'releaseToken')
try {
await dialRequest.run({ signal: controller.signal })
expect.fail('Should have thrown')
} catch (err) {
expect(err).to.be.an.instanceof(AggregateError)
}
expect(reject).to.have.property('callCount', addrs.length)
expect(dialer.releaseToken).to.have.property('callCount', tokens.length)
})
it('should abort all dials when its signal is aborted', async () => {
const deferToAbort = ({ signal }) => {
if (signal.aborted) throw new Error('already aborted')
const deferred = pDefer()
const onAbort = () => {
deferred.reject(new AbortError())
signal.removeEventListener('abort', onAbort)
}
signal.addEventListener('abort', onAbort)
return deferred.promise
}
const actions = {
[1]: deferToAbort,
[2]: deferToAbort,
[3]: deferToAbort
}
const dialAction = (num, opts) => actions[num](opts)
const addrs = Object.keys(actions)
const tokens = ['a', 'b']
const controller = new AbortController()
const dialer = {
getTokens: () => [...tokens],
releaseToken: () => {}
}
const dialRequest = new DialRequest({
addrs,
dialer,
dialAction
})
sinon.spy(actions, 1)
sinon.spy(actions, 2)
sinon.spy(actions, 3)
sinon.spy(dialer, 'getTokens')
sinon.spy(dialer, 'releaseToken')
try {
setTimeout(() => controller.abort(), 100)
await dialRequest.run({ signal: controller.signal })
expect.fail('dial should have failed')
} catch (err) {
expect(err).to.be.an.instanceof(AggregateError)
}
expect(actions[1]).to.have.property('callCount', 1)
expect(actions[2]).to.have.property('callCount', 1)
expect(actions[3]).to.have.property('callCount', 1)
expect(dialer.getTokens.calledWith(addrs.length)).to.equal(true)
expect(dialer.releaseToken).to.have.property('callCount', tokens.length)
})
})