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/package.json b/packages/vvisp-utils/package.json index fb1ee40..2197611 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", diff --git a/packages/vvisp-utils/src/Config.js b/packages/vvisp-utils/src/Config.js new file mode 100644 index 0000000..11cf9c1 --- /dev/null +++ b/packages/vvisp-utils/src/Config.js @@ -0,0 +1,250 @@ +// 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); + // TODO: file 읽지 않고 load/store 할 수 있도록 + 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..5e7336a 100644 --- a/packages/vvisp-utils/src/compile.js +++ b/packages/vvisp-utils/src/compile.js @@ -103,7 +103,7 @@ module.exports = async function(filePath, options) { } async function getSolc() { - const CompilerSupplier = require('./utils/compilerSupplier'); + const CompilerSupplier = require('./compilerSupplier'); const supplier = new CompilerSupplier({ version: process.env.SOLC_VERSION ? process.env.SOLC_VERSION 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..e5d6029 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) { 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/index.js b/packages/vvisp-utils/src/index.js index 481b121..9a0ab71 100644 --- a/packages/vvisp-utils/src/index.js +++ b/packages/vvisp-utils/src/index.js @@ -1,6 +1,9 @@ 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'); @@ -22,7 +25,10 @@ const parseLogs = require('./parseLogs'); module.exports = { compile, compileAndDeploy, + compilerSupplier, + Config, deploy, + filterPrivateKey, forIn, forInAsync, getAllFiles, diff --git a/packages/vvisp-utils/src/privateKeyToAddress.js b/packages/vvisp-utils/src/privateKeyToAddress.js index 8bedf8e..11a7142 100644 --- a/packages/vvisp-utils/src/privateKeyToAddress.js +++ b/packages/vvisp-utils/src/privateKeyToAddress.js @@ -1,5 +1,5 @@ module.exports = function(privateKey) { - const filterPrivateKey = require('./utils/filterPrivateKey'); + const filterPrivateKey = require('./filterPrivateKey'); privateKey = filterPrivateKey(privateKey); const web3 = require('./getWeb3')(); diff --git a/packages/vvisp-utils/test/Config.test.js b/packages/vvisp-utils/test/Config.test.js new file mode 100644 index 0000000..909e254 --- /dev/null +++ b/packages/vvisp-utils/test/Config.test.js @@ -0,0 +1,447 @@ +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() { + 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 = DEVELOPMENT; + expect(config.gasLimit).to.equal( + sampleConfig.networks[DEVELOPMENT].gasLimit + ); + }); + + it('should allow to receive gas also instead of gasLimit', function() { + const tmpConfig = _.cloneDeep(sampleConfig); + delete tmpConfig.networks[DEVELOPMENT].gasLimit; + const sampleGas = 123123; + tmpConfig.networks[DEVELOPMENT].gas = sampleGas; + + config.networks = tmpConfig.networks; + config.network = DEVELOPMENT; + expect(config.gasLimit).to.equal(sampleGas); + }); + }); + + describe('#gasPrice', function() { + 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 = DEVELOPMENT; + expect(config.gasPrice).to.equal( + sampleConfig.networks[DEVELOPMENT].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..ff8124c 100755 --- a/packages/vvisp-utils/test/ci.sh +++ b/packages/vvisp-utils/test/ci.sh @@ -4,6 +4,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/dummy/sample.vvisp-config.js b/packages/vvisp-utils/test/dummy/sample.vvisp-config.js new file mode 100644 index 0000000..8c19b2f --- /dev/null +++ b/packages/vvisp-utils/test/dummy/sample.vvisp-config.js @@ -0,0 +1,33 @@ +module.exports = { + networks: { + development: { + host: 'localhost', + port: 8545, + network_id: '*', // eslint-disable-line camelcase + gasLimit: 123123, + gasPrice: 10000000000 + }, + coverage: { + host: 'localhost', + port: 8545, + network_id: '*' // eslint-disable-line camelcase + } + }, + compilers: { + solc: { + version: '0.4.25', // Fetch exact version from solc-bin (default: truffle's version) + settings: { + // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: false, + runs: 200 + }, + evmVersion: 'byzantium' + } + } + }, + from: { + mnemonic: 'hello', + 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/bin/error.js b/packages/vvisp/bin/error.js index 5e113a9..9681f6b 100644 --- a/packages/vvisp/bin/error.js +++ b/packages/vvisp/bin/error.js @@ -2,6 +2,8 @@ const fs = require('fs'); const path = require('path'); const _ = require('lodash'); +const { SERVICE_PATH, SERVICE_FILE } = require('../config/Constant'); + const NETWORKS = { local: 'local', mainnet: 'mainnet', @@ -11,12 +13,11 @@ const NETWORKS = { custom: 'custom' }; -const SERVICE_PATH = path.join('./', 'service.vvisp.json'); const ENV_PATH = path.join('./', '.env'); function checkConfigExist() { if (!fs.existsSync(SERVICE_PATH)) { - throw new Error('service.vvisp.json file does not exist'); + throw new Error(`${SERVICE_FILE} file does not exist`); } } @@ -66,7 +67,6 @@ module.exports = { checkEnv, constants: { NETWORKS, - SERVICE_PATH, ENV_PATH } }; 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..c866ef9 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,11 @@ "mitm": "^1.4.0", "rewire": "^4.0.1", "std-mocks": "^1.0.1" + }, + "nyc": { + "exclude": [ + "contractApis", + "test/dummy" + ] } } diff --git a/packages/vvisp/scripts/deploy-service/constants.js b/packages/vvisp/scripts/deploy-service/constants.js index ea31075..79aa98a 100644 --- a/packages/vvisp/scripts/deploy-service/constants.js +++ b/packages/vvisp/scripts/deploy-service/constants.js @@ -4,8 +4,6 @@ module.exports = (function() { const web3 = getWeb3(); 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 diff --git a/packages/vvisp/scripts/deploy-service/index.js b/packages/vvisp/scripts/deploy-service/index.js index 7938929..dea2955 100644 --- a/packages/vvisp/scripts/deploy-service/index.js +++ b/packages/vvisp/scripts/deploy-service/index.js @@ -7,6 +7,7 @@ module.exports = async function(options) { const { writeState } = require('./utils'); const DeployState = require('./DeployState'); const preProcess = require('./preProcess'); + const { PROJECT_NAME, SERVICE_FILE } = require('../../config/Constant'); const { deployBusinesses, deployNonUpgradeables, @@ -116,7 +117,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 +139,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..81104ee 100644 --- a/packages/vvisp/scripts/deploy-service/preProcess/index.js +++ b/packages/vvisp/scripts/deploy-service/preProcess/index.js @@ -1,9 +1,11 @@ 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'); let stateClone = deployState.getState(); + const config = fs.readJsonSync(SERVICE_PATH); let processState; 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/test/bin/error.test.js b/packages/vvisp/test/bin/error.test.js index dfdc739..be4454a 100644 --- a/packages/vvisp/test/bin/error.test.js +++ b/packages/vvisp/test/bin/error.test.js @@ -8,8 +8,9 @@ const _ = require('lodash'); const dotenv = require('dotenv').config; const { checkConfigExist, checkEnv, constants } = require('../../bin/error'); +const { SERVICE_PATH, SERVICE_FILE } = require('../../config/Constant'); -const { NETWORKS, SERVICE_PATH, ENV_PATH } = constants; +const { NETWORKS, ENV_PATH } = constants; describe('# error test', function() { this.timeout(50000); @@ -46,13 +47,13 @@ describe('# error test', function() { fs.removeSync(path.join(SERVICE_PATH)); }); - it('should throw error when service.vvisp.json file does not exist', function() { + it(`should throw error when ${SERVICE_FILE} file does not exist`, function() { expect(() => checkConfigExist()).to.throw( - 'service.vvisp.json file does not exist' + `${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(); fs.removeSync(path.join(SERVICE_PATH)); @@ -70,7 +71,7 @@ describe('# error test', function() { fs.moveSync(tmpENV, ENV_PATH, { overwrite: true }); }); - it('should throw error when service.vvisp.json file does not exist', function() { + it(`should throw error when ${SERVICE_FILE} file does not exist`, function() { expect(() => checkEnv()).to.throw('.env file does not exist'); }); diff --git a/packages/vvisp/test/ci.sh b/packages/vvisp/test/ci.sh index 20b71cb..ff8124c 100755 --- a/packages/vvisp/test/ci.sh +++ b/packages/vvisp/test/ci.sh @@ -4,6 +4,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..8c19b2f --- /dev/null +++ b/packages/vvisp/test/dummy/sample.vvisp-config.js @@ -0,0 +1,33 @@ +module.exports = { + networks: { + development: { + host: 'localhost', + port: 8545, + network_id: '*', // eslint-disable-line camelcase + gasLimit: 123123, + gasPrice: 10000000000 + }, + coverage: { + host: 'localhost', + port: 8545, + network_id: '*' // eslint-disable-line camelcase + } + }, + compilers: { + solc: { + version: '0.4.25', // Fetch exact version from solc-bin (default: truffle's version) + settings: { + // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: false, + runs: 200 + }, + evmVersion: 'byzantium' + } + } + }, + from: { + mnemonic: 'hello', + index: 0 + } +}; diff --git a/packages/vvisp/test/scripts/deploy-service/index.test.js b/packages/vvisp/test/scripts/deploy-service/index.test.js index 860f4a3..e1e897c 100644 --- a/packages/vvisp/test/scripts/deploy-service/index.test.js +++ b/packages/vvisp/test/scripts/deploy-service/index.test.js @@ -5,11 +5,8 @@ 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 } = require('../../../config/Constant'); const { hasInitArgs } = require('../../../scripts/deploy-service/utils'); const { forIn,