Merge pull request #496 from embark-framework/features/wallet-in-tests

Enables specifying accounts in simulator mode in the tests
This commit is contained in:
Iuri Matias 2018-06-07 14:30:45 -04:00 committed by GitHub
commit 8aad260dfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 141 additions and 27 deletions

View File

@ -21,10 +21,32 @@ class AccountParser {
return accounts; return accounts;
} }
static getHexBalance(balanceString, web3) {
if (!balanceString) {
return 0xFFFFFFFFFFFFFFFFFF;
}
if (web3.utils.isHexStrict(balanceString)) {
return balanceString;
}
const match = balanceString.match(/([0-9]+) ?([a-zA-Z]*)/);
if (!match) {
throw new Error(__('Unrecognized balance string "%s"', balanceString));
}
if (!match[2]) {
return web3.utils.toHex(parseInt(match[1], 10));
}
return web3.utils.toHex(web3.utils.toWei(match[1], match[2]));
}
static getAccount(accountConfig, web3, logger) { static getAccount(accountConfig, web3, logger) {
if (!logger) { if (!logger) {
logger = console; logger = console;
} }
let hexBalance = null;
if (accountConfig.balance) {
hexBalance = AccountParser.getHexBalance(accountConfig.balance, web3);
}
if (accountConfig.privateKey) { if (accountConfig.privateKey) {
if (!accountConfig.privateKey.startsWith('0x')) { if (!accountConfig.privateKey.startsWith('0x')) {
accountConfig.privateKey = '0x' + accountConfig.privateKey; accountConfig.privateKey = '0x' + accountConfig.privateKey;
@ -33,7 +55,7 @@ class AccountParser {
logger.warn(`Private key ending with ${accountConfig.privateKey.substr(accountConfig.privateKey.length - 5)} is not a HEX string`); logger.warn(`Private key ending with ${accountConfig.privateKey.substr(accountConfig.privateKey.length - 5)} is not a HEX string`);
return null; return null;
} }
return web3.eth.accounts.privateKeyToAccount(accountConfig.privateKey); return Object.assign(web3.eth.accounts.privateKeyToAccount(accountConfig.privateKey), {hexBalance});
} }
if (accountConfig.privateKeyFile) { if (accountConfig.privateKeyFile) {
let fileContent = fs.readFileSync(fs.dappPath(accountConfig.privateKeyFile)).toString(); let fileContent = fs.readFileSync(fs.dappPath(accountConfig.privateKeyFile)).toString();
@ -46,7 +68,7 @@ class AccountParser {
logger.warn(`Private key is not a HEX string in file ${accountConfig.privateKeyFile} at index ${index}`); logger.warn(`Private key is not a HEX string in file ${accountConfig.privateKeyFile} at index ${index}`);
return null; return null;
} }
return web3.eth.accounts.privateKeyToAccount(key); return Object.assign(web3.eth.accounts.privateKeyToAccount(key), {hexBalance});
}); });
} }
if (accountConfig.mnemonic) { if (accountConfig.mnemonic) {
@ -59,7 +81,7 @@ class AccountParser {
const accounts = []; const accounts = [];
for (let i = addressIndex; i < addressIndex + numAddresses; i++) { for (let i = addressIndex; i < addressIndex + numAddresses; i++) {
const wallet = hdwallet.derivePath(wallet_hdpath + i).getWallet(); const wallet = hdwallet.derivePath(wallet_hdpath + i).getWallet();
accounts.push(web3.eth.accounts.privateKeyToAccount('0x' + wallet.getPrivateKey().toString('hex'))); accounts.push(Object.assign(web3.eth.accounts.privateKeyToAccount('0x' + wallet.getPrivateKey().toString('hex')), {hexBalance}));
} }
return accounts; return accounts;
} }

View File

@ -29,8 +29,8 @@ class DeployManager {
async.eachOfSeries(contracts, async.eachOfSeries(contracts,
function (contract, key, callback) { function (contract, key, callback) {
contract._gasLimit = self.gasLimit; contract._gasLimit = self.gasLimit;
self.events.request('deploy:contract', contract, () => { self.events.request('deploy:contract', contract, (err) => {
callback(); callback(err);
}); });
}, },
function (err, _results) { function (err, _results) {

View File

@ -1,8 +1,12 @@
const async = require('async'); const async = require('async');
const TARGET = 15000000000000000000; const TARGET = 0x7FFFFFFFFFFFFFFF;
const ALREADY_FUNDED = 'alreadyFunded'; const ALREADY_FUNDED = 'alreadyFunded';
function fundAccount(web3, accountAddress, callback) { function fundAccount(web3, accountAddress, hexBalance, callback) {
if (!hexBalance) {
hexBalance = TARGET;
}
const targetBalance = (typeof hexBalance === 'string') ? parseInt(hexBalance, 16) : hexBalance;
let accountBalance; let accountBalance;
let coinbaseAddress; let coinbaseAddress;
let lastNonce; let lastNonce;
@ -14,7 +18,7 @@ function fundAccount(web3, accountAddress, callback) {
if (err) { if (err) {
return next(err); return next(err);
} }
if (balance >= TARGET) { if (balance >= targetBalance) {
return next(ALREADY_FUNDED); return next(ALREADY_FUNDED);
} }
accountBalance = balance; accountBalance = balance;
@ -56,7 +60,7 @@ function fundAccount(web3, accountAddress, callback) {
web3.eth.sendTransaction({ web3.eth.sendTransaction({
from: coinbaseAddress, from: coinbaseAddress,
to: accountAddress, to: accountAddress,
value: TARGET - accountBalance, value: targetBalance - accountBalance,
gasPrice: gasPrice, gasPrice: gasPrice,
nonce: lastNonce nonce: lastNonce
}, next); }, next);

View File

@ -65,7 +65,7 @@ class Provider {
return callback(); return callback();
} }
async.each(self.accounts, (account, eachCb) => { async.each(self.accounts, (account, eachCb) => {
fundAccount(self.web3, account.address, eachCb); fundAccount(self.web3, account.address, account.hexBalance, eachCb);
}, callback); }, callback);
} }

View File

@ -60,7 +60,6 @@ Plugin.prototype.hasContext = function(context) {
Plugin.prototype.loadPlugin = function() { Plugin.prototype.loadPlugin = function() {
if (!this.isContextValid()) { if (!this.isContextValid()) {
console.log(this.acceptedContext);
this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contextes}}"', {name: this.name, contextes: this.acceptedContext.join(', ')})); this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contextes}}"', {name: this.name, contextes: this.acceptedContext.join(', ')}));
return false; return false;
} }

View File

@ -19,7 +19,7 @@ class SolcProcess extends ProcessWrapper {
return {contents: fs.readFileSync(path.join('./node_modules/', filename)).toString()}; return {contents: fs.readFileSync(path.join('./node_modules/', filename)).toString()};
} }
if (fs.existsSync(path.join(constants.httpContractsDirectory, filename))) { if (fs.existsSync(path.join(constants.httpContractsDirectory, filename))) {
return {contents: fs.readFileSync(path.join('./.embark/contracts', filename)).toString()}; return {contents: fs.readFileSync(path.join(constants.httpContractsDirectory, filename)).toString()};
} }
return {error: 'File not found'}; return {error: 'File not found'};
} }

View File

@ -37,6 +37,12 @@ module.exports = {
global.assert = assert; global.assert = assert;
global.config = test.config.bind(test); global.config = test.config.bind(test);
global.deployAll = function () {
console.error(__('%s is not supported anymore', 'deployAll').red);
console.info(__('You can learn about the new revamped tests here: %s', 'https://embark.status.im/docs/testing.html'.underline));
process.exit();
};
// TODO: this global here might not be necessary at all // TODO: this global here might not be necessary at all
global.web3 = global.embark.web3; global.web3 = global.embark.web3;

View File

@ -6,6 +6,7 @@ const utils = require('../utils/utils');
const constants = require('../constants'); const constants = require('../constants');
const Events = require('../core/events'); const Events = require('../core/events');
const cloneDeep = require('clone-deep'); const cloneDeep = require('clone-deep');
const AccountParser = require('../contracts/accountParser');
function getSimulator() { function getSimulator() {
try { try {
@ -36,12 +37,7 @@ class Test {
this.compiledContracts = {}; this.compiledContracts = {};
this.web3 = new Web3(); this.web3 = new Web3();
if (this.simOptions.node) { this.initWeb3Provider();
this.web3.setProvider(new this.web3.providers.HttpProvider(this.simOptions.node));
} else {
this.sim = getSimulator();
this.web3.setProvider(this.sim.provider(this.simOptions));
}
this.engine = new Engine({ this.engine = new Engine({
env: this.options.env || 'test', env: this.options.env || 'test',
@ -55,11 +51,31 @@ class Test {
}); });
this.versions_default = this.engine.config.contractsConfig.versions; this.versions_default = this.engine.config.contractsConfig.versions;
const deploymentConfig = this.engine.config.contractsConfig.versions;
// Reset contract config to nothing to make sure we deploy only what we want // Reset contract config to nothing to make sure we deploy only what we want
this.engine.config.contractsConfig = {contracts: {}, versions: this.versions_default}; this.engine.config.contractsConfig = {contracts: {}, versions: this.versions_default, deployment: deploymentConfig};
this.engine.startService("libraryManager"); this.engine.startService("libraryManager");
this.engine.startService("codeRunner"); this.engine.startService("codeRunner");
this.initDeployServices();
this.engine.startService("codeGenerator");
}
initWeb3Provider() {
if (this.simOptions.node) {
this.web3.setProvider(new this.web3.providers.HttpProvider(this.simOptions.node));
} else {
if (this.simOptions.accounts) {
this.simOptions.accounts = this.simOptions.accounts.map((account) => {
return {balance: account.hexBalance, secretKey: account.privateKey};
});
}
this.sim = getSimulator();
this.web3.setProvider(this.sim.provider(this.simOptions));
}
}
initDeployServices() {
this.engine.startService("web3", { this.engine.startService("web3", {
web3: this.web3 web3: this.web3
}); });
@ -67,7 +83,6 @@ class Test {
trackContracts: false, trackContracts: false,
ipcRole: 'client' ipcRole: 'client'
}); });
this.engine.startService("codeGenerator");
} }
init(callback) { init(callback) {
@ -100,6 +115,13 @@ class Test {
this.simOptions = this.options.simulatorOptions || {}; this.simOptions = this.options.simulatorOptions || {};
this.ready = false; this.ready = false;
if (this.options.deployment && this.options.deployment.accounts) {
// Account setup
this.simOptions.accounts = AccountParser.parseAccountsConfig(this.options.deployment.accounts, this.web3);
this.initWeb3Provider();
this.initDeployServices();
}
// Reset contracts // Reset contracts
this.engine.contractsManager.contracts = cloneDeep(this.builtContracts); this.engine.contractsManager.contracts = cloneDeep(this.builtContracts);
this.engine.contractsManager.compiledContracts = cloneDeep(this.compiledContracts); this.engine.contractsManager.compiledContracts = cloneDeep(this.compiledContracts);

View File

@ -3,6 +3,9 @@ const assert = require('assert');
const sinon = require('sinon'); const sinon = require('sinon');
const AccountParser = require('../lib/contracts/accountParser'); const AccountParser = require('../lib/contracts/accountParser');
let TestLogger = require('../lib/tests/test_logger.js'); let TestLogger = require('../lib/tests/test_logger.js');
const Web3 = require('web3');
const i18n = require('../lib/i18n/i18n.js');
i18n.setOrDetectLocale('en');
describe('embark.AccountParser', function () { describe('embark.AccountParser', function () {
describe('#getAccount', function () { describe('#getAccount', function () {
@ -25,7 +28,7 @@ describe('embark.AccountParser', function () {
privateKey: 'myKey' privateKey: 'myKey'
}, web3, testLogger); }, web3, testLogger);
assert.deepEqual(account, {key: '0xmyKey'}); assert.deepEqual(account, {key: '0xmyKey', hexBalance: null});
}); });
it('should return two accounts from the keys in the file', function () { it('should return two accounts from the keys in the file', function () {
@ -34,8 +37,8 @@ describe('embark.AccountParser', function () {
}, web3, testLogger); }, web3, testLogger);
assert.deepEqual(account, [ assert.deepEqual(account, [
{key: '0xkey1'}, {key: '0xkey1', hexBalance: null},
{key: '0xkey2'} {key: '0xkey2', hexBalance: null}
]); ]);
}); });
@ -45,7 +48,7 @@ describe('embark.AccountParser', function () {
}, web3, testLogger); }, web3, testLogger);
assert.deepEqual(account, assert.deepEqual(account,
[{key: "0xf942d5d524ec07158df4354402bfba8d928c99d0ab34d0799a6158d56156d986"}]); [{key: "0xf942d5d524ec07158df4354402bfba8d928c99d0ab34d0799a6158d56156d986", hexBalance: null}]);
}); });
it('should return two accounts from the mnemonic using numAddresses', function () { it('should return two accounts from the mnemonic using numAddresses', function () {
@ -56,8 +59,8 @@ describe('embark.AccountParser', function () {
assert.deepEqual(account, assert.deepEqual(account,
[ [
{key: "0xf942d5d524ec07158df4354402bfba8d928c99d0ab34d0799a6158d56156d986"}, {key: "0xf942d5d524ec07158df4354402bfba8d928c99d0ab34d0799a6158d56156d986", hexBalance: null},
{key: "0x88f37cfbaed8c0c515c62a17a3a1ce2f397d08bbf20dcc788b69f11b5a5c9791"} {key: "0x88f37cfbaed8c0c515c62a17a3a1ce2f397d08bbf20dcc788b69f11b5a5c9791", hexBalance: null}
]); ]);
}); });
@ -68,6 +71,46 @@ describe('embark.AccountParser', function () {
assert.strictEqual(account, null); assert.strictEqual(account, null);
}); });
});
describe('getHexBalance', () => {
it('should return default if no balance', () => {
const hexBalance = AccountParser.getHexBalance(null, Web3);
assert.strictEqual(hexBalance, 0xFFFFFFFFFFFFFFFFFF);
});
it('should return the balance string if already hexadecimal', () => {
const hexBalance = AccountParser.getHexBalance('0xFFF', Web3);
assert.strictEqual(hexBalance, '0xFFF');
});
it('should convert to hex when decimal', () => {
const hexBalance = AccountParser.getHexBalance('500', Web3);
assert.strictEqual(hexBalance, '0x1f4');
});
it('should convert to hex with eth string', () => {
const hexBalance = AccountParser.getHexBalance('4ether', Web3);
assert.strictEqual(hexBalance, '0x3782dace9d900000');
});
it('should convert to hex with eth string with space', () => {
const hexBalance = AccountParser.getHexBalance('673 shannon', Web3);
assert.strictEqual(hexBalance, '0x9cb1ed0a00');
});
it('should fail when string is not good', () => {
try {
AccountParser.getHexBalance('nogood', Web3);
assert.fail('Should have failed at getHexBalance');
} catch (e) {
// Ok
}
});
}); });
}); });

