mirror of https://github.com/embarklabs/embark.git
feat(@embark/embark-deploy-tracker): Add unit tests
Add unit tests for the contract tracking `embark-deploy-tracker` package. `trackingFunctions` was refactored in a way to make it more testable and now performs tracker file IO on demand, as opposed to being executed for each contract deploy. Additionally, removed any synchronous file IO in favour of their asynchronous counterparts. Added special case for config contract-level tracking: when `contract.track === false` in contracts config, contracts are deployed, then the config is changed `contract.track` is truthy, a special use case is presented, and the chains.json file is updated accordingly.
This commit is contained in:
parent
781d878a02
commit
98721034fe
|
@ -17,7 +17,7 @@ export default class Contract {
|
|||
public deploy?: boolean = true;
|
||||
public realRuntimeBytecode: string = "";
|
||||
public realArgs: any[] = [];
|
||||
constructor(logger: Logger, contractConfig: ContractConfig) {
|
||||
constructor(logger: Logger, contractConfig: ContractConfig = {}) {
|
||||
this.logger = logger;
|
||||
this.address = contractConfig.address;
|
||||
this.args = contractConfig.args;
|
||||
|
|
|
@ -36,7 +36,7 @@ class ContractsManager {
|
|||
|
||||
console.dir("---- contracts manager---- ")
|
||||
|
||||
// this.registerCommands()
|
||||
// this.registerCommands()
|
||||
// this.registerAPIs()
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,8 @@ class ContractsManager {
|
|||
const self = this;
|
||||
|
||||
self.events.setCommandHandler('contracts:add', (contract) => {
|
||||
this.contracts[contract.className] = contract;
|
||||
const contractInstance = new Contract(this.logger, contract);
|
||||
this.contracts[contract.className] = contractInstance;
|
||||
});
|
||||
|
||||
self.events.setCommandHandler('contracts:all', (cb) => {
|
||||
|
@ -279,7 +280,8 @@ class ContractsManager {
|
|||
return eachCb(err);
|
||||
}
|
||||
try {
|
||||
self.contracts[className] = JSON.parse(artifactBuf.toString());
|
||||
const contract = JSON.parse(artifactBuf.toString());
|
||||
self.contracts[className] = new Contract(self.logger, contract);
|
||||
if (self.contracts[className].deployedAddress) {
|
||||
self.contracts[className].address = self.contracts[className].deployedAddress;
|
||||
}
|
||||
|
@ -298,7 +300,12 @@ class ContractsManager {
|
|||
const compiledContract = compiledContracts[className];
|
||||
const contractConfig = contractsConfig.contracts[className];
|
||||
|
||||
const contract = self.contracts[className] || {className: className, args: []};
|
||||
let contract = self.contracts[className];
|
||||
if (!contract) {
|
||||
contract = new Contract(self.logger, contractConfig);
|
||||
contract.className = className;
|
||||
contract.args = [];
|
||||
}
|
||||
|
||||
contract.code = compiledContract.code;
|
||||
contract.runtimeBytecode = compiledContract.runtimeBytecode;
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
"qa": "npm-run-all lint build package",
|
||||
"reset": "npx rimraf .nyc_output coverage dist embark-*.tgz package",
|
||||
"start": "npm run watch",
|
||||
"// test": "nyc --reporter=html --reporter=json mocha \"dist/test/**/*.js\" --exit --no-timeouts --require source-map-support/register",
|
||||
"test": "nyc --reporter=html --reporter=json mocha \"dist/test/**/*.js\" --exit --no-timeouts --require source-map-support/register",
|
||||
"// typecheck": "tsc",
|
||||
"watch": "run-p watch:*",
|
||||
"watch:build": "npm run build -- --verbose --watch",
|
||||
|
@ -47,16 +47,19 @@
|
|||
"extends": "../../.eslintrc.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"colors": "1.3.2",
|
||||
"embark-utils": "^4.1.0-beta.5",
|
||||
"fs-extra": "7.0.1",
|
||||
"web3": "1.0.0-beta.37"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "5.7.0",
|
||||
"expect.js": "0.3.1",
|
||||
"mocha": "6.2.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"nyc": "13.1.0",
|
||||
"rimraf": "2.6.3"
|
||||
"rimraf": "2.6.3",
|
||||
"sinon": "7.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.12.0 <12.0.0",
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import {__} from 'embark-i18n';
|
||||
import {toChecksumAddress} from 'embark-utils';
|
||||
import Web3 from "web3";
|
||||
require("colors");
|
||||
|
||||
export default class DeploymentChecks {
|
||||
constructor({trackingFunctions, logger, events, plugins}) {
|
||||
constructor({trackingFunctions, events, logger}) {
|
||||
this.trackingFunctions = trackingFunctions;
|
||||
this.logger = logger;
|
||||
this.events = events;
|
||||
this.plugins = plugins;
|
||||
this.logger = logger;
|
||||
this._web3 = null;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,12 @@ export default class DeploymentChecks {
|
|||
return cb(null, params);
|
||||
}
|
||||
|
||||
// check if contract set to not deploy in the config
|
||||
if (contract.deploy === false) {
|
||||
params.shouldDeploy = false;
|
||||
return cb(null, params);
|
||||
}
|
||||
|
||||
// contract config address field set - do not deploy
|
||||
if (contract.address !== undefined) {
|
||||
try {
|
||||
|
@ -47,7 +53,7 @@ export default class DeploymentChecks {
|
|||
|
||||
async checkIfAlreadyDeployed(params, cb) {
|
||||
const {contract} = params;
|
||||
const trackedContract = this.trackingFunctions.getContract(contract);
|
||||
const trackedContract = await this.trackingFunctions.getContract(contract);
|
||||
|
||||
// previous event action check
|
||||
if (!params.shouldDeploy) {
|
||||
|
@ -60,7 +66,7 @@ export default class DeploymentChecks {
|
|||
}
|
||||
|
||||
// tracked contract has track field set - deploy anyway, but tell user
|
||||
if (trackedContract.track === false || this.trackingFunctions.trackContracts === false) {
|
||||
if (contract.track === false || this.trackingFunctions.trackContracts === false) {
|
||||
contract.log(contract.className.bold.cyan + __(" will be redeployed").green);
|
||||
return cb(null, params);
|
||||
}
|
||||
|
@ -78,6 +84,20 @@ export default class DeploymentChecks {
|
|||
contract.deployedAddress = trackedContract.address;
|
||||
contract.log(contract.className.bold.cyan + __(" already deployed at ").green + contract.deployedAddress.bold.cyan);
|
||||
params.shouldDeploy = false;
|
||||
|
||||
// handle special case where user has changed their DApp config from `track: false`,
|
||||
// deployed (which is then saved in chains.json), then changed to `track: true|undefined`
|
||||
// in this case we assume
|
||||
// 1) the contract is already tracked
|
||||
// 2) contract.track is truthy
|
||||
if (contract.track !== false) {
|
||||
return this.trackingFunctions.trackAndSaveContract(params, () => {
|
||||
// no need to wait for this function to finish as it has no impact on operation
|
||||
// past this point
|
||||
this.logger.trace(__("Contract tracking setting has been updatred in chains.json"));
|
||||
cb(null, params);
|
||||
});
|
||||
}
|
||||
}
|
||||
cb(null, params);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@ class DeployTracker {
|
|||
const trackingFunctions = new TrackingFunctions({config, fs, logger, events, env, trackContracts});
|
||||
const deploymentChecks = new DeploymentChecks({trackingFunctions, logger, events, plugins});
|
||||
|
||||
this.embark.events.on("blockchain:started", trackingFunctions.loadChainTrackerFile.bind(trackingFunctions));
|
||||
this.embark.registerActionForEvent('deployment:deployContracts:beforeAll', trackingFunctions.setCurrentChain.bind(trackingFunctions));
|
||||
this.embark.events.on("blockchain:started", trackingFunctions.ensureChainTrackerFile.bind(trackingFunctions));
|
||||
this.embark.registerActionForEvent("deployment:contract:deployed", trackingFunctions.trackAndSaveContract.bind(trackingFunctions));
|
||||
this.embark.registerActionForEvent("deployment:contract:shouldDeploy", deploymentChecks.checkContractConfig.bind(deploymentChecks));
|
||||
this.embark.registerActionForEvent("deployment:contract:shouldDeploy", deploymentChecks.checkIfAlreadyDeployed.bind(deploymentChecks));
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
/*globals describe, it*/
|
||||
import DeploymentChecks from "../deploymentChecks";
|
||||
import TrackingFunctions from "../trackingFunctions";
|
||||
const fs = require('fs-extra');
|
||||
const sinon = require('sinon');
|
||||
const expect = require('expect.js');
|
||||
import {beforeEach, afterEach} from "mocha";
|
||||
import Logger from "embark-logger";
|
||||
require("colors");
|
||||
|
||||
describe('embark.deploymentChecks', function () {
|
||||
let logger = new Logger({logLevel: 'error'});
|
||||
let events = {once: () => {}, setCommandHandler: () => {}, emit: () => {}, on: () => {}, request: () => {}, request2: () => {}};
|
||||
|
||||
let params;
|
||||
let trackingFunctions;
|
||||
let deploymentChecks;
|
||||
let contractInChainsFake;
|
||||
let chainsFake;
|
||||
let trackedContract;
|
||||
let readJSON;
|
||||
let writeJSON;
|
||||
let _web3;
|
||||
|
||||
beforeEach(() => {
|
||||
params = {
|
||||
shouldDeploy: true,
|
||||
contract: {
|
||||
className: "TestContract",
|
||||
log: () => {}
|
||||
}
|
||||
};
|
||||
trackedContract = {
|
||||
"name": "TestContract",
|
||||
"address": "0xbe474fb88709f99Ee83901eE09927005388Ab2F1"
|
||||
};
|
||||
contractInChainsFake = {
|
||||
"contracts": {
|
||||
"0x35e400a0b1817a3cfaf09eed53a76eaa169771f28c3630e23e6db2851d99a849": trackedContract
|
||||
}
|
||||
};
|
||||
chainsFake = {
|
||||
"0x7aec9250dcc5f6bedc3d0d582e0be8b8d159a4d483c47309e122ba5702ec6a16": contractInChainsFake,
|
||||
"some other hash": {
|
||||
"contracts": {
|
||||
"contract hash": {
|
||||
"name": "doesn't exist",
|
||||
"address": "0x123"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
readJSON = sinon.stub(fs, 'readJSON').returns(chainsFake);
|
||||
writeJSON = sinon.stub(fs, 'writeJSON');
|
||||
trackingFunctions = new TrackingFunctions({
|
||||
config: {
|
||||
contractsConfig: {
|
||||
tracking: undefined
|
||||
}
|
||||
},
|
||||
env: "development",
|
||||
fs,
|
||||
events,
|
||||
logger,
|
||||
trackContracts: true
|
||||
});
|
||||
_web3 = {
|
||||
eth: {
|
||||
getBlock: () => {
|
||||
return {
|
||||
hash: "0x7aec9250dcc5f6bedc3d0d582e0be8b8d159a4d483c47309e122ba5702ec6a16"
|
||||
};
|
||||
},
|
||||
getCode: () => "BYTECODE"
|
||||
}
|
||||
};
|
||||
trackingFunctions._web3 = _web3;
|
||||
deploymentChecks = new DeploymentChecks({trackingFunctions, events, logger});
|
||||
deploymentChecks._web3 = _web3;
|
||||
});
|
||||
afterEach(() => {
|
||||
readJSON.restore();
|
||||
writeJSON.restore();
|
||||
});
|
||||
|
||||
process.env.DAPP_PATH = process.cwd();
|
||||
|
||||
|
||||
describe('#checkContractConfig', function () {
|
||||
it("should deploy", async function () {
|
||||
return deploymentChecks.checkContractConfig(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not deploy if previous action has already determined it shouldn't deploy", function () {
|
||||
params.shouldDeploy = false;
|
||||
return deploymentChecks.checkContractConfig(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(false);
|
||||
});
|
||||
});
|
||||
it("should not deploy if contract config set to not deploy", async function () {
|
||||
params.contract.deploy = false;
|
||||
return deploymentChecks.checkContractConfig(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(false);
|
||||
});
|
||||
});
|
||||
it("should error with invalid address", function () {
|
||||
params.contract.address = "0x123";
|
||||
return deploymentChecks.checkContractConfig(params, (err, _params) => {
|
||||
expect(err).to.not.be(null);
|
||||
});
|
||||
});
|
||||
it("should set the deployed address accordingly", function () {
|
||||
params.contract.address = "0x901d8340A14af4a46E22FE4CCF2A012d379F1a97";
|
||||
return deploymentChecks.checkContractConfig(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.contract.deployedAddress).to.be("0x901d8340A14af4a46E22FE4CCF2A012d379F1a97");
|
||||
expect(params.shouldDeploy).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let getContract;
|
||||
|
||||
describe('#checkIfAlreadyDeployed', () => {
|
||||
beforeEach(() => {
|
||||
getContract = sinon.stub(trackingFunctions, "getContract").returns(trackedContract);
|
||||
});
|
||||
afterEach(() => {
|
||||
getContract.restore();
|
||||
});
|
||||
it("should not deploy if previous action has already determined it shouldn't deploy", async function () {
|
||||
params.shouldDeploy = false;
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(false);
|
||||
});
|
||||
});
|
||||
it("should deploy if contract not already tracked", async function () {
|
||||
getContract.restore();
|
||||
getContract = sinon.stub(trackingFunctions, "getContract").returns(null);
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(true);
|
||||
});
|
||||
});
|
||||
it("should deploy if contract tracked but set to track false", function () {
|
||||
params.contract.track = false;
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(true);
|
||||
});
|
||||
});
|
||||
it("should deploy if contract tracking disabled (ie tests)", async function () {
|
||||
trackedContract.track = false;
|
||||
trackingFunctions.trackContracts = false;
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(true);
|
||||
});
|
||||
});
|
||||
it("should not deploy if contract is tracked, but bytecode exists on chain", async function () {
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(false);
|
||||
expect(params.contract.deployedAddress).to.be("0xbe474fb88709f99Ee83901eE09927005388Ab2F1");
|
||||
});
|
||||
});
|
||||
it("should deploy if contract is tracked, but bytecode doesn't exist on chain", async function () {
|
||||
trackingFunctions._web3.eth.getCode = () => "0x0";
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(true);
|
||||
});
|
||||
});
|
||||
it("should update tracked contract in chains.json when contract.track !== false", async function () {
|
||||
const trackAndSaveContract = sinon.stub(trackingFunctions, "trackAndSaveContract");
|
||||
const {contract} = params;
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
|
||||
expect(err).to.be(null);
|
||||
expect(params.shouldDeploy).to.be(false);
|
||||
expect(trackAndSaveContract.calledWith(contract)).to.be(true);
|
||||
});
|
||||
});
|
||||
it("should error (and not deploy) if tracked contract address is invalid", async function () {
|
||||
trackingFunctions._web3.eth.getCode = () => {throw new Error()};
|
||||
return deploymentChecks.checkIfAlreadyDeployed(params, (err, _params) => {
|
||||
expect(err).to.not.be(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,271 @@
|
|||
/*globals describe, it*/
|
||||
import TrackingFunctions from "../trackingFunctions";
|
||||
const fs = require('fs-extra');
|
||||
const sinon = require('sinon');
|
||||
const expect = require('expect.js');
|
||||
import {beforeEach, afterEach} from "mocha";
|
||||
import Logger from "embark-logger";
|
||||
import {dappPath} from 'embark-utils';
|
||||
require("colors");
|
||||
|
||||
describe('embark.trackingFunctions', function () {
|
||||
let logger = new Logger({logLevel: 'error'});
|
||||
let events = {once: () => {}, setCommandHandler: () => {}, emit: () => {}, on: () => {}, request: () => {}};
|
||||
let readJSON;
|
||||
let trackingFunctions;
|
||||
let contractInChainsFake;
|
||||
let chainsFake;
|
||||
let contract;
|
||||
let trackedContract;
|
||||
let params;
|
||||
let chainsPath = dappPath(".embark/chains.json");
|
||||
let exists;
|
||||
let outputJSON;
|
||||
let writeJSON;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = {
|
||||
className: "SimpleStorage",
|
||||
deployedAddress: "0xbe474fb88709f99Ee83901eE09927005388Ab2F1",
|
||||
hash: "0x35e400a0b1817a3cfaf09eed53a76eaa169771f28c3630e23e6db2851d99a849"
|
||||
};
|
||||
params = {
|
||||
contract
|
||||
};
|
||||
trackedContract = {
|
||||
"name": "SimpleStorage",
|
||||
"address": "0xbe474fb88709f99Ee83901eE09927005388Ab2F1"
|
||||
};
|
||||
contractInChainsFake = {
|
||||
"contracts": {
|
||||
"0x35e400a0b1817a3cfaf09eed53a76eaa169771f28c3630e23e6db2851d99a849": trackedContract
|
||||
}
|
||||
};
|
||||
chainsFake = {
|
||||
"0x7aec9250dcc5f6bedc3d0d582e0be8b8d159a4d483c47309e122ba5702ec6a16": contractInChainsFake,
|
||||
"some other hash": {
|
||||
"contracts": {
|
||||
"contract hash": {
|
||||
"name": "doesn't exist",
|
||||
"address": "0x123"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
readJSON = sinon.stub(fs, 'readJSON').returns(chainsFake);
|
||||
exists = sinon.stub(fs, 'exists').returns(true);
|
||||
outputJSON = sinon.stub(fs, 'outputJSON');
|
||||
writeJSON = sinon.stub(fs, 'writeJSON');
|
||||
trackingFunctions = new TrackingFunctions({
|
||||
config: {
|
||||
contractsConfig: {
|
||||
tracking: undefined
|
||||
}
|
||||
},
|
||||
env: "development",
|
||||
fs,
|
||||
events,
|
||||
logger,
|
||||
trackContracts: true
|
||||
});
|
||||
trackingFunctions._web3 = {
|
||||
eth: {
|
||||
getBlock: () => {
|
||||
return {
|
||||
hash: "0x7aec9250dcc5f6bedc3d0d582e0be8b8d159a4d483c47309e122ba5702ec6a16"
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
afterEach(() => {
|
||||
readJSON.restore();
|
||||
exists.restore();
|
||||
outputJSON.restore();
|
||||
writeJSON.restore();
|
||||
});
|
||||
|
||||
describe('#chains property', function () {
|
||||
|
||||
it("should read chains.json", async function () {
|
||||
const chains = await trackingFunctions.chains;
|
||||
expect(readJSON.calledOnceWith(chainsPath)).to.be(true);
|
||||
expect(chains).to.equal(chainsFake);
|
||||
});
|
||||
|
||||
it("enabled is false, should not read chains.json and return null", async function () {
|
||||
trackingFunctions.enabled = false;
|
||||
const chains = await trackingFunctions.chains;
|
||||
expect(readJSON.calledOnceWith(chainsPath)).to.be(false);
|
||||
expect(chains).to.be(null);
|
||||
});
|
||||
it("trackContracts is false, should not read chains.json and return null", async function () {
|
||||
trackingFunctions = new TrackingFunctions({
|
||||
config: {
|
||||
contractsConfig: {
|
||||
tracking: undefined
|
||||
}
|
||||
},
|
||||
env: "development",
|
||||
fs,
|
||||
events,
|
||||
logger,
|
||||
trackContracts: false
|
||||
});
|
||||
const chains = await trackingFunctions.chains;
|
||||
expect(readJSON.calledOnceWith(chainsPath)).to.be(false);
|
||||
expect(chains).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#currentChain property', function () {
|
||||
it("should read chains.json for current chain", async function () {
|
||||
const currentChain = await trackingFunctions.currentChain;
|
||||
contractInChainsFake.name = "development";
|
||||
expect(currentChain).to.be.equal(contractInChainsFake);
|
||||
});
|
||||
|
||||
it("should return emtpy contracts when not enabled", async function () {
|
||||
trackingFunctions.enabled = false;
|
||||
const currentChain = await trackingFunctions.currentChain;
|
||||
expect(currentChain).to.be.eql({contracts: []});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getContract', function () {
|
||||
it("should return false when disabled", async function () {
|
||||
trackingFunctions.enabled = false;
|
||||
const returnedContract = await trackingFunctions.getContract(contract);
|
||||
expect(returnedContract).to.be.equal(false);
|
||||
});
|
||||
|
||||
it("should return tracked contract", async function () {
|
||||
const trackedContractResult = await trackingFunctions.getContract(contract);
|
||||
expect(trackedContractResult).to.be.eql(trackedContract);
|
||||
});
|
||||
|
||||
it("should return false when address is not tracked", async function () {
|
||||
readJSON.restore();
|
||||
readJSON = sinon.stub(fs, 'readJSON').returns({
|
||||
"0x7aec9250dcc5f6bedc3d0d582e0be8b8d159a4d483c47309e122ba5702ec6a16": {
|
||||
"contracts": {
|
||||
"0x35e400a0b1817a3cfaf09eed53a76eaa169771f28c3630e23e6db2851d99a849": {
|
||||
"name": "SimpleStorage"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const returnedContract = await trackingFunctions.getContract(contract);
|
||||
expect(returnedContract).to.be.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trackAndSaveContract', function () {
|
||||
it("should not call trackContract when disabled", async function (done) {
|
||||
trackingFunctions.enabled = false;
|
||||
const trackContract = sinon.stub(trackingFunctions, "trackContract");
|
||||
await trackingFunctions.trackAndSaveContract(params, (err, _params) => {
|
||||
expect(err).to.be(undefined);
|
||||
expect(trackContract.called).to.be(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should track contract when contract.track === false, but set track property to false", async function () {
|
||||
let {contract} = params;
|
||||
contract.track = false;
|
||||
const trackContract = sinon.spy(trackingFunctions, "trackContract");
|
||||
return trackingFunctions.trackAndSaveContract(params, (err, _params) => {
|
||||
expect(err).to.be(undefined);
|
||||
expect(trackContract.calledWith(contract)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should track contract and call save", async function () {
|
||||
const {contract} = params;
|
||||
const trackContract = sinon.spy(trackingFunctions, "trackContract");
|
||||
const save = sinon.spy(trackingFunctions, "save");
|
||||
return trackingFunctions.trackAndSaveContract(params, (err, _params) => {
|
||||
expect(err).to.be(undefined);
|
||||
expect(trackContract.calledWith(contract)).to.be(true);
|
||||
expect(save.called).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#ensureChainTrackerFile', function () {
|
||||
it("should do nothing when disabled", async function () {
|
||||
trackingFunctions.enabled = false;
|
||||
|
||||
await trackingFunctions.ensureChainTrackerFile();
|
||||
expect(exists.called).to.be(false);
|
||||
});
|
||||
|
||||
it("should create tracking file when not exists", async function () {
|
||||
exists.restore();
|
||||
exists = sinon.stub(fs, 'exists').returns(false);
|
||||
await trackingFunctions.ensureChainTrackerFile();
|
||||
expect(outputJSON.calledOnceWith(chainsPath, {})).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trackContract', function () {
|
||||
it("should not track contract in memory when disabled", async function () {
|
||||
trackingFunctions.enabled = false;
|
||||
|
||||
const trackContractResult = await trackingFunctions.trackContract();
|
||||
expect(trackContractResult).to.be(false);
|
||||
});
|
||||
|
||||
it("should track contract in memory", async function () {
|
||||
contract = {
|
||||
className: "test",
|
||||
deployedAddress: "0x123",
|
||||
hash: "123abc"
|
||||
};
|
||||
await trackingFunctions.trackContract(contract);
|
||||
const currentChain = await trackingFunctions.currentChain;
|
||||
expect(currentChain.contracts["123abc"]).to.eql({
|
||||
name: "test",
|
||||
address: "0x123"
|
||||
});
|
||||
expect(currentChain.contracts["123abc"].track).to.be(undefined);
|
||||
});
|
||||
it("should track contract in memory with track === false", async function () {
|
||||
contract = {
|
||||
className: "test",
|
||||
deployedAddress: "0x123",
|
||||
hash: "123abc",
|
||||
track: false
|
||||
};
|
||||
await trackingFunctions.trackContract(contract);
|
||||
const currentChain = await trackingFunctions.currentChain;
|
||||
expect(currentChain.contracts["123abc"]).to.eql({
|
||||
name: "test",
|
||||
address: "0x123",
|
||||
track: false
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#save', function () {
|
||||
it("should not save when tracking is disabled", async function () {
|
||||
trackingFunctions.enabled = false;
|
||||
expect(writeJSON.called).to.be(false);
|
||||
});
|
||||
|
||||
it("should save to chains.json", async function () {
|
||||
await trackingFunctions.save();
|
||||
expect(writeJSON.calledOnceWith(chainsPath, chainsFake, {spaces: 2})).to.equal(true);
|
||||
});
|
||||
|
||||
it("should save to chains.json when track is false", async function () {
|
||||
params.contract.track = false;
|
||||
await trackingFunctions.trackContract(params.contract);
|
||||
await trackingFunctions.save();
|
||||
trackedContract.track = false;
|
||||
const expectedChains = chainsFake;
|
||||
expect(writeJSON.calledOnceWith(chainsPath, expectedChains, {spaces: 2})).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,15 +5,15 @@ import Web3 from 'web3';
|
|||
export default class TrackingFunctions {
|
||||
constructor({config, env, fs, events, logger, trackContracts}) {
|
||||
this.config = config;
|
||||
this.chainConfig = {};
|
||||
this.chainFile = config.contractsConfig.tracking;
|
||||
this.currentChain = null;
|
||||
this.enabled = (config.contractsConfig.tracking !== false) && (trackContracts !== false);
|
||||
this.chainsFilePath = dappPath(config.contractsConfig.tracking || ".embark/chains.json");
|
||||
this.env = env;
|
||||
this.fs = fs;
|
||||
this.events = events;
|
||||
this.logger = logger;
|
||||
this._web3 = null;
|
||||
this.trackContracts = (trackContracts !== false);
|
||||
this._chains = null;
|
||||
this._currentChain = null;
|
||||
}
|
||||
|
||||
get web3() {
|
||||
|
@ -26,87 +26,91 @@ export default class TrackingFunctions {
|
|||
})();
|
||||
}
|
||||
|
||||
getContract(contract) {
|
||||
if (!this.currentChain) return false;
|
||||
let contractInFile = this.currentChain.contracts[contract.hash];
|
||||
get chains() {
|
||||
return (async () => {
|
||||
if (this._chains) {
|
||||
return this._chains;
|
||||
}
|
||||
if (!this.enabled) {
|
||||
this._chains = null;
|
||||
return this._chains;
|
||||
}
|
||||
this._chains = await this.fs.readJSON(this.chainsFilePath);
|
||||
return this._chains;
|
||||
})();
|
||||
}
|
||||
|
||||
get currentChain() {
|
||||
return (async () => {
|
||||
if (this._currentChain) {
|
||||
return this._currentChain;
|
||||
}
|
||||
if (!this.enabled) {
|
||||
this._currentChain = {contracts: []};
|
||||
return this._currentChain;
|
||||
}
|
||||
|
||||
let block;
|
||||
const web3 = await this.web3;
|
||||
try {
|
||||
block = await web3.eth.getBlock(0, true);
|
||||
} catch (err) {
|
||||
// Retry with block 1 (Block 0 fails with Ganache-cli using the --fork option)
|
||||
block = await web3.eth.getBlock(1, true);
|
||||
}
|
||||
const {hash} = block;
|
||||
this._currentChain = (await this.chains)[hash];
|
||||
if (this._currentChain === undefined) {
|
||||
const empty = {contracts: {}};
|
||||
this._chains[hash] = empty;
|
||||
this._currentChain = empty;
|
||||
}
|
||||
|
||||
this._currentChain.name = this.env;
|
||||
|
||||
return this._currentChain;
|
||||
})();
|
||||
}
|
||||
|
||||
async getContract(contract) {
|
||||
const currentChain = await this.currentChain;
|
||||
if (!currentChain || !this.enabled) return false;
|
||||
let contractInFile = currentChain.contracts[contract.hash];
|
||||
if (contractInFile && contractInFile.address === undefined) {
|
||||
return false;
|
||||
}
|
||||
return contractInFile;
|
||||
}
|
||||
|
||||
trackAndSaveContract(params, cb) {
|
||||
async trackAndSaveContract(params, cb) {
|
||||
const {contract} = params;
|
||||
if (!this.chainFile || !this.trackContracts || contract.track === false) return cb();
|
||||
this.trackContract(contract);
|
||||
if (!this.enabled) return cb();
|
||||
await this.trackContract(contract);
|
||||
this.save();
|
||||
cb();
|
||||
}
|
||||
|
||||
loadChainTrackerFile() {
|
||||
if (this.chainFile === false) return;
|
||||
if (this.chainFile === undefined) this.chainFile = ".embark/chains.json";
|
||||
this.chainFile = dappPath(this.chainFile);
|
||||
if (!this.fs.existsSync(this.chainFile)) {
|
||||
this.logger.info(this.chainFile + ' ' + __('file not found, creating it...'));
|
||||
this.fs.outputJSONSync(this.chainFile, {});
|
||||
this.chainConfig = {};
|
||||
async ensureChainTrackerFile() {
|
||||
if (!this.enabled) return;
|
||||
const exists = await this.fs.exists(this.chainsFilePath);
|
||||
if (!exists) {
|
||||
this.logger.info(this.chainsFilePath + ' ' + __('file not found, creating it...'));
|
||||
return this.fs.outputJSON(this.chainsFilePath, {});
|
||||
}
|
||||
}
|
||||
|
||||
async trackContract(contract) {
|
||||
const currentChain = await this.currentChain;
|
||||
if (!this.enabled || !currentChain) return false;
|
||||
const toTrack = {name: contract.className, address: contract.deployedAddress};
|
||||
if (contract.track === false) toTrack.track = false;
|
||||
currentChain.contracts[contract.hash] = toTrack;
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chainConfig = this.fs.readJSONSync(this.chainFile);
|
||||
}
|
||||
|
||||
setCurrentChain(_params, callback) {
|
||||
if (!this.chainFile) return callback();
|
||||
if (this.chainFile === false) return callback();
|
||||
if (this.chainConfig === false) {
|
||||
this.currentChain = {contracts: []};
|
||||
return callback();
|
||||
}
|
||||
|
||||
this.getBlock(0, (err) => {
|
||||
if (err) {
|
||||
// Retry with block 1 (Block 0 fails with Ganache-cli using the --fork option)
|
||||
return this.getBlock(1, callback);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
async getBlock(blockNum, cb) {
|
||||
try {
|
||||
const web3 = await this.web3;
|
||||
let block = await web3.eth.getBlock(blockNum, true);
|
||||
let chainId = block.hash;
|
||||
|
||||
if (this.chainConfig[chainId] === undefined) {
|
||||
this.chainConfig[chainId] = {contracts: {}};
|
||||
}
|
||||
|
||||
this.currentChain = this.chainConfig[chainId];
|
||||
|
||||
this.currentChain.name = this.env;
|
||||
cb();
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
}
|
||||
|
||||
loadConfig(config) {
|
||||
this.chainConfig = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
trackContract(contract) {
|
||||
if (!this.currentChain) return false;
|
||||
this.currentChain.contracts[contract.hash] = {name: contract.className, address: contract.deployedAddress};
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.chainConfig === false) {
|
||||
return;
|
||||
}
|
||||
this.fs.writeJSONSync(this.chainFile, this.chainConfig, {spaces: 2});
|
||||
return this.fs.writeJSON(this.chainsFilePath, await this.chains, {spaces: 2});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,6 @@ class ContractDeployer {
|
|||
}
|
||||
|
||||
deployContract(contract, callback) {
|
||||
if (contract.deploy === false) {
|
||||
this.events.emit("deployment:contract:undeployed", contract);
|
||||
return callback();
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
(next) => {
|
||||
|
|
74
yarn.lock
74
yarn.lock
|
@ -2174,6 +2174,13 @@
|
|||
universal-user-agent "^2.0.0"
|
||||
url-template "^2.0.8"
|
||||
|
||||
"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.4.0.tgz#7b3ec2d96af481d7a0321252e7b1c94724ec5a78"
|
||||
integrity sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==
|
||||
dependencies:
|
||||
type-detect "4.0.8"
|
||||
|
||||
"@sinonjs/formatio@3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.0.0.tgz#9d282d81030a03a03fa0c5ce31fd8786a4da311a"
|
||||
|
@ -2188,6 +2195,14 @@
|
|||
dependencies:
|
||||
samsam "1.3.0"
|
||||
|
||||
"@sinonjs/formatio@^3.2.1":
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.1.tgz#52310f2f9bcbc67bdac18c94ad4901b95fde267e"
|
||||
integrity sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1"
|
||||
"@sinonjs/samsam" "^3.1.0"
|
||||
|
||||
"@sinonjs/samsam@2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.0.tgz#b8b8f5b819605bd63601a6ede459156880f38ea3"
|
||||
|
@ -2195,6 +2210,20 @@
|
|||
dependencies:
|
||||
array-from "^2.1.1"
|
||||
|
||||
"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.2":
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.2.tgz#63942e3d5eb0b79f6de3bef9abfad15fb4b6401b"
|
||||
integrity sha512-ILO/rR8LfAb60Y1Yfp9vxfYAASK43NFC2mLzpvLUbCQY/Qu8YwReboseu8aheCEkyElZF2L2T9mHcR2bgdvZyA==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.0.2"
|
||||
array-from "^2.1.1"
|
||||
lodash "^4.17.11"
|
||||
|
||||
"@sinonjs/text-encoding@^0.7.1":
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
|
||||
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
|
||||
|
||||
"@storybook/addon-actions@^4.1.6":
|
||||
version "4.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-4.1.11.tgz#8946ea78f050ae2d06a2f2231ec56d1831942e15"
|
||||
|
@ -6760,7 +6789,7 @@ diagnostics@^1.1.1:
|
|||
enabled "1.0.x"
|
||||
kuler "1.0.x"
|
||||
|
||||
diff@3.5.0, diff@^3.1.0, diff@^3.2.0:
|
||||
diff@3.5.0, diff@^3.1.0, diff@^3.2.0, diff@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||
|
@ -7897,6 +7926,11 @@ expect-ct@0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.1.1.tgz#de84476a2dbcb85000d5903737e9bc8a5ba7b897"
|
||||
integrity sha512-ngXzTfoRGG7fYens3/RMb6yYoVLvLMfmsSllP/mZPxNHgFq41TmPSLF/nLY7fwoclI2vElvAmILFWGUYqdjfCg==
|
||||
|
||||
expect.js@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/expect.js/-/expect.js-0.3.1.tgz#b0a59a0d2eff5437544ebf0ceaa6015841d09b5b"
|
||||
integrity sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=
|
||||
|
||||
expect@^23.6.0:
|
||||
version "23.6.0"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98"
|
||||
|
@ -11222,6 +11256,11 @@ just-extend@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-3.0.0.tgz#cee004031eaabf6406da03a7b84e4fe9d78ef288"
|
||||
integrity sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==
|
||||
|
||||
just-extend@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc"
|
||||
integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==
|
||||
|
||||
keccak@^1.0.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/keccak/-/keccak-1.4.0.tgz#572f8a6dbee8e7b3aa421550f9e6408ca2186f80"
|
||||
|
@ -11788,6 +11827,11 @@ lolex@^2.2.0, lolex@^2.3.2:
|
|||
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733"
|
||||
integrity sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==
|
||||
|
||||
lolex@^4.1.0, lolex@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7"
|
||||
integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==
|
||||
|
||||
looper@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749"
|
||||
|
@ -12657,6 +12701,17 @@ nise@^1.2.0:
|
|||
path-to-regexp "^1.7.0"
|
||||
text-encoding "^0.6.4"
|
||||
|
||||
nise@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.1.tgz#de61d99a1d3b46b5233be4531569b9a8e27372b2"
|
||||
integrity sha512-edFWm0fsFG2n318rfEnKlTZTkjlbVOFF9XIA+fj+Ed+Qz1laYW2lobwavWoMzGrYDHH1EpiNJgDfvGnkZztR/g==
|
||||
dependencies:
|
||||
"@sinonjs/formatio" "^3.2.1"
|
||||
"@sinonjs/text-encoding" "^0.7.1"
|
||||
just-extend "^4.0.2"
|
||||
lolex "^4.1.0"
|
||||
path-to-regexp "^1.7.0"
|
||||
|
||||
no-case@^2.2.0, no-case@^2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||
|
@ -16804,6 +16859,19 @@ sinon@4.5.0:
|
|||
supports-color "^5.1.0"
|
||||
type-detect "^4.0.5"
|
||||
|
||||
sinon@7.4.1:
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.4.1.tgz#bcd0c63953893e87fa0cc502f52489c32a83d4d9"
|
||||
integrity sha512-7s9buHGHN/jqoy/v4bJgmt0m1XEkCEd/tqdHXumpBp0JSujaT4Ng84JU5wDdK4E85ZMq78NuDe0I3NAqXY8TFg==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.4.0"
|
||||
"@sinonjs/formatio" "^3.2.1"
|
||||
"@sinonjs/samsam" "^3.3.2"
|
||||
diff "^3.5.0"
|
||||
lolex "^4.2.0"
|
||||
nise "^1.5.1"
|
||||
supports-color "^5.5.0"
|
||||
|
||||
sisteransi@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce"
|
||||
|
@ -17494,7 +17562,7 @@ supports-color@^3.1.0, supports-color@^3.1.2:
|
|||
dependencies:
|
||||
has-flag "^1.0.0"
|
||||
|
||||
supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0:
|
||||
supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
|
@ -18089,7 +18157,7 @@ type-check@~0.3.2:
|
|||
dependencies:
|
||||
prelude-ls "~1.1.2"
|
||||
|
||||
type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
|
Loading…
Reference in New Issue