Name: js-handler/node_modules/restify/node_modules/backoff/tests/function_call.js 
1:
/*
2:
 * Copyright (c) 2012 Mathieu Turcotte
3:
 * Licensed under the MIT license.
4:
 */
5:
 
6:
var assert = require('assert');
7:
var events = require('events');
8:
var sinon = require('sinon');
9:
var util = require('util');
10:
 
11:
var FunctionCall = require('../lib/function_call');
12:
 
13:
function MockBackoff() {
14:
    events.EventEmitter.call(this);
15:
 
16:
    this.reset = sinon.spy();
17:
    this.backoff = sinon.spy();
18:
    this.failAfter = sinon.spy();
19:
}
20:
util.inherits(MockBackoff, events.EventEmitter);
21:
 
22:
exports["FunctionCall"] = {
23:
    setUp: function(callback) {
24:
        this.wrappedFn = sinon.stub();
25:
        this.callback = sinon.stub();
26:
        this.backoff = new MockBackoff();
27:
        this.backoffFactory = sinon.stub();
28:
        this.backoffFactory.returns(this.backoff);
29:
        callback();
30:
    },
31:
 
32:
    tearDown: function(callback) {
33:
        callback();
34:
    },
35:
 
36:
    "constructor's first argument should be a function": function(test) {
37:
        test.throws(function() {
38:
            new FunctionCall(1, [], function() {});
39:
        }, /should be a function/);
40:
        test.done();
41:
    },
42:
 
43:
    "constructor's last argument should be a function": function(test) {
44:
        test.throws(function() {
45:
            new FunctionCall(function() {}, [], 3);
46:
        }, /should be a function/);
47:
        test.done();
48:
    },
49:
 
50:
    "setStrategy should overwrite the default strategy": function(test) {
51:
        var replacementStrategy = {};
52:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
53:
        call.setStrategy(replacementStrategy);
54:
        call.start(this.backoffFactory);
55:
        test.ok(this.backoffFactory.calledWith(replacementStrategy),
56:
            'User defined strategy should be used to instantiate ' +
57:
            'the backoff instance.');
58:
        test.done();
59:
    },
60:
 
61:
    "setStrategy should throw if the call is in progress": function(test) {
62:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
63:
        call.start(this.backoffFactory);
64:
        test.throws(function() {
65:
            call.setStrategy({});
66:
        }, /in progress/);
67:
        test.done();
68:
    },
69:
 
70:
    "failAfter should not be set by default": function(test) {
71:
    var call = new FunctionCall(this.wrappedFn, [], this.callback);
72:
        call.start(this.backoffFactory);
73:
        test.equal(0, this.backoff.failAfter.callCount);
74:
    test.done();
75:
  },
76:
 
77:
    "failAfter should be used as the maximum number of backoffs": function(test) {
78:
        var failAfterValue = 99;
79:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
80:
        call.failAfter(failAfterValue);
81:
        call.start(this.backoffFactory);
82:
        test.ok(this.backoff.failAfter.calledWith(failAfterValue),
83:
            'User defined maximum number of backoffs shoud be ' +
84:
            'used to configure the backoff instance.');
85:
        test.done();
86:
    },
87:
 
88:
    "failAfter should throw if the call is in progress": function(test) {
89:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
90:
        call.start(this.backoffFactory);
91:
        test.throws(function() {
92:
            call.failAfter(1234);
93:
        }, /in progress/);
94:
        test.done();
95:
    },
96:
 
97:
    "start shouldn't allow overlapping invocation": function(test) {
98:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
99:
        var backoffFactory = this.backoffFactory;
100:
 
101:
        call.start(backoffFactory);
102:
        test.throws(function() {
103:
            call.start(backoffFactory);
104:
        }, /in progress/);
105:
        test.done();
106:
    },
107:
 
108:
    "call should forward its arguments to the wrapped function": function(test) {
109:
        var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
110:
        call.start(this.backoffFactory);
111:
        test.ok(this.wrappedFn.calledWith(1, 2, 3));
112:
        test.done();
113:
    },
114:
 
115:
    "call should complete when the wrapped function succeeds": function(test) {
116:
        var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
117:
        this.wrappedFn.yields(new Error())
118:
            .yields(new Error())
119:
            .yields(new Error())
120:
            .yields(null, 'Success!');
121:
 
122:
        call.start(this.backoffFactory);
123:
 
124:
        for (var i = 0; i < 2; i++) {
125:
            this.backoff.emit('ready');
126:
        }
127:
 
128:
        test.equals(this.callback.callCount, 0);
129:
        this.backoff.emit('ready');
130:
 
131:
        test.ok(this.callback.calledWith(null, 'Success!'));
132:
        test.ok(this.wrappedFn.alwaysCalledWith(1, 2, 3));
133:
        test.done();
134:
    },
135:
 
136:
    "call should fail when the backoff limit is reached": function(test) {
137:
        var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
138:
        var error = new Error();
139:
        this.wrappedFn.yields(error);
140:
        call.start(this.backoffFactory);
141:
 
142:
        for (var i = 0; i < 3; i++) {
143:
            this.backoff.emit('ready');
144:
        }
145:
 
146:
        test.equals(this.callback.callCount, 0);
147:
 
148:
        this.backoff.emit('fail');
149:
 
150:
        test.ok(this.callback.calledWith(error));
151:
        test.ok(this.wrappedFn.alwaysCalledWith(1, 2, 3));
152:
        test.done();
153:
    },
154:
 
155:
    "wrapped function shouldn't be called after abort": function(test) {
156:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
157:
        call.abort();
158:
        call.start(this.backoffFactory);
159:
        test.equals(this.wrappedFn.callCount, 0,
160:
            'Wrapped function shouldn\'t be called after abort.');
161:
        test.done();
162:
    },
163:
 
164:
    "wrapped function's callback shouldn't be called after abort": function(test) {
165:
        var call = new FunctionCall(function(callback) {
166:
            call.abort(); // Abort in middle of wrapped function's execution.
167:
            callback(null, 'ok');
168:
        }, [], this.callback);
169:
 
170:
        call.start(this.backoffFactory);
171:
 
172:
        test.equals(this.callback.callCount, 0,
173:
            'Wrapped function\'s callback shouldn\'t be called after abort.');
174:
        test.done();
175:
    },
176:
 
177:
    "getResults should return intermediary results": function(test) {
178:
        var call = new FunctionCall(this.wrappedFn, [], this.callback);
179:
        this.wrappedFn.yields(1);
180:
        call.start(this.backoffFactory);
181:
 
182:
        for (var i = 2; i < 5; i++) {
183:
            this.wrappedFn.yields(i);
184:
            this.backoff.emit('ready');
185:
        }
186:
 
187:
        this.wrappedFn.yields(null);
188:
        this.backoff.emit('ready');
189:
 
190:
        test.deepEqual([[1], [2], [3], [4], [null]], call.getResults());
191:
        test.done();
192:
    },
193:
 
194:
    "wrapped function's errors should be propagated": function(test) {
195:
        var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
196:
        this.wrappedFn.throws(new Error());
197:
        test.throws(function() {
198:
            call.start(this.backoffFactory);
199:
        }, Error);
200:
        test.done();
201:
    },
202:
 
203:
    "wrapped callback's errors should be propagated": function(test) {
204:
        var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
205:
        this.wrappedFn.yields(null, 'Success!');
206:
        this.callback.throws(new Error());
207:
        test.throws(function() {
208:
            call.start(this.backoffFactory);
209:
        }, Error);
210:
        test.done();
211:
    },
212:
 
213:
    "call event should be emitted when wrapped function gets called": function(test) {
214:
        this.wrappedFn.yields(1);
215:
        var callEventSpy = sinon.spy();
216:
 
217:
        var call = new FunctionCall(this.wrappedFn, [1, 'two'], this.callback);
218:
        call.on('call', callEventSpy);
219:
        call.start(this.backoffFactory);
220:
 
221:
        for (var i = 1; i < 5; i++) {
222:
            this.backoff.emit('ready');
223:
        }
224:
 
225:
        test.equal(5, callEventSpy.callCount,
226:
            'The call event should have been emitted 5 times.');
227:
        test.deepEqual([1, 'two'], callEventSpy.getCall(0).args,
228:
            'The call event should carry function\'s args.');
229:
        test.done();
230:
    },
231:
 
232:
    "callback event should be emitted when callback is called": function(test) {
233:
        var call = new FunctionCall(this.wrappedFn, [1, 'two'], this.callback);
234:
        var callbackSpy = sinon.spy();
235:
        call.on('callback', callbackSpy);
236:
 
237:
        this.wrappedFn.yields('error');
238:
        call.start(this.backoffFactory);
239:
 
240:
        this.wrappedFn.yields(null, 'done');
241:
        this.backoff.emit('ready');
242:
 
243:
        test.equal(2, callbackSpy.callCount,
244:
            'Callback event should have been emitted 2 times.');
245:
        test.deepEqual(['error'], callbackSpy.firstCall.args,
246:
            'First callback event should carry first call\'s results.');
247:
        test.deepEqual([null, 'done'], callbackSpy.secondCall.args,
248:
            'Second callback event should carry second call\'s results.');
249:
        test.done();
250:
    },
251:
 
252:
    "backoff event should be emitted on backoff start": function(test) {
253:
        var err = new Error('backoff event error');
254:
        var call = new FunctionCall(this.wrappedFn, [1, 'two'], this.callback);
255:
        var backoffSpy = sinon.spy();
256:
 
257:
        call.on('backoff', backoffSpy);
258:
 
259:
        this.wrappedFn.yields(err);
260:
        call.start(this.backoffFactory);
261:
        this.backoff.emit('backoff', 3, 1234, err);
262:
 
263:
        test.ok(this.backoff.backoff.calledWith(err),
264:
            'The backoff instance should have been called with the error.');
265:
        test.equal(1, backoffSpy.callCount,
266:
            'Backoff event should have been emitted 1 time.');
267:
        test.deepEqual([3, 1234, err], backoffSpy.firstCall.args,
268:
            'Backoff event should carry the backoff number, delay and error.');
269:
        test.done();
270:
    }
271:
};