View File

@ -1,9 +1,19 @@
/*global contract, config, it, embark*/ /*global contract, config, it, embark, web3*/
const assert = require('assert'); const assert = require('assert');
const AnotherStorage = embark.require('Embark/contracts/AnotherStorage'); const AnotherStorage = embark.require('Embark/contracts/AnotherStorage');
const SimpleStorage = embark.require('Embark/contracts/SimpleStorage'); const SimpleStorage = embark.require('Embark/contracts/SimpleStorage');
let accounts;
config({ config({
deployment: {
"accounts": [
{
"mnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm",
balance: "5ether"
}
]
},
contracts: { contracts: {
"SimpleStorage": { "SimpleStorage": {
args: [100] args: [100]
@ -12,6 +22,8 @@ config({
args: ["$SimpleStorage"] args: ["$SimpleStorage"]
} }
} }
}, (err, theAccounts) => {
accounts = theAccounts;
}); });
contract("AnotherStorage", function() { contract("AnotherStorage", function() {
@ -21,4 +33,10 @@ contract("AnotherStorage", function() {
let result = await AnotherStorage.methods.simpleStorageAddress().call(); let result = await AnotherStorage.methods.simpleStorageAddress().call();
assert.equal(result.toString(), SimpleStorage.options.address); assert.equal(result.toString(), SimpleStorage.options.address);
}); });
it('should set the balance correctly', async function () {
const balance = await web3.eth.getBalance(accounts[0]);
assert.ok(balance < 5000000000000000000);
assert.ok(balance > 4000000000000000000);
});
}); });