From bcdd8073959f048115cb80db59c6fd426498f6b5 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Sun, 15 Feb 2015 15:46:29 -0300 Subject: [PATCH] test rejection flow --- lib/model/txproposal.js | 19 +++---- lib/model/txproposalaction.js | 4 +- lib/server.js | 2 +- test/integration.js | 103 +++++++++++++++++++++++++++++----- 4 files changed, 100 insertions(+), 28 deletions(-) diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index 11546fd..f7c23e0 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -132,21 +132,16 @@ TxProposal.prototype.getActors = function() { * @return {Object} type / createdOn */ TxProposal.prototype.getActionBy = function(copayerId) { - var a = this.actions[copayerId]; - if (!a) return null; - - return { - type: a.type, - createdOn: a.createdOn, - }; + return this.actions[copayerId]; }; -TxProposal.prototype.addAction = function(copayerId, type, signatures, xpub) { +TxProposal.prototype.addAction = function(copayerId, type, comment, signatures, xpub) { var action = new TxProposalAction({ copayerId: copayerId, type: type, signatures: signatures, xpub: xpub, + comment: comment, }); this.actions[copayerId] = action; this._updateStatus(); @@ -191,19 +186,19 @@ TxProposal.prototype.sign = function(copayerId, signatures, xpub) { var t = this._getBitcoreTx(); try { this._addSignaturesToBitcoreTx(t, signatures, xpub); - this.addAction(copayerId, 'accept', signatures, xpub); + this.addAction(copayerId, 'accept', null, signatures, xpub); return true; } catch (e) { return false; } }; -TxProposal.prototype.reject = function(copayerId) { - this.addAction(copayerId, 'reject'); +TxProposal.prototype.reject = function(copayerId, reason) { + this.addAction(copayerId, 'reject', reason); }; TxProposal.prototype.isPending = function() { - return !_.any(['boradcasted', 'rejected'], this.status); + return !_.contains(['boradcasted', 'rejected'], this.status); }; TxProposal.prototype.isAccepted = function() { diff --git a/lib/model/txproposalaction.js b/lib/model/txproposalaction.js index 13fa98b..a215657 100644 --- a/lib/model/txproposalaction.js +++ b/lib/model/txproposalaction.js @@ -8,9 +8,10 @@ function TxProposalAction(opts) { this.type = opts.type || (opts.signatures ? 'accept' : 'reject'); this.signatures = opts.signatures; this.xpub = opts.xpub; + this.comment = opts.comment; }; -TxProposalAction.fromObj = function (obj) { +TxProposalAction.fromObj = function(obj) { var x = new TxProposalAction(); x.createdOn = obj.createdOn; @@ -18,6 +19,7 @@ TxProposalAction.fromObj = function (obj) { x.type = obj.type; x.signatures = obj.signatures; x.xpub = obj.xpub; + x.comment = obj.comment; return x; }; diff --git a/lib/server.js b/lib/server.js index 21b4d7f..579129a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -686,7 +686,7 @@ CopayServer.prototype.rejectTx = function(opts, cb) { if (txp.status != 'pending') return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending')); - txp.reject(self.copayerId); + txp.reject(self.copayerId, opts.reason); self.storage.storeTx(self.walletId, txp, function(err) { if (err) return cb(err); diff --git a/test/integration.js b/test/integration.js index 1faf0c1..8f6fb4c 100644 --- a/test/integration.js +++ b/test/integration.js @@ -877,6 +877,7 @@ describe('Copay server', function() { server.rejectTx({ txProposalId: txid, + reason: 'some reason', }, function(err) { should.not.exist(err); server.getPendingTxs({}, function(err, txs) { @@ -887,8 +888,9 @@ describe('Copay server', function() { var actors = tx.getActors(); actors.length.should.equal(1); actors[0].should.equal(wallet.copayers[0].id); - tx.getActionBy(wallet.copayers[0].id).type.should.equal('reject'); - + var action = tx.getActionBy(wallet.copayers[0].id); + action.type.should.equal('reject'); + action.comment.should.equal('some reason'); done(); }); }); @@ -1120,14 +1122,14 @@ describe('Copay server', function() { }); describe('Tx proposal workflow', function() { - var server, wallet, utxos; + var server, wallet; beforeEach(function(done) { helpers.createAndJoinWallet(2, 3, function(s, w) { server = s; wallet = w; server.createAddress({}, function(err, address) { - helpers.createUtxos(server, wallet, _.range(1, 9), function(inutxos) { - utxos = inutxos; + helpers.createUtxos(server, wallet, _.range(1, 9), function(utxos) { + helpers.stubBlockExplorer(server, utxos); done(); }); }); @@ -1135,7 +1137,6 @@ describe('Copay server', function() { }); it('other copayers should see pending proposal created by one copayer', function(done) { - helpers.stubBlockExplorer(server, utxos); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey); server.createTx(txOpts, function(err, txp) { should.not.exist(err); @@ -1152,15 +1153,94 @@ describe('Copay server', function() { }); }); - it.skip('tx proposals should not be broadcast until quorum is reached', function(done) { + it.skip('tx proposals should not be broadcast until quorum is reached', function(done) {}); + it('tx proposals should accept as many rejections as possible without finally rejecting', function(done) { + var txpId; + async.series([ + + function(next) { + var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey); + server.createTx(txOpts, function(err, txp) { + txpId = txp.id; + should.not.exist(err); + should.exist.txp; + next(); + }); + }, + function(next) { + server.getPendingTxs({}, function(err, txps) { + should.not.exist(err); + txps.length.should.equal(1); + var txp = txps[0]; + _.keys(txp.actions).should.be.empty; + next(); + }); + }, + function(next) { + server.rejectTx({ + txProposalId: txpId, + reason: 'just because' + }, function(err) { + should.not.exist(err); + next(); + }); + }, + function(next) { + server.getPendingTxs({}, function(err, txps) { + should.not.exist(err); + txps.length.should.equal(1); + var txp = txps[0]; + txp.isPending().should.be.true; + txp.isRejected().should.be.false; + txp.isAccepted().should.be.false; + _.keys(txp.actions).length.should.equal(1); + var action = txp.actions[wallet.copayers[0].id]; + action.type.should.equal('reject'); + action.comment.should.equal('just because'); + next(); + }); + }, + function(next) { + helpers.getAuthServer(wallet.copayers[1].id, function(server, wallet) { + server.rejectTx({ + txProposalId: txpId, + reason: 'some other reason' + }, function(err) { + should.not.exist(err); + next(); + }); + }); + }, + function(next) { + server.getPendingTxs({}, function(err, txps) { + should.not.exist(err); + txps.length.should.equal(0); + next(); + }); + }, + function(next) { + server.getTx({ + id: txpId + }, function(err, txp) { + should.not.exist(err); + txp.isPending().should.be.false; + txp.isRejected().should.be.true; + txp.isAccepted().should.be.false; + _.keys(txp.actions).length.should.equal(2); + next(); + }); + }, + ], + function() { + done(); + }); }); - it.skip('tx proposals should accept as many rejections as possible without finally rejecting', function(done) {}); - it.skip('proposal creator should be able to delete proposal if there are no other signatures', function(done) {}); }); + describe('#getTxs', function() { var server, wallet, clock; @@ -1256,8 +1336,6 @@ describe('Copay server', function() { var server, wallet; beforeEach(function(done) { - if (server) return done(); - console.log('\tCreating TXS...'); helpers.createAndJoinWallet(1, 1, function(s, w) { server = s; wallet = w; @@ -1304,9 +1382,6 @@ describe('Copay server', function() { }); }); - - - it('should pull the first 5 notifications after wallet creation', function(done) { server.getNotifications({ minTs: 0,