refactor WalletFactory test to use sinon instead of mocks WIP

This commit is contained in:
Matias Alejo Garcia 2014-09-13 10:25:13 -03:00
commit c3574ae57f
9 changed files with 223 additions and 114 deletions

3
.gitignore vendored
View file

@ -77,5 +77,8 @@ dist/windows
dist/*.dmg dist/*.dmg
dist/*.tar.gz dist/*.tar.gz
dist/*.exe dist/*.exe
<<<<<<< HEAD
doc/ doc/
/node_modules
/*-cov

View file

@ -21,8 +21,6 @@ if (localConfig) {
var log = function() { var log = function() {
if (config.verbose) console.log(arguments); if (config.verbose) console.log(arguments);
} }
var copayApp = window.copayApp = angular.module('copayApp', [
var modules = [ var modules = [
'ngRoute', 'ngRoute',

View file

@ -12,7 +12,7 @@ function Storage(opts) {
this.__uniqueid = ++id; this.__uniqueid = ++id;
if (opts.password) if (opts.password)
this._setPassphrase(opts.password); this.setPassphrase(opts.password);
try { try {
this.storage = opts.storage || localStorage; this.storage = opts.storage || localStorage;
@ -33,7 +33,7 @@ Storage.prototype._getPassphrase = function() {
return pps[this.__uniqueid]; return pps[this.__uniqueid];
} }
Storage.prototype._setPassphrase = function(password) { Storage.prototype.setPassphrase = function(password) {
pps[this.__uniqueid] = password; pps[this.__uniqueid] = password;
} }
@ -231,6 +231,7 @@ Storage.prototype.getWallets = function(cb) {
Storage.prototype.deleteWallet = function(walletId, cb) { Storage.prototype.deleteWallet = function(walletId, cb) {
preconditions.checkArgument(walletId); preconditions.checkArgument(walletId);
preconditions.checkArgument(cb); preconditions.checkArgument(cb);
var err;
var toDelete = {}; var toDelete = {};
toDelete['nameFor::' + walletId] = 1; toDelete['nameFor::' + walletId] = 1;
@ -245,11 +246,15 @@ Storage.prototype.deleteWallet = function(walletId, cb) {
}); });
var l = toDelete.length, var l = toDelete.length,
i = 0; j = 0;
if (!l)
return cb(new Error('WNOTFOUND: Wallet not found'));
for (var i in toDelete) { for (var i in toDelete) {
this.removeGlobal(i, function() { this.removeGlobal(i, function() {
if (++i == l) if (++j == l)
return cb(); return cb(err);
}); });
} }
}; };

View file

@ -1,5 +1,6 @@
'use strict'; 'use strict';
var preconditions = require('preconditions').singleton(); var preconditions = require('preconditions').singleton();
var log = require('../../log');
function PluginManager(config) { function PluginManager(config) {
this.registered = {}; this.registered = {};
@ -11,7 +12,7 @@ function PluginManager(config) {
if (!config.plugins[pluginName]) if (!config.plugins[pluginName])
continue; continue;
console.log('Loading plugin: ' + pluginName); log.info('Loading plugin: ' + pluginName);
var pluginClass = require('../plugins/' + pluginName); var pluginClass = require('../plugins/' + pluginName);
var pluginObj = new pluginClass(config[pluginName]); var pluginObj = new pluginClass(config[pluginName]);
pluginObj.init(); pluginObj.init();

View file

@ -64,7 +64,7 @@ function Wallet(opts) {
'publicKeyRing', 'txProposals', 'privateKey', 'version', 'publicKeyRing', 'txProposals', 'privateKey', 'version',
'reconnectDelay' 'reconnectDelay'
].forEach(function(k) { ].forEach(function(k) {
preconditions.checkArgument(!_.isUndefined(opts[k]), 'missing required option for Wallet: ' + k); preconditions.checkArgument(!_.isUndefined(opts[k]), 'MISSOPT: missing required option for Wallet: ' + k);
self[k] = opts[k]; self[k] = opts[k];
}); });
preconditions.checkArgument(!copayConfig.forceNetwork || this.getNetworkName() === copayConfig.networkName, preconditions.checkArgument(!copayConfig.forceNetwork || this.getNetworkName() === copayConfig.networkName,

View file

@ -120,7 +120,7 @@ WalletFactory.prototype.fromObj = function(obj, skipFields) {
* @return {Wallet} * @return {Wallet}
*/ */
WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) { WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) {
this.storage._setPassphrase(password); this.storage.setPassphrase(password);
var walletObj = this.storage.import(base64); var walletObj = this.storage.import(base64);
if (!walletObj) return false; if (!walletObj) return false;
var w = this.fromObj(walletObj, skipFields); var w = this.fromObj(walletObj, skipFields);
@ -148,18 +148,34 @@ WalletFactory.prototype.import = function(base64, password, skipFields) {
* @desc Retrieve a wallet from storage * @desc Retrieve a wallet from storage
* @param {string} walletId - the wallet id * @param {string} walletId - the wallet id
* @param {string[]} skipFields - parameters to ignore when importing * @param {string[]} skipFields - parameters to ignore when importing
* @return {Wallet} * @param {function} callback - {err, Wallet}
*/ */
WalletFactory.prototype.read = function(walletId, skipFields, cb) { WalletFactory.prototype.read = function(walletId, skipFields, cb) {
var self = this, err; var self = this,
err;
var obj = {}; var obj = {};
obj.id = walletId;
this.storage.getMany(walletId, Wallet.PERSISTED_PROPERTIES, function(ret) { this.storage.getMany(walletId, Wallet.PERSISTED_PROPERTIES, function(ret) {
for (var ii in ret) { for (var ii in ret) {
obj[ii] = ret[ii]; obj[ii] = ret[ii];
} }
return cb(err, self.fromObj(obj, skipFields));
if (!_.any(_.values(obj)))
return cb(new Error('Wallet not found'));
var w, err;
obj.id = walletId;
try {
w = self.fromObj(obj, skipFields);
} catch (e) {
if (e && e.message && e.message.indexOf('MISSOPTS')) {
err = new Error('Could not read: ' + walletId);
} else {
err = e;
}
w = null;
}
return cb(err, w);
}); });
}; };
@ -221,7 +237,7 @@ WalletFactory.prototype.create = function(opts, cb) {
}); });
log.debug('\t### TxProposals Initialized'); log.debug('\t### TxProposals Initialized');
this.storage._setPassphrase(opts.passphrase); this.storage.setPassphrase(opts.passphrase);
opts.storage = this.storage; opts.storage = this.storage;
opts.network = this.networks[opts.networkName]; opts.network = this.networks[opts.networkName];
@ -272,8 +288,8 @@ WalletFactory.prototype._checkVersion = function(inVersion) {
WalletFactory.prototype.open = function(walletId, passphrase, cb) { WalletFactory.prototype.open = function(walletId, passphrase, cb) {
preconditions.checkArgument(cb); preconditions.checkArgument(cb);
var self = this; var self = this;
self.storage._setPassphrase(passphrase); self.storage.setPassphrase(passphrase);
self.read(err, walletId, null, function(w) { self.read(walletId, null, function(err, w) {
if (err) return cb(err); if (err) return cb(err);
w.store(function(err) { w.store(function(err) {
@ -285,11 +301,11 @@ WalletFactory.prototype.open = function(walletId, passphrase, cb) {
}; };
WalletFactory.prototype.getWallets = function(cb) { WalletFactory.prototype.getWallets = function(cb) {
var ret = this.storage.getWallets(function(ret) { this.storage.getWallets(function(ret) {
ret.forEach(function(i) { ret.forEach(function(i) {
i.show = i.name ? ((i.name + ' <' + i.id + '>')) : i.id; i.show = i.name ? ((i.name + ' <' + i.id + '>')) : i.id;
}); });
return cb(ret); return cb(null, ret);
}); });
}; };
@ -303,9 +319,12 @@ WalletFactory.prototype.getWallets = function(cb) {
*/ */
WalletFactory.prototype.delete = function(walletId, cb) { WalletFactory.prototype.delete = function(walletId, cb) {
var s = this.storage; var s = this.storage;
s.deleteWallet(walletId); s.deleteWallet(walletId, function(err) {
s.setLastOpened(undefined); if (err) return cb(err);
return cb(); s.setLastOpened(null, function(err) {
return cb(err);
});
});
}; };
/** /**

View file

@ -13,7 +13,7 @@
"dependencies": { "dependencies": {
"browser-request": "^0.3.2", "browser-request": "^0.3.2",
"inherits": "^2.0.1", "inherits": "^2.0.1",
"mocha": "^1.18.2", "mocha": "^1.21.4",
"mocha-lcov-reporter": "0.0.1", "mocha-lcov-reporter": "0.0.1",
"optimist": "^0.6.1", "optimist": "^0.6.1",
"preconditions": "^1.0.7", "preconditions": "^1.0.7",
@ -27,12 +27,14 @@
"chrome": "source browser-extensions/chrome/build.sh", "chrome": "source browser-extensions/chrome/build.sh",
"setup-shell": "node shell/scripts/download-atom-shell.js", "setup-shell": "node shell/scripts/download-atom-shell.js",
"start": "node server.js", "start": "node server.js",
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test", "coverage": "mochacoverage",
"test": "sh test/run.sh", "test": "mocha --ui exports --reporter spec",
"dist": "node shell/scripts/dist.js", "dist": "node shell/scripts/dist.js",
"sign": "gpg -u 1112CFA1 --output browser-extensions/firefox/copay.xpi.sig --detach-sig browser-extensions/firefox/copay.xpi; gpg -u 1112CFA1 --output browser-extensions/chrome/copay-chrome-extension.zip.sig --detach-sig browser-extensions/chrome/copay-chrome-extension.zip", "sign": "gpg -u 1112CFA1 --output browser-extensions/firefox/copay.xpi.sig --detach-sig browser-extensions/firefox/copay.xpi; gpg -u 1112CFA1 --output browser-extensions/chrome/copay-chrome-extension.zip.sig --detach-sig browser-extensions/chrome/copay-chrome-extension.zip",
"verify": "gpg --verify browser-extensions/firefox/copay.xpi.sig browser-extensions/firefox/copay.xpi; gpg --verify browser-extensions/chrome/copay-chrome-extension.zip.sig browser-extensions/chrome/copay-chrome-extension.zip", "verify": "gpg --verify browser-extensions/firefox/copay.xpi.sig browser-extensions/firefox/copay.xpi; gpg --verify browser-extensions/chrome/copay-chrome-extension.zip.sig browser-extensions/chrome/copay-chrome-extension.zip",
"postinstall": "./node_modules/.bin/grunt" "postinstall": "./node_modules/.bin/grunt",
"monitor": "mocha --ui exports --reporter min --watch",
"debugtest": "mocha --debug-brk --ui exports --reporter spec"
}, },
"keywords": [ "keywords": [
"wallet", "wallet",
@ -81,7 +83,8 @@
"shelljs": "0.3.0", "shelljs": "0.3.0",
"socket.io-client": "1.0.6", "socket.io-client": "1.0.6",
"travis-cov": "0.2.5", "travis-cov": "0.2.5",
"uglifyify": "1.2.3" "uglifyify": "1.2.3",
"mochawrapper": ""
}, },
"main": "app.js", "main": "app.js",
"homepage": "https://github.com/bitpay/copay", "homepage": "https://github.com/bitpay/copay",

View file

@ -21,9 +21,9 @@ FakeStorage.prototype.getGlobal = function(id, cb) {
}; };
FakeStorage.prototype.getMany = function(wid, fields, cb) { FakeStorage.prototype.getMany = function(wid, fields, cb) {
var self= this; var self = this;
var ret = []; var ret = [];
for(var ii in fields){ for (var ii in fields) {
var k = fields[ii]; var k = fields[ii];
ret[k] = this.storage[wid + '::' + k]; ret[k] = this.storage[wid + '::' + k];
} }
@ -113,14 +113,23 @@ FakeStorage.prototype.deleteWallet = function(walletId, cb) {
toDelete[key] = 1; toDelete[key] = 1;
} }
} }
var l = toDelete.length,
j = 0;
if (!l)
return cb(new Error('WNOTFOUND: Wallet not found'));
for (var i in toDelete) { for (var i in toDelete) {
this.removeGlobal(i); this.removeGlobal(i, cb);
if (++j == l)
return cb(err);
} }
}; };
FakeStorage.prototype.getName = function(walletId, cb) { FakeStorage.prototype.getName = function(walletId, cb) {
return this.getGlobal('nameFor::' + walletId, cb); this.getGlobal('nameFor::' + walletId, cb);
}; };
@ -131,15 +140,16 @@ FakeStorage.prototype.setName = function(walletId, name, cb) {
FakeStorage.prototype.getWallets = function(cb) { FakeStorage.prototype.getWallets = function(cb) {
var wallets = []; var wallets = [];
var ids = this.getWalletIds(); var self= this;
this.getWalletIds(function(ids) {
for (var i in ids) { for (var i in ids) {
wallets.push({ wallets.push({
id: ids[i], id: ids[i],
name: this.getName(ids[i]), name: self.storage['nameFor::' + ids[i]],
}); });
} }
return cb(wallets); return cb(wallets);
});
}; };
FakeStorage.prototype.setFromObj = function(walletId, obj, cb) { FakeStorage.prototype.setFromObj = function(walletId, obj, cb) {

View file

@ -155,8 +155,8 @@ describe('WalletFactory model', function() {
it('should be able to get wallets', function(done) { it('should be able to get wallets', function(done) {
var wf = new WalletFactory(config, '0.0.1'); var wf = new WalletFactory(config, '0.0.1');
wf.create(null, function(err,w){ wf.create(null, function(err, w) {
wf.read(w.id, [], function(err, w2){ wf.read(w.id, [], function(err, w2) {
should.exist(w2); should.exist(w2);
w2.id.should.equal(w.id); w2.id.should.equal(w.id);
done(); done();
@ -312,107 +312,177 @@ describe('WalletFactory model', function() {
'requiredCopayers': 2, 'requiredCopayers': 2,
'totalCopayers': 3 'totalCopayers': 3
}; };
wf.create(opts, function(err,w){ wf.create(opts, function(err, w) {
should.not.exist(err); should.not.exist(err);
should.exist(w); should.exist(w);
done(); done();
}); });
}); });
it('should be able to get current wallets', function() { it('should be able to get current wallets', function(done) {
var wf = new WalletFactory(config, '0.0.1'); var wf = new WalletFactory(config, '0.0.1');
var ws = wf.getWallets(); wf.create({
var w = wf.create({
name: 'test wallet' name: 'test wallet'
}); }, function(err, ws) {
should.not.exist(err);
ws = wf.getWallets(); wf.getWallets(function(err, ws) {
ws.length.should.equal(1); should.not.exist(err);
ws[0].name.should.equal('test wallet'); ws.length.should.equal(1);
}); ws[0].name.should.equal('test wallet');
done();
it('should be able to delete wallet', function(done) { });
var wf = new WalletFactory(config, '0.0.1');
var w = wf.create({
name: 'test wallet'
});
var ws = wf.getWallets();
ws.length.should.equal(1);
wf.delete(ws[0].id, function() {
ws = wf.getWallets();
ws.length.should.equal(0);
done();
}); });
}); });
it('should clean lastOpened on delete wallet', function(done) { describe('#delete', function() {
var wf = new WalletFactory(config, '0.0.1'); it('should call deleteWallet', function(done) {
var w = wf.create({ var wf = new WalletFactory(config, '0.0.1');
name: 'test wallet' var s1 = sinon.spy(wf.storage, 'deleteWallet');
wf.delete('xxx', function() {
s1.getCall(0).args[0].should.equal('xxx');
done();
});
}); });
wf.storage.setLastOpened(w.id); it('should call lastOpened', function(done) {
wf.delete(w.id, function() { var wf = new WalletFactory(config, '0.0.1');
var last = wf.storage.getLastOpened(); var s1 = sinon.stub(wf.storage, 'deleteWallet').yields(null);
should.equal(last, undefined); var s2 = sinon.stub(wf.storage, 'setLastOpened').yields(null);
done(); wf.delete('xxx', function() {
s2.calledOnce.should.equal(true);
should.not.exist(s2.getCall(0).args[0]);
done();
});
}); });
}); });
it('should return false if wallet does not exist', function() {
describe('#read', function() {
it('should fail to read unexisting wallet', function(done) {
var wf = new WalletFactory(config, '0.0.1');
wf.read('id', [], function(err, w) {
should.not.exist(w);
should.exist(err);
should.exist(err.message);
var m = err.message.toString();
m.should.to.have.string('Wallet not found');
done();
});
});
it('should fail to read broken wallet', function(done) {
var wf = new WalletFactory(config, '0.0.1');
wf.storage.getMany = sinon.stub().yields({
'opts': 1
});
wf.read('id', [], function(err, w) {
should.not.exist(w);
should.exist(err);
should.exist(err.message);
var m = err.message.toString();
m.should.to.have.string('Could not read');
done();
});
});
it('should read existing wallet', function(done) {
var wf = new WalletFactory(config, '0.0.1');
wf.storage.getMany = sinon.stub().yields({
'opts': 1
});
wf.fromObj = sinon.stub().returns('ok');
wf.read('id', [], function(err, w) {
should.not.exist(err);
should.exist(w);
done();
});
});
});
describe('#open', function() {
var opts = { var opts = {
'requiredCopayers': 2, 'requiredcopayers': 2,
'totalCopayers': 3 'totalcopayers': 3
}; };
var wf = new WalletFactory(config, '0.0.1');
var w = wf.open('dummy', opts); it('should call setPassphrase', function(done) {
should.exist(w); var wf = new WalletFactory(config, '0.0.1');
wf.storage.setPassphrase = sinon.spy();
var s1 = sinon.stub();
s1.store = sinon.stub().yields(null);
wf.read = sinon.stub().yields(null, s1);
wf.storage.setLastOpened = sinon.stub().yields(null);
wf.open('dummy', 'xxx', function(err, w) {
wf.storage.setPassphrase.calledOnce.should.equal(true);
wf.storage.setPassphrase.getCall(0).args[0].should.equal('xxx');
done();
});
});
it('should call return wallet', function(done) {
var wf = new WalletFactory(config, '0.0.1');
wf.storage.setPassphrase = sinon.spy();
var s1 = sinon.stub();
s1.store = sinon.stub().yields(null);
wf.read = sinon.stub().yields(null, s1);
wf.storage.setLastOpened = sinon.stub().yields(null);
wf.open('dummy', 'xxx', function(err, w) {
w.should.equal(s1);
s1.store.calledOnce.should.equal(true);
done();
});
});
it('should call #store', function(done) {
var wf = new WalletFactory(config, '0.0.1');
wf.storage.setPassphrase = sinon.spy();
var s1 = sinon.stub();
s1.store = sinon.stub().yields(null);
wf.read = sinon.stub().yields(null, s1);
wf.storage.setLastOpened = sinon.stub().yields(null);
wf.open('dummy', 'xxx', function(err, w) {
s1.store.calledOnce.should.equal(true);
done();
});
});
it('should call #setLastOpened', function(done) {
var wf = new WalletFactory(config, '0.0.1');
wf.storage.setPassphrase = sinon.spy();
var s1 = sinon.stub();
s1.store = sinon.stub().yields(null);
wf.read = sinon.stub().yields(null, s1);
wf.storage.setLastOpened = sinon.stub().yields(null);
wf.open('dummy', 'xxx', function(err, w) {
wf.storage.setLastOpened.calledOnce.should.equal(true);
wf.storage.setLastOpened.getCall(0).args[0].should.equal('dummy');
done();
});
});
}); });
it('should open a wallet', function() { describe.only('#create', function() {
var opts = { it('should return error if network are differents', function() {
'requiredCopayers': 2, var opts = {
'totalCopayers': 3 'requiredCopayers': 2,
}; 'totalCopayers': 3
var wf = new WalletFactory(config, '0.0.1'); };
var w = wf.create(opts); var wf = new WalletFactory(config, '0.0.1');
var walletId = w.id; var w = wf.create(opts, function(err, w) {
err.should.to.have.string('Wallet not found');
wf.read = sinon.stub().withArgs(walletId).returns(w); should.not.exist(w);
var wo = wf.open(walletId, opts); });
should.exist(wo); });
wf.read.calledWith(walletId).should.be.true;
});
it('should save lastOpened on create/open a wallet', function() {
var opts = {
'requiredCopayers': 2,
'totalCopayers': 3
};
var wf = new WalletFactory(config, '0.0.1');
var w = wf.create(opts);
var last = wf.storage.getLastOpened();
should.equal(last, w.id);
wf.storage.setLastOpened('other_id');
var wo = wf.open(w.id, opts);
last = wf.storage.getLastOpened();
should.equal(last, w.id);
});
it('should return error if network are differents', function() {
var opts = {
'requiredCopayers': 2,
'totalCopayers': 3
};
var wf = new WalletFactory(config, '0.0.1');
var w = wf.create(opts);
(function() {
wf._checkNetwork('livenet');
}).should.throw();
}); });
describe('#joinCreateSession', function() { describe('#joinCreateSession', function() {