diff --git a/lib/emailservice.js b/lib/emailservice.js index 5e78eb9..8dc6ab4 100644 --- a/lib/emailservice.js +++ b/lib/emailservice.js @@ -69,7 +69,7 @@ EmailService.prototype._getRecipientsList = function(notification, emailType, cb self.storage.fetchPreferences(notification.walletId, null, function(err, preferences) { if (err) return cb(err); - if (_.isEmpty(preferences)) return cb(null, {}); + if (_.isEmpty(preferences)) return cb(null, []); var recipients = _.compact(_.map(preferences, function(p) { if (!p.email) return; @@ -138,7 +138,7 @@ EmailService.prototype.sendEmail = function(notification, cb) { self._getRecipientsList(notification, emailType, function(err, list) { if (_.isEmpty(list)) return cb(); recipientsList = list; - return next(); + next(); }); }, function(next) { @@ -150,13 +150,15 @@ EmailService.prototype.sendEmail = function(notification, cb) { function(next) { self._getDataForTemplate(notification, next); }, - ], next); + ], function(err, res) { + next(err, res[0], res[1]); + }); }, function(template, data, next) { self._applyTemplate(template, data, next); }, function(content, next) { - _.each(recipientsList, function(recipient) { + async.map(recipientsList, function(recipient, next) { var email = Model.Email.create({ walletId: notification.walletId, copayerId: recipient.copayerId, @@ -167,14 +169,14 @@ EmailService.prototype.sendEmail = function(notification, cb) { self.storage.storeEmail(email, function(err) { return next(err, email); }); - }); + }, next); }, - function(email, next) { - self._send(email, next); + function(emails, next) { + async.each(emails, function(email, next) { + self._send(email, next); + }, next); }, ], cb); - - return cb(); }; module.exports = EmailService; diff --git a/lib/server.js b/lib/server.js index 1a15a87..a873576 100644 --- a/lib/server.js +++ b/lib/server.js @@ -85,6 +85,8 @@ WalletService.initialize = function(opts, cb) { emailService = new EmailService({ lock: lock, storage: storage, + mailer: opts.mailer, + email: opts.email, }); return cb(); }; diff --git a/test/integration/server.js b/test/integration/server.js index bb1e8b8..6b96620 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -27,15 +27,15 @@ var TestData = require('../testdata'); var helpers = {}; helpers.getAuthServer = function(copayerId, cb) { - var signatureStub = sinon.stub(WalletService.prototype, '_verifySignature'); - signatureStub.returns(true); + var verifyStub = sinon.stub(WalletService.prototype, '_verifySignature'); + verifyStub.returns(true); WalletService.getInstanceWithAuth({ copayerId: copayerId, message: 'dummy', signature: 'dummy', }, function(err, server) { + verifyStub.restore(); if (err || !server) throw new Error('Could not login as copayerId ' + copayerId); - signatureStub.restore(); return cb(server); }); }; @@ -132,7 +132,6 @@ helpers.toSatoshi = function(btc) { } }; -// Amounts in satoshis helpers.stubUtxos = function(server, wallet, amounts, cb) { var amounts = [].concat(amounts); @@ -211,16 +210,22 @@ helpers.createAddresses = function(server, wallet, main, change, cb) { }); }; -var db, storage, blockchainExplorer; +var db, storage, blockchainExplorer, mailer; function openDb(cb) { db = new tingodb.Db('./db/test', {}); + // HACK: There appears to be a bug in TingoDB's close function where the callback is not being executed + db.__close = db.close; + db.close = function(force, cb) { + this.__close(force, cb); + return cb(); + }; return cb(); }; function resetDb(cb) { - if (!db) return cb(); - db.dropDatabase(function(err) { + if (!storage.db) return cb(); + storage.db.dropDatabase(function(err) { return cb(); }); }; @@ -228,19 +233,27 @@ function resetDb(cb) { describe('Wallet service', function() { before(function(done) { - openDb(function() { - storage = new Storage({ - db: db - }); - done(); - }); + // openDb(function() { + // storage = new Storage({ + // db: db + // }); + // done(); + // }); + storage = new Storage(); + storage.connect({ + mongoDb: { + uri: 'mongodb://localhost:27017/bws_test' + } + }, done); }); beforeEach(function(done) { resetDb(function() { blockchainExplorer = sinon.stub(); + mailer = sinon.stub(); WalletService.initialize({ storage: storage, blockchainExplorer: blockchainExplorer, + mailer: mailer, }, done); }); }); @@ -3066,4 +3079,44 @@ describe('Wallet service', function() { }); }); }); + describe('Email notifications', function() { + var server, wallet; + beforeEach(function(done) { + mailer.sendMail = sinon.stub().yields(); + 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', + }, next); + }); + }, done); + }); + }); + afterEach(function() { + NotificationBroadcaster.removeAllListeners(); + }); + + it('should notify copayers a new tx proposal has been created', function(done) { + WalletService.onNotification(function(n) { + if (n.type != 'NewTxProposal') return; + var calls = mailer.sendMail.getCalls(); + calls.length.should.equal(2); + var recipients = _.pluck(_.map(calls, function(c) { + return c.args[0]; + }), 'to'); + _.difference(['copayer1@domain.com', 'copayer2@domain.com'], recipients).should.be.empty; + done(); + }); + helpers.stubUtxos(server, wallet, [1, 1], function() { + var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0); + server.createTx(txOpts, function(err, tx) { + should.not.exist(err); + }); + }); + }); + }); }); diff --git a/test/storage.js b/test/storage.js index f66ad46..db0d8a7 100644 --- a/test/storage.js +++ b/test/storage.js @@ -16,9 +16,16 @@ var db, storage; function openDb(cb) { db = new tingodb.Db('./db/test', {}); + // HACK: There appears to be a bug in TingoDB's close function where the callback is not being executed + db.__close = db.close; + db.close = function(force, cb) { + this.__close(force, cb); + return cb(); + }; return cb(); }; + function resetDb(cb) { if (!db) return cb(); db.dropDatabase(function(err) {