diff --git a/.gitignore b/.gitignore index 489f050..f1ffff1 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ package-lock.json contractApis state.vvisp.json service.vvisp.json +vvisp-config.js example.json example diff --git a/packages/vvisp-utils/contracts/ErrorImportContract.sol b/packages/vvisp-utils/contracts/ErrorImportContract.sol new file mode 100644 index 0000000..222c557 --- /dev/null +++ b/packages/vvisp-utils/contracts/ErrorImportContract.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.23; + +import 'errorImport/where/is/contract/file.sol'; + +contract ErrorContract { + function ErrorContract(){ + } +} diff --git a/packages/vvisp-utils/package.json b/packages/vvisp-utils/package.json index fb1ee40..99f5ba1 100644 --- a/packages/vvisp-utils/package.json +++ b/packages/vvisp-utils/package.json @@ -37,6 +37,7 @@ "dependencies": { "bip39": "^2.5.0", "chalk": "^2.4.1", + "truffle-provider": "^0.1.4", "ethereumjs-tx": "^1.3.7", "ethereumjs-wallet": "^0.6.2", "find-cache-dir": "^2.0.0", @@ -54,7 +55,12 @@ "web3": "1.0.0-beta.37" }, "devDependencies": { - "dotenv": "^5.0.1", "openzeppelin-solidity": "2.0.0" + }, + "nyc": { + "exclude": [ + "test", + "*-config.js" + ] } } diff --git a/packages/vvisp-utils/src/Config.js b/packages/vvisp-utils/src/Config.js new file mode 100644 index 0000000..efd282d --- /dev/null +++ b/packages/vvisp-utils/src/Config.js @@ -0,0 +1,249 @@ +// reference: https://github.com/trufflesuite/truffle/blob/40a9c615eee78f14854ced65766ed3d370da07ae/packages/truffle-config/index.js +const _ = require('lodash'); +const Provider = require('truffle-provider'); +const path = require('path'); + +const DEFAULT_CONFIG_FILE = 'vvisp-config.js'; +const DEFAULT_NETWORK = 'development'; + +const getConfigRoot = require('./getConfigRoot'); +const getPrivateKey = require('./getPrivateKey'); +const filterPrivateKey = require('./filterPrivateKey'); +const forIn = require('./forIn'); + +function Config() { + const self = this; + + const defaultTxOptions = { + gasLimit: 6721975, + gasPrice: 20000000000 // 20 gwei, + }; + + this._deepCopy = ['networks', 'compilers']; + + this._values = { + network: null, // default config is development + networks: {}, + verboseRpc: false, + gasLimit: null, + gasPrice: null, + from: null, + compilers: { + solc: { + settings: { + optimizer: { + enabled: false, + runs: 200 + }, + evmVersion: 'byzantium' + } + }, + vyper: {} + } + }; + + const props = { + network: {}, + networks: {}, + verboseRpc: {}, + compilers: {}, + + from: { + get: function() { + const value = self._values['from']; + if (typeof value === 'string') { + return value; + } else if (typeof value === 'object' && value !== null) { + return getPrivateKey(value.mnemonic, value.index); + } else { + throw new Error(`from is not set properly, got ${value}`); + } + }, + set: function(value) { + if (typeof value === 'string') { + self._values['from'] = filterPrivateKey(value); + return; + } else if (typeof value === 'object' && value !== null) { + if (typeof value.mnemonic === 'string') { + self._values['from'] = value; + return; + } + } + throw new TypeError(`${JSON.stringify(value)} is invalid key format`); + } + }, + network_config: { + get: function() { + const network = self.network; + + if (network === null || network === undefined) { + throw new Error( + 'Network not set. Cannot determine network to use. Set config.network or add option --network ' + ); + } + + return _.extend({}, defaultTxOptions, self.networks[network]); + }, + set: function() { + throw new Error( + 'Do not set config.network_config. Instead, set config.networks with the desired values.' + ); + } + }, + network_id: { + get: function() { + try { + return self.network_config.network_id; + } catch (e) { + return null; + } + }, + set: function() { + throw new Error( + 'Do not set config.network_id. Instead, set config.networks and then config.networks[].network_id' + ); + } + }, + gasLimit: { + get: function() { + try { + return self.network_config.gas || self.network_config.gasLimit; + } catch (e) { + return defaultTxOptions.gasLimit; + } + }, + set: function() { + throw new Error( + "Don't set config.gas directly. Instead, set config.networks and then config.networks[].gas" + ); + } + }, + gasPrice: { + get: function() { + try { + return self.network_config.gasPrice; + } catch (e) { + return defaultTxOptions.gasPrice; + } + }, + set: function() { + throw new Error( + "Don't set config.gasPrice directly. Instead, set config.networks and then config.networks[].gasPrice" + ); + } + }, + provider: { + get: function() { + if (!self.network) { + return null; + } + + const options = self.network_config; + options.verboseRpc = self.verboseRpc; + + return Provider.create(options); + }, + set: function() { + throw new Error( + "Don't set config.provider directly. Instead, set config.networks and then set config.networks[].provider" + ); + } + } + }; + + forIn(props, (value, key) => { + self.addProperty(key, value); + }); +} + +Config.prototype.addProperty = function(propertyName, descriptor = {}) { + Object.defineProperty(this, propertyName, { + get: + descriptor.get || + function() { + // value is specified + if (propertyName in this._values) { + return this._values[propertyName]; + } + + // default getter is specified + if (descriptor.default) { + return descriptor.default(); + } + + // descriptor is a function + return descriptor(); + }, + set: + descriptor.set || + function(value) { + this._values[propertyName] = descriptor.transform + ? descriptor.transform(value) + : value; + }, + configurable: true, + enumerable: true + }); +}; + +Config.prototype.merge = function(obj = {}) { + const self = this; + const clone = _.cloneDeep(obj); + + forIn(clone, (value, key) => { + if (self.hasOwnProperty(key)) { + if (typeof clone[key] === 'object' && self._deepCopy.includes(key)) { + self[key] = _.merge(self[key], clone[key]); + } else { + self[key] = clone[key]; + } + } + }); + + return this; +}; + +Config.search = (options = {}) => { + const configFileName = options.configFile || DEFAULT_CONFIG_FILE; + // if there is no file, getConfigRoot will throw error + return path.join(getConfigRoot(configFileName), configFileName); +}; + +Config.load = options => { + const file = Config.search(options); + + const config = new Config(); + + const fileConfig = require(file); + config.merge(fileConfig); + config.merge(options); + + if (!config.network && config.networks.hasOwnProperty(DEFAULT_NETWORK)) { + config.network = DEFAULT_NETWORK; + } + + return config; +}; + +let storedConfig; + +Config.get = options => { + if (!storedConfig) { + storedConfig = Config.load(options); + } + + return storedConfig; +}; + +Config.setStore = config => { + if (!(config instanceof Config)) { + throw new TypeError('Should set an instance of Config'); + } + storedConfig = config; +}; + +Config.delete = () => { + storedConfig = undefined; +}; + +module.exports = Config; diff --git a/packages/vvisp-utils/src/compile.js b/packages/vvisp-utils/src/compile.js index 0b78eb7..0f799de 100644 --- a/packages/vvisp-utils/src/compile.js +++ b/packages/vvisp-utils/src/compile.js @@ -1,9 +1,11 @@ module.exports = async function(filePath, options) { const DEFAULT_COMPILER_VERSION = '0.4.24'; + const compilerOption = extractCompilerOption(options); + const fs = require('fs'); const path = require('path'); - const solc = await getSolc(); + const solc = await getSolc(compilerOption); const chalk = require('chalk'); const printOrSilent = require('./printOrSilent'); const findNodeModules = require('find-node-modules'); @@ -33,6 +35,8 @@ module.exports = async function(filePath, options) { language: 'Solidity', sources: input, settings: { + evmVersion: compilerOption.settings.evmVersion, + optimizer: compilerOption.settings.optimizer, outputSelection: { '*': { '*': ['*'] @@ -102,13 +106,30 @@ module.exports = async function(filePath, options) { return result; } - async function getSolc() { - const CompilerSupplier = require('./utils/compilerSupplier'); + async function getSolc(compilerOption) { + const CompilerSupplier = require('./compilerSupplier'); const supplier = new CompilerSupplier({ - version: process.env.SOLC_VERSION - ? process.env.SOLC_VERSION - : DEFAULT_COMPILER_VERSION + version: compilerOption.version }); return supplier.load(); } + + function extractCompilerOption(options) { + let compilers; + try { + compilers = options.config.compilers.solc || {}; + } catch (e) { + compilers = {}; + } + + if (!compilers.version) { + compilers.version = DEFAULT_COMPILER_VERSION; + } + + if (!compilers.settings) { + compilers.settings = {}; + } + + return compilers; + } }; diff --git a/packages/vvisp-utils/src/utils/compilerSupplier.js b/packages/vvisp-utils/src/compilerSupplier.js similarity index 100% rename from packages/vvisp-utils/src/utils/compilerSupplier.js rename to packages/vvisp-utils/src/compilerSupplier.js diff --git a/packages/vvisp-utils/src/deploy.js b/packages/vvisp-utils/src/deploy.js index 04577e9..398770f 100644 --- a/packages/vvisp-utils/src/deploy.js +++ b/packages/vvisp-utils/src/deploy.js @@ -4,7 +4,7 @@ module.exports = async function( arguments, options ) { - const filterPrivateKey = require('./utils/filterPrivateKey'); + const filterPrivateKey = require('./filterPrivateKey'); privateKey = filterPrivateKey(privateKey); if (arguments && arguments.length === undefined) { @@ -40,7 +40,7 @@ module.exports = async function( } const sendTx = require('./sendTx'); - const web3 = require('./getWeb3')(); + const web3 = require('./web3Store').get(); const inputs = { data: '0x' + bytecode, arguments }; const deployData = new web3.eth.Contract(abi).deploy(inputs).encodeABI(); diff --git a/packages/vvisp-utils/src/utils/filterPrivateKey.js b/packages/vvisp-utils/src/filterPrivateKey.js similarity index 100% rename from packages/vvisp-utils/src/utils/filterPrivateKey.js rename to packages/vvisp-utils/src/filterPrivateKey.js diff --git a/packages/vvisp-utils/src/getConfigRoot.js b/packages/vvisp-utils/src/getConfigRoot.js index a971473..c1ab502 100644 --- a/packages/vvisp-utils/src/getConfigRoot.js +++ b/packages/vvisp-utils/src/getConfigRoot.js @@ -1,8 +1,8 @@ -module.exports = async function(configFileName) { +module.exports = function(configFileName) { const findUp = require('find-up'); const path = require('path'); - let configPath = await findUp([configFileName]); + let configPath = findUp.sync([configFileName]); if (!configPath) { throw new Error(`${configFileName} is not found`); } diff --git a/packages/vvisp-utils/src/getPrivateKey.js b/packages/vvisp-utils/src/getPrivateKey.js index e94f7c3..da9f8e7 100644 --- a/packages/vvisp-utils/src/getPrivateKey.js +++ b/packages/vvisp-utils/src/getPrivateKey.js @@ -26,18 +26,10 @@ module.exports = function(mnemonic, index) { .toString('hex'); function checkInputs(mnemonic, index) { - switch (mnemonic) { - case undefined || null: - throw new TypeError(`Input mnemonic is ${mnemonic}`); - default: - if (typeof mnemonic !== 'string') { - throw new TypeError('Input 12 words in string type'); - } else { - const wordsNumber = mnemonic.split(' ').length; - if (wordsNumber !== 12) { - throw new TypeError('Input 12 words, not ' + wordsNumber); - } - } + if (typeof mnemonic !== 'string') { + throw new TypeError( + `Input mnemonic is ${mnemonic}, it should be string type` + ); } switch (index) { case undefined || null: diff --git a/packages/vvisp-utils/src/getTxCount.js b/packages/vvisp-utils/src/getTxCount.js index 9bbc35d..2b5477c 100644 --- a/packages/vvisp-utils/src/getTxCount.js +++ b/packages/vvisp-utils/src/getTxCount.js @@ -1,5 +1,5 @@ module.exports = async function(target) { - const web3 = require('./getWeb3')(); + const web3 = require('./web3Store').get(); if (web3.utils.isAddress(target)) { return web3.eth.getTransactionCount(target); } else { diff --git a/packages/vvisp-utils/src/getWeb3.js b/packages/vvisp-utils/src/getWeb3.js deleted file mode 100644 index 74f0253..0000000 --- a/packages/vvisp-utils/src/getWeb3.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = function() { - const Web3 = require('web3'); - - let url; - - if (process.env.NETWORK === 'local') { - url = `http://localhost:${process.env.PORT}`; - } else if (process.env.NETWORK === 'custom') { - url = process.env.URL; - } else { - url = `https://${process.env.NETWORK}.infura.io/${ - process.env.INFURA_API_KEY - }`; - } - const web3 = new Web3(new Web3.providers.HttpProvider(url)); - - return web3; -}; diff --git a/packages/vvisp-utils/src/index.js b/packages/vvisp-utils/src/index.js index 481b121..3543d37 100644 --- a/packages/vvisp-utils/src/index.js +++ b/packages/vvisp-utils/src/index.js @@ -1,13 +1,16 @@ const compile = require('./compile'); const compileAndDeploy = require('./compileAndDeploy'); +const compilerSupplier = require('./compilerSupplier'); +const Config = require('./Config'); const deploy = require('./deploy'); +const filterPrivateKey = require('./filterPrivateKey'); const forIn = require('./forIn'); const forInAsync = require('./forInAsync'); const getAllFiles = require('./getAllFiles'); const getCompiledContracts = require('./getCompiledContracts'); const getCycle = require('./getCycle'); const getTxCount = require('./getTxCount'); -const getWeb3 = require('./getWeb3'); +const web3Store = require('./web3Store'); const getPrivateKey = require('./getPrivateKey'); const printOrSilent = require('./printOrSilent'); const privateKeyToAddress = require('./privateKeyToAddress'); @@ -22,14 +25,17 @@ const parseLogs = require('./parseLogs'); module.exports = { compile, compileAndDeploy, + compilerSupplier, + Config, deploy, + filterPrivateKey, forIn, forInAsync, getAllFiles, getCompiledContracts, getCycle, getTxCount, - getWeb3, + web3Store, getPrivateKey, printOrSilent, privateKeyToAddress, diff --git a/packages/vvisp-utils/src/privateKeyToAddress.js b/packages/vvisp-utils/src/privateKeyToAddress.js index 8bedf8e..099101e 100644 --- a/packages/vvisp-utils/src/privateKeyToAddress.js +++ b/packages/vvisp-utils/src/privateKeyToAddress.js @@ -1,7 +1,7 @@ module.exports = function(privateKey) { - const filterPrivateKey = require('./utils/filterPrivateKey'); + const filterPrivateKey = require('./filterPrivateKey'); privateKey = filterPrivateKey(privateKey); - const web3 = require('./getWeb3')(); + const web3 = require('./web3Store').get(); return web3.eth.accounts.privateKeyToAccount(privateKey).address; }; diff --git a/packages/vvisp-utils/src/sendTx.js b/packages/vvisp-utils/src/sendTx.js index fc77a7b..d7dfe28 100644 --- a/packages/vvisp-utils/src/sendTx.js +++ b/packages/vvisp-utils/src/sendTx.js @@ -1,9 +1,12 @@ module.exports = async function(_toAddr, _value, _privKey, options) { const Tx = require('ethereumjs-tx'); - const web3 = require('./getWeb3')(); + const web3 = require('./web3Store').get(); const privateKeyToAddress = require('./privateKeyToAddress'); const printOrSilent = require('./printOrSilent'); + const DEFAULT_GAS_LIMIT = web3.utils.toHex(4600000); + const DEFAULT_GAS_PRICE = web3.utils.toHex(10e9); + return main(_toAddr, _value, _privKey, options); async function main(_toAddr, _value, _privKey, options) { @@ -14,8 +17,8 @@ module.exports = async function(_toAddr, _value, _privKey, options) { // construct the transaction data const txData = { nonce: web3.utils.toHex(txCount), - gasLimit: options.gasLimit || web3.utils.toHex(4600000), // TODO: mv to config file - gasPrice: options.gasPrice || web3.utils.toHex(10e9), + gasLimit: options.gasLimit || DEFAULT_GAS_LIMIT, + gasPrice: options.gasPrice || DEFAULT_GAS_PRICE, to: _toAddr, from: fromAddr, value: _value || '0x', diff --git a/packages/vvisp-utils/src/web3Store.js b/packages/vvisp-utils/src/web3Store.js new file mode 100644 index 0000000..fb1f2cd --- /dev/null +++ b/packages/vvisp-utils/src/web3Store.js @@ -0,0 +1,35 @@ +const Web3 = require('web3'); + +let web3; +const voidWeb3 = new Web3(); +module.exports = (function() { + return { + /** + @dev setWithProvider set web3 with provider + */ + setWithProvider: provider => { + web3 = new Web3(provider); + }, + /** + @dev setWithURL set web3 with http url + */ + setWithURL: url => { + web3 = new Web3(new Web3.providers.HttpProvider(url)); + }, + /** + @dev get getter of stored web3, if there isn't, return void web3 + */ + get: () => { + if (!web3) { + return voidWeb3; + } + return web3; + }, + /** + @dev delete delete stored web3 for test + */ + delete: () => { + web3 = undefined; + } + }; +})(); diff --git a/packages/vvisp-utils/test/Config.test.js b/packages/vvisp-utils/test/Config.test.js new file mode 100644 index 0000000..c55b5fd --- /dev/null +++ b/packages/vvisp-utils/test/Config.test.js @@ -0,0 +1,449 @@ +const chai = require('chai'); +const expect = chai.expect; +chai.use(require('chai-as-promised')).should(); + +const Config = require('../src/Config'); +const DEFAULT_CONFIG_FILE = 'vvisp-config.js'; +const { getPrivateKey } = require('../src'); +const _ = require('lodash'); +const path = require('path'); +const fs = require('fs-extra'); +const Provider = require('truffle-provider'); + +const DEFAULT_TX_OPTIONS = { + gasLimit: 6721975, + gasPrice: 20000000000 // 20 gwei, +}; +const DEFAULT_NETWORK = 'development'; + +const SAMPLE_CONFIG_PATH = path.join( + __dirname, + './dummy/sample.vvisp-config.js' +); +const sampleConfig = require(SAMPLE_CONFIG_PATH); +const VVISP_CONFIG_PATH = path.join('./', DEFAULT_CONFIG_FILE); + +describe('# Config test', function() { + this.timeout(50000); + + let tmpConfigName = 'eoifhsoeudhf.js'; + const TMP_CONFIG_PATH = path.join('./', tmpConfigName); + + const sampleOption = { + network: 'coverage' + }; + + before(function() { + if (fs.existsSync(VVISP_CONFIG_PATH)) { + fs.moveSync(VVISP_CONFIG_PATH, TMP_CONFIG_PATH); + } + }); + + after(function() { + if (fs.existsSync(TMP_CONFIG_PATH)) { + fs.moveSync(TMP_CONFIG_PATH, VVISP_CONFIG_PATH); + } + }); + + describe('#merge()', function() { + let config; + + beforeEach(function() { + config = new Config(); + }); + + it('should merge with option', function() { + expect(config.network).to.equal(null); + config.merge(sampleOption); + expect(config.network).to.equal(sampleOption.network); + }); + + it('should ignore input property if config does not have', function() { + expect(config.hello).to.equal(undefined); + config.merge({ hello: 'hello' }); + expect(config.hello).to.equal(undefined); + }); + }); + + describe('#addProperty()', function() { + let config; + const samplePropertyName = 'hello'; + + beforeEach(function() { + config = new Config(); + }); + + describe('add getter', function() { + it('should add given getter if descriptor has', function() { + config.addProperty(samplePropertyName, { + get: () => { + return samplePropertyName; + } + }); + expect(config[samplePropertyName]).to.equal(samplePropertyName); + }); + + it('should return exist value if propertyName is registered already', function() { + config.addProperty('network', {}); + expect(config.network).to.equal(null); + }); + + it('should add default() of descriptor if propertyName is not registered already', function() { + config.addProperty(samplePropertyName, { + default: () => { + return samplePropertyName; + } + }); + expect(config[samplePropertyName]).to.equal(samplePropertyName); + }); + + it('should add descriptor if propertyName is not registered already and descriptor is function', function() { + config.addProperty(samplePropertyName, () => { + return samplePropertyName; + }); + expect(config[samplePropertyName]).to.equal(samplePropertyName); + }); + }); + + describe('add setter', function() { + let target = 'hi'; + const changedTo = 'bye'; + const setter = value => { + target = value; + }; + it('should add given setter if descriptor has', function() { + config.addProperty(samplePropertyName, { set: setter }); + config[samplePropertyName] = changedTo; + expect(target).to.equal(changedTo); + }); + + it('should transform() of descriptor and set if propertyName is registered already and descriptor has transform()', function() { + config.addProperty('network', { + transform: value => { + return value + changedTo; + } + }); + config['network'] = target; + expect(config['network']).to.equal(target + changedTo); + }); + + it('should add just setter if there is no descriptor', function() { + config.addProperty('network'); + config['network'] = target; + expect(config['network']).to.equal(target); + }); + }); + }); + + describe('.search()', function() { + needsConfigFile(); + + it('should throw when there is no config file', function() { + fs.removeSync(path.join('./', DEFAULT_CONFIG_FILE)); + expect(() => Config.search()).to.throw(); + }); + + it('should search default config file', function() { + const configPath = Config.search(); + path.parse(configPath).base.should.equal(DEFAULT_CONFIG_FILE); + const config = require(configPath); + expect(config).to.be.an('object'); + expect(config).to.have.property('networks'); + }); + + it('should search custom config file', function() { + const customConfigFile = 'custom-config.js'; + fs.copySync( + path.join('./', DEFAULT_CONFIG_FILE), + path.join('./', customConfigFile) + ); + const configPath = Config.search({ configFile: customConfigFile }); + path.parse(configPath).base.should.equal(customConfigFile); + const config = require(configPath); + expect(config).to.be.an('object'); + expect(config).to.have.property('networks'); + + fs.removeSync(path.join('./', customConfigFile)); + }); + }); + + describe('.load()', function() { + needsConfigFile(); + + it('should load config', function() { + const config = Config.load(); + expect(config).to.be.an.instanceOf(Config); + }); + }); + + describe('.get()', function() { + needsConfigFile(); + needsDelete(); + + it('should generate and return default config if config is not generated', function() { + expect(Config.get()).to.be.an.instanceOf(Config); + }); + + it('should return stored config if config is generated', function() { + Config.get(sampleOption); + expect(Config.get()).to.be.an.instanceOf(Config); + expect(Config.get().network).to.equal(sampleOption.network); + }); + }); + + describe('.setStore()', function() { + needsConfigFile(); + needsDelete(); + + it('should throw error if input config is not an instance of Config', function() { + expect(() => Config.setStore('hello')).to.throw(TypeError); + }); + + it('should set new config', function() { + const originalConfig = Config.get(); + Config.setStore(Config.load(sampleOption)); + const newConfig = Config.get(); + expect(originalConfig).not.to.equal(newConfig); + }); + }); + + describe('.delete()', function() { + needsConfigFile(); + needsDelete(); + + it('should delete stored config and .get() will return default config', function() { + const originalConfig = Config.get(sampleOption); + expect(originalConfig.network).to.equal(sampleOption.network); + expect(Config.get().network).to.equal(sampleOption.network); + Config.delete(); + + expect(Config.get().network).to.equal(DEFAULT_NETWORK); + }); + }); + + describe('property test', function() { + let config; + beforeEach(function() { + config = new Config(); + }); + + describe('#network', function() { + const networkName = DEFAULT_NETWORK; + it('should set network name', function() { + config.network = networkName; + config.network.should.equal(networkName); + }); + }); + + describe('#networks', function() { + const networks = sampleConfig.networks; + + it('should set networks', function() { + config.networks = networks; + expect(config.networks).to.deep.equal(networks); + }); + }); + + describe('#verboseRpc', function() { + const verboseRpc = true; + + it('should set verboseRpc', function() { + config.verboseRpc = verboseRpc; + expect(config.verboseRpc).to.equal(verboseRpc); + }); + }); + + describe('#compilers', function() { + const compilers = sampleConfig.compilers; + + it('should set compilers', function() { + config.compilers = compilers; + expect(config.compilers).to.deep.equal(compilers); + }); + }); + + describe('#from', function() { + const privateKey = + '0xb178cf12d4126ea1db48ca32e3ce6743580ca6646391996032fc76652d699977'; + const mnemonicObject = { + mnemonic: 'hello', + index: 3 + }; + + it('should not set invalid type', function() { + expect(() => { + config.from = 123; + }).to.throw(TypeError); + expect(() => { + config.from = [123]; + }).to.throw(TypeError); + expect(() => { + config.from = { a: 123 }; + }).to.throw(TypeError); + expect(() => { + config.from = undefined; + }).to.throw(TypeError); + expect(() => { + config.from = null; + }).to.throw(TypeError); + }); + + it('should set privateKey and return right key', function() { + config.from = privateKey; + config.from.should.equal(privateKey); + }); + + it('should set mnemonic and index and return right key', function() { + config.from = mnemonicObject; + config.from.should.equal( + getPrivateKey(mnemonicObject.mnemonic, mnemonicObject.index) + ); + }); + + it('should return null when it is not set', function() { + let getter; + expect(() => { + getter = config.from; + }).to.throw(Error); + }); + }); + + describe('needs networks set', function() { + const DEVELOPMENT = DEFAULT_NETWORK; + beforeEach(function() { + config.networks = sampleConfig.networks; + }); + + describe('#network_config', function() { + it('should not set directly', function() { + expect(() => { + config.network_config = sampleConfig.networks[DEVELOPMENT]; + }).to.throw(); + }); + + it('should not return when network is not set', function() { + let getter; + expect(() => { + getter = config.network_config; + }).to.throw(); + }); + + it('should return network_config', function() { + config.network = DEVELOPMENT; + expect(config.network_config).to.deep.equal( + _.extend({}, DEFAULT_TX_OPTIONS, sampleConfig.networks[DEVELOPMENT]) + ); + }); + }); + + describe('#network_id', function() { + it('should not set directly', function() { + expect(() => { + config.network_id = 3; + }).to.throw(); + }); + + it('should get right network_id', function() { + config.network = DEVELOPMENT; + const expectation = sampleConfig.networks[DEVELOPMENT].network_id; + config.network_id.should.equal(expectation); + }); + + it('should return null if network is not set', function() { + expect(config.network_id).to.equal(null); + }); + }); + + describe('#gasLimit', function() { + const TEST_NETWORK = 'coverage'; + it('should not set directly', function() { + expect(() => { + config.gasLimit = 8000000; + }).to.throw(); + }); + + it('should return default gasLimit when network is not set', function() { + expect(config.gasLimit).to.equal(DEFAULT_TX_OPTIONS.gasLimit); + }); + + it('should return right gasLimit', function() { + config.network = TEST_NETWORK; + expect(config.gasLimit).to.equal( + sampleConfig.networks[TEST_NETWORK].gasLimit + ); + }); + + it('should allow to receive gas also instead of gasLimit', function() { + const tmpConfig = _.cloneDeep(sampleConfig); + delete tmpConfig.networks[TEST_NETWORK].gasLimit; + const sampleGas = 123123; + tmpConfig.networks[TEST_NETWORK].gas = sampleGas; + + config.networks = tmpConfig.networks; + config.network = TEST_NETWORK; + expect(config.gasLimit).to.equal(sampleGas); + }); + }); + + describe('#gasPrice', function() { + const TEST_NETWORK = 'coverage'; + it('should not set directly', function() { + expect(() => { + config.gasPrice = 10000000000; + }).to.throw(); + }); + + it('should return default gas when network is not set', function() { + expect(config.gasPrice).to.equal(DEFAULT_TX_OPTIONS.gasPrice); + }); + + it('should return right gas', function() { + config.network = TEST_NETWORK; + expect(config.gasPrice).to.equal( + sampleConfig.networks[TEST_NETWORK].gasPrice + ); + }); + }); + + describe('#provider', function() { + it('should not set directly', function() { + expect(() => { + config.provider = 'something'; + }).to.throw(); + }); + + it('should return null when network is not set', function() { + expect(config.provider).to.equal(null); + }); + + it('should return right provider', function(done) { + config.network = DEVELOPMENT; + const provider = config.provider; + Provider.test_connection(provider, fail => { + done(fail); + }); + }); + }); + }); + }); +}); + +function needsConfigFile() { + beforeEach(function() { + fs.copySync(SAMPLE_CONFIG_PATH, path.join('./', DEFAULT_CONFIG_FILE)); + }); + + afterEach(function() { + fs.removeSync(path.join('./', DEFAULT_CONFIG_FILE)); + }); +} + +function needsDelete() { + beforeEach(function() { + Config.delete(); + }); + + afterEach(function() { + Config.delete(); + }); +} diff --git a/packages/vvisp-utils/test/ci.sh b/packages/vvisp-utils/test/ci.sh index 20b71cb..860ff3d 100755 --- a/packages/vvisp-utils/test/ci.sh +++ b/packages/vvisp-utils/test/ci.sh @@ -2,8 +2,8 @@ set -o errexit -cp test/dummy/test.env . -mv test.env .env +cp test/dummy/sample.vvisp-config.js . +mv sample.vvisp-config.js vvisp-config.js nyc --reporter=html --reporter=text mocha $(find ./test -name '*.test.js') --recursive nyc report --reporter=text-lcov | coveralls diff --git a/packages/vvisp-utils/test/compile.test.js b/packages/vvisp-utils/test/compile.test.js index 3173dde..651cd15 100644 --- a/packages/vvisp-utils/test/compile.test.js +++ b/packages/vvisp-utils/test/compile.test.js @@ -1,12 +1,16 @@ const chai = require('chai'); const expect = chai.expect; chai.use(require('chai-as-promised')).should(); -const dotenv = require('dotenv'); +const _ = require('lodash'); const { compile, getCompiledContracts } = require('../src'); const path = require('path'); const ERROR_CONTRACT = path.join(__dirname, '../contracts/ErrorContract.sol'); +const ERROR_IMPORT_CONTRACT = path.join( + __dirname, + '../contracts/ErrorImportContract.sol' +); const RIGHT_CONTRACT1 = path.join(__dirname, '../contracts/DependencyA.sol'); const RIGHT_CONTRACT2 = path.join(__dirname, '../contracts/DependencyB.sol'); const VERSION5 = path.join(__dirname, '../contracts/Version5.sol'); @@ -14,9 +18,17 @@ const OPEN_NFT = path.join(__dirname, '../contracts/MyNFT.sol'); describe('# compile test', function() { this.timeout(50000); - beforeEach(function() { - dotenv.config(); - }); + + const configWrapper = { + compilers: { + solc: { + settings: { + optimizer: {} + } + } + } + }; + describe('# input arguments', function() { it('should reject wrong input type', async function() { await compile(1123, { silent: true }).should.be.rejectedWith(TypeError); @@ -24,59 +36,125 @@ describe('# compile test', function() { TypeError ); }); + it('should reject wrong path input', async function() { await compile('hi', { silent: true }).should.be.rejected; await compile(['hi', 'hello'], { silent: true }).should.be.rejected; }); }); + describe('# work process', function() { it('should reject error contract', async function() { await compile(ERROR_CONTRACT, { silent: true }).should.be.rejectedWith( Error ); }); + + it('should reject contract which imports wrong modules', async function() { + await compile(ERROR_IMPORT_CONTRACT, { + silent: true + }).should.be.rejectedWith(Error); + }); + it('should be fulfilled with right contract', async function() { await compile(RIGHT_CONTRACT1, { silent: true }).should.be.fulfilled; }); + it('should be fulfilled with multi contracts', async function() { const files = [RIGHT_CONTRACT1, RIGHT_CONTRACT2]; - await compile(files, true).should.be.fulfilled; + await compile(files, { silent: true }).should.be.fulfilled; }); + + it('should be fulfilled without solc compiler', async function() { + const config = { + compilers: { + otherCompiler: {} + } + }; + await compile(RIGHT_CONTRACT1, { silent: true, config: config }).should.be + .fulfilled; + }); + it('should be fulfilled with other solc version', async function() { - process.env.SOLC_VERSION = 'v0.4.24+commit.e67f0147'; - await compile(RIGHT_CONTRACT1, { silent: true }).should.be.fulfilled; + const config = _.cloneDeep(configWrapper); + config.compilers.solc.version = 'v0.4.25+commit.59dbf8f1'; + await compile(RIGHT_CONTRACT1, { silent: true, config: config }).should.be + .fulfilled; }); - it('should be fulfilled with nonOptimization', async function() { - process.env.SOLC_OPTIMIZATION = 'false'; - await compile(RIGHT_CONTRACT1, { silent: true }).should.be.fulfilled; + + it('should be fulfilled with optimization', async function() { + const config = _.cloneDeep(configWrapper); + config.compilers.solc.settings.optimizer = { + enabled: true, + run: 200 + }; + await compile(RIGHT_CONTRACT1, { silent: true, config: config }).should.be + .fulfilled; }); + it('should be fulfilled with solidity version 5', async function() { - process.env.SOLC_VERSION = '0.5.1'; - await compile(VERSION5, { silent: true }).should.be.fulfilled; - process.env.SOLC_VERSION = ''; + const config = _.cloneDeep(configWrapper); + config.compilers.solc.version = '0.5.1'; + await compile(VERSION5, { silent: true, config: config }).should.be + .fulfilled; }); + it('should be fulfilled with external solidity library', async function() { - process.env.SOLC_VERSION = '0.4.24'; - await compile(OPEN_NFT, { silent: true }).should.be.fulfilled; - process.env.SOLC_VERSION = ''; + const config = _.cloneDeep(configWrapper); + config.compilers.solc.version = '0.4.24'; + await compile(OPEN_NFT, { silent: true, config: config }).should.be + .fulfilled; }); }); + describe('# result value', function() { - // before(async function() { - // this.optimization = await compile(RIGHT_CONTRACT1, { silent: true }).should.be - // .fulfilled; - // process.env.SOLC_OPTIMIZATION = 'false'; - // this.noOptimization = await compile(RIGHT_CONTRACT1, { silent: true }).should.be - // .fulfilled; - // }); - // TODO: optimization is not working, needs to find way - // it('should return different values depending on optimization', async function() { - // const optByteCode = getCompiledContracts(this.optimization, this.dummyPath).bytecode; - // const noOptByteCode = getCompiledContracts(this.noOptimization, this.dummyPath).bytecode; - // console.log(getCompiledContracts(this.optimization, this.dummyPath)); - // console.log(getCompiledContracts(this.noOptimization, this.dummyPath)); - // console.log(optByteCode.length, noOptByteCode.length); - // optByteCode.length.should.be.below(noOptByteCode.length); - // }); + it('should return different values depending on optimization', async function() { + const optimizeConfig = _.cloneDeep(configWrapper); + optimizeConfig.compilers.solc.settings.optimizer = { + enabled: true, + run: 200 + }; + const optimization = await compile(RIGHT_CONTRACT1, { + silent: true, + config: optimizeConfig + }).should.be.fulfilled; + const nonOptimizeConfig = _.cloneDeep(configWrapper); + nonOptimizeConfig.compilers.solc.settings.optimizer = { + enabled: false + }; + const nonOptimization = await compile(RIGHT_CONTRACT1, { + silent: true, + config: nonOptimizeConfig + }).should.be.fulfilled; + + const optByteCode = getCompiledContracts(optimization, RIGHT_CONTRACT1) + .bytecode; + const nonOptByteCode = getCompiledContracts( + nonOptimization, + RIGHT_CONTRACT1 + ).bytecode; + optByteCode.length.should.be.below(nonOptByteCode.length); + }); + + it('should return different values depending on evmVersion', async function() { + const byzantiumConfig = _.cloneDeep(configWrapper); + byzantiumConfig.compilers.solc.settings.evmVersion = 'byzantium'; + const byzantium = await compile(RIGHT_CONTRACT1, { + silent: true, + config: byzantiumConfig + }).should.be.fulfilled; + const homesteadConfig = _.cloneDeep(configWrapper); + homesteadConfig.compilers.solc.settings.evmVersion = 'homestead'; + const homestead = await compile(RIGHT_CONTRACT1, { + silent: true, + config: homesteadConfig + }).should.be.fulfilled; + + const byzantiumByteCode = getCompiledContracts(byzantium, RIGHT_CONTRACT1) + .bytecode; + const homesteadByteCode = getCompiledContracts(homestead, RIGHT_CONTRACT1) + .bytecode; + byzantiumByteCode.length.should.not.equal(homesteadByteCode); + }); }); }); diff --git a/packages/vvisp-utils/test/deploy.test.js b/packages/vvisp-utils/test/deploy.test.js index 0298251..3b24d7e 100644 --- a/packages/vvisp-utils/test/deploy.test.js +++ b/packages/vvisp-utils/test/deploy.test.js @@ -1,17 +1,17 @@ const chai = require('chai'); chai.use(require('chai-as-promised')).should(); -require('dotenv').config(); const { compile, deploy, getCompiledContracts, - getWeb3, + web3Store, getPrivateKey, privateKeyToAddress } = require('../src'); -const web3 = getWeb3(); const path = require('path'); +const fs = require('fs-extra'); +const testEnv = fs.readJsonSync(path.join(__dirname, 'test.env.json')); const RIGHT_CONTRACT = path.join(__dirname, '../contracts/DependencyA.sol'); const ARRAY_INPUT_CONTRACT = path.join( @@ -19,13 +19,18 @@ const ARRAY_INPUT_CONTRACT = path.join( '../contracts/DependencyD.sol' ); const NO_INPUT_CONTRACT = path.join(__dirname, '../contracts/SecondB.sol'); -const PRIV_KEY = getPrivateKey(process.env.MNEMONIC); +const PRIV_KEY = getPrivateKey(testEnv.mnemonic); const SENDER = privateKeyToAddress(PRIV_KEY); describe('# deploy test', function() { this.timeout(50000); + let web3; + before(async function() { + web3Store.setWithURL(testEnv.url); + web3 = web3Store.get(); + const compileOutput = await compile( [RIGHT_CONTRACT, ARRAY_INPUT_CONTRACT, NO_INPUT_CONTRACT], { silent: true } @@ -38,6 +43,10 @@ describe('# deploy test', function() { this.secondB = getCompiledContracts(compileOutput, NO_INPUT_CONTRACT); }); + after(function() { + web3Store.delete(); + }); + describe('# input arguments', function() { it('should reject wrong input arguments length', async function() { await deploy(this.dependencyA, PRIV_KEY).should.be.rejectedWith(Error); diff --git a/packages/vvisp-utils/test/dummy/sample.vvisp-config.js b/packages/vvisp-utils/test/dummy/sample.vvisp-config.js new file mode 100644 index 0000000..96c0db3 --- /dev/null +++ b/packages/vvisp-utils/test/dummy/sample.vvisp-config.js @@ -0,0 +1,34 @@ +module.exports = { + networks: { + development: { + host: 'localhost', + port: 8545, + network_id: '*' // eslint-disable-line camelcase + }, + coverage: { + host: 'localhost', + port: 8545, + network_id: '*', // eslint-disable-line camelcase + gasLimit: 123123, + gasPrice: 10000000000 + } + }, + compilers: { + solc: { + version: '0.4.25', // Fetch exact version from solc-bin + settings: { + // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: false, + runs: 200 + }, + evmVersion: 'byzantium' + } + } + }, + from: { + mnemonic: + 'piano garage flag neglect spare title drill basic strong aware enforce fury', + index: 0 + } +}; diff --git a/packages/vvisp-utils/test/utils/filterPrivateKey.test.js b/packages/vvisp-utils/test/filterPrivateKey.test.js similarity index 96% rename from packages/vvisp-utils/test/utils/filterPrivateKey.test.js rename to packages/vvisp-utils/test/filterPrivateKey.test.js index 481619b..8401f07 100644 --- a/packages/vvisp-utils/test/utils/filterPrivateKey.test.js +++ b/packages/vvisp-utils/test/filterPrivateKey.test.js @@ -2,7 +2,7 @@ const chai = require('chai'); const expect = chai.expect; chai.should(); -const filterPrivateKey = require('../../src/utils/filterPrivateKey'); +const { filterPrivateKey } = require('../src'); const SAMPLE_PRIVATE_KEY = '8bb0722ff8cb8161da257dc2d3712a17db1753d1de2d8b6b27b0e4636d9899f6'; diff --git a/packages/vvisp-utils/test/getPrivateKey.test.js b/packages/vvisp-utils/test/getPrivateKey.test.js index 67e1d42..58b5f59 100644 --- a/packages/vvisp-utils/test/getPrivateKey.test.js +++ b/packages/vvisp-utils/test/getPrivateKey.test.js @@ -18,14 +18,8 @@ describe('# getPrivateKey test', function() { expect(() => getPrivateKey(['hello', 'there'])).to.throw(TypeError); }); - it('should reject when length of mnemonic is not 12', function() { - const mnemonic11 = - 'away clutch still element short tooth spy hood army split stomach'; - const mnemonic13 = - 'away clutch still element short tooth spy hood army split stomach sail still'; - expect(() => getPrivateKey('')).to.throw(TypeError); - expect(() => getPrivateKey(mnemonic11)).to.throw(TypeError); - expect(() => getPrivateKey(mnemonic13)).to.throw(TypeError); + it('should receive one word', function() { + expect(() => getPrivateKey('hello')).to.not.throw(); }); }); diff --git a/packages/vvisp-utils/test/getWeb3.test.js b/packages/vvisp-utils/test/getWeb3.test.js deleted file mode 100644 index e0fddfb..0000000 --- a/packages/vvisp-utils/test/getWeb3.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const chai = require('chai'); -const expect = chai.expect; -chai.should(); - -const { getWeb3 } = require('../src'); -const dotenv = require('dotenv'); - -describe('# getWeb3 test', function() { - beforeEach(function() { - dotenv.config(); - }); // TODO: fill it - it('should ', function() {}); -}); diff --git a/packages/vvisp-utils/test/test.env.json b/packages/vvisp-utils/test/test.env.json new file mode 100644 index 0000000..4eedffe --- /dev/null +++ b/packages/vvisp-utils/test/test.env.json @@ -0,0 +1,4 @@ +{ + "mnemonic": "piano garage flag neglect spare title drill basic strong aware enforce fury", + "url": "http://localhost:8545" +} diff --git a/packages/vvisp-utils/test/web3Store.test.js b/packages/vvisp-utils/test/web3Store.test.js new file mode 100644 index 0000000..e0dbac5 --- /dev/null +++ b/packages/vvisp-utils/test/web3Store.test.js @@ -0,0 +1,57 @@ +const chai = require('chai'); +const expect = chai.expect; +chai.use(require('chai-as-promised')).should(); + +const Web3 = require('web3'); +const { web3Store } = require('../src'); + +const path = require('path'); +const fs = require('fs-extra'); +const testEnv = fs.readJsonSync(path.join(__dirname, 'test.env.json')); + +describe('web3Store', function() { + beforeEach(function() { + web3Store.delete(); + }); + + after(function() { + web3Store.delete(); + }); + + describe('#setWithProvider()', function() { + it('should set with provider', async function() { + web3Store.setWithProvider(new Web3.providers.HttpProvider(testEnv.url)); + const web3 = web3Store.get(); + expect(web3).to.be.an.instanceOf(Web3); + await web3.eth.getCoinbase().should.be.fulfilled; + }); + }); + + describe('#setWithURL()', function() { + it('should set with url', async function() { + web3Store.setWithURL(testEnv.url); + const web3 = web3Store.get(); + expect(web3).to.be.an.instanceOf(Web3); + await web3.eth.getCoinbase().should.be.fulfilled; + }); + }); + + describe('#get()', function() { + it('should return void web3 when it is not set', async function() { + const web3 = web3Store.get(); + expect(web3).to.be.an.instanceOf(Web3); + await web3.eth.getCoinbase().should.be.rejected; + }); + }); + + describe('#delete()', function() { + it('should delete stored web3', async function() { + web3Store.setWithURL(testEnv.url); + web3Store.delete(); + + const web3 = web3Store.get(); + expect(web3).to.be.an.instanceOf(Web3); + await web3.eth.getCoinbase().should.be.rejected; + }); + }); +}); diff --git a/packages/vvisp/bin/error.js b/packages/vvisp/bin/error.js index 5e113a9..dbfb117 100644 --- a/packages/vvisp/bin/error.js +++ b/packages/vvisp/bin/error.js @@ -1,72 +1,13 @@ const fs = require('fs'); -const path = require('path'); -const _ = require('lodash'); -const NETWORKS = { - local: 'local', - mainnet: 'mainnet', - ropsten: 'ropsten', - kovan: 'kovan', - rinkeby: 'rinkeby', - custom: 'custom' -}; - -const SERVICE_PATH = path.join('./', 'service.vvisp.json'); -const ENV_PATH = path.join('./', '.env'); +const { SERVICE_PATH, SERVICE_FILE } = require('../config/Constant'); -function checkConfigExist() { +function checkServiceFileExist() { if (!fs.existsSync(SERVICE_PATH)) { - throw new Error('service.vvisp.json file does not exist'); - } -} - -function checkEnv() { - if (!fs.existsSync(ENV_PATH)) { - throw new Error('.env file does not exist'); - } - - if (!process.env.NETWORK) { - throw new Error('You should set a NETWORK in .env file'); - } else if (_.includes(NETWORKS, process.env.NETWORK) === false) { - throw new Error(`You should choose a NETWORK from [${_.values(NETWORKS)}]`); - } else { - switch (process.env.NETWORK) { - case NETWORKS.local: { - if (!process.env.PORT) { - throw new Error( - `You should set PORT in .env file when you choose 'local'` - ); - } - break; - } - case NETWORKS.custom: { - if (!process.env.URL) { - throw new Error( - `You should set URL in .env file when you choose 'custom'` - ); - } - break; - } - default: { - if (!process.env.INFURA_API_KEY) { - throw new Error('You should set INFURA_API_KEY in .env file'); - } - break; - } - } - } - - if (!process.env.MNEMONIC && !process.env.PRIVATE_KEY) { - throw new Error('You should set MNEMONIC or PRIVATE_KEY in .env file'); + throw new Error(`${SERVICE_FILE} file does not exist`); } } module.exports = { - checkConfigExist, - checkEnv, - constants: { - NETWORKS, - SERVICE_PATH, - ENV_PATH - } + checkServiceFileExist }; diff --git a/packages/vvisp/bin/vvisp.js b/packages/vvisp/bin/vvisp.js index f488bd1..5f47da7 100755 --- a/packages/vvisp/bin/vvisp.js +++ b/packages/vvisp/bin/vvisp.js @@ -1,6 +1,5 @@ #! /usr/bin/env node -require('dotenv').config(); // TODO: will change to configuration file const commander = require('./commander'); try { diff --git a/packages/vvisp/config/Constant.js b/packages/vvisp/config/Constant.js new file mode 100644 index 0000000..9c8fc53 --- /dev/null +++ b/packages/vvisp/config/Constant.js @@ -0,0 +1,20 @@ +const path = require('path'); + +const PROJECT_NAME = 'vvisp'; +const SERVICE_FILE = `service.${PROJECT_NAME}.json`; +const STATE_FILE = `state.${PROJECT_NAME}.json`; + +module.exports = { + TEST: { + MNEMONIC: + 'piano garage flag neglect spare title drill basic strong aware enforce fury', + URL: 'http://localhost:8545' + }, + PROJECT_NAME: PROJECT_NAME, + SERVICE_FILE: SERVICE_FILE, + SERVICE_PATH: path.join('./', SERVICE_FILE), + STATE_FILE: STATE_FILE, + STATE_PATH: path.join('./', STATE_FILE), + DEFAULT_CONFIG_FILE: `${PROJECT_NAME}-config.js`, + DEFAULT_NETWORK: `development` +}; diff --git a/packages/vvisp/package.json b/packages/vvisp/package.json index 2cfbaf1..782a3d3 100644 --- a/packages/vvisp/package.json +++ b/packages/vvisp/package.json @@ -61,7 +61,6 @@ "bip39": "^2.5.0", "chalk": "^2.4.1", "commander": "^2.17.1", - "dotenv": "^5.0.1", "ethereumjs-tx": "^1.3.7", "ethereumjs-wallet": "^0.6.2", "find-up": "^3.0.0", @@ -87,5 +86,12 @@ "mitm": "^1.4.0", "rewire": "^4.0.1", "std-mocks": "^1.0.1" + }, + "nyc": { + "exclude": [ + "contractApis", + "test", + "*-config.js" + ] } } diff --git a/packages/vvisp/scripts/console.js b/packages/vvisp/scripts/console.js index 89f0f4a..7168b73 100644 --- a/packages/vvisp/scripts/console.js +++ b/packages/vvisp/scripts/console.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const stringArgv = require('string-argv'); +const _ = require('lodash'); const { parseLogs } = require('@haechi-labs/vvisp-utils'); if (!String.prototype.format) { @@ -34,6 +35,8 @@ const defaultStateFile = 'state.vvisp.json'; */ module.exports = async function(scriptPath, options) { + options = require('./utils/injectConfig')(options); + let apis = setApi(scriptPath); if (fs.existsSync(defaultStateFile)) { apis = setApiAddress(apis, defaultStateFile); @@ -360,7 +363,11 @@ function setApi(scriptPath) { } // set script api - const apis = require(path.join(scriptPath, 'back') + '/index.js'); + // omit configuration functions + const apis = _.omit(require(path.join(scriptPath, 'back') + '/index.js')(), [ + 'config', + 'setWeb3' + ]); // set abi for (const key of Object.keys(apis)) { diff --git a/packages/vvisp/scripts/deploy-contract.js b/packages/vvisp/scripts/deploy-contract.js index 13af117..18295a4 100755 --- a/packages/vvisp/scripts/deploy-contract.js +++ b/packages/vvisp/scripts/deploy-contract.js @@ -1,28 +1,17 @@ module.exports = async function(file, arguments, options) { - const { checkEnv } = require('../bin/error'); - checkEnv(); + options = require('./utils/injectConfig')(options); const { compileAndDeploy, - getPrivateKey, - getWeb3, printOrSilent } = require('@haechi-labs/vvisp-utils'); - const web3 = getWeb3(); - const privateKey = getPrivateKey( - process.env.MNEMONIC, - process.env.PRIV_INDEX - ); + const privateKey = options.config.from; options = { ...options, - gasPrice: process.env.GAS_PRICE - ? web3.utils.toHex(process.env.GAS_PRICE) - : undefined, - gasLimit: process.env.GAS_LIMIT - ? web3.utils.toHex(process.env.GAS_LIMIT) - : undefined + gasPrice: options.config.gasPrice, + gasLimit: options.config.gasLimit }; await compileAndDeploy(file, privateKey, arguments, options); diff --git a/packages/vvisp/scripts/deploy-service/constants.js b/packages/vvisp/scripts/deploy-service/constants.js index ea31075..f4670d6 100644 --- a/packages/vvisp/scripts/deploy-service/constants.js +++ b/packages/vvisp/scripts/deploy-service/constants.js @@ -1,21 +1,15 @@ module.exports = (function() { const path = require('path'); - const { getWeb3, getPrivateKey } = require('@haechi-labs/vvisp-utils'); - const web3 = getWeb3(); + const { Config } = require('@haechi-labs/vvisp-utils'); + const config = Config.get(); return { - SERVICE_PATH: path.join('./', 'service.vvisp.json'), - STATE_PATH: path.join('./', 'state.vvisp.json'), REGISTRY_PATH: path.join('./', 'contracts/upgradeable/VvispRegistry.sol'), TX_OPTIONS: { - gasPrice: process.env.GAS_PRICE - ? web3.utils.toHex(process.env.GAS_PRICE) - : undefined, - gasLimit: process.env.GAS_LIMIT - ? web3.utils.toHex(process.env.GAS_LIMIT) - : undefined + gasPrice: config.gasPrice, + gasLimit: config.gasLimit }, - PRIVATE_KEY: getPrivateKey(process.env.MNEMONIC, process.env.PRIV_INDEX), + PRIVATE_KEY: config.from, // service contract property name VARIABLES: 'variables', diff --git a/packages/vvisp/scripts/deploy-service/index.js b/packages/vvisp/scripts/deploy-service/index.js index 7938929..86ac8ca 100644 --- a/packages/vvisp/scripts/deploy-service/index.js +++ b/packages/vvisp/scripts/deploy-service/index.js @@ -1,12 +1,13 @@ module.exports = async function(options) { - const { checkConfigExist, checkEnv } = require('../../bin/error'); - checkEnv(); - checkConfigExist(); + options = require('../utils/injectConfig')(options); + const { checkServiceFileExist } = require('../../bin/error'); + checkServiceFileExist(); - const { printOrSilent, getWeb3 } = require('@haechi-labs/vvisp-utils'); + const { printOrSilent } = require('@haechi-labs/vvisp-utils'); const { writeState } = require('./utils'); const DeployState = require('./DeployState'); const preProcess = require('./preProcess'); + const { PROJECT_NAME, SERVICE_FILE } = require('../../config/Constant'); const { deployBusinesses, deployNonUpgradeables, @@ -29,7 +30,6 @@ module.exports = async function(options) { notImportant: chk.gray, warning: chk.yellow }; - global.web3 = getWeb3(); await main(); @@ -116,7 +116,7 @@ module.exports = async function(options) { options ); printOrSilent( - `You can see result in ${chalk.keyWord('state.haechi.json')}`, + `You can see result in ${chalk.keyWord(SERVICE_FILE)}`, options ); } else { @@ -138,7 +138,7 @@ module.exports = async function(options) { printOrSilent(chalk.error(`Service State Paused!`), options); printOrSilent( `Resume Process by Running ${chalk.keyWord( - 'vvisp deploy-service' + `${PROJECT_NAME} deploy-service` )} Again.`, options ); diff --git a/packages/vvisp/scripts/deploy-service/preProcess/index.js b/packages/vvisp/scripts/deploy-service/preProcess/index.js index 5ccd84a..063798e 100644 --- a/packages/vvisp/scripts/deploy-service/preProcess/index.js +++ b/packages/vvisp/scripts/deploy-service/preProcess/index.js @@ -1,9 +1,12 @@ module.exports = async function(deployState, options) { const fs = require('fs-extra'); - const { SERVICE_PATH, STATE_PATH, VARIABLES } = require('../constants'); + const { SERVICE_PATH, STATE_PATH } = require('../../../config/Constant'); + const { VARIABLES } = require('../constants'); const { forIn, printOrSilent } = require('@haechi-labs/vvisp-utils'); + const web3 = options.web3; let stateClone = deployState.getState(); + const config = fs.readJsonSync(SERVICE_PATH); let processState; diff --git a/packages/vvisp/scripts/deploy-service/utils/pathToInstance.js b/packages/vvisp/scripts/deploy-service/utils/pathToInstance.js index 8034a33..7258687 100644 --- a/packages/vvisp/scripts/deploy-service/utils/pathToInstance.js +++ b/packages/vvisp/scripts/deploy-service/utils/pathToInstance.js @@ -1,6 +1,9 @@ module.exports = function(compileOutput, filePath) { - const { getCompiledContracts, getWeb3 } = require('@haechi-labs/vvisp-utils'); - const web3 = getWeb3(); + const { + getCompiledContracts, + web3Store + } = require('@haechi-labs/vvisp-utils'); + const web3 = web3Store.get(); const abi = getCompiledContracts(compileOutput, filePath).interface; return new web3.eth.Contract(JSON.parse(abi)); diff --git a/packages/vvisp/scripts/deploy-service/utils/writeState.js b/packages/vvisp/scripts/deploy-service/utils/writeState.js index 5862da1..cd5e60e 100644 --- a/packages/vvisp/scripts/deploy-service/utils/writeState.js +++ b/packages/vvisp/scripts/deploy-service/utils/writeState.js @@ -1,7 +1,7 @@ module.exports = function(state, options) { const fs = require('fs'); const { printOrSilent } = require('@haechi-labs/vvisp-utils'); - const { STATE_PATH } = require('../constants'); + const { STATE_PATH } = require('../../../config/Constant'); const stateString = JSON.stringify(state, null, ' '); // printOrSilent(chalk.head('Service State Result'), options); // Just for debugging diff --git a/packages/vvisp/scripts/flatten.js b/packages/vvisp/scripts/flatten.js index 032f9e5..41bd3cd 100644 --- a/packages/vvisp/scripts/flatten.js +++ b/packages/vvisp/scripts/flatten.js @@ -11,8 +11,10 @@ module.exports = async function(files, options) { const path = require('path'); const fs = require('fs'); + const { SERVICE_FILE } = require('../config/Constant'); + try { - const configRootPath = await getConfigRoot('service.vvisp.json'); + const configRootPath = getConfigRoot(SERVICE_FILE); const relativeFilePaths = await getRelativeFilePathsFromRoot( configRootPath, files diff --git a/packages/vvisp/scripts/utils/injectConfig.js b/packages/vvisp/scripts/utils/injectConfig.js new file mode 100644 index 0000000..296dfd2 --- /dev/null +++ b/packages/vvisp/scripts/utils/injectConfig.js @@ -0,0 +1,14 @@ +module.exports = function(options = {}) { + const { Config, web3Store } = require('@haechi-labs/vvisp-utils'); + + // inject options + const config = Config.get(options); + + // generate web3 + web3Store.setWithProvider(config.provider); + + options.config = config; + options.web3 = web3Store.get(); + + return options; +}; diff --git a/packages/vvisp/template/index.js b/packages/vvisp/template/index.js index 8294dc0..2e0c89a 100644 --- a/packages/vvisp/template/index.js +++ b/packages/vvisp/template/index.js @@ -1,8 +1,29 @@ const fs = require('fs'); const path = require('path'); +const { Config, web3Store } = require('@haechi-labs/vvisp-utils'); const files = fs.readdirSync(path.join(__dirname, 'js')); -const apis = {}; +const apis = { + config: configObj => { + let config; + try { + config = Config.get(configObj); + } catch (e) {} + if (!config) { + config = new Config(); + config.merge(configObj); + } + Config.setStore(config); + }, + setWeb3: web3Setter => { + if (web3Setter && typeof web3Setter === 'string') { + web3Store.setWithURL(web3Setter); + } else if (web3Setter) { + web3Store.setWithProvider(web3Setter); + } + } +}; + for (let i = 0; i < files.length; i++) { if (files[i].slice(-3) === '.js') { if (files[i] === 'index.js') { @@ -12,4 +33,8 @@ for (let i = 0; i < files.length; i++) { } } -module.exports = apis; +module.exports = function(configOption, web3Setter) { + apis.config(configOption); + apis.setWeb3(web3Setter); + return apis; +}; diff --git a/packages/vvisp/template/script.mustache b/packages/vvisp/template/script.mustache index 02243c0..f8cff5c 100644 --- a/packages/vvisp/template/script.mustache +++ b/packages/vvisp/template/script.mustache @@ -1,9 +1,10 @@ const path = require('path'); -const { getWeb3, getPrivateKey, sendTx } = require('@haechi-labs/vvisp-utils'); -const web3 = getWeb3(); +const { Config, web3Store, sendTx } = require('@haechi-labs/vvisp-utils'); +const config = Config.get(); +const web3 = web3Store.get(); const fs = require('fs'); -const privateKey = getPrivateKey(process.env.MNEMONIC, process.env.PRIV_INDEX); +const privateKey = config.from; const abi = fs.readFileSync(path.join(__dirname, '../abi/', '{{name}}.json'), {'encoding': 'utf8'}); diff --git a/packages/vvisp/test/bin/error.test.js b/packages/vvisp/test/bin/error.test.js index dfdc739..2be5709 100644 --- a/packages/vvisp/test/bin/error.test.js +++ b/packages/vvisp/test/bin/error.test.js @@ -4,213 +4,28 @@ chai.use(require('chai-as-promised')).should(); const path = require('path'); const fs = require('fs-extra'); -const _ = require('lodash'); -const dotenv = require('dotenv').config; -const { checkConfigExist, checkEnv, constants } = require('../../bin/error'); - -const { NETWORKS, SERVICE_PATH, ENV_PATH } = constants; +const { checkServiceFileExist } = require('../../bin/error'); +const { SERVICE_PATH, SERVICE_FILE } = require('../../config/Constant'); describe('# error test', function() { this.timeout(50000); - before(function() { - delete process.env.NETWORK; - delete process.env.URL; - delete process.env.PORT; - delete process.env.INFURA_API_KEY; - delete process.env.MNEMONIC; - delete process.env.PRIV_INDEX; - delete process.env.GAS_PRICE; - delete process.env.GAS_LIMIT; - delete process.env.SOLC_VERSION; - delete process.env.SOLC_OPTIMIZATION; - }); - - after(function() { - delete process.env.NETWORK; - delete process.env.URL; - delete process.env.PORT; - delete process.env.INFURA_API_KEY; - delete process.env.MNEMONIC; - delete process.env.PRIV_INDEX; - delete process.env.GAS_PRICE; - delete process.env.GAS_LIMIT; - delete process.env.SOLC_VERSION; - delete process.env.SOLC_OPTIMIZATION; - dotenv(); - }); - - describe('checkConfigExist()', function() { + describe('checkServiceFileExist()', function() { beforeEach(function() { fs.removeSync(path.join(SERVICE_PATH)); }); - it('should throw error when service.vvisp.json file does not exist', function() { - expect(() => checkConfigExist()).to.throw( - 'service.vvisp.json file does not exist' + it(`should throw error when ${SERVICE_FILE} file does not exist`, function() { + expect(() => checkServiceFileExist()).to.throw( + `${SERVICE_FILE} file does not exist` ); }); - it('should not throw error when service.vvisp.json exists', function() { + it(`should not throw error when ${SERVICE_FILE} exists`, function() { fs.writeJsonSync(SERVICE_PATH, { serviceName: 'test' }); - expect(() => checkConfigExist()).to.not.throw(); + expect(() => checkServiceFileExist()).to.not.throw(); fs.removeSync(path.join(SERVICE_PATH)); }); }); - - describe('checkEnv()', function() { - const tmpENV = path.join('./', 'tmp.env'); - - before(function() { - fs.moveSync(ENV_PATH, tmpENV, { overwrite: true }); - }); - - after(function() { - fs.moveSync(tmpENV, ENV_PATH, { overwrite: true }); - }); - - it('should throw error when service.vvisp.json file does not exist', function() { - expect(() => checkEnv()).to.throw('.env file does not exist'); - }); - - describe('when .env exists', function() { - before(function() { - fs.copyFileSync(tmpENV, ENV_PATH); - }); - - beforeEach(function() { - setMockEnv(); - }); - - describe('NETWORK', function() { - it('should throw error when NETWORK is not set', function() { - delete process.env.NETWORK; - expect(() => checkEnv()).to.throw( - 'You should set a NETWORK in .env file' - ); - }); - - it('should throw error when wrong NETWORK is set', function() { - process.env.NETWORK = 'wrong'; - expect(() => checkEnv()).to.throw( - `You should choose a NETWORK from [${_.values(NETWORKS)}]` - ); - }); - - it('should throw error when PORT is not set in local NETWORK', function() { - process.env.NETWORK = NETWORKS.local; - delete process.env.PORT; - expect(() => checkEnv()).to.throw( - `You should set PORT in .env file when you choose 'local'` - ); - }); - - it('should throw error when URL is not set in custom NETWORK', function() { - process.env.NETWORK = NETWORKS.custom; - delete process.env.URL; - expect(() => checkEnv()).to.throw( - `You should set URL in .env file when you choose 'custom'` - ); - }); - - describe('should throw error when INFURA_API_KEY is not set in other NETWORK', function() { - beforeEach(function() { - delete process.env.INFURA_API_KEY; - }); - - it(NETWORKS.kovan, function() { - process.env.NETWORK = NETWORKS.kovan; - expect(() => checkEnv()).to.throw( - 'You should set INFURA_API_KEY in .env file' - ); - }); - - it(NETWORKS.ropsten, function() { - process.env.NETWORK = NETWORKS.ropsten; - expect(() => checkEnv()).to.throw( - 'You should set INFURA_API_KEY in .env file' - ); - }); - - it(NETWORKS.rinkeby, function() { - process.env.NETWORK = NETWORKS.rinkeby; - expect(() => checkEnv()).to.throw( - 'You should set INFURA_API_KEY in .env file' - ); - }); - - it(NETWORKS.mainnet, function() { - process.env.NETWORK = NETWORKS.mainnet; - expect(() => checkEnv()).to.throw( - 'You should set INFURA_API_KEY in .env file' - ); - }); - }); - - it('should not throw error with correct setting in local NETWORK', function() { - process.env.NETWORK = NETWORKS.local; - process.env.PORT = '8545'; - expect(() => checkEnv()).to.not.throw(); - }); - - it('should not throw error with correct setting in custom NETWORK', function() { - process.env.NETWORK = NETWORKS.custom; - process.env.URL = 'sample.link.io:1212'; - expect(() => checkEnv()).to.not.throw(); - }); - - describe('should not throw error with correct setting in other NETWORK', function() { - it(NETWORKS.kovan, function() { - process.env.NETWORK = NETWORKS.kovan; - expect(() => checkEnv()).to.not.throw(); - }); - - it(NETWORKS.ropsten, function() { - process.env.NETWORK = NETWORKS.ropsten; - expect(() => checkEnv()).to.not.throw(); - }); - - it(NETWORKS.rinkeby, function() { - process.env.NETWORK = NETWORKS.rinkeby; - expect(() => checkEnv()).to.not.throw(); - }); - - it(NETWORKS.mainnet, function() { - process.env.NETWORK = NETWORKS.mainnet; - expect(() => checkEnv()).to.not.throw(); - }); - }); - }); - - describe('MNEMONIC', function() { - const mnemonic = process.env.MNEMONIC; - - after(function() { - process.env.MNEMONIC = mnemonic; - }); - - it('should throw error when MNEMONIC is not set', function() { - delete process.env.MNEMONIC; - expect(() => checkEnv()).to.throw( - 'You should set MNEMONIC or PRIVATE_KEY in .env file' - ); - }); - }); - }); - }); }); - -function setMockEnv() { - process.env.NETWORK = NETWORKS.local; - process.env.URL = 'sample/link'; - process.env.PORT = '8545'; - process.env.INFURA_API_KEY = 'samplekey'; - process.env.MNEMONIC = - 'piano garage flag neglect spare title drill basic strong aware enforce fury'; - process.env.PRIV_INDEX = '0'; - process.env.GAS_PRICE = '20000000000'; - process.env.GAS_LIMIT = '6000000'; - process.env.SOLC_VERSION = '0.4.24'; - process.env.SOLC_OPTIMIZATION = 'true'; -} diff --git a/packages/vvisp/test/ci.sh b/packages/vvisp/test/ci.sh index 20b71cb..860ff3d 100755 --- a/packages/vvisp/test/ci.sh +++ b/packages/vvisp/test/ci.sh @@ -2,8 +2,8 @@ set -o errexit -cp test/dummy/test.env . -mv test.env .env +cp test/dummy/sample.vvisp-config.js . +mv sample.vvisp-config.js vvisp-config.js nyc --reporter=html --reporter=text mocha $(find ./test -name '*.test.js') --recursive nyc report --reporter=text-lcov | coveralls diff --git a/packages/vvisp/test/dummy/sample.vvisp-config.js b/packages/vvisp/test/dummy/sample.vvisp-config.js new file mode 100644 index 0000000..96c0db3 --- /dev/null +++ b/packages/vvisp/test/dummy/sample.vvisp-config.js @@ -0,0 +1,34 @@ +module.exports = { + networks: { + development: { + host: 'localhost', + port: 8545, + network_id: '*' // eslint-disable-line camelcase + }, + coverage: { + host: 'localhost', + port: 8545, + network_id: '*', // eslint-disable-line camelcase + gasLimit: 123123, + gasPrice: 10000000000 + } + }, + compilers: { + solc: { + version: '0.4.25', // Fetch exact version from solc-bin + settings: { + // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: false, + runs: 200 + }, + evmVersion: 'byzantium' + } + } + }, + from: { + mnemonic: + 'piano garage flag neglect spare title drill basic strong aware enforce fury', + index: 0 + } +}; diff --git a/packages/vvisp/test/dummy/testContractApis/back/index.js b/packages/vvisp/test/dummy/testContractApis/back/index.js index 8294dc0..34cf399 100644 --- a/packages/vvisp/test/dummy/testContractApis/back/index.js +++ b/packages/vvisp/test/dummy/testContractApis/back/index.js @@ -1,8 +1,22 @@ const fs = require('fs'); const path = require('path'); +const { Config, web3Store } = require('@haechi-labs/vvisp-utils'); const files = fs.readdirSync(path.join(__dirname, 'js')); -const apis = {}; +const apis = { + config: configObj => { + const config = new Config(); + config.merge(configObj); + Config.setStore(config); + }, + setWeb3: web3Setter => { + if (web3Setter && typeof web3Setter === 'string') { + web3Store.setWithURL(web3Setter); + } else if (web3Setter) { + web3Store.setWithProvider(web3Setter); + } + } +}; for (let i = 0; i < files.length; i++) { if (files[i].slice(-3) === '.js') { if (files[i] === 'index.js') { @@ -12,4 +26,8 @@ for (let i = 0; i < files.length; i++) { } } -module.exports = apis; +module.exports = function(config, web3Setter) { + apis.config(config); + apis.setWeb3(web3Setter); + return apis; +}; diff --git a/packages/vvisp/test/dummy/testContractApis/back/js/HaechiV1.js b/packages/vvisp/test/dummy/testContractApis/back/js/HaechiV1.js index 3fe5620..3816182 100644 --- a/packages/vvisp/test/dummy/testContractApis/back/js/HaechiV1.js +++ b/packages/vvisp/test/dummy/testContractApis/back/js/HaechiV1.js @@ -1,9 +1,10 @@ const path = require('path'); -const { getWeb3, getPrivateKey, sendTx } = require('@haechi-labs/vvisp-utils'); -const web3 = getWeb3(); +const { Config, web3Store, sendTx } = require('@haechi-labs/vvisp-utils'); +const config = Config.get(); +const web3 = web3Store.get(); const fs = require('fs'); -const privateKey = getPrivateKey(process.env.MNEMONIC, process.env.PRIV_INDEX); +const privateKey = config.from; const abi = fs.readFileSync(path.join(__dirname, '../abi/', 'HaechiV1.json'), { encoding: 'utf8' diff --git a/packages/vvisp/test/scripts/abi-to-script/index.test.js b/packages/vvisp/test/scripts/abi-to-script/index.test.js index 0b15572..f5069f2 100644 --- a/packages/vvisp/test/scripts/abi-to-script/index.test.js +++ b/packages/vvisp/test/scripts/abi-to-script/index.test.js @@ -1,20 +1,20 @@ const chai = require('chai'); const assert = chai.assert; chai.use(require('chai-as-promised')).should(); -require('dotenv').config(); const path = require('path'); const fs = require('fs-extra'); const abiToScript = require('../../../scripts/abi-to-script'); +const _ = require('lodash'); const { compileAndDeploy, + Config, forIn, - getWeb3, - getPrivateKey, + web3Store, privateKeyToAddress } = require('@haechi-labs/vvisp-utils'); -const web3 = getWeb3(); +const { TEST } = require('../../../config/Constant'); const CONTRACT1 = 'Attachment'; const CONTRACT2 = 'Token_V0'; @@ -23,17 +23,28 @@ const CONTRACT_PATH2 = path.join(`./contracts/test/${CONTRACT2}.sol`); const FILES = [CONTRACT_PATH1, CONTRACT_PATH2]; const ROOT = path.join('./contractApis'); -const PRIV_KEY = getPrivateKey(process.env.MNEMONIC); +const config = Config.get(); + +const PRIV_KEY = config.from; const SENDER = privateKeyToAddress(PRIV_KEY); describe('# abi-to-script process test', function() { this.timeout(50000); + let web3; + before(function() { + web3Store.setWithURL(TEST.URL); + web3 = web3Store.get(); + Config.delete(); fs.removeSync(ROOT); }); + after(function() { fs.removeSync(ROOT); + Config.delete(); + web3Store.delete(); }); + describe('# back version', function() { describe('# process test', function() { before(function() { @@ -42,9 +53,11 @@ describe('# abi-to-script process test', function() { this.abi = path.join(this.root, 'abi'); this.js = path.join(this.root, 'js'); }); + it('should be fulfilled', async function() { await abiToScript(this.files, { silent: true }).should.be.fulfilled; }); + it('should make all directories', function() { fs.lstatSync(ROOT) .isDirectory() @@ -59,11 +72,13 @@ describe('# abi-to-script process test', function() { .isDirectory() .should.be.equal(true); }); + it('should make index files', function() { fs.lstatSync(path.join(this.root, 'index.js')) .isFile() .should.be.equal(true); }); + it('should make all abi files', function() { const files = fs.readdirSync(this.abi); files.length.should.be.equal(this.files.length); @@ -71,6 +86,7 @@ describe('# abi-to-script process test', function() { path.parse(files[i]).ext.should.be.equal('.json'); } }); + it('should make all js files', function() { const files = fs.readdirSync(this.js); files.length.should.be.equal(this.files.length); @@ -79,9 +95,19 @@ describe('# abi-to-script process test', function() { } }); }); + describe('# output test', function() { before(async function() { - this.apis = require('../../../contractApis/back'); + const configSetting = { + from: PRIV_KEY + }; + + const web3Setting = TEST.URL; + + this.apis = _.omit( + require('../../../contractApis/back')(configSetting, web3Setting), + ['config', 'setWeb3'] + ); const txCount = await web3.eth.getTransactionCount(SENDER); this.receipt1 = await compileAndDeploy(CONTRACT_PATH1, PRIV_KEY, [], { silent: true, @@ -92,11 +118,13 @@ describe('# abi-to-script process test', function() { txCount: txCount + 1 }); }); + it('should have two constructor functions', function() { forIn(this.apis, Contract => { Contract.should.be.a('function'); }); }); + it('should make api instances', function() { this.attachment = new this.apis[CONTRACT1]( this.receipt1.contractAddress @@ -107,6 +135,7 @@ describe('# abi-to-script process test', function() { .should.equal(this.receipt1.contractAddress); this.token.getAddress().should.equal(this.receipt2.contractAddress); }); + it('should change address', function() { const tmpAddress = '0x88C22c3Fe7A049e42cF4f3a5507e6820F0EceE61'; const address1 = this.attachment.getAddress(); @@ -118,18 +147,21 @@ describe('# abi-to-script process test', function() { this.attachment.at(address1); this.token.at(address2); }); + it('should call function', async function() { const txCount = await web3.eth.getTransactionCount(SENDER); await this.attachment.methods.initialize(SENDER).should.be.fulfilled; await this.token.methods.initialize(SENDER, { txCount: txCount + 1 }) .should.be.fulfilled; }); + it('should get owner', async function() { (await this.attachment.methods.owner()).should.equal(SENDER); (await this.token.methods.owner()).should.equal(SENDER); }); }); }); + describe('# front version', function() { describe('# process test', function() { before(function() { @@ -139,10 +171,12 @@ describe('# abi-to-script process test', function() { this.abi = path.join(this.root, 'abi'); this.js = path.join(this.root, 'js'); }); + it('should be fulfilled', async function() { await abiToScript(this.files, { silent: true, front: this.name }).should .be.fulfilled; }); + it('should make all directories', function() { fs.lstatSync(ROOT) .isDirectory() @@ -157,6 +191,7 @@ describe('# abi-to-script process test', function() { .isDirectory() .should.be.equal(true); }); + it('should make all abi files', function() { const files = fs.readdirSync(this.abi); files.length.should.be.equal(this.files.length); @@ -164,6 +199,7 @@ describe('# abi-to-script process test', function() { path.parse(files[i]).ext.should.be.equal('.json'); } }); + it('should make all js files', function() { const files = fs.readdirSync(this.js); files.length.should.be.equal(this.files.length + 1); // with index file @@ -177,6 +213,7 @@ describe('# abi-to-script process test', function() { } hasIndex.should.be.equal(true); }); + it('should make rollup files', function() { fs.lstatSync(path.join(this.root, 'vvisp.js')) .isFile() diff --git a/packages/vvisp/test/scripts/console.test.js b/packages/vvisp/test/scripts/console.test.js index 92650d6..e495ca4 100644 --- a/packages/vvisp/test/scripts/console.test.js +++ b/packages/vvisp/test/scripts/console.test.js @@ -13,11 +13,6 @@ const consoleTest = rewire('../../scripts/console.js'); describe('# console script test', async function() { this.timeout(50000); - before('set ganache env', function() { - process.env.MNEMONIC = - 'piano garage flag neglect spare title drill basic strong aware enforce fury'; - }); - describe('parseArgs', function() { const parseArgs = consoleTest.__get__('parseArgs'); diff --git a/packages/vvisp/test/scripts/deploy-contract.test.js b/packages/vvisp/test/scripts/deploy-contract.test.js index 833bc76..01e7b97 100644 --- a/packages/vvisp/test/scripts/deploy-contract.test.js +++ b/packages/vvisp/test/scripts/deploy-contract.test.js @@ -1,6 +1,5 @@ const deployContract = require('../../scripts/deploy-contract'); const path = require('path'); -require('dotenv').config(); const chai = require('chai'); chai.use(require('chai-as-promised')).should(); diff --git a/packages/vvisp/test/scripts/deploy-service/index.test.js b/packages/vvisp/test/scripts/deploy-service/index.test.js index 860f4a3..fdde02f 100644 --- a/packages/vvisp/test/scripts/deploy-service/index.test.js +++ b/packages/vvisp/test/scripts/deploy-service/index.test.js @@ -1,28 +1,25 @@ const chai = require('chai'); const assert = chai.assert; chai.use(require('chai-as-promised')).should(); -require('dotenv').config(); const deployService = require('../../../scripts/deploy-service/'); const compareConfigAndState = require('../../../scripts/deploy-service/preProcess/compareConfigAndState'); -const { - PENDING_STATE, - SERVICE_PATH, - STATE_PATH -} = require('../../../scripts/deploy-service/constants'); +const { PENDING_STATE } = require('../../../scripts/deploy-service/constants'); +const { SERVICE_PATH, STATE_PATH, TEST } = require('../../../config/Constant'); const { hasInitArgs } = require('../../../scripts/deploy-service/utils'); const { + Config, forIn, - getWeb3, - getPrivateKey, + web3Store, privateKeyToAddress } = require('@haechi-labs/vvisp-utils'); -const web3 = getWeb3(); const path = require('path'); const fs = require('fs-extra'); const Mitm = require('mitm'); -const SENDER = privateKeyToAddress(getPrivateKey(process.env.MNEMONIC)); +const config = Config.get(); + +const SENDER = privateKeyToAddress(config.from); const SERVICE1 = path.join('./test/dummy/service1.json'); const SERVICE2 = path.join('./test/dummy/service2.json'); const STATE1 = path.join('./test/dummy/state1.json'); @@ -59,8 +56,26 @@ const { deploy: nrDeployNum, upgrade: nrUpgradeNum } = getTxcount( N_R_STATE1 ); +let web3; + describe('# deploy-service process test', function() { this.timeout(50000); + before(function() { + web3Store.setWithURL(TEST.URL); + web3 = web3Store.get(); + web3Store.delete(); + Config.delete(); + }); + + beforeEach(function() { + web3Store.delete(); + Config.delete(); + }); + + after(function() { + web3Store.delete(); + Config.delete(); + }); describe('# whole process test', function() { afterEach(function() { diff --git a/yarn.lock b/yarn.lock index 7c03db0..c096124 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3467,11 +3467,6 @@ dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" -dotenv@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" - integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow== - drbg.js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" @@ -9911,6 +9906,19 @@ trim@0.0.1: resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= +truffle-error@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/truffle-error/-/truffle-error-0.0.4.tgz#c3ff027570c26ae1f20db78637902497857f898f" + integrity sha512-hER0TNR4alBIhUp7SNrZRRiZtM/MBx+xBdM9qXP0tC3YASFmhNAxPuOyB8JDHFRNbDx12K7nvaqmyYGsI5c8BQ== + +truffle-provider@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/truffle-provider/-/truffle-provider-0.1.4.tgz#0f445c40734a63077ddada50a6cbeff1eae57fb7" + integrity sha512-BE/3yhtarBqv5/o43UpwZ1ym2lQuF37cCpTBcHhb814T9iv5qXr8AsuwRhJSm/9x+Nu4wOvdd8TWzZeltjVdZQ== + dependencies: + truffle-error "^0.0.4" + web3 "1.0.0-beta.37" + tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"