四好公路
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
8.1 KiB

3 years ago
var chai = require('chai')
, expect = chai.expect
, Promise = require('bluebird')
, moment = require('moment')
, sinon = require('sinon')
, sinonChai = require('sinon-chai')
, retry = require('../');
chai.use(require('chai-as-promised'));
require('sinon-as-promised')(Promise);
describe('bluebird', function () {
var count
, soRejected
, soResolved;
beforeEach(function () {
count = 0;
soRejected = Math.random().toString();
soResolved = Math.random().toString();
});
it('should reject immediately if max is 1 (using options)', function () {
var callback = sinon.stub();
callback.resolves(soResolved);
callback.onCall(0).rejects(soRejected);
return expect(retry(callback, {max: 1, backoffBase: 0})).to.eventually.be.rejectedWith(soRejected).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should reject immediately if max is 1 (using integer)', function () {
var callback = sinon.stub();
callback.resolves(soResolved);
callback.onCall(0).rejects(soRejected);
return expect(retry(callback, 1)).to.eventually.be.rejectedWith(soRejected).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should reject after all tries if still rejected', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
return expect(retry(callback, {max: 3, backoffBase: 0})).to.eventually.be.rejectedWith(soRejected).then(function () {
expect(callback.firstCall.args).to.deep.equal([{ current: 1 }]);
expect(callback.secondCall.args).to.deep.equal([{ current: 2 }]);
expect(callback.thirdCall.args).to.deep.equal([{ current: 3 }]);
expect(callback.callCount).to.equal(3);
});
});
it('should resolve immediately if resolved on first try', function () {
var callback = sinon.stub();
callback.resolves(soResolved);
callback.onCall(0).resolves(soResolved);
return expect(retry(callback, {max: 10, backoffBase: 0})).to.eventually.equal(soResolved).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should resolve if resolved before hitting max', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
callback.onCall(3).resolves(soResolved);
return expect(retry(callback, {max: 10, backoffBase: 0})).to.eventually.equal(soResolved).then(function () {
expect(callback.firstCall.args).to.deep.equal([{ current: 1 }]);
expect(callback.secondCall.args).to.deep.equal([{ current: 2 }]);
expect(callback.thirdCall.args).to.deep.equal([{ current: 3 }]);
expect(callback.callCount).to.equal(4);
});
});
describe('timeout', function () {
it('should throw if reject on first attempt', function () {
return expect(retry(function () {
return Promise.delay(2000);
}, {
max: 1,
backoffBase: 0,
timeout: 1000
})).to.eventually.be.rejectedWith(Promise.TimeoutError);
});
it('should throw if reject on last attempt', function () {
return expect(retry(function () {
count++;
if (count === 3) {
return Promise.delay(3500);
}
return Promise.reject();
}, {
max: 3,
backoffBase: 0,
timeout: 1500
})).to.eventually.be.rejectedWith(Promise.TimeoutError).then(function () {
expect(count).to.equal(3);
});
});
});
describe('match', function () {
it('should continue retry while error is equal to match string', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
callback.onCall(3).resolves(soResolved);
return expect(retry(callback, {max: 15, backoffBase: 0, match: 'Error: ' + soRejected})).to.eventually.equal(soResolved).then(function () {
expect(callback.callCount).to.equal(4);
});
});
it('should reject immediately if error is not equal to match string', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
return expect(retry(callback, {max: 15, backoffBase: 0, match: 'A custom error string'})).to.eventually.be.rejectedWith(soRejected).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should continue retry while error is instanceof match', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
callback.onCall(4).resolves(soResolved);
return expect(retry(callback, {max: 15, backoffBase: 0, match: Error})).to.eventually.equal(soResolved).then(function () {
expect(callback.callCount).to.equal(5);
});
});
it('should reject immediately if error is not instanceof match', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
return expect(retry(callback, {max: 15, backoffBase: 0, match: function foo(){}})).to.eventually.be.rejectedWith(Error).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should continue retry while error is equal to match string in array', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
callback.onCall(4).resolves(soResolved);
return expect(retry(callback, {max: 15, backoffBase: 0, match: ['Error: ' + (soRejected + 1), 'Error: ' + soRejected]})).to.eventually.equal(soResolved).then(function () {
expect(callback.callCount).to.equal(5);
});
});
it('should reject immediately if error is not equal to match string in array', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
return expect(retry(callback, {max: 15, backoffBase: 0, match: ['Error: ' + (soRejected + 1), 'Error: ' + (soRejected + 2)]})).to.eventually.be.rejectedWith(Error).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should reject immediately if error is not instanceof match in array', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
return expect(retry(callback, {max: 15, backoffBase: 0, match: ['Error: ' + (soRejected + 1), function foo(){}]})).to.eventually.be.rejectedWith(Error).then(function () {
expect(callback.callCount).to.equal(1);
});
});
it('should continue retry while error is instanceof match in array', function () {
var callback = sinon.stub();
callback.rejects(soRejected);
callback.onCall(4).resolves(soResolved);
return expect(retry(callback, {max: 15, backoffBase: 0, match: ['Error: ' + (soRejected + 1), Error]})).to.eventually.equal(soResolved).then(function () {
expect(callback.callCount).to.equal(5);
});
});
});
describe('backoff', function () {
it('should resolve after 5 retries and an eventual delay over 1800ms using default backoff', function () {
var startTime = moment();
var callback = sinon.stub();
callback.rejects(soRejected);
callback.onCall(5).resolves(soResolved);
return expect(retry(callback, {max: 15})).to.eventually.equal(soResolved).then(function () {
expect(callback.callCount).to.equal(6);
expect(moment().diff(startTime)).to.be.above(1800);
expect(moment().diff(startTime)).to.be.below(3400);
});
});
it('should resolve after 1 retry and initial delay equal to the backoffBase', function() {
var initialDelay = 100;
var callback = sinon.stub();
var startTime = moment();
callback.onCall(0).rejects(soRejected);
callback.onCall(1).resolves(soResolved);
return expect(retry(callback, { max: 2, backoffBase: initialDelay, backoffExponent: 3 }))
.to.eventually.equal(soResolved)
.then(function() {
expect(callback.callCount).to.equal(2);
expect(moment().diff(startTime)).to.be.within(initialDelay, initialDelay + 50); // allow for some overhead
});
});
it('should throw TimeoutError and cancel backoff delay if timeout is reached', function () {
return expect(retry(function () {
return Promise.delay(2000);
}, {
max: 15,
timeout: 1000
})).to.eventually.be.rejectedWith(Promise.TimeoutError);
});
});
});