diff options
Diffstat (limited to 'test/unit')
-rw-r--r-- | test/unit/actions/restore_vault_test.js | 60 | ||||
-rw-r--r-- | test/unit/actions/set_selected_account_test.js | 1 | ||||
-rw-r--r-- | test/unit/actions/tx_test.js | 8 | ||||
-rw-r--r-- | test/unit/config-manager-test.js | 28 | ||||
-rw-r--r-- | test/unit/idStore-migration-test.js | 160 | ||||
-rw-r--r-- | test/unit/idStore-test.js | 3 | ||||
-rw-r--r-- | test/unit/keyring-controller-test.js | 188 | ||||
-rw-r--r-- | test/unit/keyrings/hd-test.js | 129 | ||||
-rw-r--r-- | test/unit/keyrings/simple-test.js | 94 |
9 files changed, 590 insertions, 81 deletions
diff --git a/test/unit/actions/restore_vault_test.js b/test/unit/actions/restore_vault_test.js deleted file mode 100644 index 609f5429e..000000000 --- a/test/unit/actions/restore_vault_test.js +++ /dev/null @@ -1,60 +0,0 @@ -var jsdom = require('mocha-jsdom') -var assert = require('assert') -var freeze = require('deep-freeze-strict') -var path = require('path') -var sinon = require('sinon') - -var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js')) -var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js')) - -describe('#recoverFromSeed(password, seed)', function() { - - beforeEach(function() { - // sinon allows stubbing methods that are easily verified - this.sinon = sinon.sandbox.create() - }) - - afterEach(function() { - // sinon requires cleanup otherwise it will overwrite context - this.sinon.restore() - }) - - // stub out account manager - actions._setAccountManager({ - recoverFromSeed(pw, seed, cb) { - cb(null, { - identities: { - foo: 'bar' - } - }) - }, - }) - - it('sets metamask.isUnlocked to true', function() { - var initialState = { - metamask: { - isUnlocked: false, - isInitialized: false, - } - } - freeze(initialState) - - const restorePhrase = 'invite heavy among daring outdoor dice jelly coil stable note seat vicious' - const password = 'foo' - const dispatchFunc = actions.recoverFromSeed(password, restorePhrase) - - var dispatchStub = this.sinon.stub() - dispatchStub.withArgs({ TYPE: actions.unlockMetamask() }).onCall(0) - dispatchStub.withArgs({ TYPE: actions.showAccountsPage() }).onCall(1) - - var action - var resultingState = initialState - dispatchFunc((newAction) => { - action = newAction - resultingState = reducers(resultingState, action) - }) - - assert.equal(resultingState.metamask.isUnlocked, true, 'was unlocked') - assert.equal(resultingState.metamask.isInitialized, true, 'was initialized') - }); -}); diff --git a/test/unit/actions/set_selected_account_test.js b/test/unit/actions/set_selected_account_test.js index 69eb11e47..f72ca82e4 100644 --- a/test/unit/actions/set_selected_account_test.js +++ b/test/unit/actions/set_selected_account_test.js @@ -44,6 +44,5 @@ describe('SHOW_ACCOUNT_DETAIL', function() { var resultingState = reducers(initialState, action) assert.equal(resultingState.metamask.selectedAccount, action.value) - assert.equal(resultingState.metamask.selectedAddress, action.value) }) }) diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js index c08a8aa26..1f06b1120 100644 --- a/test/unit/actions/tx_test.js +++ b/test/unit/actions/tx_test.js @@ -46,7 +46,7 @@ describe('tx confirmation screen', function() { describe('cancelTx', function() { before(function(done) { - actions._setAccountManager({ + actions._setBackgroundConnection({ approveTransaction(txId, cb) { cb('An error!') }, cancelTransaction(txId) { /* noop */ }, clearSeedWordCache(cb) { cb() }, @@ -75,7 +75,7 @@ describe('tx confirmation screen', function() { before(function(done) { alert = () => {/* noop */} - actions._setAccountManager({ + actions._setBackgroundConnection({ approveTransaction(txId, cb) { cb({message: 'An error!'}) }, }) @@ -96,7 +96,7 @@ describe('tx confirmation screen', function() { describe('when there is success', function() { it('should complete tx and go home', function() { - actions._setAccountManager({ + actions._setBackgroundConnection({ approveTransaction(txId, cb) { cb() }, }) @@ -135,7 +135,7 @@ describe('tx confirmation screen', function() { } freeze(initialState) - actions._setAccountManager({ + actions._setBackgroundConnection({ approveTransaction(txId, cb) { cb() }, }) diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js index 6aa7146f0..206460ffb 100644 --- a/test/unit/config-manager-test.js +++ b/test/unit/config-manager-test.js @@ -100,31 +100,31 @@ describe('config-manager', function() { describe('confirmation', function() { - describe('#getConfirmed', function() { + describe('#getConfirmedDisclaimer', function() { it('should return false if no previous key exists', function() { - var result = configManager.getConfirmed() + var result = configManager.getConfirmedDisclaimer() assert.ok(!result) }) }) - describe('#setConfirmed', function() { - it('should make getConfirmed return true once set', function() { - assert.equal(configManager.getConfirmed(), false) - configManager.setConfirmed(true) - var result = configManager.getConfirmed() + describe('#setConfirmedDisclaimer', function() { + it('should make getConfirmedDisclaimer return true once set', function() { + assert.equal(configManager.getConfirmedDisclaimer(), false) + configManager.setConfirmedDisclaimer(true) + var result = configManager.getConfirmedDisclaimer() assert.equal(result, true) }) it('should be able to set false', function() { - configManager.setConfirmed(false) - var result = configManager.getConfirmed() + configManager.setConfirmedDisclaimer(false) + var result = configManager.getConfirmedDisclaimer() assert.equal(result, false) }) it('should persist to local storage', function() { - configManager.setConfirmed(true) + configManager.setConfirmedDisclaimer(true) var data = configManager.getData() - assert.equal(data.isConfirmed, true) + assert.equal(data.isDisclaimerConfirmed, true) }) }) }) @@ -153,7 +153,7 @@ describe('config-manager', function() { rpcTarget: 'foobar' }, } - configManager.setConfirmed(true) + configManager.setConfirmedDisclaimer(true) configManager.setConfig(testConfig) var testWallet = { @@ -164,7 +164,7 @@ describe('config-manager', function() { var result = configManager.getData() assert.equal(result.wallet.name, testWallet.name, 'wallet name is set') assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget) - assert.equal(configManager.getConfirmed(), true) + assert.equal(configManager.getConfirmedDisclaimer(), true) testConfig.provider.type = 'something else!' configManager.setConfig(testConfig) @@ -173,7 +173,7 @@ describe('config-manager', function() { assert.equal(result.wallet.name, testWallet.name, 'wallet name is set') assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget) assert.equal(result.config.provider.type, testConfig.provider.type) - assert.equal(configManager.getConfirmed(), true) + assert.equal(configManager.getConfirmedDisclaimer(), true) }) }) diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js new file mode 100644 index 000000000..ac8e23d22 --- /dev/null +++ b/test/unit/idStore-migration-test.js @@ -0,0 +1,160 @@ +const async = require('async') +const assert = require('assert') +const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN +const ConfigManager = require('../../app/scripts/lib/config-manager') +const delegateCallCode = require('../lib/example-code.json').delegateCallCode + +// The old way: +const IdentityStore = require('../../app/scripts/lib/idStore') +const STORAGE_KEY = 'metamask-config' +const extend = require('xtend') + +// The new ways: +var KeyringController = require('../../app/scripts/keyring-controller') +const mockEncryptor = require('../lib/mock-encryptor') +const MockSimpleKeychain = require('../lib/mock-simple-keychain') +const sinon = require('sinon') + +const mockVault = { + seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague', + account: '0x5d8de92c205279c10e5669f797b853ccef4f739a', +} + +describe('IdentityStore to KeyringController migration', function() { + + // The stars of the show: + let idStore, keyringController, seedWords, configManager + + let password = 'password123' + let entropy = 'entripppppyy duuude' + let accounts = [] + let newAccounts = [] + let originalKeystore + + // This is a lot of setup, I know! + // We have to create an old style vault, populate it, + // and THEN create a new one, before we can run tests on it. + beforeEach(function(done) { + this.sinon = sinon.sandbox.create() + window.localStorage = {} // Hacking localStorage support into JSDom + configManager = new ConfigManager({ + loadData, + setData: (d) => { window.localStorage = d } + }) + + + idStore = new IdentityStore({ + configManager: configManager, + ethStore: { + addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) }, + del(acct) { delete accounts[acct] }, + }, + }) + + idStore._createVault(password, mockVault.seed, (err) => { + assert.ifError(err, 'createNewVault threw error') + originalKeystore = idStore._idmgmt.keyStore + + idStore.setLocked((err) => { + assert.ifError(err, 'createNewVault threw error') + keyringController = new KeyringController({ + configManager, + ethStore: { + addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) }, + del(acct) { delete newAccounts[acct] }, + }, + }) + + // Stub out the browser crypto for a mock encryptor. + // Browser crypto is tested in the integration test suite. + keyringController.encryptor = mockEncryptor + done() + }) + }) + }) + + describe('entering a password', function() { + it('should identify an old wallet as an initialized keyring', function() { + keyringController.configManager.setWallet('something') + const state = keyringController.getState() + assert(state.isInitialized, 'old vault counted as initialized.') + }) + + /* + it('should use the password to migrate the old vault', function(done) { + this.timeout(5000) + console.log('calling submitPassword') + console.dir(keyringController) + keyringController.submitPassword(password, function (err, state) { + assert.ifError(err, 'submitPassword threw error') + + function log(str, dat) { console.log(str + ': ' + JSON.stringify(dat)) } + + let newAccounts = keyringController.getAccounts() + log('new accounts: ', newAccounts) + + let newAccount = ethUtil.addHexPrefix(newAccounts[0]) + assert.equal(ethUtil.addHexPrefix(newAccount), mockVault.account, 'restored the correct account') + const newSeed = keyringController.keyrings[0].mnemonic + log('keyringController keyrings', keyringController.keyrings) + assert.equal(newSeed, mockVault.seed, 'seed phrase transferred.') + + assert(configManager.getVault(), 'new type of vault is persisted') + done() + }) + }) + */ + + }) +}) + +function loadData () { + var oldData = getOldStyleData() + var newData + try { + newData = JSON.parse(window.localStorage[STORAGE_KEY]) + } catch (e) {} + + var data = extend({ + meta: { + version: 0, + }, + data: { + config: { + provider: { + type: 'testnet', + }, + }, + }, + }, oldData || null, newData || null) + return data +} + +function setData (data) { + window.localStorage[STORAGE_KEY] = JSON.stringify(data) +} + +function getOldStyleData () { + var config, wallet, seedWords + + var result = { + meta: { version: 0 }, + data: {}, + } + + try { + config = JSON.parse(window.localStorage['config']) + result.data.config = config + } catch (e) {} + try { + wallet = JSON.parse(window.localStorage['lightwallet']) + result.data.wallet = wallet + } catch (e) {} + try { + seedWords = window.localStorage['seedWords'] + result.data.seedWords = seedWords + } catch (e) {} + + return result +} diff --git a/test/unit/idStore-test.js b/test/unit/idStore-test.js index bf8270540..3ca89cd38 100644 --- a/test/unit/idStore-test.js +++ b/test/unit/idStore-test.js @@ -11,7 +11,6 @@ describe('IdentityStore', function() { describe('#createNewVault', function () { let idStore let password = 'password123' - let entropy = 'entripppppyy duuude' let seedWords let accounts = [] let originalKeystore @@ -26,7 +25,7 @@ describe('IdentityStore', function() { }, }) - idStore.createNewVault(password, entropy, (err, seeds) => { + idStore.createNewVault(password, (err, seeds) => { assert.ifError(err, 'createNewVault threw error') seedWords = seeds originalKeystore = idStore._idmgmt.keyStore diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js new file mode 100644 index 000000000..056e465ca --- /dev/null +++ b/test/unit/keyring-controller-test.js @@ -0,0 +1,188 @@ +var assert = require('assert') +var KeyringController = require('../../app/scripts/keyring-controller') +var configManagerGen = require('../lib/mock-config-manager') +const ethUtil = require('ethereumjs-util') +const BN = ethUtil.BN +const async = require('async') +const mockEncryptor = require('../lib/mock-encryptor') +const MockSimpleKeychain = require('../lib/mock-simple-keychain') +const sinon = require('sinon') + +describe('KeyringController', function() { + + let keyringController, state + let password = 'password123' + let seedWords = 'puzzle seed penalty soldier say clay field arctic metal hen cage runway' + let addresses = ['eF35cA8EbB9669A35c31b5F6f249A9941a812AC1'.toLowerCase()] + let accounts = [] + let originalKeystore + + beforeEach(function(done) { + this.sinon = sinon.sandbox.create() + window.localStorage = {} // Hacking localStorage support into JSDom + + keyringController = new KeyringController({ + configManager: configManagerGen(), + ethStore: { + addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) }, + }, + }) + + // Stub out the browser crypto for a mock encryptor. + // Browser crypto is tested in the integration test suite. + keyringController.encryptor = mockEncryptor + + keyringController.createNewVaultAndKeychain(password, function (err, newState) { + assert.ifError(err) + state = newState + done() + }) + }) + + afterEach(function() { + // Cleanup mocks + this.sinon.restore() + }) + + describe('#createNewVaultAndKeychain', function () { + this.timeout(10000) + + it('should set a vault on the configManager', function(done) { + keyringController.configManager.setVault(null) + assert(!keyringController.configManager.getVault(), 'no previous vault') + keyringController.createNewVaultAndKeychain(password, (err, state) => { + assert.ifError(err) + const vault = keyringController.configManager.getVault() + assert(vault, 'vault created') + done() + }) + }) + }) + + describe('#restoreKeyring', function() { + + it(`should pass a keyring's serialized data back to the correct type.`, function(done) { + const mockSerialized = { + type: 'HD Key Tree', + data: { + mnemonic: seedWords, + numberOfAccounts: 1, + } + } + const mock = this.sinon.mock(keyringController) + + mock.expects('getBalanceAndNickname') + .exactly(1) + + keyringController.restoreKeyring(mockSerialized) + .then((keyring) => { + assert.equal(keyring.wallets.length, 1, 'one wallet restored') + return keyring.getAccounts() + }) + .then((accounts) => { + assert.equal(accounts[0], addresses[0]) + mock.verify() + done() + }) + .catch((reason) => { + assert.ifError(reason) + done() + }) + }) + }) + + describe('#migrateOldVaultIfAny', function() { + it('should return and init a new vault', function(done) { + keyringController.migrateOldVaultIfAny(password) + .then(() => { + assert(keyringController.configManager.getVault(), 'now has a vault') + assert(keyringController.password, 'has a password set') + done() + }) + .catch((reason) => { + assert.ifError(reason) + done() + }) + }) + }) + + describe('#createNickname', function() { + it('should add the address to the identities hash', function() { + const fakeAddress = '0x12345678' + keyringController.createNickname(fakeAddress) + const identities = keyringController.identities + const identity = identities[fakeAddress] + assert.equal(identity.address, fakeAddress) + + const nick = keyringController.configManager.nicknameForWallet(fakeAddress) + assert.equal(typeof nick, 'string') + }) + }) + + describe('#saveAccountLabel', function() { + it ('sets the nickname', function(done) { + const account = addresses[0] + var nick = 'Test nickname' + keyringController.identities[ethUtil.addHexPrefix(account)] = {} + keyringController.saveAccountLabel(account, nick, (err, label) => { + assert.ifError(err) + assert.equal(label, nick) + const persisted = keyringController.configManager.nicknameForWallet(account) + assert.equal(persisted, nick) + done() + }) + }) + + this.timeout(10000) + it('retrieves the persisted nickname', function(done) { + const account = addresses[0] + var nick = 'Test nickname' + keyringController.configManager.setNicknameForWallet(account, nick) + keyringController.createNewVaultAndRestore(password, seedWords, (err, state) => { + assert.ifError(err) + + const identity = keyringController.identities['0x' + account] + assert.equal(identity.name, nick) + + assert(accounts) + done() + }) + }) + }) + + describe('#getAccounts', function() { + it('returns the result of getAccounts for each keyring', function() { + keyringController.keyrings = [ + { getAccounts() { return Promise.resolve([1,2,3]) } }, + { getAccounts() { return Promise.resolve([4,5,6]) } }, + ] + + keyringController.getAccounts() + .then((result) => { + assert.deepEqual(result, [1,2,3,4,5,6]) + done() + }) + }) + }) + + describe('#addGasBuffer', function() { + it('adds 100k gas buffer to estimates', function() { + + const gas = '0x04ee59' // Actual estimated gas example + const tooBigOutput = '0x80674f9' // Actual bad output + const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16) + const correctBuffer = new BN('100000', 10) + const correct = bnGas.add(correctBuffer) + + const tooBig = new BN(tooBigOutput, 16) + const result = keyringController.addGasBuffer(gas) + const bnResult = new BN(ethUtil.stripHexPrefix(result), 16) + + assert.equal(result.indexOf('0x'), 0, 'included hex prefix') + assert(bnResult.gt(bnGas), 'Estimate increased in value.') + assert.equal(bnResult.sub(bnGas).toString(10), '100000', 'added 100k gas') + assert.equal(result, '0x' + correct.toString(16), 'Added the right amount') + assert.notEqual(result, tooBigOutput, 'not that bad estimate') + }) + }) +}) diff --git a/test/unit/keyrings/hd-test.js b/test/unit/keyrings/hd-test.js new file mode 100644 index 000000000..2d9e0ffd9 --- /dev/null +++ b/test/unit/keyrings/hd-test.js @@ -0,0 +1,129 @@ +const assert = require('assert') +const extend = require('xtend') +const HdKeyring = require('../../../app/scripts/keyrings/hd') + +// Sample account: +const privKeyHex = 'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952' + +const sampleMnemonic = 'finish oppose decorate face calm tragic certain desk hour urge dinosaur mango' +const firstAcct = '1c96099350f13d558464ec79b9be4445aa0ef579' +const secondAcct = '1b00aed43a693f3a957f9feb5cc08afa031e37a0' + +describe('hd-keyring', function() { + + let keyring + beforeEach(function() { + keyring = new HdKeyring() + }) + + describe('constructor', function(done) { + keyring = new HdKeyring({ + mnemonic: sampleMnemonic, + numberOfAccounts: 2, + }) + + const accounts = keyring.getAccounts() + .then((accounts) => { + assert.equal(accounts[0], firstAcct) + assert.equal(accounts[1], secondAcct) + done() + }) + }) + + describe('Keyring.type', function() { + it('is a class property that returns the type string.', function() { + const type = HdKeyring.type + assert.equal(typeof type, 'string') + }) + }) + + describe('#type', function() { + it('returns the correct value', function() { + const type = keyring.type + const correct = HdKeyring.type + assert.equal(type, correct) + }) + }) + + describe('#serialize empty wallets.', function() { + it('serializes a new mnemonic', function() { + keyring.serialize() + .then((output) => { + assert.equal(output.numberOfAccounts, 0) + assert.equal(output.mnemonic, null) + }) + }) + }) + + describe('#deserialize a private key', function() { + it('serializes what it deserializes', function(done) { + console.log('deserializing ' + sampleMnemonic) + keyring.deserialize({ + mnemonic: sampleMnemonic, + numberOfAccounts: 1 + }) + .then(() => { + console.dir(keyring) + assert.equal(keyring.wallets.length, 1, 'restores two accounts') + return keyring.addAccounts(1) + }).then(() => { + return keyring.getAccounts() + }).then((accounts) => { + assert.equal(accounts[0], firstAcct) + assert.equal(accounts[1], secondAcct) + assert.equal(accounts.length, 2) + + return keyring.serialize() + }).then((serialized) => { + assert.equal(serialized.mnemonic, sampleMnemonic) + done() + }) + }) + }) + + describe('#addAccounts', function() { + describe('with no arguments', function() { + it('creates a single wallet', function(done) { + keyring.addAccounts() + .then(() => { + assert.equal(keyring.wallets.length, 1) + done() + }) + }) + }) + + describe('with a numeric argument', function() { + it('creates that number of wallets', function(done) { + keyring.addAccounts(3) + .then(() => { + assert.equal(keyring.wallets.length, 3) + done() + }) + }) + }) + }) + + describe('#getAccounts', function() { + it('calls getAddress on each wallet', function(done) { + + // Push a mock wallet + const desiredOutput = 'foo' + keyring.wallets.push({ + getAddress() { + return { + toString() { + return desiredOutput + } + } + } + }) + + const output = keyring.getAccounts() + .then((output) => { + assert.equal(output[0], desiredOutput) + assert.equal(output.length, 1) + done() + }) + }) + }) +}) diff --git a/test/unit/keyrings/simple-test.js b/test/unit/keyrings/simple-test.js new file mode 100644 index 000000000..979abdb69 --- /dev/null +++ b/test/unit/keyrings/simple-test.js @@ -0,0 +1,94 @@ +const assert = require('assert') +const extend = require('xtend') +const SimpleKeyring = require('../../../app/scripts/keyrings/simple') +const TYPE_STR = 'Simple Key Pair' + +// Sample account: +const privKeyHex = 'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952' + +describe('simple-keyring', function() { + + let keyring + beforeEach(function() { + keyring = new SimpleKeyring() + }) + + describe('Keyring.type', function() { + it('is a class property that returns the type string.', function() { + const type = SimpleKeyring.type + assert.equal(type, TYPE_STR) + }) + }) + + describe('#type', function() { + it('returns the correct value', function() { + const type = keyring.type + assert.equal(type, TYPE_STR) + }) + }) + + describe('#serialize empty wallets.', function() { + it('serializes an empty array', function(done) { + keyring.serialize() + .then((output) => { + assert.deepEqual(output, []) + done() + }) + }) + }) + + describe('#deserialize a private key', function() { + it('serializes what it deserializes', function() { + keyring.deserialize([privKeyHex]) + .then(() => { + assert.equal(keyring.wallets.length, 1, 'has one wallet') + const serialized = keyring.serialize() + assert.equal(serialized[0], privKeyHex) + }) + }) + }) + + describe('#addAccounts', function() { + describe('with no arguments', function() { + it('creates a single wallet', function() { + keyring.addAccounts() + .then(() => { + assert.equal(keyring.wallets.length, 1) + }) + }) + }) + + describe('with a numeric argument', function() { + it('creates that number of wallets', function() { + keyring.addAccounts(3) + .then(() => { + assert.equal(keyring.wallets.length, 3) + }) + }) + }) + }) + + describe('#getAccounts', function() { + it('calls getAddress on each wallet', function(done) { + + // Push a mock wallet + const desiredOutput = 'foo' + keyring.wallets.push({ + getAddress() { + return { + toString() { + return desiredOutput + } + } + } + }) + + keyring.getAccounts() + .then((output) => { + assert.equal(output[0], desiredOutput) + assert.equal(output.length, 1) + done() + }) + }) + }) +}) |