From a8f1bdd3c9b8040a78f54ffa8dbc26e97beb31e6 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 11 Jan 2016 17:58:39 -0300 Subject: [PATCH 1/2] WIP integrations test --- test/integration/pushNotifications.js | 402 ++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 test/integration/pushNotifications.js diff --git a/test/integration/pushNotifications.js b/test/integration/pushNotifications.js new file mode 100644 index 0000000..49d80de --- /dev/null +++ b/test/integration/pushNotifications.js @@ -0,0 +1,402 @@ +'use strict'; + +var _ = require('lodash'); +var async = require('async'); + +var chai = require('chai'); +var sinon = require('sinon'); +var should = chai.should(); +var log = require('npmlog'); +log.debug = log.verbose; +log.level = 'info'; + +var WalletService = require('../../lib/server'); +var PushNotificationsService = require('../../lib/pushNotificationsService'); + +var TestData = require('../testdata'); +var helpers = require('./helpers'); + +describe('Push notifications', function() { + var server, wallet, requestStub, pushNotificationsService; + + before(function(done) { + helpers.before(done); + }); + after(function(done) { + helpers.after(done); + }); + + describe('Single wallet', function() { + beforeEach(function(done) { + helpers.beforeEach(function(res) { + helpers.createAndJoinWallet(1, 1, function(s, w) { + server = s; + wallet = w; + + var i = 0; + async.eachSeries(w.copayers, function(copayer, next) { + helpers.getAuthServer(copayer.id, function(server) { + server.savePreferences({ + email: 'copayer' + (++i) + '@domain.com', + unit: 'bit', + }, next); + }); + }, function(err) { + should.not.exist(err); + + requestStub = sinon.stub(); + requestStub.yields(); + + pushNotificationsService = new PushNotificationsService(); + pushNotificationsService.start({ + lockOpts: {}, + messageBroker: server.messageBroker, + storage: helpers.getStorage(), + request: requestStub, + pushNotificationsOpts: { + templatePath: '../lib/templates', + defaultLanguage: 'en', + defaultUnit: 'btc', + subjectPrefix: '', + publicTxUrlTemplate: { + livenet: 'https://insight.bitpay.com/tx/{{txid}}', + testnet: 'https://test-insight.bitpay.com/tx/{{txid}}', + }, + pushServerUrl: 'http://192.168.1.111:8000/send', + }, + }, function(err) { + should.not.exist(err); + done(); + }); + }); + }); + }); + }); + + it('should build each notifications using preferences of the copayers', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + + // Simulate incoming tx notification + server._notify('NewIncomingTx', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: true + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + var args = _.map(calls, function(c) { + return c.args[0]; + }); + + args[0].body.android.data.title.should.contain('New payment received'); + args[0].body.android.data.message.should.contain('123,000'); + + done(); + }, 100); + }); + }); + }); + + it('number of calls should be 0', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + + // Simulate incoming tx notification + server._notify('NewIncomingTx', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: false + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + calls.length.should.equal(0); + + done(); + }, 100); + }); + }); + }); + + it('number of calls should be 1', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + + // Simulate incoming tx notification + server._notify('NewIncomingTx', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: true + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + calls.length.should.equal(1); + + done(); + }, 100); + }); + }); + }); + }); + + describe('Shared wallet', function() { + beforeEach(function(done) { + helpers.beforeEach(function(res) { + helpers.createAndJoinWallet(2, 3, function(s, w) { + server = s; + wallet = w; + + var i = 0; + async.eachSeries(w.copayers, function(copayer, next) { + helpers.getAuthServer(copayer.id, function(server) { + server.savePreferences({ + email: 'copayer' + (++i) + '@domain.com', + language: 'en', + unit: 'bit', + }, next); + }); + }, function(err) { + should.not.exist(err); + + requestStub = sinon.stub(); + requestStub.yields(); + + pushNotificationsService = new PushNotificationsService(); + pushNotificationsService.start({ + lockOpts: {}, + messageBroker: server.messageBroker, + storage: helpers.getStorage(), + request: requestStub, + pushNotificationsOpts: { + templatePath: '../lib/templates', + defaultLanguage: 'en', + defaultUnit: 'btc', + subjectPrefix: '', + publicTxUrlTemplate: { + livenet: 'https://insight.bitpay.com/tx/{{txid}}', + testnet: 'https://test-insight.bitpay.com/tx/{{txid}}', + }, + pushServerUrl: 'http://192.168.1.111:8000/send', + }, + }, function(err) { + should.not.exist(err); + done(); + }); + }); + }); + }); + }); + + it('should build each notifications using preferences of the copayers', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + + // Simulate incoming tx notification + server._notify('NewIncomingTx', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: true + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + var args = _.map(calls, function(c) { + return c.args[0]; + }); + + args[0].body.android.data.title.should.contain('New payment received'); + args[0].body.android.data.message.should.contain('123,000'); + + done(); + }, 100); + }); + }); + }); + + it('number of calls should be 3', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + + // Simulate incoming tx notification + server._notify('NewIncomingTx', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: true + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + calls.length.should.equal(3); + + done(); + }, 100); + }); + }); + }); + + it('number of calls should be 2', function(done) { + server.createAddress({}, function(err, address) { + should.not.exist(err); + + // Simulate incoming tx notification + server._notify('NewIncomingTx', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: false + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + calls.length.should.equal(2); + + done(); + }, 100); + }); + }); + }); + + it('should notify copayers a new tx proposal has been created', function(done) { + helpers.stubUtxos(server, wallet, [1, 1], function() { + var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, TestData.copayers[0].privKey_1H_0, { + message: 'some message' + }); + // server.createTxLegacy(txOpts, function(err, tx) { + // should.not.exist(err); + server.createAddress({}, function(err, address) { + should.not.exist(err); + server._notify('NewTxProposal', { + txid: '999', + address: address, + amount: 12300000, + }, { + isGlobal: false + }, function(err) { + setTimeout(function() { + var calls = requestStub.getCalls(); + calls.length.should.equal(2); + + done(); + }, 100); + }); + }); + }); + // }); + }); + + it('should notify copayers a tx has been finally rejected', function(done) { + helpers.stubUtxos(server, wallet, 1, function() { + var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, TestData.copayers[0].privKey_1H_0, { + message: 'some message' + }); + + var txpId; + async.waterfall([ + + function(next) { + server.createTxLegacy(txOpts, next); + }, + function(txp, next) { + txpId = txp.id; + async.eachSeries(_.range(1, 3), function(i, next) { + var copayer = TestData.copayers[i]; + helpers.getAuthServer(copayer.id44, function(server) { + server.rejectTx({ + txProposalId: txp.id, + }, next); + }); + }, next); + }, + ], function(err) { + should.not.exist(err); + + setTimeout(function() { + var calls = requestStub.getCalls(); + var args = _.map(_.takeRight(calls, 2), function(c) { + return c.args[0]; + }); + + args[0].body.android.data.title.should.contain('Payment proposal rejected'); + args[0].body.android.data.message.should.contain('copayer 2, copayer 3'); + args[0].body.android.data.message.should.not.contain('copayer 1'); + done(); + }, 100); + }); + }); + }); + + it('should notify copayers when a new copayer just joined into your wallet ', function(done) { + helpers.stubUtxos(server, wallet, 1, function() { + var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, TestData.copayers[0].privKey_1H_0, { + message: 'some message' + }); + + var txpId; + async.waterfall([ + + function(next) { + server.createTxLegacy(txOpts, next); + }, + function(txp, next) { + txpId = txp.id; + async.eachSeries(_.range(1, 3), function(i, next) { + var copayer = TestData.copayers[i]; + helpers.getAuthServer(copayer.id44, function(server) { + server.rejectTx({ + txProposalId: txp.id, + }, next); + }); + }, next); + }, + ], function(err) { + should.not.exist(err); + + setTimeout(function() { + var calls = requestStub.getCalls(); + var args = _.map(_.takeRight(calls, 2), function(c) { + return c.args[0]; + }); + + args[0].body.android.data.title.should.contain('Payment proposal rejected'); + args[0].body.android.data.message.should.contain('copayer 2, copayer 3'); + args[0].body.android.data.message.should.not.contain('copayer 1'); + done(); + }, 100); + }); + }); + }); + + + // it('should join existing wallet', function(done) { + // var copayerOpts = helpers.getSignedCopayerOpts({ + // walletId: walletId, + // name: 'me', + // xPubKey: TestData.copayers[0].xPubKey_44H_0H_0H, + // requestPubKey: TestData.copayers[0].pubKey_1H_0, + // customData: 'dummy custom data', + // }); + // server.joinWallet(copayerOpts, function(err, result) { + // should.not.exist(err); + // setTimeout(function() { + // var calls = requestStub.getCalls(); + // var args = _.map(_.takeRight(calls, 2), function(c) { + // return c.args[0]; + // }); + // console.log(args); + + // done(); + // }, 100); + // }); + // }); + }); + + +}); From a3ed3a512898f422ebb897634864156725995cb3 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 12 Jan 2016 09:44:30 -0300 Subject: [PATCH 2/2] config and pushNotifications service modified --- config.js | 2 +- lib/pushnotificationsservice.js | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/config.js b/config.js index c197cda..f5f944f 100644 --- a/config.js +++ b/config.js @@ -54,7 +54,7 @@ var config = { livenet: 'https://insight.bitpay.com/tx/{{txid}}', testnet: 'https://test-insight.bitpay.com/tx/{{txid}}', }, - pushServerUrl: 'http://192.168.1.143:8000/send', + pushServerUrl: 'http://192.168.1.111:8000/send', }, // To use email notifications uncomment this: // emailOpts: { diff --git a/lib/pushnotificationsservice.js b/lib/pushnotificationsservice.js index 8b1c695..d680612 100644 --- a/lib/pushnotificationsservice.js +++ b/lib/pushnotificationsservice.js @@ -3,7 +3,7 @@ var _ = require('lodash'); var async = require('async'); var Mustache = require('mustache'); -var request = require('request'); +var defaultRequest = require('request'); var MessageBroker = require('./messagebroker'); var Storage = require('./storage'); var fs = require('fs'); @@ -39,6 +39,7 @@ function PushNotificationsService() {}; PushNotificationsService.prototype.start = function(opts, cb) { var self = this; opts = opts || {}; + self.request = opts.request || defaultRequest; function _readDirectories(basePath, cb) { fs.readdir(basePath, function(err, files) { @@ -53,14 +54,14 @@ PushNotificationsService.prototype.start = function(opts, cb) { }); }; - self.templatePath = opts.pushNotificationsOpts.templatePath || templatePathpath.normalize(((__dirname + '/templates')) + '/'); + self.templatePath = path.normalize((opts.pushNotificationsOpts.templatePath || (__dirname + '/templates')) + '/'); self.defaultLanguage = opts.pushNotificationsOpts.defaultLanguage || 'en'; self.defaultUnit = opts.pushNotificationsOpts.defaultUnit || 'btc'; self.subjectPrefix = opts.pushNotificationsOpts.subjectPrefix || ''; self.publicTxUrlTemplate = opts.pushNotificationsOpts.publicTxUrlTemplate || {}; self.pushServerUrl = opts.pushNotificationsOpts.pushServerUrl; - async.parallel([ + function(done) { _readDirectories(self.templatePath, function(err, res) { self.availableLanguages = res; @@ -68,8 +69,13 @@ PushNotificationsService.prototype.start = function(opts, cb) { }); }, function(done) { - self.storage = new Storage(); - self.storage.connect(opts.storageOpts, done); + if (opts.storage) { + self.storage = opts.storage; + done(); + } else { + self.storage = new Storage(); + self.storage.connect(opts.storageOpts, done); + } }, function(done) { self.messageBroker = opts.messageBroker || new MessageBroker(opts.messageBrokerOpts); @@ -82,6 +88,7 @@ PushNotificationsService.prototype.start = function(opts, cb) { } return cb(err); }); + }; PushNotificationsService.prototype._sendPushNotifications = function(notification, cb) { @@ -91,12 +98,13 @@ PushNotificationsService.prototype._sendPushNotifications = function(notificatio var notifType = PUSHNOTIFICATIONS_TYPES[notification.type]; if (!notifType) return cb(); - console.log(notification); + // console.log(notification); self._getRecipientsList(notification, function(err, recipientsList) { if (err) return cb(err); async.waterfall([ + function(next) { self._readAndApplyTemplates(notification, notifType, recipientsList, next); }, @@ -124,7 +132,8 @@ PushNotificationsService.prototype._sendPushNotifications = function(notificatio self._makeRequest(opts, next()); }, function(err) { - log.error(err); + if (err) + log.error(err); return cb(err); } ); @@ -145,6 +154,7 @@ PushNotificationsService.prototype._getRecipientsList = function(notification, c if (err) return cb(err); self.storage.fetchPreferences(notification.walletId, null, function(err, preferences) { + if (err) log.error(err); if (_.isEmpty(preferences)) preferences = []; @@ -202,6 +212,7 @@ PushNotificationsService.prototype._readAndApplyTemplates = function(notificatio async.map(recipientsList, function(recipient, next) { async.waterfall([ + function(next) { self._getDataForTemplate(notification, recipient, next); }, @@ -340,7 +351,7 @@ PushNotificationsService.prototype._compileTemplate = function(template, extensi PushNotificationsService.prototype._makeRequest = function(opts, cb) { var self = this; - request({ + self.request({ url: self.pushServerUrl, method: 'POST', json: true,