feat(@embark/test-runner): make vm default node (#1846)

* feat: make vm default node

* feat(@embark/tests): enable switching node between tests
This commit is contained in:
Jonathan Rainville 2019-09-12 17:30:28 -04:00 committed by Iuri Matias
parent 2f9d5e6085
commit f54fbf0b3d
22 changed files with 368 additions and 163 deletions

View File

@ -5,7 +5,7 @@ let accounts;
// For documentation please see https://embark.status.im/docs/contracts_testing.html
config({
//deployment: {
//blockchain: {
// accounts: [
// // you can configure custom accounts with a custom balance
// // see https://embark.status.im/docs/contracts_testing.html#Configuring-accounts
@ -31,7 +31,7 @@ contract("SimpleStorage", function () {
});
it("set storage value", async function () {
await SimpleStorage.methods.set(150).send();
await SimpleStorage.methods.set(150).send({from: web3.eth.defaultAccount});
let result = await SimpleStorage.methods.get().call();
assert.strictEqual(parseInt(result, 10), 150);
});

View File

@ -35,6 +35,7 @@
"webpackDone": "webpackDone"
},
"blockchain": {
"vm": "vm",
"call": "eth_call",
"clients": {
"geth": "geth",

View File

@ -708,9 +708,7 @@ simulator(_options) {
const Engine = require('../lib/core/engine.js');
const engine = new Engine({
// TODO: this should not be necessary
env: "development",
//env: options.env,
env: options.env,
client: options.client,
locale: options.locale,
version: this.version,
@ -743,23 +741,6 @@ simulator(_options) {
engine.registerModuleGroup("pipeline");
engine.registerModuleGroup("tests", options);
let plugin = engine.plugins.createPlugin('cmdcontrollerplugin', {});
plugin.registerActionForEvent("embark:engine:started", async (_params, cb) => {
try {
await engine.events.request2("blockchain:node:start", engine.config.blockchainConfig);
await Promise.all([
engine.events.request2("storage:node:start", engine.config.storageConfig),
engine.events.request2("communication:node:start", engine.config.communicationConfig),
engine.events.request2("namesystem:node:start", engine.config.namesystemConfig)
]);
} catch (e) {
return cb(e);
}
cb();
});
engine.startEngine(next);
},
function setupTestEnvironment(next) {

View File

@ -22,7 +22,7 @@ export function getBlockchainDefaults(env) {
wsHost: "localhost",
wsPort: 8546,
networkType: "custom",
miningMode: 'dev',
isDev: true,
nodiscover: true,
maxpeers: 0,
targetGasLimit: 8000000,

View File

@ -211,6 +211,7 @@ class Engine {
contractsComponents(_options) {
this.registerModule('ethereum-blockchain-client');
this.registerModule('ganache');
this.registerModulePackage('embark-web3');
this.registerModulePackage('embark-accounts-manager');
this.registerModulePackage('embark-specialconfigs', {plugins: this.plugins});

View File

@ -1,5 +1,6 @@
import async from 'async';
const {__} = require('embark-i18n');
const constants = require('embark-core/constants');
const Web3RequestManager = require('web3-core-requestmanager');
import BlockchainAPI from "./api";
@ -12,7 +13,7 @@ class Blockchain {
this.blockchainConfig = embark.config.blockchainConfig;
this.contractConfig = embark.config.contractConfig;
this.blockchainApi = new BlockchainAPI(embark);
this.startedClient = null;
embark.registerActionForEvent("pipeline:generateAll:before", this.addArtifactFile.bind(this));
@ -22,6 +23,16 @@ class Blockchain {
});
this.events.setCommandHandler("blockchain:node:start", async (blockchainConfig, cb) => {
const self = this;
const clientName = blockchainConfig.client;
function started() {
self.startedClient = clientName;
self.events.emit("blockchain:started", clientName);
}
if (clientName === constants.blockchain.vm) {
started();
return cb();
}
const requestManager = new Web3RequestManager.Manager(blockchainConfig.endpoint);
const ogConsoleError = console.error;
@ -37,21 +48,52 @@ class Blockchain {
console.error = ogConsoleError;
if (!err) {
// Node is already started
this.events.emit("blockchain:started");
started();
return cb(null, true);
}
const clientName = blockchainConfig.client;
const client = this.blockchainNodes[clientName];
if (!client) return cb("client " + clientName + " not found");
const clientFunctions = this.blockchainNodes[clientName];
if (!clientFunctions) {
return cb(__("Client %s not found", clientName));
}
let onStart = () => {
this.events.emit("blockchain:started", clientName);
started();
cb();
};
client.apply(client, [onStart]);
this.startedClient = clientName;
clientFunctions.launchFn.apply(clientFunctions, [onStart]);
});
});
this.events.setCommandHandler("blockchain:node:stop", (clientName, cb) => {
if (typeof clientName === 'function') {
if (!this.startedClient) {
return cb(__('No blockchain client is currently started'));
}
cb = clientName;
clientName = this.startedClient;
}
if (clientName === constants.blockchain.vm) {
this.startedClient = null;
this.events.emit("blockchain:stopped", clientName);
return cb();
}
const clientFunctions = this.blockchainNodes[clientName];
if (!clientFunctions) {
return cb(__("Client %s not found", clientName));
}
clientFunctions.stopFn.apply(clientFunctions, [
() => {
this.events.emit("blockchain:stopped", clientName);
cb();
}
]);
this.startedClient = null;
});
this.blockchainApi.registerAPIs("ethereum");
this.blockchainApi.registerRequests("ethereum");
}

View File

@ -29,6 +29,10 @@ class EthereumBlockchainClient {
this.events.request("blockchain:client:register", "ethereum", this.getClient.bind(this));
this.events.request("deployment:deployer:register", "ethereum", this.deployer.bind(this));
this.events.on("blockchain:started", () => {
this._web3 = null;
});
this.registerAPIRequests();
}
@ -54,23 +58,18 @@ class EthereumBlockchainClient {
async deployer(contract, done) {
const web3 = await this.web3;
// var web3 = new Web3("ws://localhost:8556")
// web3.eth.getAccounts().then((accounts) => {
let accounts = await web3.eth.getAccounts();
let account = accounts[0];
// let contractObject = this.blockchain.ContractObject({abi: contract.abiDefinition});
let contractObj = new web3.eth.Contract(contract.abiDefinition, contract.address);
// let deployObject = this.blockchain.deployContractObject(contractObject, {arguments: contractParams, data: dataCode});
let contractObject = contractObj.deploy({arguments: (contract.args || []), data: ("0x" + contract.code)});
const [account] = await web3.eth.getAccounts();
const contractObj = new web3.eth.Contract(contract.abiDefinition, contract.address);
const contractObject = contractObj.deploy({arguments: (contract.args || []), data: ("0x" + contract.code)});
if (contract.gas === 'auto' || !contract.gas) {
let gasValue = await contractObject.estimateGas();
let increase_per = 1 + (Math.random() / 10.0);
const gasValue = await contractObject.estimateGas();
const increase_per = 1 + (Math.random() / 10.0);
contract.gas = Math.floor(gasValue * increase_per);
}
if (!contract.gasPrice) {
let gasPrice = await web3.eth.getGasPrice();
const gasPrice = await web3.eth.getGasPrice();
contract.gasPrice = contract.gasPrice || gasPrice;
}
@ -198,8 +197,7 @@ class EthereumBlockchainClient {
}
async determineAccounts(params, callback) {
let provider = await this.events.request2("blockchain:client:provider", "ethereum");
let web3 = new Web3(provider);
const web3 = await this.web3;
let accounts = await web3.eth.getAccounts();
let deploymentAccount = accounts[0];
let contract = params.contract;

View File

@ -0,0 +1,10 @@
class Ganache {
constructor(embark) {
embark.events.request('proxy:vm:register', () => {
const ganache = require('ganache-cli');
return ganache.provider();
});
}
}
module.exports = Ganache;

View File

@ -6,7 +6,6 @@ import {ws, rpc} from './check.js';
const constants = require('embark-core/constants');
class Geth {
constructor(embark, options) {
this.embark = embark;
this.embarkConfig = embark.config.embarkConfig;
@ -23,7 +22,8 @@ class Geth {
return;
}
this.events.request("blockchain:node:register", constants.blockchain.clients.geth, (readyCb) => {
this.events.request("blockchain:node:register", constants.blockchain.clients.geth, {
launchFn: (readyCb) => {
this.events.request('processes:register', 'blockchain', {
launchFn: (cb) => {
this.startBlockchainNode(cb);
@ -39,6 +39,11 @@ class Geth {
readyCb();
});
this.registerServiceCheck();
},
stopFn: async (cb) => {
await this.events.request("processes:stop", "blockchain");
cb();
}
});
this.events.request("whisper:node:register", constants.blockchain.clients.geth, readyCb => {

View File

@ -34,6 +34,10 @@ export default class AccountsManager {
this.embark.registerActionForEvent("blockchain:proxy:request", this.checkBlockchainRequest.bind(this));
this.embark.registerActionForEvent("blockchain:proxy:response", this.checkBlockchainResponse.bind(this));
this.events.on("blockchain:started", () => {
this._web3 = null;
});
// Allow to run transaction in parallel by resolving the nonce manually.
// For each transaction, resolve the nonce by taking the max of current transaction count and the cache we keep locally.
// Update the nonce and sign it

View File

@ -9,6 +9,10 @@ export default class DeploymentChecks {
this.events = events;
this.logger = logger;
this._web3 = null;
this.events.on("blockchain:started", () => {
this._web3 = null;
});
}
get web3() {

View File

@ -17,6 +17,10 @@ export default class TrackingFunctions {
this._block = null;
this.ensureChainTrackerFile();
this.events.on("blockchain:started", () => {
this._web3 = null;
});
}
get web3() {

View File

@ -84,6 +84,10 @@ class ENS {
setImmediate(cb, this.isENSName(name));
});
this.events.on("blockchain:started", () => {
this._web3 = null;
});
this.init(() => {});
}

View File

@ -47,6 +47,14 @@
},
"extends": "../../../.eslintrc.json"
},
"dependencies": {
"@babel/runtime-corejs2": "7.3.1",
"async": "3.1.0",
"embark-i18n": "^4.1.1",
"embark-utils": "^4.1.1",
"mocha": "6.2.0",
"web3": "1.2.1"
},
"devDependencies": {
"@babel/cli": "7.2.3",
"@babel/core": "7.2.2",
@ -60,12 +68,7 @@
"tslint": "5.16.0",
"typescript": "3.4.5"
},
"dependencies": {
"async": "2.6.1",
"embarkjs": "^4.1.1",
"mocha": "6.2.0",
"web3": "1.2.1"
},
"engines": {
"node": ">=8.12.0 <12.0.0",
"npm": ">=6.4.1",

View File

@ -1,3 +1,5 @@
import {__} from 'embark-i18n';
const assert = require('assert').strict;
const async = require('async');
const EmbarkJS = require('embarkjs');
@ -14,8 +16,12 @@ class MochaTestRunner {
this.embark = embark;
this.events = embark.events;
this.plugins = options.plugins;
this.logger = embark.logger;
this.fs = embark.fs;
this.files = [];
this.options = {};
this.web3 = null;
this.events.request('tests:runner:register',
'JavaScript (Mocha)',
@ -37,20 +43,31 @@ class MochaTestRunner {
return JAVASCRIPT_TEST_MATCH.test(path);
}
run(options, cb) {
const {events, plugins} = this;
async run(options, cb) {
const {events} = this.embark;
const {reporter} = options;
this.options = options;
const Module = require("module");
const originalRequire = require("module").prototype.require;
let accounts = [];
let compiledContracts = {};
let web3;
const config = (cfg, acctCb) => {
global.before((done) => {
async.waterfall([
(next) => {
events.request("tests:deployment:check", cfg, this.options, (err, provider) => {
if (err) {
return next(err);
}
if (provider) {
this.web3.setProvider(provider);
}
next();
});
},
(next) => {
events.request("contracts:build", cfg, compiledContracts, next);
},
@ -86,6 +103,12 @@ class MochaTestRunner {
}
next();
},
(next) => {
this.web3.eth.getAccounts((err, accts) => {
accounts = accts;
next(err);
});
}
], (err) => {
// Reset the gas accumulator so that we don't show deployment gas on the
@ -102,32 +125,37 @@ class MochaTestRunner {
});
};
const provider = await this.events.request2("tests:blockchain:start", this.options);
this.web3 = new Web3(provider);
accounts = await this.web3.eth.getAccounts();
await events.request2("contracts:reset");
let contractFiles = await events.request2("config:contractsFiles");
async.waterfall([
(next) => { // request provider
events.request("blockchain:client:provider", "ethereum", next);
(next) => {
this.plugins.emitAndRunActionsForEvent('tests:contracts:compile:before', contractFiles, next);
},
(provider, next) => { // set provider and fetch account list
web3 = new Web3(provider);
web3.eth.getAccounts(next);
(_contractFiles, next) => {
contractFiles = _contractFiles;
events.request("compiler:contracts:compile", _contractFiles, next);
},
(accts, next) => { // reset contracts as we might have state leakage from other plugins
accounts = accts;
events.request("contracts:reset", next);
(_compiledContracts, next) => {
this.plugins.emitAndRunActionsForEvent('tests:contracts:compile:after', _compiledContracts, next);
},
(next) => { // get contract files
events.request("config:contractsFiles", next);
},
(cf, next) => {
plugins.emitAndRunActionsForEvent('tests:contracts:compile:before', cf, next);
},
(cf, next) => { // compile contracts
events.request("compiler:contracts:compile", cf, next);
},
(cc, next) => {
plugins.emitAndRunActionsForEvent('tests:contracts:compile:after', cc, next);
},
(cc, next) => { // override require
compiledContracts = cc;
(_compiledContracts, next) => {
compiledContracts = _compiledContracts;
const fns = this.files.map((file) => {
return (seriesCb) => {
this.fs.readFile(file, (err, data) => {
if (err) {
self.logger.error(__('Error reading file %s', file));
self.logger.error(err);
seriesCb(null, 1);
}
if (data.toString().search(/contract\(|describe\(/) === -1) {
return seriesCb(null, 0);
}
Module.prototype.require = function(req) {
const prefix = "Embark/contracts/";
@ -141,15 +169,11 @@ class MochaTestRunner {
if (!instance) {
throw new Error(`Cannot find module '${req}'`);
}
return instance;
};
next();
},
(next) => { // initialize Mocha
const mocha = new Mocha();
mocha.reporter(Reporter, { reporter: reporter });
const mocha = new Mocha();
mocha.reporter(Reporter, {reporter: options.reporter});
const describeWithAccounts = (scenario, cb) => {
Mocha.describe(scenario, cb.bind(mocha, accounts));
};
@ -161,14 +185,19 @@ class MochaTestRunner {
global.config = config;
});
mocha.suite.timeout(TEST_TIMEOUT);
for(const file of this.files) {
mocha.addFile(file);
}
mocha.run((_failures) => {
next();
mocha.suite.timeout(TEST_TIMEOUT);
mocha.addFile(file);
mocha.run((failures) => {
Module.prototype.require = originalRequire;
seriesCb(null, failures);
});
});
};
});
async.series(fns, next);
}
], (err) => {
events.emit('tests:finished');

View File

@ -10,6 +10,9 @@ class TransactionTracker {
this._web3 = null;
embark.events.on("block:header", this.onBlockHeader.bind(this));
this.events.on("blockchain:started", () => {
this._web3 = null;
});
this.registerAPICalls();
this.subscribeToPendingTransactions();
}

View File

@ -40,11 +40,6 @@ class EmbarkWeb3 {
}
async registerWeb3Object() {
const checkWeb3 = `return (typeof web3 === 'undefined');`;
const web3NotDefined = await this.events.request2('runcode:eval', checkWeb3);
if (!web3NotDefined) return;
const provider = await this.events.request2("blockchain:client:provider", "ethereum");
const web3 = new Web3(provider);
await this.events.request2("runcode:register", 'web3', web3);

View File

@ -44,6 +44,10 @@ class ContractsManager {
cb(null, this.contracts[contract.className]);
});
this.events.on("blockchain:started", () => {
this._web3 = null;
});
this.registerCommands();
this.registerAPIs();
}
@ -262,6 +266,9 @@ class ContractsManager {
function prepareContractsFromConfig(callback) {
self.events.emit("status", __("Building..."));
if (contractsConfig.contracts.deploy) {
contractsConfig.contracts = contractsConfig.contracts.deploy;
}
async.eachOf(contractsConfig.contracts, (contract, className, eachCb) => {
contract = new Contract(self.logger, contract);
if (!contract.artifact) {

View File

@ -13,7 +13,6 @@ class ContractDeployer {
}
deployContract(contract, callback) {
async.waterfall([
(next) => {
this.plugins.emitAndRunActionsForEvent('deployment:contract:beforeDeploy', {contract: contract}, (err, _params) => {
@ -22,7 +21,6 @@ class ContractDeployer {
});
},
(next) => {
// self.plugins.emitAndRunActionsForEvent('deployment:contract:arguments', {contract: contract}, (_params) => {
this.plugins.emitAndRunActionsForEvent('deployment:contract:shouldDeploy', {contract: contract, shouldDeploy: true}, (err, params) => {
next(err, params);
});

View File

@ -11,26 +11,29 @@ export default class ProxyManager {
private proxy: any;
private plugins: any;
private readonly host: string;
private rpcPort: number;
private wsPort: number;
private ready: boolean;
private rpcPort = 0;
private wsPort = 0;
private ready = false;
private isWs = false;
private vms: any[];
constructor(private embark: Embark, options: any) {
this.logger = embark.logger;
this.events = embark.events;
this.plugins = options.plugins;
this.ready = false;
this.rpcPort = 0;
this.wsPort = 0;
this.vms = [];
this.host = "localhost";
this.events.once("blockchain:started", async () => {
await this.setupProxy();
this.events.on("blockchain:started", async (clientName: string) => {
await this.setupProxy(clientName);
this.ready = true;
this.events.emit("proxy:ready");
});
this.events.on("blockchain:stopped", async (clientName: string, node?: string) => {
this.ready = false;
await this.stopProxy();
});
if (!this.embark.config.blockchainConfig.proxy) {
this.logger.warn(__("The proxy has been disabled -- some Embark features will not work."));
@ -48,6 +51,10 @@ export default class ProxyManager {
}
cb(null, buildUrl("http", this.host, this.rpcPort, "rpc"));
});
this.events.setCommandHandler("proxy:vm:register", (handler: any) => {
this.vms.push(handler);
});
}
public onReady() {
@ -61,23 +68,31 @@ export default class ProxyManager {
});
}
private async setupProxy() {
private async setupProxy(clientName: string) {
if (!this.embark.config.blockchainConfig.proxy) {
return;
}
if (this.proxy) {
throw new Error("Proxy is already started");
}
const port = await findNextPort(this.embark.config.blockchainConfig.rpcPort + constants.blockchain.servicePortOnProxy);
this.rpcPort = port;
this.wsPort = port + 1;
this.isWs = (/wss?/).test(this.embark.config.blockchainConfig.endpoint);
this.isWs = clientName === constants.blockchain.vm || (/wss?/).test(this.embark.config.blockchainConfig.endpoint);
this.proxy = await new Proxy({events: this.events, plugins: this.plugins, logger: this.logger})
.serve(
this.embark.config.blockchainConfig.endpoint,
this.proxy = await new Proxy({events: this.events, plugins: this.plugins, logger: this.logger, vms: this.vms});
await this.proxy.serve(
clientName === constants.blockchain.vm ? constants.blockchain.vm : this.embark.config.blockchainConfig.endpoint,
this.host,
this.isWs ? this.wsPort : this.rpcPort,
this.isWs,
);
return;
}
private stopProxy() {
this.proxy.stop();
this.proxy = null;
}
}

View File

@ -4,6 +4,7 @@ import express from 'express';
import expressWs from 'express-ws';
import cors from 'cors';
const Web3RequestManager = require('web3-core-requestmanager');
const constants = require("embark-core/constants");
const ACTION_TIMEOUT = 5000;
@ -15,9 +16,15 @@ export class Proxy {
this.timeouts = {};
this.plugins = options.plugins;
this.logger = options.logger;
this.vms = options.vms;
this.app = null;
this.server = null;
}
async serve(endpoint, localHost, localPort, ws) {
if (endpoint === constants.blockchain.vm) {
endpoint = this.vms[this.vms.length - 1]();
}
const requestManager = new Web3RequestManager.Manager(endpoint);
try {
@ -26,17 +33,17 @@ export class Proxy {
throw new Error(__('Unable to connect to the blockchain endpoint'));
}
const app = express();
this.app = express();
if (ws) {
expressWs(app);
expressWs(this.app);
}
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({extended: true}));
this.app.use(cors());
this.app.use(express.json());
this.app.use(express.urlencoded({extended: true}));
if (ws) {
app.ws('/', (ws, _wsReq) => {
this.app.ws('/', (ws, _wsReq) => {
ws.on('message', (msg) => {
let jsonMsg;
try {
@ -50,7 +57,7 @@ export class Proxy {
// Send the possibly modified request to the Node
requestManager.send(resp.reqData, (err, result) => {
if (err) {
return this.logger.error(__('Error executing the request on the Node'), JSON.stringify(err));
return this.logger.error(__('Error executing the request on the Node'), err.message || err);
}
this.emitActionsForResponse(resp.reqData, {jsonrpc: "2.0", id: resp.reqData.id, result}, (_err, resp) => {
// Send back to the caller (web3)
@ -62,7 +69,7 @@ export class Proxy {
});
} else {
// HTTP
app.use((req, res) => {
this.app.use((req, res) => {
// Modify request
this.emitActionsForRequest(req.body, (_err, resp) => {
// Send the possibly modified request to the Node
@ -80,9 +87,9 @@ export class Proxy {
}
return new Promise(resolve => {
app.listen(localPort, localHost, null,
this.server = this.app.listen(localPort, localHost, null,
() => {
resolve(app);
resolve(this.app);
});
});
}
@ -147,4 +154,17 @@ export class Proxy {
calledBack = true;
});
}
stop() {
if (!this.server) {
return;
}
this.server.close();
this.server = null;
this.app = null;
this.commList = {};
this.receipts = {};
this.transactions = {};
this.timeouts = {};
}
}

View File

@ -1,9 +1,12 @@
import { __ } from 'embark-i18n';
import {buildUrl, deconstructUrl, recursiveMerge} from "embark-utils";
const async = require('async');
const chalk = require('chalk');
const path = require('path');
const { dappPath } = require('embark-utils');
import cloneDeep from "lodash.clonedeep";
import { COVERAGE_GAS_LIMIT, GAS_LIMIT } from './constants';
const constants = require('embark-core/constants');
const coverage = require('istanbul-lib-coverage');
const reporter = require('istanbul-lib-report');
@ -22,6 +25,10 @@ class TestRunner {
this.gasLimit = options.coverage ? COVERAGE_GAS_LIMIT : GAS_LIMIT;
this.files = [];
this.configObj = embark.config;
this.originalConfigObj = cloneDeep(embark.config);
this.simOptions = {};
this.events.setCommandHandler('tests:run', (options, callback) => {
this.run(options, callback);
});
@ -32,6 +39,9 @@ class TestRunner {
// like Jest tests and such.
this.runners.unshift({pluginName, matchFn, addFn, runFn});
});
this.events.setCommandHandler('tests:deployment:check', this.checkDeploymentOptions.bind(this));
this.events.setCommandHandler('tests:blockchain:start', this.startBlockchainNode.bind(this));
}
run(options, cb) {
@ -65,7 +75,7 @@ class TestRunner {
});
async.series(runnerFns, next);
},
}
], (err) => {
reporter.footer();
@ -85,16 +95,16 @@ class TestRunner {
open(dappPath('coverage/index.html')).then(() => {
cb(err, reporter.passes, reporter.fails);
});
} catch(err) {
process.stdout.write(chalk`{red Coverage report could not be created:}\n{white ${err.message}}\n`);
cb(err, reporter.passes, reporter.fails);
} catch(e) {
process.stdout.write(chalk`{red Coverage report could not be created:}\n{white ${e.message}}\n`);
cb(e, reporter.passes, reporter.fails);
}
});
}
generateCoverageReport() {
const coveragePath = dappPath(".embark", "coverage.json");
const coverageMap = JSON.parse(fs.readFileSync(coveragePath));
const coverageMap = JSON.parse(this.fs.readFileSync(coveragePath));
const map = coverage.createCoverageMap(coverageMap);
const tree = reporter.summarizers.nested(map);
@ -131,6 +141,77 @@ class TestRunner {
cb(null, [filePath]);
});
}
async checkDeploymentOptions(config, options, cb = () => {}) {
let resetServices = false;
const blockchainConfig = config.blockchain || {};
let {host, port, type, protocol} = blockchainConfig.endpoint ? deconstructUrl(blockchainConfig.endpoint) : {};
const accounts = blockchainConfig.accounts;
if (host && port && !['rpc', 'ws'].includes(type)) {
return cb(__("contracts config error: unknown deployment type %s", type));
}
if (!type) {
type = constants.blockchain.vm;
}
if (accounts || port !== this.simOptions.port || type !== this.simOptions.type || host !== this.simOptions.host) {
resetServices = true;
}
Object.assign(this.simOptions, {host, port, type, protocol, accounts, client: config.blockchain && config.blockchain.client});
if (!resetServices) {
return cb();
}
const provider = await this.startBlockchainNode(options);
cb(null, provider);
return provider;
}
async startBlockchainNode(options, cb = () => {}) {
let node = options.node;
if (!this.simOptions.host && (node && node === constants.blockchain.vm)) {
this.simOptions.type = constants.blockchain.vm;
this.simOptions.client = constants.blockchain.vm;
} else if (this.simOptions.host || (node && node !== constants.blockchain.vm)) {
let options = this.simOptions;
if (node && node !== constants.blockchain.vm) {
options = deconstructUrl(node);
}
if (!options.protocol) {
options.protocol = (options.type === "rpc") ? 'http' : 'ws';
}
Object.assign(this.simOptions, options);
node = null;
}
this.configObj.blockchainConfig = recursiveMerge({}, this.originalConfigObj.blockchainConfig, {
endpoint: this.simOptions.host ? buildUrl(this.simOptions.protocol, this.simOptions.host, this.simOptions.port, this.simOptions.type) : null,
type: this.simOptions.type,
accounts: this.simOptions.accounts,
coverage: options.coverage
});
if (this.simOptions.client) {
this.configObj.blockchainConfig.client = this.simOptions.client;
}
this.logger.trace('Setting blockchain configs:', this.configObj.blockchainConfig);
await this.events.request2('config:blockchainConfig:set', this.configObj.blockchainConfig);
try {
await this.events.request2("blockchain:node:stop");
} catch (e) {
// Nothing to do here, the node probably wasn't even started
}
await this.events.request2("blockchain:node:start", this.configObj.blockchainConfig);
const provider = await this.events.request2("blockchain:client:provider", "ethereum");
cb(null, provider);
return provider;
}
}
module.exports = TestRunner;