diff --git a/js/models/Identity.js b/js/models/Identity.js index 396e78ae5..8cda88980 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -379,8 +379,14 @@ Identity.prototype.setBackupNeeded = function(backupNeeded) { self.backupNeeded = !!backupNeeded; self.verifyChecksum(function(err, match) { - if (err) return cb(err); - if (!match) return cb('The profile is out of sync. Please re-login to get the latest changes.'); + if (err) { + log.error(err); + return; + } + if (!match) { + log.error('The profile is out of sync. Please re-login to get the latest changes.'); + return; + } self.store({ noWallets: true diff --git a/js/models/TxProposal.js b/js/models/TxProposal.js index 63d672093..ba3def705 100644 --- a/js/models/TxProposal.js +++ b/js/models/TxProposal.js @@ -154,12 +154,6 @@ TxProposal.prototype._check = function() { }; -TxProposal.prototype.trimForStorage = function() { - // TODO (remove builder / builderObj. utxos, etc) - // - return this; -}; - TxProposal.prototype.addMerchantData = function(merchantData) { preconditions.checkArgument(merchantData.pr); preconditions.checkArgument(merchantData.request_url); diff --git a/test/Identity.js b/test/Identity.js index c74572245..a7f728070 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -175,8 +175,122 @@ describe('Identity model', function() { done(); }); }); + + + it('should return an error', function(done) { + var storage = sinon.stub(); + storage.setCredentials = sinon.stub(); + + var opts = { + email: 'test@test.com', + password: '123', + network: { + testnet: { + url: 'https://test-insight.bitpay.com:443' + }, + livenet: { + url: 'https://insight.bitpay.com:443' + }, + }, + storage: storage, + }; + + var iden = new Identity(opts); + + storage.getItem = sinon.stub().yields('error', JSON.stringify(iden)); + + Identity.open(opts, function(err, res) { + should.exist(err); + done(); + }); + }); + + it('should return an error because data has bad format', function(done) { + var storage = sinon.stub(); + storage.setCredentials = sinon.stub(); + + var opts = { + email: 'test@test.com', + password: '123', + network: { + testnet: { + url: 'https://test-insight.bitpay.com:443' + }, + livenet: { + url: 'https://insight.bitpay.com:443' + }, + }, + storage: storage, + }; + + var iden = new Identity(opts); + + storage.getItem = sinon.stub().yields(null, '{badformat'); //bad format of object + + Identity.open(opts, function(err, res) { + should.exist(err); + done(); + }); + }); + }); + + describe('#verifyChecksum', function(done) { + it('should return an error', function(done) { + var storage = sinon.stub(); + storage.setCredentials = sinon.stub(); + + var opts = { + email: 'test@test.com', + password: '123', + network: { + testnet: { + url: 'https://test-insight.bitpay.com:443' + }, + livenet: { + url: 'https://insight.bitpay.com:443' + }, + }, + storage: storage, + }; + + var iden = new Identity(opts); + storage.getItem = sinon.stub().yields('error', null); + iden.verifyChecksum(function(err, res) { + should.exist(err); + done(); + }); + }); + + it('should return an error bad object format', function(done) { + var storage = sinon.stub(); + storage.setCredentials = sinon.stub(); + + var opts = { + email: 'test@test.com', + password: '123', + network: { + testnet: { + url: 'https://test-insight.bitpay.com:443' + }, + livenet: { + url: 'https://insight.bitpay.com:443' + }, + }, + storage: storage, + }; + + var iden = new Identity(opts); + storage.getItem = sinon.stub().yields(null, '{badformat'); + iden.verifyChecksum(function(err, res) { + should.exist(err); + done(); + }); + }); + }); + + describe('#resendVerificationEmail', function(done) { it('should resend verification email', function() { var storage = sinon.stub(); @@ -233,6 +347,20 @@ describe('Identity model', function() { iden.store.calledOnce.should.be.true; iden.store.getCall(0).args[0].noWallets.should.equal(true); }); + it('should not set the flag backupNeeded ', function() { + var iden = new Identity(getDefaultParams()); + iden.verifyChecksum = sinon.stub().yields('error', true); + iden.setBackupNeeded(false); + iden.backupNeeded.should.be.false; + iden.store.calledOnce.should.be.false; + }); + it('should not set the flag backupNeeded match returns false', function() { + var iden = new Identity(getDefaultParams()); + iden.verifyChecksum = sinon.stub().yields(null, false); + iden.setBackupNeeded(false); + iden.backupNeeded.should.be.false; + iden.store.calledOnce.should.be.false; + }); }); describe('#close', function(done) { @@ -481,6 +609,30 @@ describe('Identity model', function() { }); }); + it('should return error', function(done) { + args.storage.getItem = sinon.stub().yields(null, JSON.stringify(iden)); + args.storage.setItem = sinon.stub(); + iden.verifyChecksum = sinon.stub().yields('error'); + iden.createWallet({ + walletClass: walletClass, + }, function(err, w) { + should.exist(err); + done(); + }); + }); + + it('should return error because verifyChecksum returns false', function(done) { + args.storage.getItem = sinon.stub().yields(null, JSON.stringify(iden)); + args.storage.setItem = sinon.stub(); + iden.verifyChecksum = sinon.stub().yields(null, false); + iden.createWallet({ + walletClass: walletClass, + }, function(err, w) { + should.exist(err); + done(); + }); + }); + it('should be able to create wallets with random pk', function(done) { args.storage.getItem = sinon.stub().yields(null, JSON.stringify(iden)); args.storage.setItem = sinon.stub(); @@ -528,11 +680,61 @@ describe('Identity model', function() { }); }); }); + it('should return error', function(done) { + var args = createIdentity(); + args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"walletError'); + var backup = Wallet.fromUntrustedObj; + args.params.noWallets = true; + sinon.stub().returns(args.wallet); + + var opts = { + importWallet: sinon.stub().returns(getNewWallet()), + }; + + Identity.create(args.params, function(err, iden) { + iden.retrieveWalletFromStorage('dummy', opts, function(err, wallet) { + + should.exist(err); + done(); + }); + }); + }); }); describe('#importWalletFromObj', function() { + it('should return an error', function(done) { + var iden = new Identity(getDefaultParams()); + iden.verifyChecksum = sinon.stub().yields('error', true); + iden.importWalletFromObj({}, {}, function(err) { + should.exist(err); + done(); + }); + }); + it('should return an error case 2', function(done) { + var iden = new Identity(getDefaultParams()); + iden.verifyChecksum = sinon.stub().yields(null, false); + iden.importWalletFromObj({}, {}, function(err) { + should.exist(err); + done(); + }); + }); + + it('should return an error case 3', function(done) { + var iden = new Identity(getDefaultParams()); + iden.verifyChecksum = sinon.stub().yields(null, true); + iden.importWalletFromObj({}, { + importWallet: function() { + return false; + } + }, function(err) { + console.log(err); + should.exist(err); + done(); + }); + }); + it('should import a wallet, call the right encryption functions', function(done) { var args = createIdentity(); args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"wallet": "fakeData"}'); @@ -659,6 +861,34 @@ describe('Identity model', function() { done(); }); }); + + it('should return an error ', function(done) { + iden.addWallet(w); + iden.storage.getItem = sinon.stub().yields('error', JSON.stringify(iden)); + iden.deleteWallet('32', function(err) { + should.exist(err); + done(); + }); + }); + + it('should return an checksum error ', function(done) { + iden.addWallet(w); + iden.verifyChecksum = sinon.stub().yields(null, false); + iden.deleteWallet('32', function(err) { + should.exist(err); + done(); + }); + }); + + it('should return an error because of removeItem ', function(done) { + iden.addWallet(w); + iden.storage.getItem = sinon.stub().yields(null, JSON.stringify(iden)); + iden.storage.removeItem = sinon.stub().yields('error', JSON.stringify(iden)); + iden.deleteWallet('32', function(err) { + should.exist(err); + done(); + }); + }); }); @@ -690,6 +920,58 @@ describe('Identity model', function() { }); */ + describe('#bindWallet', function() { + var opts = { + secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', + nickname: 'test', + password: 'pass' + }; + var iden = null; + var args = null; + var net = null; + + beforeEach(function() { + args = createIdentity(); + args.params.Async = net = sinon.stub(); + net.cleanUp = sinon.spy(); + net.on = sinon.stub(); + net.start = sinon.spy(); + var old = Identity.prototype.createWallet; + Identity.create(args.params, function(err, res) { + iden = res; + iden.store.restore(); + }); + }); + + it('should bindWallet', function() { + var net = sinon.stub(); + + net.greet = sinon.stub(); + net.cleanUp = sinon.stub(); + net.start = sinon.stub().yields(null); + net.on = sinon.stub(); + net.on.withArgs('data').yields('senderId', { + type: 'walletId', + networkName: 'aWeirdNetworkName', + opts: {}, + }); + + opts.privHex = undefined; + opts.Async = net; + + var w = { + on: sinon.stub(), + getId: sinon.stub().returns('1'), + getName: sinon.stub().returns('wName') + } + iden.getWalletById = sinon.stub().returns(true); + iden.emitAndKeepAlive = sinon.spy(); + iden.bindWallet(w); + iden.emitAndKeepAlive.calledOnce.should.be.true; + }); + }); + + describe('#joinWallet', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', @@ -734,6 +1016,28 @@ describe('Identity model', function() { }); }); + it('should yield bad secret', function(done) { + var net = sinon.stub(); + + net.greet = sinon.stub(); + net.cleanUp = sinon.stub(); + net.start = sinon.stub().yields(null); + net.on = sinon.stub(); + net.on.withArgs('data').yields('senderId', { + type: 'walletId', + networkName: 'testnet', + opts: {}, + }); + + opts.privHex = undefined; + opts.Async = net; + iden.decodeSecret = sinon.stub().returns(false); + iden.joinWallet(opts, function(err, w) { + err.should.equal('badSecret'); + done(); + }); + }); + it('should callback with a join error in case of a problem', function(done) { opts.privHex = undefined; @@ -1267,6 +1571,47 @@ describe('Identity model', function() { }); + describe('readAndBindWallet', function(done) { + var opts; + var storage = sinon.stub(); + beforeEach(function() { + + storage.setCredentials = sinon.stub(); + storage.getItem = sinon.stub().yields({}); + opts = { + email: 'test@test.com', + password: '123', + network: { + testnet: { + url: 'https://test-insight.bitpay.com:443' + }, + livenet: { + url: 'https://insight.bitpay.com:443' + }, + }, + storage: storage, + }; + }); + it('should throw error', function(done) { + var iden = new Identity(opts); + iden.retrieveWalletFromStorage = sinon.stub().yields('error'); + iden.readAndBindWallet('1', function(err) { + should.exist(err); + done(); + }); + }); + it('should call addWallet', function(done) { + var iden = new Identity(opts); + iden.retrieveWalletFromStorage = sinon.stub().yields(null); + iden.addWallet = sinon.spy(); + iden.bindWallet = sinon.spy(); + iden.readAndBindWallet('1', function(err) { + iden.addWallet.callCount.should.be.equal(1); + iden.bindWallet.callCount.should.be.equal(1); + done(); + }); + }); + }); }); diff --git a/test/TxProposal.js b/test/TxProposal.js index 931ce4cfc..dc6d5490f 100644 --- a/test/TxProposal.js +++ b/test/TxProposal.js @@ -349,9 +349,6 @@ describe('TxProposal', function() { Buffer.isBuffer(info.script.getBuffer()).should.equal(true); }); - - - it('#getSignersPubKeys', function() { var txp = dummyProposal(); var pubkeys = txp.getSignersPubKeys(); @@ -431,6 +428,17 @@ describe('TxProposal', function() { it('OK', function() { dummyProposal({})._check(); }); + it('FAIL Invalid tx proposal', function() { + var txp = dummyProposal(); + var old = txp.builder.signhash; + txp.builder.signhash = 'ppp'; + (function() { + txp._check(); + }).should.throw('Invalid tx proposal'); + + txp.builder.signhash = old; + }); + it('FAIL ins', function() { (function() { dummyProposal({ @@ -438,6 +446,24 @@ describe('TxProposal', function() { })._check(); }).should.throw('no ins'); }); + + it('FAIL txp too big ', function() { + var txp = dummyProposal(); + var old_builder = txp.builder.build; + + var tx = { + getSize: function() { + return 90000; + } + }; + + txp.builder.build = sinon.stub().returns(tx);; + (function() { + txp._check(); + }).should.throw('BIG: Invalid TX proposal. Too big'); + txp.builder.build = old_builder; + }); + it('FAIL signhash SINGLE', function() { var txp = dummyProposal({ hashtype: Transaction.SIGHASH_SINGLE @@ -519,12 +545,29 @@ describe('TxProposal', function() { txp.addMerchantData(md); }).should.throw('outputs'); }); + it('NOK OUTS (case 3)', function() { md.outs = [{}, {}]; (function() { txp.addMerchantData(md); }).should.throw('outputs'); }); + + it('NOK OUTS (case 4)', function() { + txp.builder.vanilla.outs = '[1,2]'; + txp.merchant = {}; + txp.paymentProtocolURL = txp.merchant.request_url; + txp.merchant.total = 1; + txp.merchant.outs = [{ + amountSatStr: '1', + address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6' + }]; + + (function() { + txp._checkPayPro(); + }).should.throw('Wrong outs in Tx'); + }); + it('NOK Amount', function() { md.total = undefined; (function() { @@ -688,7 +731,7 @@ describe('TxProposal', function() { txp.creator.should.equal('creator'); txp.createdTs.should.gte(ts); txp.seenBy['creator'].should.equal(txp.createdTs); - }) + }); it("New tx should have only 1 signature", function() { var txp = dummyProposal(); var ts = Date.now(); @@ -705,7 +748,7 @@ describe('TxProposal', function() { 'creator2': 1 }); }).should.throw('only 1'); - }) + }); it("if signed, should not change ts", function() { var txp = dummyProposal(); @@ -724,7 +767,8 @@ describe('TxProposal', function() { txp.creator.should.equal('creator'); txp.signedBy['creator'].should.equal(1); txp.signedBy['pepe'].should.gte(ts); - }) + }); + }); }); diff --git a/test/Wallet.js b/test/Wallet.js index 3de9e0be2..c00a0d8f1 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -2081,12 +2081,12 @@ describe('Wallet model', function() { o.type.should.equal('publicKeyRing'); }); - // For some unknown reason this test times out on - // the Travis server, so we skip it for now. - it.skip('should lock incomming connections', function() { + + it('should lock incomming connections', function() { var obj = JSON.parse(pkr); - sinon.stub(w.network, 'send').returns(); - sinon.stub(w.network, 'lockIncommingConnections').returns(); + sinon.stub(w, 'subscribeToAddresses').returns(); + sinon.stub(w, '_lockIncomming').returns(); + sinon.stub(w, 'emitAndKeepAlive').returns(); obj.requiredCopayers = 3; obj.totalCopayers = 5; @@ -2096,8 +2096,10 @@ describe('Wallet model', function() { w._onPublicKeyRing('sender', { publicKeyRing: obj, }); - w.network.send.calledOnce.should.equal(false); // wasComplete - w.network.lockIncommingConnections.calledOnce.should.equal(true); + w.subscribeToAddresses.calledOnce.should.equal(true); + w._lockIncomming.calledOnce.should.equal(true); + w.emitAndKeepAlive.calledOnce.should.equal(true); + }); }); diff --git a/test/blockchain.Insight.js b/test/blockchain.Insight.js index b7772cb96..e5aa63a52 100644 --- a/test/blockchain.Insight.js +++ b/test/blockchain.Insight.js @@ -291,6 +291,7 @@ describe('Insight model', function() { }); }); + describe('getActivity', function() { it('should get activity for an innactive address', function(done) { var blockchain = new Insight(FAKE_OPTS); @@ -309,6 +310,19 @@ describe('Insight model', function() { }); }); + it('should not get activity because of error', function(done) { + var blockchain = new Insight(FAKE_OPTS); + + sinon.stub(blockchain, "getTransactions", function(addresses, from, to, cb) { + cb('error', []); + }); + + blockchain.getActivity(ADDRESSES, function(err, actives) { + chai.expect(err).to.be.not.null; + done(); + }); + }); + it('should get activity for active addresses', function(done) { var blockchain = new Insight(FAKE_OPTS); diff --git a/test/network.Async.js b/test/network.Async.js index 12dba5622..671796076 100644 --- a/test/network.Async.js +++ b/test/network.Async.js @@ -138,6 +138,59 @@ describe('Network / Async', function() { n2._deletePeer.calledOnce.should.equal(false); }); + it('should ignore messages', function() { + var n1 = createN(pk1); + var n2 = createN(pk2); + n2._deletePeer = sinon.spy(); + + var message = { + type: 'hello', + copayerId: cid1, + secretNumber: 'mySecret', + ts: 1 + }; + var enc = n1.encode(cid2, message); + + enc.ts = 1; + n2.ignoreMessageFromTs = 1; + n2._onMessage(enc); + n2._deletePeer.calledOnce.should.equal(false); + }); + + + it('should delete peer if copayer does not belong', function() { + var n1 = createN(pk1); + var n2 = createN(pk2); + n2.lockIncommingConnections([cid3]); + n2._deletePeer = sinon.spy(); + + var message = { + type: 'hello', + copayerId: cid1, + secretNumber: 'mySecret', + }; + var enc = n1.encode(cid2, message); + + n2._onMessage(enc); + n2._deletePeer.calledOnce.should.equal(true); + }); + + it('should emit data', function() { + var n1 = createN(pk1); + var n2 = createN(pk2); + n2.emit = sinon.spy(); + + var message = { + type: 'helloxxx', + copayerId: cid1, + secretNumber: 'mySecret', + }; + var enc = n1.encode(cid2, message); + + n2._onMessage(enc); + n2.emit.calledOnce.should.equal(true); + }); + it('should reject data sent from a peer with hijacked pubkey', function() { var n = createN(pk2); @@ -396,6 +449,13 @@ describe('Network / Async', function() { var n = createN(); n.getCopayerIds().length.should.be.equal(1); }); + + it('should return the allowed copayer ids', function() { + var n = createN(); + var lockIds = ['abc001', 'abc002']; + n.lockIncommingConnections(lockIds); + n.getCopayerIds().length.should.be.equal(2); + }); }); describe('#isOnline', function() { @@ -449,4 +509,41 @@ describe('Network / Async', function() { }); }); + describe('#_onError', function() { + it('should set criticalError ', function() { + var n = createN(); + expect(n.criticalError).to.be.undefined; + var myError = { + message: 'Some error' + }; + n._onError(myError); + n.criticalError.should.be.equal('Some error'); + }); + }); + + describe('#_setInboundPeerAuth', function() { + it('should set isInboundPeerAuth ', function() { + var n = createN(); + expect(n.isInboundPeerAuth['1']).to.be.undefined; + + n._setInboundPeerAuth('1'); + n.isInboundPeerAuth['1'].should.be.true; + }); + }); + + describe('#start', function() { + it('should not start ', function(done) { + var n = createN(); + var opts = { + privkey: 'fb23b9074ca5e7163719b86b41c7ce8348cf3d2839bb5f6125ef6efd5d40d7d3', + copayerId: '0311a10109320efb3646c832d3e140c6d9c4f69b16e73fc3f0c23b3d014ec77828' + }; + n.started = 1; + + n.start(opts, function() { + done(); + }); + }); + }); + });