diff --git a/lib/sender.js b/lib/sender.js index 1fd87ed..7a4c24f 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -9,8 +9,15 @@ function Sender(key, options) { return new Sender(key, options); } - this.key = key; - this.options = options || {}; + this.requestOptions = defaultsDeep({ + method: 'POST', + headers: { + 'Authorization': 'key=' + key + }, + uri: Constants.GCM_SEND_URI + }, options, { + timeout: Constants.SOCKET_TIMEOUT + }); } Sender.prototype.send = function(message, recipient, options, callback) { @@ -23,42 +30,15 @@ Sender.prototype.send = function(message, recipient, options, callback) { } options = cleanOptions(options); - if(options.retries == 0) { - return this.sendNoRetry(message, recipient, callback); - } - - var self = this; - - this.sendNoRetry(message, recipient, function(err, response, attemptedRegTokens) { - if (err) { - if (typeof err === 'number' && err > 399 && err < 500) { - debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); - return callback(err); - } - return retry(self, message, recipient, options, callback); + getRequestBody(message, recipient, function(err, body) { + if(err) { + return callback(err); } - if(!response.results) { - return callback(null, response); + if(options.retries == 0) { + return sendMessage(this.requestOptions, body, callback); } - checkForBadTokens(response.results, attemptedRegTokens, function(err, unsentRegTokens, regTokenPositionMap) { - if(err) { - return callback(err); - } - if (unsentRegTokens.length == 0) { - return callback(null, response); - } - - debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); - - retry(self, message, unsentRegTokens, options, function(err, retriedResponse) { - if(err) { - return callback(null, response); - } - response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens); - callback(null, response); - }); - }); - }); + sendMessageWithRetries(this.requestOptions, body, options, callback); + }.bind(this)); }; function cleanOptions(options) { @@ -86,13 +66,86 @@ function cleanOptions(options) { return options; } -function retry(self, message, recipient, options, callback) { - return setTimeout(function() { - self.send(message, recipient, { - retries: options.retries - 1, - backoff: options.backoff * 2 +function getRequestBody(message, recipient, callback) { + var body = cleanParams(message); + + if(typeof recipient == "string") { + body.to = recipient; + return nextTick(callback, null, body); + } + if(Array.isArray(recipient)) { + if(recipient.length < 1) { + return nextTick(callback, new Error('Empty recipient array passed!')); + } + body.registration_ids = recipient; + return nextTick(callback, null, body); + } + return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!')); +} + +function cleanParams(raw) { + var params = {}; + Object.keys(raw).forEach(function(param) { + var paramOptions = messageOptions[param]; + if(!paramOptions) { + return console.warn("node-gcm ignored unknown message parameter " + param); + } + if(paramOptions.__argType != typeof raw[param]) { + return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")"); + } + params[param] = raw[param]; + }); + return params; +} + +function nextTick(func) { + var args = Array.prototype.slice.call(arguments, 1); + process.nextTick(function() { + func.apply(this, args); + }.bind(this)); +} + +function sendMessageWithRetries(requestOptions, body, messageOptions, callback) { + sendMessage(requestOptions, body, function(err, response, attemptedRegTokens) { + if (err) { + if (typeof err === 'number' && err > 399 && err < 500) { + debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); + return callback(err); + } + return retry(requestOptions, body, messageOptions, callback); + } + checkForBadTokens(response.results, attemptedRegTokens, function(err, unsentRegTokens, regTokenPositionMap) { + if(err) { + return callback(err); + } + if (unsentRegTokens.length == 0) { + return callback(null, response); + } + + debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); + + body.registration_ids = unsentRegTokens; + retry(requestOptions, body, messageOptions, function(err, retriedResponse) { + if(err) { + return callback(null, response); + } + response = updateResponse(response, retriedResponse, regTokenPositionMap, unsentRegTokens); + callback(null, response); + }); + }); + }); +} + +function retry(requestOptions, body, messageOptions, callback) { + setTimeout(function() { + if(messageOptions.retries <= 1) { + return sendMessage(requestOptions, body, callback); + } + sendMessageWithRetries(requestOptions, body, { + retries: messageOptions.retries - 1, + backoff: messageOptions.backoff * 2 }, callback); - }, options.backoff); + }, messageOptions.backoff); } function checkForBadTokens(results, originalRecipients, callback) { @@ -125,86 +178,26 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { response.failure -= unsentRegTokens.length - retriedResponse.failure; } -Sender.prototype.sendNoRetry = function(message, recipient, callback) { - if(!callback) { - callback = function() {}; - } - - getRequestBody(message, recipient, function(err, body) { - if(err) { +function sendMessage(requestOptions, body, callback) { + requestOptions.json = body; + request(requestOptions, function (err, res, resBodyJSON) { + if (err) { return callback(err); } - - //Build request options, allowing some to be overridden - var request_options = defaultsDeep({ - method: 'POST', - headers: { - 'Authorization': 'key=' + this.key - }, - uri: Constants.GCM_SEND_URI, - json: body - }, this.options, { - timeout: Constants.SOCKET_TIMEOUT - }); - - request(request_options, function (err, res, resBodyJSON) { - if (err) { - return callback(err); - } - if (res.statusCode >= 500) { - debug('GCM service is unavailable (500)'); - return callback(res.statusCode); - } - if (res.statusCode === 401) { - debug('Unauthorized (401). Check that your API token is correct.'); - return callback(res.statusCode); - } - if (res.statusCode !== 200) { - debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON); - return callback(res.statusCode); - } - callback(null, resBodyJSON, body.registration_ids || [ body.to ]); - }); - }.bind(this)); -}; - -function getRequestBody(message, recipient, callback) { - var body = cleanParams(message); - - if(typeof recipient == "string") { - body.to = recipient; - return nextTick(callback, null, body); - } - if(Array.isArray(recipient)) { - if(recipient.length < 1) { - return nextTick(callback, new Error('Empty recipient array passed!')); + if (res.statusCode >= 500) { + debug('GCM service is unavailable (500)'); + return callback(res.statusCode); } - body.registration_ids = recipient; - return nextTick(callback, null, body); - } - return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!')); -} - -function cleanParams(raw) { - var params = {}; - Object.keys(raw).forEach(function(param) { - var paramOptions = messageOptions[param]; - if(!paramOptions) { - return console.warn("node-gcm ignored unknown message parameter " + param); + if (res.statusCode === 401) { + debug('Unauthorized (401). Check that your API token is correct.'); + return callback(res.statusCode); } - if(paramOptions.__argType != typeof raw[param]) { - return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")"); + if (res.statusCode !== 200) { + debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON); + return callback(res.statusCode); } - params[param] = raw[param]; + callback(null, resBodyJSON, body.registration_ids || [ body.to ]); }); - return params; -} - -function nextTick(func) { - var args = Array.prototype.slice.call(arguments, 1); - process.nextTick(function() { - func.apply(this, args); - }.bind(this)); } module.exports = Sender; diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 90312a5..0670b20 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -9,10 +9,15 @@ var chai = require('chai'), describe('UNIT Sender', function () { // Use object to set arguments passed into callback var args = {}; - var requestStub = function (options, callback) { + var requestStub = sinon.spy(function (options, callback) { args.options = options; - return callback( args.err, args.res, args.resBody ); - }; + var resBody = args.resBody; + if(Array.isArray(args.resBody)) { + resBody = args.resBody[0]; + args.resBody = args.resBody.slice(1); + } + return callback( args.err, args.res, resBody ); + }); var Sender = proxyquire(senderPath, { 'request': requestStub }); describe('constructor', function () { @@ -23,34 +28,22 @@ describe('UNIT Sender', function () { expect(sender).to.not.be.undefined; expect(sender).to.be.instanceOf(gcm); }); - - it('should create a Sender with key and options passed in', function () { - var options = { - proxy: 'http://myproxy.com', - maxSockets: 100, - timeout: 100 - }; - var key = 'myAPIKey', - sender = new gcm(key, options); - expect(sender).to.be.instanceOf(gcm); - expect(sender.key).to.equal(key); - expect(sender.options).to.deep.equal(options); - }); }); - describe('sendNoRetry()', function () { + describe('send() without retries', function () { function setArgs(err, res, resBody) { args = { err: err, res: res, resBody: resBody }; - }; - before(function() { + } + beforeEach(function() { + requestStub.reset(); setArgs(null, { statusCode: 200 }, {}); }); - it('should set proxy, maxSockets, timeout and/or strictSSL of req object if passed into constructor', function (done) { + it('should set key, proxy, maxSockets, timeout and/or strictSSL of req object if passed into constructor', function (done) { var options = { proxy: 'http://myproxy.com', maxSockets: 100, @@ -59,8 +52,9 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { + expect(args.options.headers["Authorization"]).to.equal("key=mykey"); expect(args.options.proxy).to.equal(options.proxy); expect(args.options.maxSockets).to.equal(options.maxSockets); expect(args.options.timeout).to.equal(options.timeout); @@ -80,7 +74,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.method).to.not.equal(options.method); expect(args.options.headers).to.not.deep.equal(options.headers); @@ -98,7 +92,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.headers.Authorization).to.not.equal(options.headers.Auhtorization); done(); @@ -113,7 +107,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.headers.Custom).to.deep.equal(options.headers.Custom); done(); @@ -128,7 +122,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.strictSSL).to.be.an('undefined'); done(); @@ -138,7 +132,7 @@ describe('UNIT Sender', function () { it('should set the API key of req object if passed in API key', function (done) { var sender = new Sender('myKey'); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.headers.Authorization).to.equal('key=myKey'); done(); @@ -148,7 +142,7 @@ describe('UNIT Sender', function () { it('should send a JSON object as the body of the request', function (done) { var sender = new Sender('mykey'); var m = { collapseKey: 'Message', data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.json).to.be.a('object'); done(); @@ -166,7 +160,7 @@ describe('UNIT Sender', function () { } }; var sender = new Sender('mykey'); - sender.sendNoRetry(mess, '', function () {}); + sender.send(mess, '', { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.delay_while_idle).to.equal(mess.delay_while_idle); @@ -190,7 +184,7 @@ describe('UNIT Sender', function () { unknown_property: "hello" }; var sender = new Sender('mykey'); - sender.sendNoRetry(mess, '', function () {}); + sender.send(mess, '', { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.delay_while_idle).to.equal(undefined); @@ -206,7 +200,7 @@ describe('UNIT Sender', function () { it('should set the registration_ids to reg tokens implicitly passed in', function (done) { var sender = new Sender('myKey'); var m = { data: {} }; - sender.sendNoRetry(m, ["registration token 1", "registration token 2"], function () {}); + sender.send(m, ["registration token 1", "registration token 2"], { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.registration_ids).to.deep.equal(["registration token 1", "registration token 2"]); @@ -214,34 +208,10 @@ describe('UNIT Sender', function () { }, 10); }); - it('should set the registration_ids to reg tokens explicitly passed in', function (done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var regTokens = ["registration token 1", "registration token 2"]; - sender.sendNoRetry(m, { registrationIds: regTokens }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.registration_ids).to.deep.equal(regTokens); - done(); - }, 10); - }); - - it('should set the registration_ids to reg tokens explicitly passed in', function (done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var regTokens = ["registration token 1", "registration token 2"]; - sender.sendNoRetry(m, { registrationTokens: regTokens }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.registration_ids).to.deep.equal(regTokens); - done(); - }, 10); - }); - it('should set the to field if a single reg (or other) token is passed in', function(done) { var sender = new Sender('myKey'); var m = { data: {} }; - sender.sendNoRetry(m, "registration token 1", function () {}); + sender.send(m, "registration token 1", { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.to).to.deep.equal("registration token 1"); @@ -254,7 +224,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); var m = { data: {} }; var token = "registration token 1"; - sender.sendNoRetry(m, token, function () {}); + sender.send(m, token, { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.to).to.deep.equal(token); @@ -267,7 +237,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); var m = { data: {} }; var token = "registration token 1"; - sender.sendNoRetry(m, [ token ], function () {}); + sender.send(m, [ token ], { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.registration_ids).to.deep.equal([ token ]); @@ -279,7 +249,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if recipient is an empty object', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry({}, {}, callback); + sender.send({}, {}, { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -290,7 +260,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if no recipient provided', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry({}, [], callback); + sender.send({}, [], { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -303,7 +273,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs('an error', {}, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.calledWith('an error')).to.be.ok; @@ -316,7 +286,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs(null, { statusCode: 500 }, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(500); @@ -329,7 +299,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs(null, { statusCode: 401 }, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(401); @@ -342,7 +312,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs(null, { statusCode: 400 }, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(400); @@ -356,7 +326,7 @@ describe('UNIT Sender', function () { parseError = {error: 'Failed to parse JSON'}; setArgs(parseError, null, null); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.deep.equal(parseError); @@ -373,7 +343,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); setArgs(null, { statusCode: 200 }, resBody); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][1]).to.deep.equal(resBody); @@ -383,163 +353,112 @@ describe('UNIT Sender', function () { }); describe('send()', function () { - var restore = {}; - // Set args passed into sendNoRetry - function setArgs(err, response) { + function setArgs(err, res, resBody) { args = { err: err, - response: response, - tries: 0 - }; - }; - - before( function () { - restore.sendNoRetry = Sender.prototype.sendNoRetry; - Sender.prototype.sendNoRetry = function (message, reg_tokens, callback) { - args.message = message; - args.reg_tokens = reg_tokens; - args.tries++; - var nextResponse; - if(!args.response) { - nextResponse = args.response; - } - else if(args.response.length > 1) { - nextResponse = args.response.slice(0,1)[0]; - args.response = args.response.slice(1,args.response.length); - } - else if(args.response.length == 1) { - args.response = args.response[0]; - nextResponse = args.response; - } - else { - nextResponse = args.response; - } - callback( args.err, nextResponse, args.reg_tokens ); - }; - }); - - after( function () { - Sender.prototype.sendNoRetry = restore.sendNoRetry; - }); - - it('should pass reg tokens to sendNoRetry, even if it is an empty array', function (done) { - var emptyRegTokenArray = []; - var callback = function(error) { - expect(args.reg_tokens).to.equal(emptyRegTokenArray); - done(); - }; - var sender = new Sender('myKey'); - sender.send({}, emptyRegTokenArray, 0, callback); - }); - - it('should pass reg tokens to sendNoRetry, even if it is an empty object', function (done) { - var emptyRegTokenObject = {}; - var callback = function(error) { - expect(args.reg_tokens).to.equal(emptyRegTokenObject); - done(); - }; - var sender = new Sender('myKey'); - sender.send({}, emptyRegTokenObject, 0, callback); - }); - - it('should pass reg tokens to sendNoRetry, even if some keys are invalid', function (done) { - var invalidRegTokenObject = { invalid: ['regToken'] }; - var callback = function(error) { - expect(args.reg_tokens).to.equal(invalidRegTokenObject); - done(); + res: res, + resBody: resBody }; - var sender = new Sender('myKey'); - sender.send({}, invalidRegTokenObject, 0, callback); - }); + } - it('should pass the message and the regToken to sendNoRetry on call', function () { - var sender = new Sender('myKey'), - message = { data: {} }, - regToken = [24]; - setArgs(null, {}); - sender.send(message, regToken, 0, function () {}); - expect(args.message).to.equal(message); - expect(args.reg_tokens).to.equal(regToken); - expect(args.tries).to.equal(1); + beforeEach(function() { + requestStub.reset(); + setArgs(null, { statusCode: 200 }, {}); }); - it('should pass the message and the regTokens to sendNoRetry on call', function () { - var sender = new Sender('myKey'), - message = { data: {} }, - regTokens = [24, 34, 44]; - setArgs(null, {}); - sender.send(message, regTokens, 0, function () {}); - expect(args.message).to.equal(message); - expect(args.reg_tokens).to.equal(regTokens); - expect(args.tries).to.equal(1); - }); - - it('should pass the response into callback if successful for token', function () { + it('should pass the response into callback if successful for token', function (done) { var callback = sinon.spy(), - response = { success: true }, + response = { + success: 1, + failure: 0, + results: [ + { message_id: "something" } + ] + }, sender = new Sender('myKey'); - setArgs(null, response); - sender.send({}, [1], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][1]).to.equal(response); - expect(args.tries).to.equal(1); + setArgs(null, { statusCode: 200 }, response); + sender.send({}, [1], callback); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][1]).to.equal(response); + expect(requestStub.args.length).to.equal(1); + done(); + }, 10); }); - it('should pass the response into callback if successful for tokens', function () { + it('should pass the response into callback if successful for tokens', function (done) { var callback = sinon.spy(), - response = { success: true }, + response = { + success: 1, + failure: 0, + results: [ + { message_id: "something" } + ] + }, sender = new Sender('myKey'); - setArgs(null, response); - sender.send({}, [1, 2, 3], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][1]).to.equal(response); - expect(args.tries).to.equal(1); + setArgs(null, { statusCode: 200 }, response); + sender.send({}, [1, 2, 3], callback); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][1]).to.equal(response); + expect(requestStub.args.length).to.equal(1); + done(); + }, 10); }); - it('should pass the error into callback if failure and no retry for token', function () { + it('should pass the error into callback if failure for token', function (done) { var callback = sinon.spy(), error = 'my error', sender = new Sender('myKey'); setArgs(error); sender.send({}, [1], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.equal(error); - expect(args.tries).to.equal(1); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][0]).to.equal(error); + expect(requestStub.args.length).to.equal(1); + done(); + }, 10); }); - it('should pass the error into callback if failure and no retry for tokens', function () { + it('should pass the error into callback if failure for tokens', function (done) { var callback = sinon.spy(), error = 'my error', sender = new Sender('myKey'); setArgs(error); sender.send({}, [1, 2, 3], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.equal(error); - expect(args.tries).to.equal(1); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][0]).to.equal(error); + expect(requestStub.args.length).to.equal(1); + done(); + }, 10); }); it('should retry number of times passed into call and do exponential backoff', function (done) { var start = new Date(); var callback = function () { - expect(args.tries).to.equal(2); - expect(new Date() - start).to.be.gte(1000); + expect(requestStub.args.length).to.equal(2); + expect(new Date() - start).to.be.gte(200); done(); }; var sender = new Sender('myKey'); - setArgs('my error'); - sender.send({ data: {}}, [1], 1, callback); + setArgs(null, { statusCode: 500 }); + sender.send({ data: {}}, [1], { + retries: 1, + backoff: 200 + }, callback); }); it('should retry if not all regTokens were successfully sent', function (done) { var callback = function () { - expect(args.tries).to.equal(3); - // Last call of sendNoRetry should be for only failed regTokens - expect(args.reg_tokens.length).to.equal(1); - expect(args.reg_tokens[0]).to.equal(3); + expect(requestStub.args.length).to.equal(3); + var requestOptions = requestStub.args[2][0]; + expect(requestOptions.json.registration_ids.length).to.equal(1); + expect(requestOptions.json.registration_ids[0]).to.equal(3); done(); }; var sender = new Sender('myKey'); - setArgs(null, [{ results: [{}, { error: 'Unavailable' }, { error: 'Unavailable' }]}, { results: [ {}, { error: 'Unavailable' } ] }, { results: [ {} ] } ]); + setArgs(null, { statusCode: 200}, [{ results: [{}, { error: 'Unavailable' }, { error: 'Unavailable' }]}, { results: [ {}, { error: 'Unavailable' } ] }, { results: [ {} ] } ]); sender.send({ data: {}}, [1,2,3], { retries: 5, backoff: 100 @@ -549,8 +468,9 @@ describe('UNIT Sender', function () { it('should retry all regTokens in event of an error', function (done) { var start = new Date(); var callback = function () { - expect(args.tries).to.equal(2); - expect(args.reg_tokens.length).to.equal(3); + expect(requestStub.args.length).to.equal(2); + var requestOptions = requestStub.args[1][0]; + expect(requestOptions.json.registration_ids.length).to.equal(3); done(); }; var sender = new Sender('myKey'); @@ -567,7 +487,7 @@ describe('UNIT Sender', function () { done(); }; var sender = new Sender('myKey'); - setArgs(null, [ + setArgs(null, { statusCode: 200 }, [ { success: 1, failure: 2, canonical_ids: 0, results: [ {}, { error: 'Unavailable' }, { error: 'Unavailable' } ] }, { success: 1, canonical_ids: 1, failure: 0, results: [ {}, {} ] } ]); @@ -583,13 +503,13 @@ describe('UNIT Sender', function () { done(); }; var sender = new Sender('myKey'); - setArgs(null, [ + setArgs(null, { statusCode: 200 }, [ { success: 0, failure: 3, canonical_ids: 0, results: [ { error: 'Unavailable' }, { error: 'Unavailable' }, { error: 'Unavailable' } ] }, { success: 1, canonical_ids: 0, failure: 2, results: [ { error: 'Unavailable' }, { error: 'Unavailable' }, {} ] }, { success: 0, canonical_ids: 0, failure: 2, results: [ { error: 'Unavailable' }, { error: 'Unavailable' } ] } ]); sender.send({ data: {}}, [1,2,3], { - retries: 3, + retries: 2, backoff: 100 }, callback); });