fix(@embark/core): Fix tests for shim/monorepo

When embark was running as module inside the dapp’s `node_modules`, the tests were failing due to several issues:
1. `web3` was not being set in the global namespace of vm2. `EmbarkJS.Blockchain.setProvider` was therefore failing because it relies on `global.web3` to be set. I guess somehow this works when the test app was running in a child tree of the executing program. maybe this is a security feature of vm2, but i’m not sure.
2. `embarkjs` provider code being injected to the vm twice. This really was the initial point of failure, as this piece of code is requiring embarkjs, so i’m assuming, but again not sure, that maybe it was getting a second instance of `EmbarkJS` which did not have it’s providers set up correctly (hence the error with `ENS provider not set`).

Fixes for those issues in this PR:
1. To circumvent the web3 issue, we are now setting `global.web3` for tests only (the global web3 accessible in the tests), and `web3` is now explicitly passed in to `EmbarkJS.Blockchain.setProvider`
2. To fix the `embarkjs` code being called twice, we are not re-injecting this code to the VM during test initialisations
This commit is contained in:
emizzle 2019-01-23 15:57:24 +11:00 committed by Iuri Matias
parent 756a916428
commit eb4da28a61
5 changed files with 83 additions and 60 deletions

View File

@ -3,6 +3,7 @@ const fs = require('../../fs');
class CodeRunner { class CodeRunner {
constructor(options) { constructor(options) {
this.ready = false;
this.config = options.config; this.config = options.config;
this.plugins = options.plugins; this.plugins = options.plugins;
this.logger = options.logger; this.logger = options.logger;
@ -38,6 +39,7 @@ class CodeRunner {
this.registerEvents(); this.registerEvents();
this.registerCommands(); this.registerCommands();
this.events.emit('runcode:ready'); this.events.emit('runcode:ready');
this.ready = true;
} }
registerIpcEvents() { registerIpcEvents() {
@ -74,6 +76,23 @@ class CodeRunner {
cb(this.vm.options.sandbox); cb(this.vm.options.sandbox);
}); });
this.events.setCommandHandler('runcode:eval', this.evalCode.bind(this)); this.events.setCommandHandler('runcode:eval', this.evalCode.bind(this));
this.events.setCommandHandler('runcode:ready', (cb) => {
if (this.ready) {
return cb();
}
this.events.once("runcode:ready", cb);
});
this.events.setCommandHandler('runcode:embarkjs:reset', this.resetEmbarkJS.bind(this));
}
resetEmbarkJS(cb) {
this.events.request('blockchain:get', (web3) => {
this.events.emit("runcode:register", "web3", web3, false, () => {
this.events.request("code-generator:embarkjs:init-provider-code", async (code) => {
await this.evalCode(code, cb, true);
});
});
});
} }
registerVar(varName, code, toRecord = true, cb = () => {}) { registerVar(varName, code, toRecord = true, cb = () => {}) {

View File

@ -16,6 +16,7 @@ class TestRunner {
this.fs = embark.fs; this.fs = embark.fs;
this.ipc = options.ipc; this.ipc = options.ipc;
this.runResults = []; this.runResults = [];
this.embarkjs = null;
this.events.setCommandHandler('tests:run', (options, callback) => { this.events.setCommandHandler('tests:run', (options, callback) => {
this.run(options, callback); this.run(options, callback);
@ -145,10 +146,10 @@ class TestRunner {
runJSTests(files, options, cb) { runJSTests(files, options, cb) {
const self = this; const self = this;
const test = new Test({loglevel: options.loglevel, node: options.node, events: self.events, logger: self.logger,
config: self.embark.config, ipc: self.ipc, coverage: options.coverage, inProcess: options.inProcess});
async.waterfall([ async.waterfall([
function setupGlobalNamespace(next) { function setupGlobalNamespace(next) {
const test = new Test({loglevel: options.loglevel, node: options.node, events: self.events, logger: self.logger,
config: self.embark.config, ipc: self.ipc, coverage: options.coverage, inProcess: options.inProcess});
global.embark = test; global.embark = test;
global.assert = assert; global.assert = assert;
global.config = test.config.bind(test); global.config = test.config.bind(test);
@ -162,20 +163,27 @@ class TestRunner {
global.deployAll = deprecatedWarning; global.deployAll = deprecatedWarning;
global.EmbarkSpec = {}; global.EmbarkSpec = {};
global.EmbarkSpec.deployAll = deprecatedWarning; global.EmbarkSpec.deployAll = deprecatedWarning;
global.contract = function (describeName, callback) {
return Mocha.describe(describeName, callback);
};
next();
},
function overrideRequire (next) {
// Override require to enable `require('Embark/contracts/contractName');` // Override require to enable `require('Embark/contracts/contractName');`
const Module = require('module'); const Module = require('module');
const originalRequire = require('module').prototype.require; const originalRequire = require('module').prototype.require;
Module.prototype.require = function (requireName) { Module.prototype.require = function (requireName) {
if (requireName.startsWith('Embark/EmbarkJS')) {
return self.embarkjs;
}
if (requireName.startsWith('Embark')) { if (requireName.startsWith('Embark')) {
return test.require(...arguments); return test.require(...arguments);
} }
return originalRequire.apply(this, arguments); return originalRequire.apply(this, arguments);
}; };
next();
global.contract = function (describeName, callback) { },
return Mocha.describe(describeName, callback); function initTest(next) {
};
test.init((err) => { test.init((err) => {
next(err, files); next(err, files);
@ -200,7 +208,11 @@ class TestRunner {
global.config({}); global.config({});
} }
global.embark.onReady((err) => { global.embark.onReady((err) => {
done(err); if(err) return done(err);
self.events.request('runcode:eval', 'EmbarkJS', (err, embarkjs) => {
self.embarkjs = embarkjs;
done(err);
});
}); });
}); });
mocha.run(function (fails) { mocha.run(function (fails) {

View File

@ -120,10 +120,16 @@ class SolcTest extends Test {
methodIdentifiers: contract.functionHashes methodIdentifiers: contract.functionHashes
} }
}; };
remixTests.runTest(contract.className, Test.getWeb3Contract(contract, web3), contractDetails, {accounts}, this.getWeb3Contract(contract, web3, (err, web3contract) => {
self._prettyPrint.bind(self), _callback); if(err) {
return _callback(err);
}
remixTests.runTest(contract.className, web3contract, contractDetails, {accounts},
self._prettyPrint.bind(self), _callback);
});
}; };
fns.push(fn); fns.push(fn);
}); });
async.series(fns, next); async.series(fns, next);
} }

View File

@ -2,13 +2,10 @@ import * as utilsContractsConfig from "../../utils/contractsConfig";
const async = require('async'); const async = require('async');
const AccountParser = require('../../utils/accountParser'); const AccountParser = require('../../utils/accountParser');
const EmbarkJS = require('embarkjs');
const utils = require('../../utils/utils'); const utils = require('../../utils/utils');
const constants = require('../../constants'); const constants = require('../../constants');
const web3Utils = require('web3-utils'); const web3Utils = require('web3-utils');
const VM = require('../../core/modules/coderunner/vm'); const EmbarkJS = require('embarkjs');
const Web3 = require('web3');
const IpfsApi = require("ipfs-api");
const BALANCE_10_ETHER_IN_HEX = '0x8AC7230489E80000'; const BALANCE_10_ETHER_IN_HEX = '0x8AC7230489E80000';
@ -32,24 +29,26 @@ class Test {
} }
init(callback) { init(callback) {
this.gasLimit = constants.tests.gasLimit; this.events.request('runcode:ready', () => {
this.events.request('deploy:setGasLimit', this.gasLimit); this.gasLimit = constants.tests.gasLimit;
const waitingForReady = setTimeout(() => { this.events.request('deploy:setGasLimit', this.gasLimit);
this.logger.warn('Waiting for the blockchain connector to be ready...'); const waitingForReady = setTimeout(() => {
// TODO add docs link to how to install one this.logger.warn('Waiting for the blockchain connector to be ready...');
this.logger.warn('If you did not install a blockchain connector, stop this process and install one'); // TODO add docs link to how to install one
}, 5000); this.logger.warn('If you did not install a blockchain connector, stop this process and install one');
this.events.request('blockchain:connector:ready', () => { }, 5000);
clearTimeout(waitingForReady); this.events.request('blockchain:connector:ready', () => {
if (this.options.node !== 'embark') { clearTimeout(waitingForReady);
this.showNodeHttpWarning(); if (this.options.node !== 'embark') {
return callback(); this.showNodeHttpWarning();
} return callback();
if (!this.ipc.connected) { }
this.logger.error("Could not connect to Embark's IPC. Is embark running?"); if (!this.ipc.connected) {
if (!this.options.inProcess) process.exit(1); this.logger.error("Could not connect to Embark's IPC. Is embark running?");
} if (!this.options.inProcess) process.exit(1);
this.connectToIpcNode(callback); }
this.connectToIpcNode(callback);
});
}); });
} }
@ -229,7 +228,6 @@ class Test {
} }
self.ready = true; self.ready = true;
self.error = false; self.error = false;
self.events.emit('tests:ready');
next(null, accounts); next(null, accounts);
}); });
}, },
@ -243,31 +241,16 @@ class Test {
// TODO Do not exit in case of not a normal run (eg after a change) // TODO Do not exit in case of not a normal run (eg after a change)
if (!self.options.inProcess) process.exit(1); if (!self.options.inProcess) process.exit(1);
} }
self.events.emit('tests:ready');
callback(null, accounts); callback(null, accounts);
}); });
} }
resetEmbarkJS(cb) { resetEmbarkJS(cb) {
this.events.request('blockchain:get', (web3) => { this.events.request('blockchain:get', (web3) => {
// global web3 used in the tests, not in the vm
global.web3 = web3; global.web3 = web3;
this.vm = new VM({ this.events.request("runcode:embarkjs:reset", cb);
sandbox: {
EmbarkJS,
web3: web3,
Web3: Web3,
IpfsApi
}
});
this.events.request("code-generator:embarkjs:provider-code", (code) => {
this.vm.doEval(code, false, (err, _result) => {
if(err) return cb(err);
this.events.request("code-generator:embarkjs:init-provider-code", (code) => {
this.vm.doEval(code, false, (err, _result) => {
cb(err);
});
});
});
});
}); });
} }
@ -339,7 +322,14 @@ class Test {
const testContractFactoryPlugin = self.plugins.getPluginsFor('testContractFactory').slice(-1)[0]; const testContractFactoryPlugin = self.plugins.getPluginsFor('testContractFactory').slice(-1)[0];
const newContract = testContractFactoryPlugin ? testContractFactoryPlugin.testContractFactory(contract, web3) : Test.getWeb3Contract(contract, web3); if (!testContractFactoryPlugin) {
return self.getWeb3Contract(contract, web3, (err, web3Contract) => {
Object.setPrototypeOf(self.contracts[contract.className], web3Contract);
eachCb();
});
}
const newContract = testContractFactoryPlugin.testContractFactory(contract, web3);
Object.setPrototypeOf(self.contracts[contract.className], newContract); Object.setPrototypeOf(self.contracts[contract.className], newContract);
eachCb(); eachCb();
@ -359,7 +349,7 @@ class Test {
}); });
} }
static getWeb3Contract(contract, web3) { getWeb3Contract(contract, web3, cb) {
const newContract = new EmbarkJS.Blockchain.Contract({ const newContract = new EmbarkJS.Blockchain.Contract({
abi: contract.abiDefinition, abi: contract.abiDefinition,
address: contract.deployedAddress, address: contract.deployedAddress,
@ -378,11 +368,11 @@ class Test {
newContract.options.gas = constants.tests.gasLimit; newContract.options.gas = constants.tests.gasLimit;
} }
return newContract; cb(null, newContract);
} }
require(path) { require(path) {
const [contractsPrefix, embarkJSPrefix] = ['Embark/contracts/', 'Embark/EmbarkJS']; const contractsPrefix = 'Embark/contracts/';
// Contract require // Contract require
if (path.startsWith(contractsPrefix)) { if (path.startsWith(contractsPrefix)) {
@ -397,11 +387,6 @@ class Test {
return newContract; return newContract;
} }
// EmbarkJS require
if (path.startsWith(embarkJSPrefix)) {
return EmbarkJS;
}
throw new Error(__('Unknown module %s', path)); throw new Error(__('Unknown module %s', path));
} }
} }

View File

@ -2,6 +2,7 @@
const web3Connector = {}; const web3Connector = {};
web3Connector.init = function(_config) { web3Connector.init = function(_config) {
global.web3 = config.web3 || global.web3;
// Check if the global web3 object uses the old web3 (0.x) // Check if the global web3 object uses the old web3 (0.x)
if (global.web3 && typeof global.web3.version !== 'string') { if (global.web3 && typeof global.web3.version !== 'string') {
// If so, use a new instance using 1.0, but use its provider // If so, use a new instance using 1.0, but use its provider