Simulator first commit
This commit is contained in:
68
node_modules/backoff/tests/api.js
generated
vendored
Normal file
68
node_modules/backoff/tests/api.js
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Mathieu Turcotte
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
var sinon = require('sinon');
|
||||
|
||||
var backoff = require('../index');
|
||||
|
||||
exports["API"] = {
|
||||
"backoff.fibonnaci should be a function that returns a backoff instance": function(test) {
|
||||
test.ok(backoff.fibonacci, 'backoff.fibonacci should be defined.');
|
||||
test.equal(typeof backoff.fibonacci, 'function',
|
||||
'backoff.fibonacci should be a function.');
|
||||
test.equal(backoff.fibonacci().constructor.name, 'Backoff');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff.exponential should be a function that returns a backoff instance": function(test) {
|
||||
test.ok(backoff.exponential, 'backoff.exponential should be defined.');
|
||||
test.equal(typeof backoff.exponential, 'function',
|
||||
'backoff.exponential should be a function.');
|
||||
test.equal(backoff.exponential().constructor.name, 'Backoff');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff.call should be a function that returns a FunctionCall instance": function(test) {
|
||||
var fn = function() {};
|
||||
var callback = function() {};
|
||||
test.ok(backoff.Backoff, 'backoff.call should be defined.');
|
||||
test.equal(typeof backoff.call, 'function',
|
||||
'backoff.call should be a function.');
|
||||
test.equal(backoff.call(fn, 1, 2, 3, callback).constructor.name,
|
||||
'FunctionCall');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff.Backoff should be defined and a function": function(test) {
|
||||
test.ok(backoff.Backoff, 'backoff.Backoff should be defined.');
|
||||
test.equal(typeof backoff.Backoff, 'function',
|
||||
'backoff.Backoff should be a function.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff.FunctionCall should be defined and a function": function(test) {
|
||||
test.ok(backoff.FunctionCall,
|
||||
'backoff.FunctionCall should be defined.');
|
||||
test.equal(typeof backoff.FunctionCall, 'function',
|
||||
'backoff.FunctionCall should be a function.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff.FibonacciStrategy should be defined and a function": function(test) {
|
||||
test.ok(backoff.FibonacciStrategy,
|
||||
'backoff.FibonacciStrategy should be defined.');
|
||||
test.equal(typeof backoff.FibonacciStrategy, 'function',
|
||||
'backoff.FibonacciStrategy should be a function.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff.ExponentialStrategy should be defined and a function": function(test) {
|
||||
test.ok(backoff.ExponentialStrategy,
|
||||
'backoff.ExponentialStrategy should be defined.');
|
||||
test.equal(typeof backoff.ExponentialStrategy, 'function',
|
||||
'backoff.ExponentialStrategy should be a function.');
|
||||
test.done();
|
||||
}
|
||||
};
|
166
node_modules/backoff/tests/backoff.js
generated
vendored
Normal file
166
node_modules/backoff/tests/backoff.js
generated
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Mathieu Turcotte
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
var sinon = require('sinon');
|
||||
|
||||
var Backoff = require('../lib/backoff');
|
||||
var BackoffStrategy = require('../lib/strategy/strategy');
|
||||
|
||||
exports["Backoff"] = {
|
||||
setUp: function(callback) {
|
||||
this.backoffStrategy = sinon.stub(new BackoffStrategy());
|
||||
this.backoff = new Backoff(this.backoffStrategy);
|
||||
this.clock = sinon.useFakeTimers();
|
||||
this.spy = new sinon.spy();
|
||||
callback();
|
||||
},
|
||||
|
||||
tearDown: function(callback) {
|
||||
this.clock.restore();
|
||||
callback();
|
||||
},
|
||||
|
||||
"the backoff event should be emitted when backoff starts": function(test) {
|
||||
this.backoffStrategy.next.returns(10);
|
||||
this.backoff.on('backoff', this.spy);
|
||||
|
||||
this.backoff.backoff();
|
||||
|
||||
test.ok(this.spy.calledOnce,
|
||||
'Backoff event should be emitted when backoff starts.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the ready event should be emitted on backoff completion": function(test) {
|
||||
this.backoffStrategy.next.returns(10);
|
||||
this.backoff.on('ready', this.spy);
|
||||
|
||||
this.backoff.backoff();
|
||||
this.clock.tick(10);
|
||||
|
||||
test.ok(this.spy.calledOnce,
|
||||
'Ready event should be emitted when backoff ends.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the backoff event should be passed the backoff delay": function(test) {
|
||||
this.backoffStrategy.next.returns(989);
|
||||
this.backoff.on('backoff', this.spy);
|
||||
|
||||
this.backoff.backoff();
|
||||
|
||||
test.equal(this.spy.getCall(0).args[1], 989, 'Backoff event should ' +
|
||||
'carry the backoff delay as its second argument.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the ready event should be passed the backoff delay": function(test) {
|
||||
this.backoffStrategy.next.returns(989);
|
||||
this.backoff.on('ready', this.spy);
|
||||
|
||||
this.backoff.backoff();
|
||||
this.clock.tick(989);
|
||||
|
||||
test.equal(this.spy.getCall(0).args[1], 989, 'Ready event should ' +
|
||||
'carry the backoff delay as its second argument.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the fail event should be emitted when backoff limit is reached": function(test) {
|
||||
var err = new Error('Fail');
|
||||
|
||||
this.backoffStrategy.next.returns(10);
|
||||
this.backoff.on('fail', this.spy);
|
||||
|
||||
this.backoff.failAfter(2);
|
||||
|
||||
// Consume first 2 backoffs.
|
||||
for (var i = 0; i < 2; i++) {
|
||||
this.backoff.backoff();
|
||||
this.clock.tick(10);
|
||||
}
|
||||
|
||||
// Failure should occur on the third call, and not before.
|
||||
test.ok(!this.spy.calledOnce, 'Fail event shouldn\'t have been emitted.');
|
||||
this.backoff.backoff(err);
|
||||
test.ok(this.spy.calledOnce, 'Fail event should have been emitted.');
|
||||
test.equal(this.spy.getCall(0).args[0], err, 'Error should be passed');
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"calling backoff while a backoff is in progress should throw an error": function(test) {
|
||||
this.backoffStrategy.next.returns(10);
|
||||
var backoff = this.backoff;
|
||||
|
||||
backoff.backoff();
|
||||
|
||||
test.throws(function() {
|
||||
backoff.backoff();
|
||||
}, /in progress/);
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff limit should be greater than 0": function(test) {
|
||||
var backoff = this.backoff;
|
||||
test.throws(function() {
|
||||
backoff.failAfter(0);
|
||||
}, /greater than 0 but got 0/);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"reset should cancel any backoff in progress": function(test) {
|
||||
this.backoffStrategy.next.returns(10);
|
||||
this.backoff.on('ready', this.spy);
|
||||
|
||||
this.backoff.backoff();
|
||||
|
||||
this.backoff.reset();
|
||||
this.clock.tick(100); // 'ready' should not be emitted.
|
||||
|
||||
test.equals(this.spy.callCount, 0, 'Reset should have aborted the backoff.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"reset should reset the backoff strategy": function(test) {
|
||||
this.backoff.reset();
|
||||
test.ok(this.backoffStrategy.reset.calledOnce,
|
||||
'The backoff strategy should have been resetted.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff should be reset after fail": function(test) {
|
||||
this.backoffStrategy.next.returns(10);
|
||||
|
||||
this.backoff.failAfter(1);
|
||||
|
||||
this.backoff.backoff();
|
||||
this.clock.tick(10);
|
||||
this.backoff.backoff();
|
||||
|
||||
test.ok(this.backoffStrategy.reset.calledOnce,
|
||||
'Backoff should have been resetted after failure.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the backoff number should increase from 0 to N - 1": function(test) {
|
||||
this.backoffStrategy.next.returns(10);
|
||||
this.backoff.on('backoff', this.spy);
|
||||
|
||||
var expectedNumbers = [0, 1, 2, 3, 4];
|
||||
var actualNumbers = [];
|
||||
|
||||
for (var i = 0; i < expectedNumbers.length; i++) {
|
||||
this.backoff.backoff();
|
||||
this.clock.tick(10);
|
||||
actualNumbers.push(this.spy.getCall(i).args[0]);
|
||||
}
|
||||
|
||||
test.deepEqual(expectedNumbers, actualNumbers,
|
||||
'Backoff number should increase from 0 to N - 1.');
|
||||
test.done();
|
||||
}
|
||||
};
|
123
node_modules/backoff/tests/backoff_strategy.js
generated
vendored
Normal file
123
node_modules/backoff/tests/backoff_strategy.js
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Mathieu Turcotte
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
var sinon = require('sinon');
|
||||
var util = require('util');
|
||||
|
||||
var BackoffStrategy = require('../lib/strategy/strategy');
|
||||
|
||||
function SampleBackoffStrategy(options) {
|
||||
BackoffStrategy.call(this, options);
|
||||
}
|
||||
util.inherits(SampleBackoffStrategy, BackoffStrategy);
|
||||
|
||||
SampleBackoffStrategy.prototype.next_ = function() {
|
||||
return this.getInitialDelay();
|
||||
};
|
||||
|
||||
SampleBackoffStrategy.prototype.reset_ = function() {};
|
||||
|
||||
exports["BackoffStrategy"] = {
|
||||
setUp: function(callback) {
|
||||
this.random = sinon.stub(Math, 'random');
|
||||
callback();
|
||||
},
|
||||
|
||||
tearDown: function(callback) {
|
||||
this.random.restore();
|
||||
callback();
|
||||
},
|
||||
|
||||
"the randomisation factor should be between 0 and 1": function(test) {
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
randomisationFactor: -0.1
|
||||
});
|
||||
});
|
||||
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
randomisationFactor: 1.1
|
||||
});
|
||||
});
|
||||
|
||||
test.doesNotThrow(function() {
|
||||
new BackoffStrategy({
|
||||
randomisationFactor: 0.5
|
||||
});
|
||||
});
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the raw delay should be randomized based on the randomisation factor": function(test) {
|
||||
var strategy = new SampleBackoffStrategy({
|
||||
randomisationFactor: 0.5,
|
||||
initialDelay: 1000
|
||||
});
|
||||
this.random.returns(0.5);
|
||||
|
||||
var backoffDelay = strategy.next();
|
||||
|
||||
test.equals(backoffDelay, 1000 + (1000 * 0.5 * 0.5));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the initial backoff delay should be greater than 0": function(test) {
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
initialDelay: -1
|
||||
});
|
||||
});
|
||||
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
initialDelay: 0
|
||||
});
|
||||
});
|
||||
|
||||
test.doesNotThrow(function() {
|
||||
new BackoffStrategy({
|
||||
initialDelay: 1
|
||||
});
|
||||
});
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the maximal backoff delay should be greater than 0": function(test) {
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
maxDelay: -1
|
||||
});
|
||||
});
|
||||
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
maxDelay: 0
|
||||
});
|
||||
});
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"the maximal backoff delay should be greater than the initial backoff delay": function(test) {
|
||||
test.throws(function() {
|
||||
new BackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 10
|
||||
});
|
||||
});
|
||||
|
||||
test.doesNotThrow(function() {
|
||||
new BackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 11
|
||||
});
|
||||
});
|
||||
|
||||
test.done();
|
||||
}
|
||||
};
|
61
node_modules/backoff/tests/exponential_backoff_strategy.js
generated
vendored
Normal file
61
node_modules/backoff/tests/exponential_backoff_strategy.js
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Mathieu Turcotte
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
var sinon = require('sinon');
|
||||
|
||||
var ExponentialBackoffStrategy = require('../lib/strategy/exponential');
|
||||
|
||||
exports["ExponentialBackoffStrategy"] = {
|
||||
|
||||
"backoff delays should follow an exponential sequence": function(test) {
|
||||
var strategy = new ExponentialBackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 1000
|
||||
});
|
||||
|
||||
// Exponential sequence: x[i] = x[i-1] * 2.
|
||||
var expectedDelays = [10, 20, 40, 80, 160, 320, 640, 1000, 1000];
|
||||
var actualDelays = expectedDelays.map(function () {
|
||||
return strategy.next();
|
||||
});
|
||||
|
||||
test.deepEqual(expectedDelays, actualDelays,
|
||||
'Generated delays should follow an exponential sequence.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff delay factor should be configurable": function (test) {
|
||||
var strategy = new ExponentialBackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 270,
|
||||
factor: 3
|
||||
});
|
||||
|
||||
// Exponential sequence: x[i] = x[i-1] * 3.
|
||||
var expectedDelays = [10, 30, 90, 270, 270];
|
||||
var actualDelays = expectedDelays.map(function () {
|
||||
return strategy.next();
|
||||
});
|
||||
|
||||
test.deepEqual(expectedDelays, actualDelays,
|
||||
'Generated delays should follow a configurable exponential sequence.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff delays should restart from the initial delay after reset": function(test) {
|
||||
var strategy = new ExponentialBackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 1000
|
||||
});
|
||||
|
||||
strategy.next();
|
||||
strategy.reset();
|
||||
|
||||
var backoffDelay = strategy.next();
|
||||
test.equals(backoffDelay, 10,
|
||||
'Strategy should return the initial delay after reset.');
|
||||
test.done();
|
||||
}
|
||||
};
|
47
node_modules/backoff/tests/fibonacci_backoff_strategy.js
generated
vendored
Normal file
47
node_modules/backoff/tests/fibonacci_backoff_strategy.js
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Mathieu Turcotte
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
var sinon = require('sinon');
|
||||
|
||||
var FibonacciBackoffStrategy = require('../lib/strategy/fibonacci');
|
||||
|
||||
exports["FibonacciBackoffStrategy"] = {
|
||||
setUp: function(callback) {
|
||||
this.strategy = new FibonacciBackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 1000
|
||||
});
|
||||
callback();
|
||||
},
|
||||
|
||||
"backoff delays should follow a Fibonacci sequence": function(test) {
|
||||
// Fibonacci sequence: x[i] = x[i-1] + x[i-2].
|
||||
var expectedDelays = [10, 10, 20, 30, 50, 80, 130, 210, 340, 550, 890, 1000];
|
||||
var actualDelays = [];
|
||||
|
||||
for (var i = 0; i < expectedDelays.length; i++) {
|
||||
actualDelays.push(this.strategy.next());
|
||||
}
|
||||
|
||||
test.deepEqual(expectedDelays, actualDelays,
|
||||
'Generated delays should follow a Fibonacci sequence.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff delays should restart from the initial delay after reset": function(test) {
|
||||
var strategy = new FibonacciBackoffStrategy({
|
||||
initialDelay: 10,
|
||||
maxDelay: 1000
|
||||
});
|
||||
|
||||
strategy.next();
|
||||
strategy.reset();
|
||||
|
||||
var backoffDelay = strategy.next();
|
||||
test.equals(backoffDelay, 10,
|
||||
'Strategy should return the initial delay after reset.');
|
||||
test.done();
|
||||
}
|
||||
};
|
406
node_modules/backoff/tests/function_call.js
generated
vendored
Normal file
406
node_modules/backoff/tests/function_call.js
generated
vendored
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Mathieu Turcotte
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
var events = require('events');
|
||||
var sinon = require('sinon');
|
||||
var util = require('util');
|
||||
|
||||
var FunctionCall = require('../lib/function_call');
|
||||
|
||||
function MockBackoff() {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.reset = sinon.spy();
|
||||
this.backoff = sinon.spy();
|
||||
this.failAfter = sinon.spy();
|
||||
}
|
||||
util.inherits(MockBackoff, events.EventEmitter);
|
||||
|
||||
exports["FunctionCall"] = {
|
||||
setUp: function(callback) {
|
||||
this.wrappedFn = sinon.stub();
|
||||
this.callback = sinon.stub();
|
||||
this.backoff = new MockBackoff();
|
||||
this.backoffFactory = sinon.stub();
|
||||
this.backoffFactory.returns(this.backoff);
|
||||
callback();
|
||||
},
|
||||
|
||||
tearDown: function(callback) {
|
||||
callback();
|
||||
},
|
||||
|
||||
"constructor's first argument should be a function": function(test) {
|
||||
test.throws(function() {
|
||||
new FunctionCall(1, [], function() {});
|
||||
}, /Expected fn to be a function./);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"constructor's last argument should be a function": function(test) {
|
||||
test.throws(function() {
|
||||
new FunctionCall(function() {}, [], 3);
|
||||
}, /Expected callback to be a function./);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"isPending should return false once the call is started": function(test) {
|
||||
this.wrappedFn.
|
||||
onFirstCall().yields(new Error()).
|
||||
onSecondCall().yields(null, 'Success!');
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
|
||||
test.ok(call.isPending());
|
||||
|
||||
call.start(this.backoffFactory);
|
||||
test.ok(!call.isPending());
|
||||
|
||||
this.backoff.emit('ready');
|
||||
test.ok(!call.isPending());
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"isRunning should return true when call is in progress": function(test) {
|
||||
this.wrappedFn.
|
||||
onFirstCall().yields(new Error()).
|
||||
onSecondCall().yields(null, 'Success!');
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
|
||||
test.ok(!call.isRunning());
|
||||
|
||||
call.start(this.backoffFactory);
|
||||
test.ok(call.isRunning());
|
||||
|
||||
this.backoff.emit('ready');
|
||||
test.ok(!call.isRunning());
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"isCompleted should return true once the call completes": function(test) {
|
||||
this.wrappedFn.
|
||||
onFirstCall().yields(new Error()).
|
||||
onSecondCall().yields(null, 'Success!');
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
|
||||
test.ok(!call.isCompleted());
|
||||
|
||||
call.start(this.backoffFactory);
|
||||
test.ok(!call.isCompleted());
|
||||
|
||||
this.backoff.emit('ready');
|
||||
test.ok(call.isCompleted());
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"isAborted should return true once the call is aborted": function(test) {
|
||||
this.wrappedFn.
|
||||
onFirstCall().yields(new Error()).
|
||||
onSecondCall().yields(null, 'Success!');
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
|
||||
test.ok(!call.isAborted());
|
||||
call.abort();
|
||||
test.ok(call.isAborted());
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"setStrategy should overwrite the default strategy": function(test) {
|
||||
var replacementStrategy = {};
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
call.setStrategy(replacementStrategy);
|
||||
call.start(this.backoffFactory);
|
||||
test.ok(this.backoffFactory.calledWith(replacementStrategy),
|
||||
'User defined strategy should be used to instantiate ' +
|
||||
'the backoff instance.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"setStrategy should throw if the call is in progress": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
call.start(this.backoffFactory);
|
||||
test.throws(function() {
|
||||
call.setStrategy({});
|
||||
}, /in progress/);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"failAfter should not be set by default": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
call.start(this.backoffFactory);
|
||||
test.equal(0, this.backoff.failAfter.callCount);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"failAfter should be used as the maximum number of backoffs": function(test) {
|
||||
var failAfterValue = 99;
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
call.failAfter(failAfterValue);
|
||||
call.start(this.backoffFactory);
|
||||
test.ok(this.backoff.failAfter.calledWith(failAfterValue),
|
||||
'User defined maximum number of backoffs shoud be ' +
|
||||
'used to configure the backoff instance.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"failAfter should throw if the call is in progress": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
call.start(this.backoffFactory);
|
||||
test.throws(function() {
|
||||
call.failAfter(1234);
|
||||
}, /in progress/);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"start shouldn't allow overlapping invocation": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
var backoffFactory = this.backoffFactory;
|
||||
|
||||
call.start(backoffFactory);
|
||||
test.throws(function() {
|
||||
call.start(backoffFactory);
|
||||
}, /already started/);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"start shouldn't allow invocation of aborted call": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
var backoffFactory = this.backoffFactory;
|
||||
|
||||
call.abort();
|
||||
test.throws(function() {
|
||||
call.start(backoffFactory);
|
||||
}, /aborted/);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"call should forward its arguments to the wrapped function": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
|
||||
call.start(this.backoffFactory);
|
||||
test.ok(this.wrappedFn.calledWith(1, 2, 3));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"call should complete when the wrapped function succeeds": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
|
||||
this.wrappedFn.
|
||||
onCall(0).yields(new Error()).
|
||||
onCall(1).yields(new Error()).
|
||||
onCall(2).yields(new Error()).
|
||||
onCall(3).yields(null, 'Success!');
|
||||
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
this.backoff.emit('ready');
|
||||
}
|
||||
|
||||
test.equals(this.callback.callCount, 0);
|
||||
this.backoff.emit('ready');
|
||||
|
||||
test.ok(this.callback.calledWith(null, 'Success!'));
|
||||
test.ok(this.wrappedFn.alwaysCalledWith(1, 2, 3));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"call should fail when the backoff limit is reached": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
|
||||
var error = new Error();
|
||||
this.wrappedFn.yields(error);
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
for (var i = 0; i < 3; i++) {
|
||||
this.backoff.emit('ready');
|
||||
}
|
||||
|
||||
test.equals(this.callback.callCount, 0);
|
||||
|
||||
this.backoff.emit('fail');
|
||||
|
||||
test.ok(this.callback.calledWith(error));
|
||||
test.ok(this.wrappedFn.alwaysCalledWith(1, 2, 3));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"call should fail when the retry predicate returns false": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
|
||||
call.retryIf(function(err) { return err.retriable; });
|
||||
|
||||
var retriableError = new Error();
|
||||
retriableError.retriable = true;
|
||||
|
||||
var fatalError = new Error();
|
||||
fatalError.retriable = false;
|
||||
|
||||
this.wrappedFn.
|
||||
onCall(0).yields(retriableError).
|
||||
onCall(1).yields(retriableError).
|
||||
onCall(2).yields(fatalError);
|
||||
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
this.backoff.emit('ready');
|
||||
}
|
||||
|
||||
test.equals(this.callback.callCount, 1);
|
||||
test.ok(this.callback.calledWith(fatalError));
|
||||
test.ok(this.wrappedFn.alwaysCalledWith(1, 2, 3));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"wrapped function's callback shouldn't be called after abort": function(test) {
|
||||
var call = new FunctionCall(function(callback) {
|
||||
call.abort(); // Abort in middle of wrapped function's execution.
|
||||
callback(null, 'ok');
|
||||
}, [], this.callback);
|
||||
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
test.equals(this.callback.callCount, 1,
|
||||
'Wrapped function\'s callback shouldn\'t be called after abort.');
|
||||
test.ok(this.callback.calledWithMatch(sinon.match(function (err) {
|
||||
return !!err.message.match(/Backoff aborted/);
|
||||
}, "abort error")));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"abort event is emitted once when abort is called": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
this.wrappedFn.yields(new Error());
|
||||
var callEventSpy = sinon.spy();
|
||||
|
||||
call.on('abort', callEventSpy);
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
call.abort();
|
||||
call.abort();
|
||||
call.abort();
|
||||
|
||||
test.equals(callEventSpy.callCount, 1);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"getLastResult should return the last intermediary result": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
this.wrappedFn.yields(1);
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
for (var i = 2; i < 5; i++) {
|
||||
this.wrappedFn.yields(i);
|
||||
this.backoff.emit('ready');
|
||||
test.deepEqual([i], call.getLastResult());
|
||||
}
|
||||
|
||||
this.wrappedFn.yields(null);
|
||||
this.backoff.emit('ready');
|
||||
test.deepEqual([null], call.getLastResult());
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"getNumRetries should return the number of retries": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [], this.callback);
|
||||
|
||||
this.wrappedFn.yields(1);
|
||||
call.start(this.backoffFactory);
|
||||
// The inital call doesn't count as a retry.
|
||||
test.equals(0, call.getNumRetries());
|
||||
|
||||
for (var i = 2; i < 5; i++) {
|
||||
this.wrappedFn.yields(i);
|
||||
this.backoff.emit('ready');
|
||||
test.equals(i - 1, call.getNumRetries());
|
||||
}
|
||||
|
||||
this.wrappedFn.yields(null);
|
||||
this.backoff.emit('ready');
|
||||
test.equals(4, call.getNumRetries());
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"wrapped function's errors should be propagated": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
|
||||
this.wrappedFn.throws(new Error());
|
||||
test.throws(function() {
|
||||
call.start(this.backoffFactory);
|
||||
}, Error);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"wrapped callback's errors should be propagated": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
|
||||
this.wrappedFn.yields(null, 'Success!');
|
||||
this.callback.throws(new Error());
|
||||
test.throws(function() {
|
||||
call.start(this.backoffFactory);
|
||||
}, Error);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"call event should be emitted when wrapped function gets called": function(test) {
|
||||
this.wrappedFn.yields(1);
|
||||
var callEventSpy = sinon.spy();
|
||||
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 'two'], this.callback);
|
||||
call.on('call', callEventSpy);
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
for (var i = 1; i < 5; i++) {
|
||||
this.backoff.emit('ready');
|
||||
}
|
||||
|
||||
test.equal(5, callEventSpy.callCount,
|
||||
'The call event should have been emitted 5 times.');
|
||||
test.deepEqual([1, 'two'], callEventSpy.getCall(0).args,
|
||||
'The call event should carry function\'s args.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"callback event should be emitted when callback is called": function(test) {
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 'two'], this.callback);
|
||||
var callbackSpy = sinon.spy();
|
||||
call.on('callback', callbackSpy);
|
||||
|
||||
this.wrappedFn.yields('error');
|
||||
call.start(this.backoffFactory);
|
||||
|
||||
this.wrappedFn.yields(null, 'done');
|
||||
this.backoff.emit('ready');
|
||||
|
||||
test.equal(2, callbackSpy.callCount,
|
||||
'Callback event should have been emitted 2 times.');
|
||||
test.deepEqual(['error'], callbackSpy.firstCall.args,
|
||||
'First callback event should carry first call\'s results.');
|
||||
test.deepEqual([null, 'done'], callbackSpy.secondCall.args,
|
||||
'Second callback event should carry second call\'s results.');
|
||||
test.done();
|
||||
},
|
||||
|
||||
"backoff event should be emitted on backoff start": function(test) {
|
||||
var err = new Error('backoff event error');
|
||||
var call = new FunctionCall(this.wrappedFn, [1, 'two'], this.callback);
|
||||
var backoffSpy = sinon.spy();
|
||||
|
||||
call.on('backoff', backoffSpy);
|
||||
|
||||
this.wrappedFn.yields(err);
|
||||
call.start(this.backoffFactory);
|
||||
this.backoff.emit('backoff', 3, 1234, err);
|
||||
|
||||
test.ok(this.backoff.backoff.calledWith(err),
|
||||
'The backoff instance should have been called with the error.');
|
||||
test.equal(1, backoffSpy.callCount,
|
||||
'Backoff event should have been emitted 1 time.');
|
||||
test.deepEqual([3, 1234, err], backoffSpy.firstCall.args,
|
||||
'Backoff event should carry the backoff number, delay and error.');
|
||||
test.done();
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user