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:
commit
8aad260dfa
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue