From b23f412bf3d7a2eedebfc989fec355b4e1f90264 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 15 Oct 2015 15:14:29 -0300 Subject: [PATCH 1/6] getLatestNotifications + tests --- lib/model/notification.js | 1 + lib/server.js | 18 +++++++++ lib/storage.js | 38 ++++++++++++++++++ test/integration/server.js | 82 ++++++++++++++++++++------------------ 4 files changed, 101 insertions(+), 38 deletions(-) diff --git a/lib/model/notification.js b/lib/model/notification.js index 13c29f2..2a7de39 100644 --- a/lib/model/notification.js +++ b/lib/model/notification.js @@ -32,6 +32,7 @@ Notification.create = function(opts) { x.version = '1.0.0'; var now = Date.now(); + x.createdOn = Math.floor(now / 1000); x.id = _.padLeft(now, 14, '0') + _.padLeft(opts.ticker || 0, 4, '0'); x.type = opts.type || 'general'; diff --git a/lib/server.js b/lib/server.js index 683069b..bd01531 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1658,6 +1658,24 @@ WalletService.prototype.getNotifications = function(opts, cb) { }); }; +/** + * Retrieves notifications after a specific id or from a given ts (whichever is more recent). + * + * @param {Object} opts + * @param {Object} opts.notificationId (optional) + * @param {Object} opts.minTs (optional) - default now - 60 sec. + * @returns {Notification[]} Notifications + */ +WalletService.prototype.getLatestNotifications = function(opts, cb) { + var self = this; + opts = opts || {}; + + var minTs = _.isNumber(opts.minTs) ? opts.minTs : +Date.now() - (60 * 1000); + self.storage.fetchLatestNotifications(self.walletId, opts.notificationId, minTs, function(err, notifications) { + if (err) return cb(err); + return cb(null, notifications); + }); +}; WalletService.prototype._normalizeTxHistory = function(txs) { var now = Math.floor(Date.now() / 1000); diff --git a/lib/storage.js b/lib/storage.js index 61dd719..82894db 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -315,6 +315,44 @@ Storage.prototype.fetchNotifications = function(walletId, opts, cb) { }); }; +/** + * Retrieves notifications after a specific id or from a given ts (whichever is more recent). + * + * @param {String} notificationId + * @param {Number} minTs + * @returns {Notification[]} Notifications + */ +Storage.prototype.fetchLatestNotifications = function(walletId, notificationId, minTs, cb) { + function makeId(timestamp) { + return _.padLeft(timestamp, 14, '0') + _.repeat('0', 4); + }; + + var self = this; + + var minId = makeId(minTs); + if (notificationId) { + minId = notificationId > minId ? notificationId : minId; + } + + this.db.collection(collections.NOTIFICATIONS) + .find({ + walletId: walletId, + id: { + $gt: minId, + }, + }) + .sort({ + id: 1 + }) + .toArray(function(err, result) { + if (err) return cb(err); + if (!result) return cb(); + var notifications = _.map(result, function(notification) { + return Model.Notification.fromObj(notification); + }); + return cb(null, notifications); + }); +}; // TODO: remove walletId from signature Storage.prototype.storeNotification = function(walletId, notification, cb) { diff --git a/test/integration/server.js b/test/integration/server.js index 41fc629..b8be06b 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -3876,72 +3876,78 @@ describe('Wallet service', function() { }); describe('Notifications', function() { + var clock; var server, wallet; beforeEach(function(done) { + clock = sinon.useFakeTimers(10 * 1000, 'Date'); helpers.createAndJoinWallet(1, 1, function(s, w) { server = s; wallet = w; helpers.stubUtxos(server, wallet, _.range(4), function() { var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.01, TestData.copayers[0].privKey_1H_0); async.eachSeries(_.range(3), function(i, next) { + clock.tick(25 * 1000); server.createTx(txOpts, function(err, tx) { should.not.exist(err); next(); }); }, function(err) { + clock.tick(20 * 1000); return done(err); }); }); }); }); + afterEach(function() { + clock.restore(); + }); - it('should pull the last 4 notifications after 3 TXs', function(done) { - server.getNotifications({ - limit: 4, - reverse: true, + it('should pull all notifications', function(done) { + server.getLatestNotifications({ + minTs: 0 }, function(err, notifications) { should.not.exist(err); var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['NewTxProposal', 'NewTxProposal', 'NewTxProposal', 'NewAddress']); + types.should.deep.equal(['NewCopayer', 'NewAddress', 'NewAddress', 'NewTxProposal', 'NewTxProposal', 'NewTxProposal']); var walletIds = _.uniq(_.pluck(notifications, 'walletId')); walletIds.length.should.equal(1); walletIds[0].should.equal(wallet.id); - var creators = _.uniq(_.pluck(notifications, 'creatorId')); + var creators = _.uniq(_.compact(_.pluck(notifications, 'creatorId'))); creators.length.should.equal(1); creators[0].should.equal(wallet.copayers[0].id); done(); }); }); - it('should pull the last 4 notifications, using now', function(done) { - server.getNotifications({ - limit: 4, - reverse: true, - maxTs: Date.now() / 1000, - minTs: Date.now() / 1000 - 1000, - }, function(err, notifications) { + it('should pull notifications in the last 60 seconds', function(done) { + server.getLatestNotifications({}, function(err, notifications) { should.not.exist(err); var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['NewTxProposal', 'NewTxProposal', 'NewTxProposal', 'NewAddress']); + types.should.deep.equal(['NewTxProposal', 'NewTxProposal']); done(); }); }); - it('should pull all notifications after wallet creation', function(done) { - server.getNotifications({ - minTs: 0, + it('should pull notifications after a given notification id', function(done) { + server.getLatestNotifications({ + minTs: 0 }, function(err, notifications) { should.not.exist(err); - var types = _.pluck(notifications, 'type'); - types[0].should.equal('NewCopayer'); - types[types.length - 1].should.equal('NewTxProposal'); - done(); + var from = _.first(_.takeRight(notifications, 2)).id; // second to last + server.getLatestNotifications({ + notificationId: from + }, function(err, res) { + should.not.exist(err); + res.length.should.equal(1); + res[0].id.should.equal(_.first(_.takeRight(notifications)).id); + done(); + }); }); }); it('should contain walletId & creatorId on NewCopayer', function(done) { - server.getNotifications({ + server.getLatestNotifications({ minTs: 0, }, function(err, notifications) { should.not.exist(err); @@ -3962,13 +3968,13 @@ describe('Wallet service', function() { txProposalId: tx.id, signatures: signatures, }, function(err) { - server.getNotifications({ - limit: 3, - reverse: true, + server.getLatestNotifications({ + minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); + notifications.length.should.equal(2); var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['TxProposalFinallyAccepted', 'TxProposalAcceptedBy', 'NewTxProposal']); + types.should.deep.equal(['TxProposalAcceptedBy', 'TxProposalFinallyAccepted']); done(); }); }); @@ -3982,13 +3988,13 @@ describe('Wallet service', function() { txProposalId: tx.id, }, function(err) { should.not.exist(err); - server.getNotifications({ - limit: 2, - reverse: true, + server.getLatestNotifications({ + minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); + notifications.length.should.equal(2); var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['TxProposalFinallyRejected', 'TxProposalRejectedBy']); + types.should.deep.equal(['TxProposalRejectedBy', 'TxProposalFinallyRejected']); done(); }); }); @@ -4010,13 +4016,13 @@ describe('Wallet service', function() { txProposalId: tx.id }, function(err, txp) { should.not.exist(err); - server.getNotifications({ - limit: 3, - reverse: true, + server.getLatestNotifications({ + minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); + notifications.length.should.equal(3); var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['NewOutgoingTx', 'TxProposalFinallyAccepted', 'TxProposalAcceptedBy']); + types.should.deep.equal(['TxProposalAcceptedBy', 'TxProposalFinallyAccepted', 'NewOutgoingTx']); done(); }); }); @@ -4042,13 +4048,13 @@ describe('Wallet service', function() { txProposalId: tx.id }, function(err, txp) { should.not.exist(err); - server.getNotifications({ - limit: 3, - reverse: true, + server.getLatestNotifications({ + minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); + notifications.length.should.equal(3); var types = _.pluck(notifications, 'type'); - types.should.deep.equal(['NewOutgoingTxByThirdParty', 'TxProposalFinallyAccepted', 'TxProposalAcceptedBy']); + types.should.deep.equal(['TxProposalAcceptedBy', 'TxProposalFinallyAccepted', 'NewOutgoingTxByThirdParty']); done(); }); }); From 3bb097c7f461c3616711b1cf051c26689d863a94 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 15 Oct 2015 15:18:11 -0300 Subject: [PATCH 2/6] rm getNotifications --- lib/server.js | 18 ------------------ test/integration/server.js | 36 +++++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/server.js b/lib/server.js index bd01531..18a250b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1640,24 +1640,6 @@ WalletService.prototype.getTxs = function(opts, cb) { }; -/** - * Retrieves notifications in the range (maxTs-minTs). - * Times are in UNIX EPOCH. Order is assured even for events with the same time - * - * @param {Object} opts.minTs (defaults to 0) - * @param {Object} opts.maxTs (defaults to now) - * @param {Object} opts.limit - * @param {Object} opts.reverse (default false) - * @returns {Notification[]} Notifications - */ -WalletService.prototype.getNotifications = function(opts, cb) { - var self = this; - self.storage.fetchNotifications(self.walletId, opts, function(err, notifications) { - if (err) return cb(err); - return cb(null, notifications); - }); -}; - /** * Retrieves notifications after a specific id or from a given ts (whichever is more recent). * diff --git a/test/integration/server.js b/test/integration/server.js index b8be06b..66cb63f 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -1041,7 +1041,9 @@ describe('Wallet service', function() { copayer.name.should.equal('me'); copayer.id.should.equal(copayerId); copayer.customData.should.equal('dummy custom data'); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewCopayer' @@ -1250,7 +1252,9 @@ describe('Wallet service', function() { should.not.exist(err); wallet.status.should.equal('complete'); wallet.publicKeyRing.length.should.equal(3); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'WalletComplete' @@ -1265,7 +1269,9 @@ describe('Wallet service', function() { it('should not notify WalletComplete if 1-of-1', function(done) { helpers.createAndJoinWallet(1, 1, function(server) { - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'WalletComplete' @@ -1554,7 +1560,9 @@ describe('Wallet service', function() { address.isChange.should.be.false; address.path.should.equal('m/2147483647/0/0'); address.type.should.equal('P2SH'); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewAddress' @@ -1617,7 +1625,9 @@ describe('Wallet service', function() { address.isChange.should.be.false; address.path.should.equal('m/0/0'); address.type.should.equal('P2SH'); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewAddress' @@ -1684,7 +1694,9 @@ describe('Wallet service', function() { address.isChange.should.be.false; address.path.should.equal('m/0/0'); address.type.should.equal('P2PKH'); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewAddress' @@ -3605,7 +3617,9 @@ describe('Wallet service', function() { txp.actions.length.should.equal(1); var action = txp.getActionBy(wallet.copayers[0].id); action.type.should.equal('accept'); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var last = _.last(notifications); last.type.should.not.equal('TxProposalFinallyAccepted'); @@ -3635,7 +3649,9 @@ describe('Wallet service', function() { txp.isBroadcasted().should.be.false; should.exist(txp.txid); txp.actions.length.should.equal(2); - server.getNotifications({}, function(err, notifications) { + server.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); var last = _.last(notifications); last.type.should.equal('TxProposalFinallyAccepted'); @@ -4180,7 +4196,9 @@ describe('Wallet service', function() { }); }, function(next) { - server2.getNotifications({}, function(err, notifications) { + server2.getLatestNotifications({ + minTs: 0 + }, function(err, notifications) { should.not.exist(err); should.exist(notifications); notifications.length.should.above(0); From bf40b5a0007410dd197dabd9ed99432a3c7099ea Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 15 Oct 2015 15:26:02 -0300 Subject: [PATCH 3/6] getLatestNotifications -> getNotifications --- lib/server.js | 7 ++-- lib/storage.js | 2 +- test/integration/server.js | 67 +++++++++++++------------------------- 3 files changed, 27 insertions(+), 49 deletions(-) diff --git a/lib/server.js b/lib/server.js index 18a250b..1062171 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1645,15 +1645,14 @@ WalletService.prototype.getTxs = function(opts, cb) { * * @param {Object} opts * @param {Object} opts.notificationId (optional) - * @param {Object} opts.minTs (optional) - default now - 60 sec. + * @param {Object} opts.minTs (optional) - default 0. * @returns {Notification[]} Notifications */ -WalletService.prototype.getLatestNotifications = function(opts, cb) { +WalletService.prototype.getNotifications = function(opts, cb) { var self = this; opts = opts || {}; - var minTs = _.isNumber(opts.minTs) ? opts.minTs : +Date.now() - (60 * 1000); - self.storage.fetchLatestNotifications(self.walletId, opts.notificationId, minTs, function(err, notifications) { + self.storage.fetchNotifications(self.walletId, opts.notificationId, opts.minTs || 0, function(err, notifications) { if (err) return cb(err); return cb(null, notifications); }); diff --git a/lib/storage.js b/lib/storage.js index 82894db..73e4941 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -322,7 +322,7 @@ Storage.prototype.fetchNotifications = function(walletId, opts, cb) { * @param {Number} minTs * @returns {Notification[]} Notifications */ -Storage.prototype.fetchLatestNotifications = function(walletId, notificationId, minTs, cb) { +Storage.prototype.fetchNotifications = function(walletId, notificationId, minTs, cb) { function makeId(timestamp) { return _.padLeft(timestamp, 14, '0') + _.repeat('0', 4); }; diff --git a/test/integration/server.js b/test/integration/server.js index 66cb63f..3cbcf34 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -1041,9 +1041,7 @@ describe('Wallet service', function() { copayer.name.should.equal('me'); copayer.id.should.equal(copayerId); copayer.customData.should.equal('dummy custom data'); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewCopayer' @@ -1252,9 +1250,7 @@ describe('Wallet service', function() { should.not.exist(err); wallet.status.should.equal('complete'); wallet.publicKeyRing.length.should.equal(3); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'WalletComplete' @@ -1269,9 +1265,7 @@ describe('Wallet service', function() { it('should not notify WalletComplete if 1-of-1', function(done) { helpers.createAndJoinWallet(1, 1, function(server) { - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'WalletComplete' @@ -1560,9 +1554,7 @@ describe('Wallet service', function() { address.isChange.should.be.false; address.path.should.equal('m/2147483647/0/0'); address.type.should.equal('P2SH'); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewAddress' @@ -1625,9 +1617,7 @@ describe('Wallet service', function() { address.isChange.should.be.false; address.path.should.equal('m/0/0'); address.type.should.equal('P2SH'); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewAddress' @@ -1694,9 +1684,7 @@ describe('Wallet service', function() { address.isChange.should.be.false; address.path.should.equal('m/0/0'); address.type.should.equal('P2PKH'); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var notif = _.find(notifications, { type: 'NewAddress' @@ -3617,9 +3605,7 @@ describe('Wallet service', function() { txp.actions.length.should.equal(1); var action = txp.getActionBy(wallet.copayers[0].id); action.type.should.equal('accept'); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var last = _.last(notifications); last.type.should.not.equal('TxProposalFinallyAccepted'); @@ -3649,9 +3635,7 @@ describe('Wallet service', function() { txp.isBroadcasted().should.be.false; should.exist(txp.txid); txp.actions.length.should.equal(2); - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var last = _.last(notifications); last.type.should.equal('TxProposalFinallyAccepted'); @@ -3920,9 +3904,7 @@ describe('Wallet service', function() { }); it('should pull all notifications', function(done) { - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var types = _.pluck(notifications, 'type'); types.should.deep.equal(['NewCopayer', 'NewAddress', 'NewAddress', 'NewTxProposal', 'NewTxProposal', 'NewTxProposal']); @@ -3937,7 +3919,9 @@ describe('Wallet service', function() { }); it('should pull notifications in the last 60 seconds', function(done) { - server.getLatestNotifications({}, function(err, notifications) { + server.getNotifications({ + minTs: +Date.now() - (60 * 1000), + }, function(err, notifications) { should.not.exist(err); var types = _.pluck(notifications, 'type'); types.should.deep.equal(['NewTxProposal', 'NewTxProposal']); @@ -3946,13 +3930,12 @@ describe('Wallet service', function() { }); it('should pull notifications after a given notification id', function(done) { - server.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var from = _.first(_.takeRight(notifications, 2)).id; // second to last - server.getLatestNotifications({ - notificationId: from + server.getNotifications({ + notificationId: from, + minTs: +Date.now() - (60 * 1000), }, function(err, res) { should.not.exist(err); res.length.should.equal(1); @@ -3963,9 +3946,7 @@ describe('Wallet service', function() { }); it('should contain walletId & creatorId on NewCopayer', function(done) { - server.getLatestNotifications({ - minTs: 0, - }, function(err, notifications) { + server.getNotifications({}, function(err, notifications) { should.not.exist(err); var newCopayer = notifications[0]; newCopayer.type.should.equal('NewCopayer'); @@ -3984,7 +3965,7 @@ describe('Wallet service', function() { txProposalId: tx.id, signatures: signatures, }, function(err) { - server.getLatestNotifications({ + server.getNotifications({ minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); @@ -4004,7 +3985,7 @@ describe('Wallet service', function() { txProposalId: tx.id, }, function(err) { should.not.exist(err); - server.getLatestNotifications({ + server.getNotifications({ minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); @@ -4032,7 +4013,7 @@ describe('Wallet service', function() { txProposalId: tx.id }, function(err, txp) { should.not.exist(err); - server.getLatestNotifications({ + server.getNotifications({ minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); @@ -4064,7 +4045,7 @@ describe('Wallet service', function() { txProposalId: tx.id }, function(err, txp) { should.not.exist(err); - server.getLatestNotifications({ + server.getNotifications({ minTs: Date.now(), }, function(err, notifications) { should.not.exist(err); @@ -4123,7 +4104,7 @@ describe('Wallet service', function() { }); }, function(next) { - server.storage.fetchNotifications(wallet.id, {}, function(err, items) { + server.getNotifications({}, function(err, items) { items.length.should.equal(0); next(); }); @@ -4196,9 +4177,7 @@ describe('Wallet service', function() { }); }, function(next) { - server2.getLatestNotifications({ - minTs: 0 - }, function(err, notifications) { + server2.getNotifications({}, function(err, notifications) { should.not.exist(err); should.exist(notifications); notifications.length.should.above(0); From 685df45e36a9f30576982a5b22c69933ee524402 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 15 Oct 2015 16:30:14 -0300 Subject: [PATCH 4/6] express endpoint + tests --- lib/expressapp.js | 22 +++++++++++++--- test/expressapp.js | 62 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/lib/expressapp.js b/lib/expressapp.js index ea7e860..3ad7b2d 100644 --- a/lib/expressapp.js +++ b/lib/expressapp.js @@ -13,7 +13,7 @@ var Stats = require('./stats'); log.disableColor(); log.debug = log.verbose; -log.level = 'debug'; +log.level = 'info'; var ExpressApp = function() { this.app = express(); @@ -85,7 +85,8 @@ ExpressApp.prototype.start = function(opts, cb) { message: err.message, }).end(); } else { - var code = 500, message; + var code = 500, + message; if (_.isObject(err)) { code = err.code || err.statusCode; message = err.message || err.body; @@ -430,10 +431,25 @@ ExpressApp.prototype.start = function(opts, cb) { router.get('/v1/version/', function(req, res) { var server = getServer(req, res); - res.json( { serviceVersion: server.serviceVersion } ); + res.json({ + serviceVersion: server.serviceVersion + }); res.end(); }); + router.get('/v1/notifications/', function(req, res) { + getServerWithAuth(req, res, function(server) { + var opts = { + minTs: +Date.now() - (60 * 1000), + notificationId: req.query.notificationId, + }; + server.getNotifications(opts, function(err, notifications) { + if (err) return returnError(err, res, req); + res.json(notifications); + }); + }); + }); + this.app.use(opts.basePath || '/bws/api', router); WalletService.initialize(opts, cb); diff --git a/test/expressapp.js b/test/expressapp.js index 3e2b84f..2a9e8b0 100644 --- a/test/expressapp.js +++ b/test/expressapp.js @@ -38,6 +38,22 @@ describe('ExpressApp', function() { describe('Routes', function() { var testPort = 3239; var testHost = 'http://127.0.0.1'; + var httpServer; + + function start(ExpressApp, done) { + var app = new ExpressApp(); + httpServer = http.Server(app.app); + + app.start(config, function(err) { + should.not.exist(err); + httpServer.listen(testPort); + done(); + }); + }; + + afterEach(function() { + httpServer.close(); + }); it('/v2/wallets', function(done) { var server = { @@ -49,20 +65,15 @@ describe('ExpressApp', function() { getInstanceWithAuth: sinon.stub().callsArgWith(1, null, server), } }); - var app = new TestExpressApp(); - var httpServer = http.Server(app.app); - - app.start(config, function(err) { - should.not.exist(err); - httpServer.listen(testPort); + start(TestExpressApp, function() { var requestOptions = { - url: testHost + ':' + testPort + config.basePath + '/v1/wallets', + url: testHost + ':' + testPort + config.basePath + '/v2/wallets', headers: { 'x-identity': 'identity', 'x-signature': 'signature' } }; - request(requestOptions, function(err, response, body){ + request(requestOptions, function(err, response, body) { should.not.exist(err); response.statusCode.should.equal(200); body.should.equal('{}'); @@ -70,6 +81,41 @@ describe('ExpressApp', function() { }); }); }); + + it('/v1/notifications', function(done) { + var clock = sinon.useFakeTimers(1234000, 'Date'); + + var server = { + getNotifications: sinon.stub().callsArgWith(1, null, {}) + }; + var TestExpressApp = proxyquire('../lib/expressapp', { + './server': { + initialize: sinon.stub().callsArg(1), + getInstanceWithAuth: sinon.stub().callsArgWith(1, null, server), + } + }); + start(TestExpressApp, function() { + var requestOptions = { + url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?notificationId=123&minTs=0', + headers: { + 'x-identity': 'identity', + 'x-signature': 'signature' + } + }; + request(requestOptions, function(err, response, body) { + should.not.exist(err); + response.statusCode.should.equal(200); + body.should.equal('{}'); + server.getNotifications.calledWith({ + notificationId: '123', + minTs: 1234000 - 60000, // override minTs argument with a hardcoded 60 seconds span + }).should.be.true; + + clock.restore(); + done(); + }); + }); + }); }); }); }); From c35a504696506958208236ddc2dd9f53d93a2100 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 15 Oct 2015 16:37:25 -0300 Subject: [PATCH 5/6] test notifications return empty set if no new data --- test/integration/server.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/integration/server.js b/test/integration/server.js index 3cbcf34..2bfa182 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -3875,7 +3875,7 @@ describe('Wallet service', function() { }); }); - describe('Notifications', function() { + describe('#getNotifications', function() { var clock; var server, wallet; @@ -3945,6 +3945,31 @@ describe('Wallet service', function() { }); }); + it('should return empty if no notifications found after a given id', function(done) { + server.getNotifications({}, function(err, notifications) { + should.not.exist(err); + var from = _.first(_.takeRight(notifications)).id; // last one + server.getNotifications({ + notificationId: from, + }, function(err, res) { + should.not.exist(err); + res.length.should.equal(0); + done(); + }); + }); + }); + + it('should return empty if no notifications exist in the given timespan', function(done) { + clock.tick(100 * 1000); + server.getNotifications({ + minTs: +Date.now() - (60 * 1000), + }, function(err, res) { + should.not.exist(err); + res.length.should.equal(0); + done(); + }); + }); + it('should contain walletId & creatorId on NewCopayer', function(done) { server.getNotifications({}, function(err, notifications) { should.not.exist(err); From d8fa5b7b8a7af337222bd632d08e72bc0b2e8470 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 15 Oct 2015 16:42:51 -0300 Subject: [PATCH 6/6] v0.4.0 --- package.json | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 0608da2..c3a76a2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-service", "description": "A service for Mutisig HD Bitcoin Wallets", "author": "BitPay Inc", - "version": "0.3.0", + "version": "0.4.0", "keywords": [ "bitcoin", "copay", @@ -64,18 +64,14 @@ "coveralls": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" }, "bitcoreNode": "./bitcorenode", - "contributors": [ - { - "name": "Braydon Fuller", - "email": "braydon@bitpay.com" - }, - { - "name": "Ivan Socolsky", - "email": "ivan@bitpay.com" - }, - { - "name": "Matias Alejo Garcia", - "email": "ematiu@gmail.com" - } - ] + "contributors": [{ + "name": "Braydon Fuller", + "email": "braydon@bitpay.com" + }, { + "name": "Ivan Socolsky", + "email": "ivan@bitpay.com" + }, { + "name": "Matias Alejo Garcia", + "email": "ematiu@gmail.com" + }] }