mirror of https://github.com/BTCPrivate/copay.git
413 lines
11 KiB
JavaScript
413 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
|
|
var _ = require('lodash');
|
|
var chai = chai || require('chai');
|
|
var sinon = sinon || require('sinon');
|
|
var should = chai.should();
|
|
var PluginManager = require('../js/models/PluginManager');
|
|
var Insight = require('../js/models/Insight');
|
|
|
|
var Identity = copay.Identity;
|
|
var Wallet = copay.Wallet;
|
|
var Passphrase = copay.Passphrase;
|
|
|
|
var FakeBlockchain = require('./mocks/FakeBlockchain');
|
|
|
|
var PERSISTED_PROPERTIES = (copay.Wallet || require('../js/models/Wallet')).PERSISTED_PROPERTIES;
|
|
|
|
function assertObjectEqual(a, b) {
|
|
PERSISTED_PROPERTIES.forEach(function(k) {
|
|
if (a[k] && b[k]) {
|
|
_.omit(a[k], 'name').should.be.deep.equal(b[k], k + ' differs');
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
describe('Identity model', function() {
|
|
var wallet;
|
|
var email = '[email protected]';
|
|
var password = 'password';
|
|
var blockchain;
|
|
|
|
var config = {
|
|
walletDefaults: {
|
|
requiredCopayers: 3,
|
|
totalCopayers: 5,
|
|
spendUnconfirmed: 1,
|
|
reconnectDelay: 100,
|
|
|
|
},
|
|
blockchain: {
|
|
host: 'test.insight.is',
|
|
port: 80,
|
|
schema: 'https'
|
|
},
|
|
networkName: 'testnet',
|
|
passphrase: {
|
|
iterations: 100,
|
|
storageSalt: 'mjuBtGybi/4=',
|
|
},
|
|
|
|
// network layer config
|
|
network: {
|
|
testnet: {
|
|
url: 'https://test-insight.bitpay.com:443'
|
|
},
|
|
livenet: {
|
|
url: 'https://insight.bitpay.com:443'
|
|
},
|
|
},
|
|
version: '0.0.1'
|
|
};
|
|
|
|
function getDefaultParams() {
|
|
var params = _.cloneDeep(config);
|
|
_.extend(params, {
|
|
email: email,
|
|
password: password
|
|
});
|
|
params.storage = sinon.stub();
|
|
params.storage.setCredentials = sinon.stub();
|
|
params.storage.getItem = sinon.stub();
|
|
params.storage.setItem = sinon.stub();
|
|
params.storage.setItem.onFirstCall().callsArgWith(2, null);
|
|
params.storage.setItem.onSecondCall().callsArgWith(2, null);
|
|
return params;
|
|
}
|
|
|
|
function getNewWallet(args) {
|
|
var w = sinon.stub();
|
|
w.getId = sinon.stub().returns('wid');
|
|
w.getStorageKey = sinon.stub().returns('wkey');
|
|
w.toObj = sinon.stub().returns({
|
|
obj: 1
|
|
});
|
|
w.getName = sinon.stub().returns('name');
|
|
w.on = sinon.stub();
|
|
w.netStart = sinon.stub();
|
|
w.args = args;
|
|
return w;
|
|
}
|
|
|
|
|
|
var walletClass = function(args) {
|
|
return getNewWallet(args);
|
|
};
|
|
|
|
function createIdentity(done) {
|
|
|
|
// TODO (eordano): Change this to proper dependency injection
|
|
var blockchain = new FakeBlockchain(config.blockchain);
|
|
var params = getDefaultParams();
|
|
blockchain.on = sinon.stub();
|
|
Wallet._newInsight = sinon.stub().returns(blockchain);
|
|
|
|
return {
|
|
blockchain: blockchain,
|
|
storage: params.storage,
|
|
wallet: wallet,
|
|
params: params
|
|
};
|
|
};
|
|
|
|
describe('new Identity()', function() {
|
|
it('returns an identity', function() {
|
|
var iden = new Identity(getDefaultParams());
|
|
should.exist(iden);
|
|
iden.walletDefaults.should.deep.equal(config.walletDefaults);
|
|
});
|
|
});
|
|
|
|
describe('Identity.create()', function() {
|
|
it('should create and store identity', function() {
|
|
var args = createIdentity();
|
|
args.blockchain.on = sinon.stub();
|
|
sinon.stub(Identity.prototype, 'store').yields(null);
|
|
Identity.create(args.params, function(err, iden) {
|
|
should.not.exist(err);
|
|
should.exist(iden);
|
|
should.exist(iden.wallets);
|
|
Identity.prototype.store.calledOnce.should.be.true;
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#open', function(done) {
|
|
it.skip('should return last focused wallet', function(done) {
|
|
var wallets = [{
|
|
id: 'wallet1',
|
|
store: sinon.stub().yields(null),
|
|
netStart: sinon.stub(),
|
|
}, {
|
|
id: 'wallet2',
|
|
store: sinon.stub().yields(null),
|
|
netStart: sinon.stub(),
|
|
}, {
|
|
id: 'wallet3',
|
|
store: sinon.stub().yields(null),
|
|
netStart: sinon.stub(),
|
|
}];
|
|
var args = createIdentity();
|
|
Identity.create(args.params, function(err, identity) {
|
|
// TODO: Add checks for what is this testing
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe.skip('#storeWallet', function() {
|
|
// TODO test storeWallet
|
|
});
|
|
|
|
|
|
describe('#createWallet', function() {
|
|
var iden = null;
|
|
var args = null;
|
|
beforeEach(function() {
|
|
args = createIdentity();
|
|
args.params.noWallets = true;
|
|
var old = Identity.prototype.createWallet;
|
|
Identity.create(args.params, function(err, res) {
|
|
iden = res;
|
|
});
|
|
});
|
|
it('should be able to create wallets with given pk', function(done) {
|
|
var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m';
|
|
args.storage.setItem = sinon.stub();
|
|
args.storage.setItem.onFirstCall().callsArg(2);
|
|
args.storage.setItem.onSecondCall().callsArg(2);
|
|
should.exist(walletClass, 'check walletClass stub');
|
|
iden.createWallet({
|
|
privateKeyHex: priv,
|
|
walletClass: walletClass,
|
|
}, function(err, w) {
|
|
should.not.exist(err);
|
|
w.args.privateKey.toObj().extendedPrivateKeyString.should.equal(priv);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should be able to create wallets with random pk', function(done) {
|
|
args.storage.setItem = sinon.stub();
|
|
args.storage.setItem.onCall(0).callsArg(2);
|
|
args.storage.setItem.onCall(1).callsArg(2);
|
|
args.storage.setItem.onCall(2).callsArg(2);
|
|
args.storage.setItem.onCall(3).callsArg(2);
|
|
iden.createWallet({
|
|
walletClass: walletClass,
|
|
}, function(err, w1) {
|
|
should.exist(w1);
|
|
|
|
iden.createWallet({
|
|
walletClass: walletClass,
|
|
}, function(err, w2) {
|
|
should.exist(w2);
|
|
w2.args.privateKey.toObj().extendedPrivateKeyString.should.not.equal(
|
|
w1.args.privateKey.toObj().extendedPrivateKeyString
|
|
); + done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#retrieveWalletFromStorage', function() {
|
|
|
|
|
|
it('should return wallet', function(done) {
|
|
var args = createIdentity();
|
|
args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"wallet": "fakeData"}');
|
|
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.not.exist(err);
|
|
opts.importWallet.calledOnce.should.equal(true);
|
|
should.exist(wallet);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#importWallet', function() {
|
|
it('should import a wallet, call the right encryption functions', function(done) {
|
|
var args = createIdentity();
|
|
args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"wallet": "fakeData"}');
|
|
var backup = Wallet.fromUntrustedObj;
|
|
args.params.noWallets = true;
|
|
sinon.stub().returns(args.wallet);
|
|
|
|
var fakeCrypto = {
|
|
kdf: sinon.stub().returns('passphrase'),
|
|
decrypt: sinon.stub().returns('{"walletId":123}'),
|
|
};
|
|
|
|
var opts = {
|
|
importWallet: sinon.stub().returns(getNewWallet()),
|
|
cryptoUtil: fakeCrypto,
|
|
};
|
|
|
|
Identity.create(args.params, function(err, iden) {
|
|
sinon.stub(iden, 'importWalletFromObj').yields(null);
|
|
iden.importEncryptedWallet(123, 'password', opts, function(err) {
|
|
should.not.exist(err);
|
|
fakeCrypto.decrypt.getCall(0).args[0].should.equal('password');
|
|
fakeCrypto.decrypt.getCall(0).args[1].should.equal(123);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
|
|
describe('#export', function() {
|
|
|
|
});
|
|
|
|
describe('#import', function() {
|
|
|
|
});
|
|
|
|
/**
|
|
* TODO (eordano): Move this to a different test file
|
|
*
|
|
describe('#pluginManager', function() {
|
|
it('should create a new PluginManager object', function() {
|
|
var pm = new PluginManager({plugins: { FakeLocalStorage: true }, pluginsPath: '../../test/mocks/'});
|
|
should.exist(pm);
|
|
});
|
|
});
|
|
|
|
describe('#Insight', function() {
|
|
it('should parse a uri', function() {
|
|
var uri = Insight.setCompleteUrl('http://someurl.bitpay.com:443');
|
|
should.exist(uri);
|
|
});
|
|
});
|
|
*/
|
|
|
|
describe('#joinWallet', 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;
|
|
});
|
|
});
|
|
|
|
it('should yield bad network error', 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: 'aWeirdNetworkName',
|
|
opts: {},
|
|
});
|
|
|
|
opts.privHex = undefined;
|
|
opts.Async = net;
|
|
iden.joinWallet(opts, function(err, w) {
|
|
err.should.equal('badNetwork');
|
|
done();
|
|
});
|
|
});
|
|
|
|
|
|
it('should callback with a join error in case of a problem', function(done) {
|
|
opts.privHex = undefined;
|
|
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('serverError').yields(null);
|
|
net.on.withArgs('data').yields('senderId', {
|
|
type: 'walletId',
|
|
networkName: iden.networkName,
|
|
});
|
|
opts.Async = net;
|
|
|
|
iden.joinWallet(opts, function(err, w) {
|
|
err.should.equal('joinError');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should return walletFull', function(done) {
|
|
net = sinon.stub();
|
|
net.on = sinon.stub();
|
|
net.start = sinon.stub();
|
|
net.start.onFirstCall().callsArg(1);
|
|
net.greet = sinon.stub();
|
|
iden.createWallet = sinon.stub();
|
|
iden.createWallet.onFirstCall().yields(null, null);
|
|
net.on.withArgs('data').yields('senderId', {
|
|
type: 'walletId',
|
|
networkName: 'testnet',
|
|
opts: {},
|
|
});
|
|
opts.privHex = undefined;
|
|
opts.Async = net;
|
|
|
|
iden.joinWallet(opts, function(err, w) {
|
|
err.should.equal('walletFull');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should accept a priv key as an input', function(done) {
|
|
net = sinon.stub();
|
|
net.on = sinon.stub();
|
|
net.start = sinon.stub();
|
|
net.start.onFirstCall().callsArg(1);
|
|
net.greet = sinon.stub();
|
|
iden.createWallet = sinon.stub();
|
|
var fakeWallet = {
|
|
sendWalletReady: _.noop
|
|
};
|
|
iden.createWallet.onFirstCall().yields(null, fakeWallet);
|
|
net.on.withArgs('data').yields('senderId', {
|
|
type: 'walletId',
|
|
networkName: 'testnet',
|
|
opts: {},
|
|
});
|
|
|
|
opts.privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd';
|
|
opts.Async = net;
|
|
iden.joinWallet(opts, function(err, w) {
|
|
w.should.equal(fakeWallet);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|