Browse Source

more tests for notifications

activeAddress
Matias Alejo Garcia 10 years ago
parent
commit
5757ec5e06
  1. 11
      lib/model/notification.js
  2. 6
      lib/model/txproposal.js
  3. 27
      lib/server.js
  4. 16
      lib/storage.js
  5. 120
      test/integration.js

11
lib/model/notification.js

@ -25,23 +25,22 @@ var Uuid = require('uuid');
*
*/
function Notification(opts) {
opts = opts || {};
this.createdOn = Math.floor(Date.now() / 1000);
this.id = ('000000000000' + this.createdOn).slice(-12) + Uuid.v4();
var now = Date.now();
this.createdOn = Math.floor(now / 1000);
this.id = ('00000000000000' + now).slice(-14) + ('0000' + opts.ticker||0).slice(-4) ;
this.type = opts.type || 'general';
this.data = opts.data;
};
Notification.prototype.fromObj = function(obj) {
Notification.fromObj = function(obj) {
var x= new Notification();
x.createdOn = obj.createdOn;
x.type = obj.type,
x.data = opts.data;
x.data = obj.data;
return x;
};

6
lib/model/txproposal.js

@ -13,8 +13,10 @@ function TxProposal(opts) {
opts = opts || {};
this.version = VERSION;
this.createdOn = Math.floor(Date.now() / 1000);
this.id = ('000000000000' + this.createdOn).slice(-12) + Uuid.v4();
var now = Date.now();
this.createdOn = Math.floor(now / 1000);
this.id = ('00000000000000' + now).slice(-14) + Uuid.v4();
this.creatorId = opts.creatorId;
this.toAddress = opts.toAddress;
this.amount = opts.amount;

27
lib/server.js

@ -36,6 +36,7 @@ var storage;
function CopayServer() {
if (!initialized) throw new Error('Server not initialized');
this.storage = storage;
this.notifyTicker = 0;
};
nodeutil.inherits(CopayServer, events.EventEmitter);
@ -61,7 +62,8 @@ CopayServer.initialize = function(opts) {
*/
CopayServer.getInstanceWithAuth = function(opts, cb) {
if (!Utils.checkRequired(opts, ['copayerId', 'message', 'signature'])) return cb(new ClientError('Required argument missing'));
if (!Utils.checkRequired(opts, ['copayerId', 'message', 'signature']))
return cb(new ClientError('Required argument missing'));
var server = new CopayServer();
server.storage.fetchCopayerLookup(opts.copayerId, function(err, copayer) {
@ -163,6 +165,7 @@ CopayServer.prototype._notify = function(type, data) {
var n = new Notification({
type: type,
data: data,
ticker: this.notifyTicker++,
});
this.storage.storeNotification(walletId, n, function() {
self.emit(n);
@ -180,9 +183,11 @@ CopayServer.prototype._notify = function(type, data) {
CopayServer.prototype.joinWallet = function(opts, cb) {
var self = this;
if (!Utils.checkRequired(opts, ['walletId', 'name', 'xPubKey', 'xPubKeySignature'])) return cb(new ClientError('Required argument missing'));
if (!Utils.checkRequired(opts, ['walletId', 'name', 'xPubKey', 'xPubKeySignature']))
return cb(new ClientError('Required argument missing'));
if (_.isEmpty(opts.name)) return cb(new ClientError('Invalid copayer name'));
if (_.isEmpty(opts.name))
return cb(new ClientError('Invalid copayer name'));
Utils.runLocked(opts.walletId, cb, function(cb) {
self.storage.fetchWallet(opts.walletId, function(err, wallet) {
@ -196,7 +201,8 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
if (_.find(wallet.copayers, {
xPubKey: opts.xPubKey
})) return cb(new ClientError('CINWALLET', 'Copayer already in wallet'));
if (wallet.copayers.length == wallet.n) return cb(new ClientError('WFULL', 'Wallet full'));
if (wallet.copayers.length == wallet.n)
return cb(new ClientError('WFULL', 'Wallet full'));
var copayer = new Copayer({
name: opts.name,
@ -209,6 +215,7 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
self._notify('NewCopayer', {
walletId: opts.walletId,
copayerId: copayer.id,
});
return cb(err, copayer.id);
});
@ -227,7 +234,8 @@ CopayServer.prototype.createAddress = function(opts, cb) {
Utils.runLocked(self.walletId, cb, function(cb) {
self.getWallet({}, function(err, wallet) {
if (err) return cb(err);
if (!wallet.isComplete()) return cb(new ClientError('Wallet is not complete'));
if (!wallet.isComplete())
return cb(new ClientError('Wallet is not complete'));
var address = wallet.createAddress(false);
@ -624,7 +632,7 @@ CopayServer.prototype.signTx = function(opts, cb) {
self.storage.storeTx(self.walletId, txp, function(err) {
if (err) return cb(err);
self._notify('newOutgoingTx', {
self._notify('NewOutgoingTx', {
txProposalId: opts.txProposalId,
txid: txid
});
@ -704,6 +712,8 @@ CopayServer.prototype.getPendingTxs = function(opts, cb) {
/**
* Retrieves pending transaction proposals in the range (maxTs-minTs)
* Times are in UNIX EPOCH
*
* @param {Object} opts.minTs (defaults to 0)
* @param {Object} opts.maxTs (defaults to now)
* @param {Object} opts.limit
@ -720,10 +730,13 @@ CopayServer.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
* @returns {Notification[]} Notifications, first newer
* @param {Object} opts.reverse (default false)
* @returns {Notification[]} Notifications
*/
CopayServer.prototype.getNotifications = function(opts, cb) {
var self = this;

16
lib/storage.js

@ -31,7 +31,7 @@ var opKey = function(key) {
var MAX_TS = '999999999999';
var opKeyTs = function(key) {
return key ? '!' + ('000000000000' + key).slice(-12) : '';
return key ? '!' + ('00000000000000' + key).slice(-14) : '';
};
@ -144,7 +144,7 @@ Storage.prototype.fetchPendingTxs = function(walletId, cb) {
};
/**
* fetchTxs
* fetchTxs. Times are in UNIX EPOCH (seconds)
*
* @param walletId
* @param opts.minTs
@ -155,8 +155,8 @@ Storage.prototype.fetchTxs = function(walletId, opts, cb) {
var txs = [];
opts = opts || {};
opts.limit = _.isNumber(opts.limit) ? parseInt(opts.limit) : -1;
opts.minTs = _.isNumber(opts.minTs) ? ('000000000000' + parseInt(opts.minTs)).slice(-12) : 0;
opts.maxTs = _.isNumber(opts.maxTs) ? ('000000000000' + parseInt(opts.maxTs)).slice(-12) : MAX_TS;
opts.minTs = _.isNumber(opts.minTs) ? ('00000000000' + parseInt(opts.minTs)).slice(-11) : 0;
opts.maxTs = _.isNumber(opts.maxTs) ? ('00000000000' + parseInt(opts.maxTs)).slice(-11) : MAX_TS;
var key = KEY.TXP(walletId, opts.minTs);
var endkey = KEY.TXP(walletId, opts.maxTs);
@ -192,8 +192,8 @@ Storage.prototype.fetchNotifications = function(walletId, opts, cb) {
var txs = [];
opts = opts || {};
opts.limit = _.isNumber(opts.limit) ? parseInt(opts.limit) : -1;
opts.minTs = _.isNumber(opts.minTs) ? ('000000000000' + parseInt(opts.minTs)).slice(-12) : 0;
opts.maxTs = _.isNumber(opts.maxTs) ? ('000000000000' + parseInt(opts.maxTs)).slice(-12) : MAX_TS;
opts.minTs = _.isNumber(opts.minTs) ? ('00000000000000' + parseInt(opts.minTs)).slice(-14) : 0;
opts.maxTs = _.isNumber(opts.maxTs) ? ('00000000000000' + parseInt(opts.maxTs)).slice(-14) : MAX_TS;
var key = KEY.NOTIFICATION(walletId, opts.minTs);
var endkey = KEY.NOTIFICATION(walletId, opts.maxTs);
@ -201,11 +201,11 @@ Storage.prototype.fetchNotifications = function(walletId, opts, cb) {
this.db.createReadStream({
gt: key,
lt: endkey + '~',
reverse: true,
reverse: opts.reverse,
limit: opts.limit,
})
.on('data', function(data) {
txs.push(TxProposal.fromObj(data.value));
txs.push(Notification.fromObj(data.value));
})
.on('error', function(err) {
if (err.notFound) return cb();

120
test/integration.js

@ -1168,6 +1168,126 @@ describe('Copay server', function() {
});
describe('Notifications', function() {
var server, wallet, copayerPriv;
beforeEach(function(done) {
if (server) return done();
console.log('\tCreating TXS...');
helpers.createAndJoinWallet(1, 1, function(s, w, c) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, helpers.toSatoshi(_.range(4)), function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.01, null, copayerPriv[0].privKey);
async.eachSeries(_.range(3), function(i, next) {
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
next();
});
}, function(err) {
return done(err);
});
});
});
});
});
it('should pull the last 5 notifications after 3 TXs', function(done) {
server.getNotifications({
limit: 5,
reverse: true,
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['NewTxProposal', 'NewTxProposal', 'NewTxProposal', 'NewAddress', 'NewAddress']);
done();
});
});
it('should pull the first 5 notifications after wallet creation', function(done) {
server.getNotifications({
minTs: 0,
limit: 5
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['NewCopayer', 'NewAddress', 'NewAddress', 'NewAddress', 'NewAddress']);
done();
});
});
it('should notify sign and acceptance', function(done) {
server.getPendingTxs({}, function(err, txs) {
var tx = txs[0];
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
server.signTx({
txProposalId: tx.id,
signatures: signatures,
}, function(err) {
server.getNotifications({
limit: 3,
reverse: true,
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['TxProposalFinallyAccepted', 'TxProposalAcceptedBy', 'NewTxProposal']);
done();
});
});
});
});
it('should notify rejection', function(done) {
server.getPendingTxs({}, function(err, txs) {
var tx = txs[1];
server.rejectTx({
txProposalId: tx.id,
}, function(err) {
should.not.exist(err);
server.getNotifications({
limit: 2,
reverse: true,
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['TxProposalFinallyRejected', 'TxProposalRejectedBy']);
done();
});
});
});
});
it('should notify sign, acceptance, and broadcast', function(done) {
server.getPendingTxs({}, function(err, txs) {
var tx = txs[2];
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
helpers.stubBlockExplorer(server, [], '1122334455');
server.signTx({
txProposalId: tx.id,
signatures: signatures,
}, function(err) {
server.getNotifications({
limit: 3,
reverse: true,
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['NewOutgoingTx','TxProposalFinallyAccepted', 'TxProposalAcceptedBy']);
done();
});
});
});
});
});
describe('#removeWallet', function() {
var server, wallet, clock;

Loading…
Cancel
Save