From 37fa637b16bfb47557ed0c0df25b69422abd8af1 Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 29 May 2020 13:21:13 +0300 Subject: [PATCH 01/35] cli command shape - unit tests --- cli-commands/commands/shape/index.js | 7 +-- package.json | 4 +- tests/cli-commands/shape-tests.js | 84 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 tests/cli-commands/shape-tests.js diff --git a/cli-commands/commands/shape/index.js b/cli-commands/commands/shape/index.js index d8c3df6..6f004f9 100644 --- a/cli-commands/commands/shape/index.js +++ b/cli-commands/commands/shape/index.js @@ -19,11 +19,10 @@ class ShapeCommand extends Command { if (!optionsResults.framework) { commandMessages.InvalidShapeName(args.framework); + } else { + await git.clone(optionsResults.framework); + commandMessages.SuccessfulShaping(); } - - await git.clone(optionsResults.framework); - - commandMessages.SuccessfulShaping(); } catch (error) { commandMessages.UnsuccessfulShaping(error); } diff --git a/package.json b/package.json index b924d01..329d5b8 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "test": "bash ./tests/testing-contracts/compile.sh && nyc --check-coverage mocha './tests/*.js'", "test-dev": "bash ./tests/testing-contracts/compile.sh && mocha './tests/*.js'", + "test-cli": "mocha './tests/cli-commands/*.js'", "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" }, "author": "Lyubomir Kiprov (Limechain)", @@ -42,6 +43,7 @@ "eoslime": "./cli.js" }, "devDependencies": { - "nyc": "14.1.1" + "nyc": "14.1.1", + "sinon": "9.0.2" } } \ No newline at end of file diff --git a/tests/cli-commands/shape-tests.js b/tests/cli-commands/shape-tests.js new file mode 100644 index 0000000..3408935 --- /dev/null +++ b/tests/cli-commands/shape-tests.js @@ -0,0 +1,84 @@ +const sinon = require('sinon'); +const assert = require('assert'); + +const Git = require('simple-git/src/git'); +const Command = require('../../cli-commands/commands/command'); +const ShapeCommand = require('../../cli-commands/commands/shape/index'); +const GroupCommand = require('../../cli-commands/commands/group-command'); +const definition = require('../../cli-commands/commands/shape/definition'); +const FrameworkOption = require('../../cli-commands/commands/shape/options/framework-option'); + +describe('ShapeCommand', function () { + const FRAMEWORK_REACT = "react"; + const FRAMEWORK_ANGULAR = "angular"; + + let shapeCommand; + let cloneSpy; + let processSpy; + let processOptionsSpy; + + beforeEach(async () => { + shapeCommand = new ShapeCommand(); + processSpy = sinon.spy(FrameworkOption, "process"); + processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); + }); + + afterEach(async () => { + sinon.restore(); + }); + + function prepareArgs (framework = FRAMEWORK_REACT) { + return { _: [ 'shape' ], framework: framework, '$0': 'eoslime' }; + } + + it('Should initialize command propertly', async () => { + assert(shapeCommand instanceof Command); + assert(shapeCommand.template == definition.template); + assert(shapeCommand.description = definition.description); + assert(shapeCommand.options == definition.options); + }); + + it('Should not configure any subcommands', async () => { + assert(!(shapeCommand instanceof GroupCommand)); + assert(shapeCommand.subcommands.length == 0); + }); + + it('Should shape a dApp with default React front-end', async () => { + sinon.stub(Git.prototype, 'clone').callsFake(function (command, callback) { + callback.call(); + }); + + assert(await shapeCommand.execute(prepareArgs())); + + sinon.assert.calledWith(processOptionsSpy, prepareArgs()); + sinon.assert.calledWith(processSpy, FRAMEWORK_REACT); + }); + + it('Should shape a dApp with React front-end', async () => { + sinon.stub(Git.prototype, 'clone').callsFake(function (command, callback) { + callback.call(); + }); + + assert(await shapeCommand.execute(prepareArgs())); + + sinon.assert.calledWith(processOptionsSpy, prepareArgs()); + sinon.assert.calledWith(processSpy, FRAMEWORK_REACT); + }); + + it('Should throw when an unknown front-end framework is specified', async () => { + cloneSpy = sinon.spy(Git.prototype, 'clone'); + + assert(await shapeCommand.execute(prepareArgs(FRAMEWORK_ANGULAR))); + + sinon.assert.calledWith(processOptionsSpy, prepareArgs(FRAMEWORK_ANGULAR)); + sinon.assert.calledWith(processSpy, FRAMEWORK_ANGULAR); + sinon.assert.notCalled(cloneSpy); + }); + + it('Should throw when repository cloning fails', async () => { + sinon.stub(Git.prototype, 'clone').throws('Test Failure'); + + assert(await shapeCommand.execute(prepareArgs())); + }); + +}); \ No newline at end of file From 4048a5db30fb6c79be81011d239a3394aa546cbf Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 29 May 2020 20:56:06 +0300 Subject: [PATCH 02/35] cli command init - tests --- tests/cli-commands/init-tests.js | 107 ++++++++++++++++++++++++++++++ tests/cli-commands/shape-tests.js | 22 ++---- 2 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 tests/cli-commands/init-tests.js diff --git a/tests/cli-commands/init-tests.js b/tests/cli-commands/init-tests.js new file mode 100644 index 0000000..14a098b --- /dev/null +++ b/tests/cli-commands/init-tests.js @@ -0,0 +1,107 @@ +const fs = require('fs-extra'); +const sinon = require('sinon'); +const assert = require('assert'); + +const Command = require('../../cli-commands/commands/command'); +const InitCommand = require('../../cli-commands/commands/init/index'); +const GroupCommand = require('../../cli-commands/commands/group-command'); +const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); +const definition = require('../../cli-commands/commands/init/definition'); +const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); +const directories = require('../../cli-commands/commands/init/specific/directories.json'); +const WithExampleOption = require('../../cli-commands/commands/init/options/with-example/with-example-option'); + +describe('InitCommand', function () { + const TEST_DIR = "./cli-commands-test"; + const WITH_EXAMPLE = { 'with-example': true }; + const WITHOUT_EXAMPLE = { 'with-example': false }; + + let initialDir; + let initCommand; + let processSpy; + let processOptionsSpy; + let copyAllFilesFromDirToSpy; + + before(async () => { + initialDir = process.cwd(); + fs.mkdirSync(TEST_DIR); + process.chdir(TEST_DIR); + }); + + beforeEach(async () => { + initCommand = new InitCommand(); + sinon.stub(AsyncSoftExec.prototype, "exec"); + processSpy = sinon.spy(WithExampleOption, "process"); + processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); + copyAllFilesFromDirToSpy = sinon.spy(fileSystemUtil, "copyAllFilesFromDirTo"); + }); + + afterEach(async () => { + sinon.restore(); + cleanDirectories(); + }); + + after(async () => { + process.chdir(initialDir); + fs.removeSync(TEST_DIR); + }); + + function checkDirectories() { + assert(fs.existsSync(directories.DEPLOYMENT)); + assert(fs.existsSync(directories.CONTRACTS)); + assert(fs.existsSync(directories.TESTS)); + assert(fs.existsSync(directories.PACKAGE_JSON)); + } + + function checkExampleFiles () { + assert(fs.existsSync(`${directories.DEPLOYMENT}/example-deploy.js`)); + assert(fs.existsSync(`${directories.CONTRACTS}/example`)); + assert(fs.existsSync(`${directories.TESTS}/example-tests.js`)); + } + + function cleanDirectories () { + fs.removeSync(directories.DEPLOYMENT); + fs.removeSync(directories.CONTRACTS); + fs.removeSync(directories.TESTS); + fs.removeSync(directories.PACKAGE_JSON); + } + + it('Should initialize command properly', async () => { + assert(initCommand instanceof Command); + assert(initCommand.template == definition.template); + assert(initCommand.description = definition.description); + assert(initCommand.options == definition.options); + }); + + it('Should not have any subcommands', async () => { + assert(!(initCommand instanceof GroupCommand)); + assert(initCommand.subcommands.length == 0); + }); + + it('Should init project structure', async () => { + assert(await initCommand.execute({})); + + sinon.assert.calledWith(processOptionsSpy, {}); + sinon.assert.notCalled(processSpy); + checkDirectories(); + }); + + it('Should init project structure [with-example = false]', async () => { + assert(await initCommand.execute(WITHOUT_EXAMPLE)); + + sinon.assert.calledWith(processOptionsSpy, WITHOUT_EXAMPLE); + sinon.assert.calledWith(processSpy, false); + sinon.assert.notCalled(copyAllFilesFromDirToSpy); + checkDirectories(); + }); + + it('Should init project structure and provide an example files', async () => { + assert(await initCommand.execute(WITH_EXAMPLE)); + + sinon.assert.calledWith(processOptionsSpy, WITH_EXAMPLE); + sinon.assert.calledWith(processSpy, true); + checkDirectories(); + checkExampleFiles(); + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/shape-tests.js b/tests/cli-commands/shape-tests.js index 3408935..cbbe213 100644 --- a/tests/cli-commands/shape-tests.js +++ b/tests/cli-commands/shape-tests.js @@ -6,6 +6,7 @@ const Command = require('../../cli-commands/commands/command'); const ShapeCommand = require('../../cli-commands/commands/shape/index'); const GroupCommand = require('../../cli-commands/commands/group-command'); const definition = require('../../cli-commands/commands/shape/definition'); +const repositories = require('../../cli-commands/commands/shape/specific/repositories.json'); const FrameworkOption = require('../../cli-commands/commands/shape/options/framework-option'); describe('ShapeCommand', function () { @@ -28,32 +29,21 @@ describe('ShapeCommand', function () { }); function prepareArgs (framework = FRAMEWORK_REACT) { - return { _: [ 'shape' ], framework: framework, '$0': 'eoslime' }; + return { framework: framework }; } - it('Should initialize command propertly', async () => { + it('Should initialize command properly', async () => { assert(shapeCommand instanceof Command); assert(shapeCommand.template == definition.template); assert(shapeCommand.description = definition.description); assert(shapeCommand.options == definition.options); }); - it('Should not configure any subcommands', async () => { + it('Should not have any subcommands', async () => { assert(!(shapeCommand instanceof GroupCommand)); assert(shapeCommand.subcommands.length == 0); }); - it('Should shape a dApp with default React front-end', async () => { - sinon.stub(Git.prototype, 'clone').callsFake(function (command, callback) { - callback.call(); - }); - - assert(await shapeCommand.execute(prepareArgs())); - - sinon.assert.calledWith(processOptionsSpy, prepareArgs()); - sinon.assert.calledWith(processSpy, FRAMEWORK_REACT); - }); - it('Should shape a dApp with React front-end', async () => { sinon.stub(Git.prototype, 'clone').callsFake(function (command, callback) { callback.call(); @@ -63,6 +53,7 @@ describe('ShapeCommand', function () { sinon.assert.calledWith(processOptionsSpy, prepareArgs()); sinon.assert.calledWith(processSpy, FRAMEWORK_REACT); + assert(processSpy.returnValues[0] == repositories[FRAMEWORK_REACT]); }); it('Should throw when an unknown front-end framework is specified', async () => { @@ -72,11 +63,12 @@ describe('ShapeCommand', function () { sinon.assert.calledWith(processOptionsSpy, prepareArgs(FRAMEWORK_ANGULAR)); sinon.assert.calledWith(processSpy, FRAMEWORK_ANGULAR); + assert(processSpy.returnValues[0] == repositories[FRAMEWORK_ANGULAR]); sinon.assert.notCalled(cloneSpy); }); it('Should throw when repository cloning fails', async () => { - sinon.stub(Git.prototype, 'clone').throws('Test Failure'); + sinon.stub(Git.prototype, 'clone').throws('Test: Cloning Repo Failure'); assert(await shapeCommand.execute(prepareArgs())); }); From f496cf7b5a496d6e843320373aec28e03fdf7a71 Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Tue, 2 Jun 2020 16:32:56 +0300 Subject: [PATCH 03/35] cli command compile - tests --- .../commands/compile/options/path-option.js | 3 +- tests/cli-commands/compile-tests.js | 129 ++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 tests/cli-commands/compile-tests.js diff --git a/cli-commands/commands/compile/options/path-option.js b/cli-commands/commands/compile/options/path-option.js index 935a823..07afd15 100644 --- a/cli-commands/commands/compile/options/path-option.js +++ b/cli-commands/commands/compile/options/path-option.js @@ -1,4 +1,5 @@ const fileSystemUtil = require('../../../helpers/file-system-util'); +const path = require('path'); const Option = require('../../option'); @@ -28,7 +29,7 @@ class PathOption extends Option { }); } - return optionValue.endsWith('.cpp') ? [`${__dirname}/${optionValue}`] : []; + return optionValue.endsWith('.cpp') ? [ { fullPath: optionValue, fileName: path.basename(optionValue, '.cpp') } ] : []; } } diff --git a/tests/cli-commands/compile-tests.js b/tests/cli-commands/compile-tests.js new file mode 100644 index 0000000..301bd73 --- /dev/null +++ b/tests/cli-commands/compile-tests.js @@ -0,0 +1,129 @@ +const fs = require('fs-extra'); +const sinon = require('sinon'); +const assert = require('assert'); + +const Command = require('../../cli-commands/commands/command'); +const CompileCommand = require('../../cli-commands/commands/compile/index'); +const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); +const fileSysUtils = require('../../cli-commands/helpers/file-system-util'); +const definition = require('../../cli-commands/commands/compile/definition'); +const PathOption = require('../../cli-commands/commands/compile/options/path-option'); +const directories = require('../../cli-commands/commands/compile/specific/directories.json'); + +describe('CompileCommand', function () { + const TEST_DIR = './cli-commands-test'; + const DEFAULT_PATH = './contracts'; + const INVALID_PATH = './unknown_folder'; + const INVALID_FILE = 'file.txt'; + + let initialDir; + let compileCommand; + let pathOptionSpy; + let fsCreateDirSpy; + + before(async () => { + initialDir = process.cwd(); + fs.mkdirSync(TEST_DIR); + process.chdir(TEST_DIR); + }); + + beforeEach(async () => { + compileCommand = new CompileCommand(); + sinon.stub(AsyncSoftExec.prototype, "exec"); + pathOptionSpy = sinon.spy(PathOption, "process"); + fsCreateDirSpy = sinon.spy(fileSysUtils, "createDir"); + }); + + afterEach(async () => { + sinon.restore(); + cleanDirectories(); + }); + + after(async () => { + process.chdir(initialDir); + fs.removeSync(TEST_DIR); + }); + + function cleanDirectories () { + fs.removeSync('./contracts'); + fs.removeSync('./compiled'); + } + + function createContractsFolder () { + fs.mkdirSync('./contracts'); + } + + function preloadContracts() { + fs.copyFileSync('../tests/testing-contracts/eosio.token.cpp', './contracts/eosio.token.cpp'); + fs.copyFileSync('../tests/testing-contracts/eosio.token.hpp', './contracts/eosio.token.hpp'); + } + + it('Should initialize command properly', async () => { + assert(compileCommand instanceof Command); + assert(compileCommand.template == definition.template); + assert(compileCommand.description = definition.description); + assert(compileCommand.options == definition.options); + assert(compileCommand.subcommands.length == 0); + }); + + it('Should compile when valid contracts folder is specified', async () => { + createContractsFolder(); + preloadContracts(); + + assert(await compileCommand.execute({ path: DEFAULT_PATH })); + + sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); + sinon.assert.calledOnceWithExactly(fsCreateDirSpy, directories.COMPILED); + + let result = await pathOptionSpy.returnValues[0]; + assert(result[0].fullPath == `${DEFAULT_PATH}/eosio.token.cpp`); + assert(result[0].fileName == 'eosio.token'); + }); + + it('Should throw when invalid contracts folder is specified', async () => { + assert(await compileCommand.execute({ path: INVALID_PATH })); + + sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); + sinon.assert.notCalled(fsCreateDirSpy); + }); + + it('Should throw when specified contracts folder is empty', async () => { + createContractsFolder(); + + assert(await compileCommand.execute({ path: DEFAULT_PATH })); + + sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); + sinon.assert.notCalled(fsCreateDirSpy); + }); + + it('Should compile when valid contract path is specified', async () => { + createContractsFolder(); + preloadContracts(); + + assert(await compileCommand.execute({ path: `${DEFAULT_PATH}/eosio.token.cpp` })); + + sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/eosio.token.cpp`); + sinon.assert.calledOnceWithExactly(fsCreateDirSpy, directories.COMPILED); + + let result = await pathOptionSpy.returnValues[0]; + assert(result[0].fullPath == `${DEFAULT_PATH}/eosio.token.cpp`); + assert(result[0].fileName == 'eosio.token'); + }); + + it('Should throw when invalid contract path is specified', async () => { + assert(await compileCommand.execute({ path: `${INVALID_PATH}/eosio.token.cpp` })); + + sinon.assert.calledWith(pathOptionSpy, `${INVALID_PATH}/eosio.token.cpp`); + sinon.assert.notCalled(fsCreateDirSpy); + }); + + it('Should throw when invalid contract name is specified', async () => { + fs.createFileSync(INVALID_FILE); + + assert(await compileCommand.execute({ path: `./${INVALID_FILE}` })); + + sinon.assert.calledWith(pathOptionSpy, `./${INVALID_FILE}`); + sinon.assert.notCalled(fsCreateDirSpy); + }); + +}); \ No newline at end of file From 72ac6a712a06a986fb3590514f9b10dbf94d2661 Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Thu, 4 Jun 2020 12:17:22 +0300 Subject: [PATCH 04/35] cli command deploy - tests --- tests/cli-commands/deploy-tests.js | 170 +++++++++++++++++++++++++ tests/cli-commands/mocks/deployment.js | 3 + 2 files changed, 173 insertions(+) create mode 100644 tests/cli-commands/deploy-tests.js create mode 100644 tests/cli-commands/mocks/deployment.js diff --git a/tests/cli-commands/deploy-tests.js b/tests/cli-commands/deploy-tests.js new file mode 100644 index 0000000..dbf67d8 --- /dev/null +++ b/tests/cli-commands/deploy-tests.js @@ -0,0 +1,170 @@ +const fs = require('fs-extra'); +const sinon = require('sinon'); +const assert = require('assert'); +const prompts = require('prompts'); +const eoslime = require('../../index'); + +const Command = require('../../cli-commands/commands/command'); +const Account = require('../../src/account/normal-account/account'); +const DeployCommand = require('../../cli-commands/commands/deploy/index'); +const definition = require('../../cli-commands/commands/deploy/definition'); +const PathOption = require('../../cli-commands/commands/deploy/options/path-option'); +const NetworkOption = require('../../cli-commands/commands/deploy/options/network-option'); +const DeployerOption = require('../../cli-commands/commands/deploy/options/deployer-option'); + +describe('DeployCommand', function () { + const TEST_DIR = './cli-commands-test'; + const DEFAULT_PATH = './deployment'; + const INVALID_PATH = './unknown_folder'; + const DEFAULT_NETWORK = 'local'; + const INVALID_NETWORK = 'invalid_network'; + const CUSTOM_NETWORK = { url: "https://test.custom.net", chainId: "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f" }; + const DEPLOYER_NAME = 'alice'; + const DEPLOYER_PERMISSION = 'active'; + const DEPLOYER_PRIVATE_KEY = '5JymWHYohPMXTckmmFkmiRZZDQTWN91eDFSnEvjYAbXTd6oFtie'; + const INVALID_PRIVATE_KEY = 'invalid_private_key'; + + let initialDir; + let deployCommand; + let eoslimeSpy; + let pathOptionSpy; + let networkOptionSpy; + let deployerOptionSpy; + + before(async () => { + initialDir = process.cwd(); + fs.mkdirSync(TEST_DIR); + process.chdir(TEST_DIR); + }); + + beforeEach(async () => { + deployCommand = new DeployCommand(); + eoslimeSpy = sinon.spy(eoslime, "init"); + pathOptionSpy = sinon.spy(PathOption, "process"); + networkOptionSpy = sinon.spy(NetworkOption, "process"); + deployerOptionSpy = sinon.spy(DeployerOption, "process"); + + preloadDeploymentScript(); + }); + + afterEach(async () => { + sinon.restore(); + fs.removeSync('./contracts'); + fs.removeSync('./deployment'); + }); + + after(async () => { + process.chdir(initialDir); + fs.removeSync(TEST_DIR); + }); + + function preloadDeploymentScript () { + fs.mkdirSync('./deployment'); + fs.copyFileSync('../tests/cli-commands/mocks/deployment.js', './deployment/deployment.js'); + } + + it('Should initialize command properly', async () => { + assert(deployCommand instanceof Command); + assert(deployCommand.template == definition.template); + assert(deployCommand.description = definition.description); + assert(deployCommand.options == definition.options); + assert(deployCommand.subcommands.length == 0); + }); + + it('Should deploy when valid deployment folder is specified', async () => { + assert(await deployCommand.execute({ path: DEFAULT_PATH })); + + sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); + + let result = await pathOptionSpy.returnValues[0]; + assert(result[0].fileName, 'deployment.js'); + assert(typeof(result[0].deploy) == 'function'); + assert(result[0].deploy.name, 'deploy'); + }); + + it('Should throw when invalid deployment folder is specified', async () => { + assert(await deployCommand.execute({ path: INVALID_PATH })); + + sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); + }); + + it('Should not throw when deployment folder is empty', async () => { + assert(await deployCommand.execute({ path: DEFAULT_PATH })); + + sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); + }); + + it('Should deploy when valid deployment script is specified', async () => { + assert(await deployCommand.execute({ path: `${DEFAULT_PATH}/deployment.js` })); + + sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/deployment.js`); + + let result = await pathOptionSpy.returnValues[0]; + assert(result[0].fileName, 'deployment.js'); + assert(typeof(result[0].deploy) == 'function'); + assert(result[0].deploy.name, 'deploy'); + }); + + it('Should throw when invalid deployment script is specified', async () => { + fs.createFileSync('./test.txt'); + + assert(await deployCommand.execute({ path: './test.txt', network: DEFAULT_NETWORK })); + + sinon.assert.calledWith(pathOptionSpy, './test.txt'); + }); + + it('Should deploy when valid deployment network is specified', async () => { + assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK })); + + sinon.assert.calledWith(networkOptionSpy, DEFAULT_NETWORK); + sinon.assert.calledWith(eoslimeSpy, DEFAULT_NETWORK); + + let result = await networkOptionSpy.returnValues[0]; + assert(result.hasOwnProperty('Account')); + assert(result.hasOwnProperty('Contract')); + assert(result.hasOwnProperty('Provider')); + assert(result.hasOwnProperty('MultiSigAccount')); + }); + + it('Should deploy when custom network url and chainId are provided', async () => { + assert(await deployCommand.execute({ path: DEFAULT_PATH, network: CUSTOM_NETWORK })); + + sinon.assert.calledWith(networkOptionSpy, CUSTOM_NETWORK); + sinon.assert.calledWith(eoslimeSpy, CUSTOM_NETWORK); + + let result = await networkOptionSpy.returnValues[0]; + assert(result.hasOwnProperty('Account')); + assert(result.hasOwnProperty('Contract')); + assert(result.hasOwnProperty('Provider')); + assert(result.hasOwnProperty('MultiSigAccount')); + }); + + it('Should throw when invalid network is specified', async () => { + assert(await deployCommand.execute({ path: DEFAULT_PATH, network: INVALID_NETWORK })); + + sinon.assert.calledWith(networkOptionSpy, INVALID_NETWORK); + }); + + it('Should deploy when deployer is provided', async () => { + prompts.inject(`${DEPLOYER_PRIVATE_KEY}@${DEPLOYER_PERMISSION}`); + + assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, deployer: DEPLOYER_NAME })); + + sinon.assert.calledWith(deployerOptionSpy, DEPLOYER_NAME); + + let result = await deployerOptionSpy.returnValues[0]; + assert(result instanceof Account); + assert(result.name == DEPLOYER_NAME); + assert(result.privateKey == DEPLOYER_PRIVATE_KEY); + assert(result.executiveAuthority.permission == DEPLOYER_PERMISSION); + }); + + it('Should throw when invalid deployer private key is provided', async () => { + prompts.inject(`${INVALID_PRIVATE_KEY}@${DEPLOYER_PERMISSION}`); + + assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, deployer: DEPLOYER_NAME })); + + sinon.assert.calledWith(deployerOptionSpy, DEPLOYER_NAME); + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/mocks/deployment.js b/tests/cli-commands/mocks/deployment.js new file mode 100644 index 0000000..f114235 --- /dev/null +++ b/tests/cli-commands/mocks/deployment.js @@ -0,0 +1,3 @@ +let deploy = async function (eoslime, deployer) {} + +module.exports = deploy; \ No newline at end of file From 996c3aeb7857dd91c5c835026d4f361a584654f0 Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 5 Jun 2020 21:01:27 +0300 Subject: [PATCH 05/35] cli command test - tests --- tests/cli-commands/mocks/tests-mock.js | 7 ++ tests/cli-commands/test-tests.js | 168 +++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 tests/cli-commands/mocks/tests-mock.js create mode 100644 tests/cli-commands/test-tests.js diff --git a/tests/cli-commands/mocks/tests-mock.js b/tests/cli-commands/mocks/tests-mock.js new file mode 100644 index 0000000..a9dbf1e --- /dev/null +++ b/tests/cli-commands/mocks/tests-mock.js @@ -0,0 +1,7 @@ +describe("Mocked Tests", function () { + + it("Should execute mocked test", async () => { + + }); + +}); diff --git a/tests/cli-commands/test-tests.js b/tests/cli-commands/test-tests.js new file mode 100644 index 0000000..9940df7 --- /dev/null +++ b/tests/cli-commands/test-tests.js @@ -0,0 +1,168 @@ +const fs = require('fs-extra'); +const sinon = require('sinon'); +const assert = require('assert'); +const eoslime = require('../../index'); + +const Command = require('../../cli-commands/commands/command'); +const TestCommand = require('../../cli-commands/commands/test/index'); +const ProviderFactory = require('../../src/network-providers/provider-factory'); +const MochaFramework = require('../../cli-commands/commands/test/specific/test-frameworks/mocha'); +const ReportTable = require('../../cli-commands/commands/test/options/resource-usage-option/report-table'); + +const definition = require('../../cli-commands/commands/test/definition'); +const PathOption = require('../../cli-commands/commands/test/options/path-option'); +const NetworkOption = require('../../cli-commands/commands/test/options/network-option'); +const ResourceReportOption = require('../../cli-commands/commands/test/options/resource-usage-option/resource-usage-option'); + +describe('TestCommand', function () { + const TEST_DIR = './cli-commands-test'; + const DEFAULT_PATH = './tests'; + const INVALID_PATH = './unknown_folder'; + const DEFAULT_NETWORK = 'local'; + const INVALID_NETWORK = 'invalid_network'; + const CUSTOM_NETWORK = { url: "https://test.custom.net", chainId: "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f" }; + + let initialDir; + let testCommand; + let eoslimeSpy; + let pathOptionSpy; + let networkOptionSpy; + let resourceReportOptionSpy; + let mochaAddTestFilesSpy; + let mochaSetDescribeArgs; + let mochaRunTestsSpy; + let providerFactorySpy; + let reportTableSpy; + + before(async () => { + initialDir = process.cwd(); + fs.mkdirSync(TEST_DIR); + process.chdir(TEST_DIR); + }); + + beforeEach(async () => { + eoslimeSpy = sinon.spy(eoslime, "init"); + testCommand = new TestCommand(MochaFramework); + pathOptionSpy = sinon.spy(PathOption, "process"); + networkOptionSpy = sinon.spy(NetworkOption, "process"); + resourceReportOptionSpy = sinon.spy(ResourceReportOption, "process"); + mochaAddTestFilesSpy = sinon.spy(MochaFramework.prototype, "addTestFiles"); + mochaSetDescribeArgs = sinon.spy(MochaFramework.prototype, "setDescribeArgs"); + mochaRunTestsSpy = sinon.spy(MochaFramework.prototype, "runTests"); + providerFactorySpy = sinon.spy(ProviderFactory.prototype, "reset"); + reportTableSpy = sinon.spy(ReportTable.prototype, "draw"); + + preloadMockedTests(); + }); + + afterEach(async () => { + sinon.restore(); + fs.removeSync('./tests'); + }); + + after(async () => { + process.chdir(initialDir); + fs.removeSync(TEST_DIR); + }); + + function preloadMockedTests () { + fs.mkdirSync('./tests'); + fs.copyFileSync('../tests/cli-commands/mocks/tests-mock.js', './tests/tests-mock.js'); + } + + it('Should initialize command properly', async () => { + assert(testCommand instanceof Command); + assert(testCommand.template == definition.template); + assert(testCommand.description = definition.description); + assert(testCommand.options == definition.options); + assert(testCommand.subcommands.length == 0); + }); + + it('Should execute tests when valid tests folder is specified', async () => { + assert(await testCommand.execute({ path: DEFAULT_PATH })); + + sinon.assert.calledOnce(eoslimeSpy); + sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); + sinon.assert.calledWith(mochaAddTestFilesSpy, ["./tests/tests-mock.js"]); + sinon.assert.calledOnce(mochaRunTestsSpy); + }); + + it('Should throw when invalid tests folder is specified', async () => { + assert(await testCommand.execute({ path: INVALID_PATH })); + + sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); + sinon.assert.notCalled(mochaAddTestFilesSpy); + sinon.assert.notCalled(mochaRunTestsSpy); + }); + + it('Should not throw when tests folder is empty', async () => { + fs.removeSync('./tests/tests-mock.js'); + + assert(await testCommand.execute({ path: DEFAULT_PATH })); + + sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); + sinon.assert.calledWith(mochaAddTestFilesSpy, []); + sinon.assert.calledOnce(mochaRunTestsSpy); + }); + + it('Should execute tests when path to file with tests is provided', async () => { + assert(await testCommand.execute({ path: `${DEFAULT_PATH}/tests-mock.js` })); + + sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/tests-mock.js`); + sinon.assert.calledOnce(mochaAddTestFilesSpy); + sinon.assert.calledOnce(mochaRunTestsSpy); + }); + + it('Should not throw when file without tests is provided', async () => { + fs.createFileSync('./tests.txt'); + + assert(await testCommand.execute({ path: './tests.txt' })); + + sinon.assert.calledWith(pathOptionSpy, './tests.txt'); + sinon.assert.calledOnce(mochaAddTestFilesSpy); + sinon.assert.calledOnce(mochaRunTestsSpy); + }); + + it('Should execute tests when valid network is specified', async () => { + assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK })); + + sinon.assert.calledWith(networkOptionSpy, DEFAULT_NETWORK); + sinon.assert.calledOnce(providerFactorySpy); + sinon.assert.calledOnce(mochaSetDescribeArgs); + sinon.assert.calledOnce(mochaRunTestsSpy); + }); + + it('Should execute tests when custom network url and chainId are provided', async () => { + assert(await testCommand.execute({ path: DEFAULT_PATH, network: CUSTOM_NETWORK })); + + sinon.assert.calledWith(networkOptionSpy, CUSTOM_NETWORK); + sinon.assert.calledOnce(providerFactorySpy); + sinon.assert.calledOnce(mochaSetDescribeArgs); + sinon.assert.calledOnce(mochaRunTestsSpy); + }); + + it('Should throw when invalid network is specified', async () => { + assert(await testCommand.execute({ path: DEFAULT_PATH, network: INVALID_NETWORK })); + + sinon.assert.calledWith(networkOptionSpy, INVALID_NETWORK); + sinon.assert.notCalled(providerFactorySpy); + sinon.assert.notCalled(mochaRunTestsSpy); + }); + + it('Should execute tests and display resource usage report', async () => { + assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'true' })); + + sinon.assert.calledWith(resourceReportOptionSpy, 'true'); + sinon.assert.calledOnce(mochaRunTestsSpy); + // sinon.assert.calledOnce(reportTableSpy); + }); + + it('Should execute tests and display resource usage report', async () => { + assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'false' })); + + sinon.assert.calledWith(resourceReportOptionSpy, 'false'); + sinon.assert.calledOnce(mochaRunTestsSpy); + // sinon.assert.notCalled(reportTableSpy); + }); + +}); \ No newline at end of file From 0a5915f175445ca8f60a3ebb558cb873d7e2bb2f Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Mon, 8 Jun 2020 18:50:44 +0300 Subject: [PATCH 06/35] updated tests for cli command shape --- tests/cli-commands/shape-tests.js | 41 +++++++++++-------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/tests/cli-commands/shape-tests.js b/tests/cli-commands/shape-tests.js index cbbe213..5f237d9 100644 --- a/tests/cli-commands/shape-tests.js +++ b/tests/cli-commands/shape-tests.js @@ -4,73 +4,60 @@ const assert = require('assert'); const Git = require('simple-git/src/git'); const Command = require('../../cli-commands/commands/command'); const ShapeCommand = require('../../cli-commands/commands/shape/index'); -const GroupCommand = require('../../cli-commands/commands/group-command'); const definition = require('../../cli-commands/commands/shape/definition'); const repositories = require('../../cli-commands/commands/shape/specific/repositories.json'); const FrameworkOption = require('../../cli-commands/commands/shape/options/framework-option'); describe('ShapeCommand', function () { const FRAMEWORK_REACT = "react"; - const FRAMEWORK_ANGULAR = "angular"; + const NOT_SUPPORTED_FRAMEWORK = "angular"; let shapeCommand; - let cloneSpy; - let processSpy; - let processOptionsSpy; + let simpleGitSpy; + let frameworkOptionSpy; beforeEach(async () => { shapeCommand = new ShapeCommand(); - processSpy = sinon.spy(FrameworkOption, "process"); - processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); + frameworkOptionSpy = sinon.spy(FrameworkOption, "process"); }); afterEach(async () => { sinon.restore(); }); - function prepareArgs (framework = FRAMEWORK_REACT) { - return { framework: framework }; - } - it('Should initialize command properly', async () => { assert(shapeCommand instanceof Command); assert(shapeCommand.template == definition.template); assert(shapeCommand.description = definition.description); assert(shapeCommand.options == definition.options); - }); - - it('Should not have any subcommands', async () => { - assert(!(shapeCommand instanceof GroupCommand)); assert(shapeCommand.subcommands.length == 0); }); - it('Should shape a dApp with React front-end', async () => { + it('Should prepare React project', async () => { sinon.stub(Git.prototype, 'clone').callsFake(function (command, callback) { callback.call(); }); - assert(await shapeCommand.execute(prepareArgs())); + assert(await shapeCommand.execute({ framework: FRAMEWORK_REACT })); - sinon.assert.calledWith(processOptionsSpy, prepareArgs()); - sinon.assert.calledWith(processSpy, FRAMEWORK_REACT); - assert(processSpy.returnValues[0] == repositories[FRAMEWORK_REACT]); + sinon.assert.calledWith(frameworkOptionSpy, FRAMEWORK_REACT); + assert(frameworkOptionSpy.returnValues[0] == repositories[FRAMEWORK_REACT]); }); it('Should throw when an unknown front-end framework is specified', async () => { - cloneSpy = sinon.spy(Git.prototype, 'clone'); + simpleGitSpy = sinon.spy(Git.prototype, 'clone'); - assert(await shapeCommand.execute(prepareArgs(FRAMEWORK_ANGULAR))); + assert(await shapeCommand.execute({ framework: NOT_SUPPORTED_FRAMEWORK })); - sinon.assert.calledWith(processOptionsSpy, prepareArgs(FRAMEWORK_ANGULAR)); - sinon.assert.calledWith(processSpy, FRAMEWORK_ANGULAR); - assert(processSpy.returnValues[0] == repositories[FRAMEWORK_ANGULAR]); - sinon.assert.notCalled(cloneSpy); + sinon.assert.calledWith(frameworkOptionSpy, NOT_SUPPORTED_FRAMEWORK); + assert(frameworkOptionSpy.returnValues[0] == repositories[NOT_SUPPORTED_FRAMEWORK]); + sinon.assert.notCalled(simpleGitSpy); }); it('Should throw when repository cloning fails', async () => { sinon.stub(Git.prototype, 'clone').throws('Test: Cloning Repo Failure'); - assert(await shapeCommand.execute(prepareArgs())); + assert(await shapeCommand.execute({ framework: FRAMEWORK_REACT })); }); }); \ No newline at end of file From d29013baf37297c49fac5fc2cf88167641a53e5d Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Mon, 8 Jun 2020 18:53:57 +0300 Subject: [PATCH 07/35] updated tests for cli command init --- tests/cli-commands/init-tests.js | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/tests/cli-commands/init-tests.js b/tests/cli-commands/init-tests.js index 14a098b..6707c97 100644 --- a/tests/cli-commands/init-tests.js +++ b/tests/cli-commands/init-tests.js @@ -4,7 +4,6 @@ const assert = require('assert'); const Command = require('../../cli-commands/commands/command'); const InitCommand = require('../../cli-commands/commands/init/index'); -const GroupCommand = require('../../cli-commands/commands/group-command'); const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); const definition = require('../../cli-commands/commands/init/definition'); const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); @@ -18,9 +17,8 @@ describe('InitCommand', function () { let initialDir; let initCommand; - let processSpy; - let processOptionsSpy; - let copyAllFilesFromDirToSpy; + let exampleOptionSpy; + let fileSystemUtilSpy; before(async () => { initialDir = process.cwd(); @@ -31,9 +29,8 @@ describe('InitCommand', function () { beforeEach(async () => { initCommand = new InitCommand(); sinon.stub(AsyncSoftExec.prototype, "exec"); - processSpy = sinon.spy(WithExampleOption, "process"); - processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); - copyAllFilesFromDirToSpy = sinon.spy(fileSystemUtil, "copyAllFilesFromDirTo"); + exampleOptionSpy = sinon.spy(WithExampleOption, "process"); + fileSystemUtilSpy = sinon.spy(fileSystemUtil, "copyAllFilesFromDirTo"); }); afterEach(async () => { @@ -71,35 +68,28 @@ describe('InitCommand', function () { assert(initCommand.template == definition.template); assert(initCommand.description = definition.description); assert(initCommand.options == definition.options); - }); - - it('Should not have any subcommands', async () => { - assert(!(initCommand instanceof GroupCommand)); assert(initCommand.subcommands.length == 0); }); it('Should init project structure', async () => { assert(await initCommand.execute({})); - sinon.assert.calledWith(processOptionsSpy, {}); - sinon.assert.notCalled(processSpy); + sinon.assert.notCalled(exampleOptionSpy); checkDirectories(); }); it('Should init project structure [with-example = false]', async () => { assert(await initCommand.execute(WITHOUT_EXAMPLE)); - sinon.assert.calledWith(processOptionsSpy, WITHOUT_EXAMPLE); - sinon.assert.calledWith(processSpy, false); - sinon.assert.notCalled(copyAllFilesFromDirToSpy); + sinon.assert.calledWith(exampleOptionSpy, false); + sinon.assert.notCalled(fileSystemUtilSpy); checkDirectories(); }); - it('Should init project structure and provide an example files', async () => { + it('Should init project structure and provide example files', async () => { assert(await initCommand.execute(WITH_EXAMPLE)); - sinon.assert.calledWith(processOptionsSpy, WITH_EXAMPLE); - sinon.assert.calledWith(processSpy, true); + sinon.assert.calledWith(exampleOptionSpy, true); checkDirectories(); checkExampleFiles(); }); From 96eb2715249865bbaaee8c8fa1320151d17a4e8f Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Mon, 8 Jun 2020 19:01:23 +0300 Subject: [PATCH 08/35] updated cli command shape --- cli-commands/commands/shape/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli-commands/commands/shape/index.js b/cli-commands/commands/shape/index.js index 6f004f9..1026b44 100644 --- a/cli-commands/commands/shape/index.js +++ b/cli-commands/commands/shape/index.js @@ -19,10 +19,11 @@ class ShapeCommand extends Command { if (!optionsResults.framework) { commandMessages.InvalidShapeName(args.framework); - } else { - await git.clone(optionsResults.framework); - commandMessages.SuccessfulShaping(); + return true; } + + await git.clone(optionsResults.framework); + commandMessages.SuccessfulShaping(); } catch (error) { commandMessages.UnsuccessfulShaping(error); } From ca687950fea3257bbdc52df75d79aa91f1872496 Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Mon, 8 Jun 2020 19:01:56 +0300 Subject: [PATCH 09/35] updated tests for cli command test --- tests/cli-commands/test-tests.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/cli-commands/test-tests.js b/tests/cli-commands/test-tests.js index 9940df7..173e4fe 100644 --- a/tests/cli-commands/test-tests.js +++ b/tests/cli-commands/test-tests.js @@ -7,7 +7,6 @@ const Command = require('../../cli-commands/commands/command'); const TestCommand = require('../../cli-commands/commands/test/index'); const ProviderFactory = require('../../src/network-providers/provider-factory'); const MochaFramework = require('../../cli-commands/commands/test/specific/test-frameworks/mocha'); -const ReportTable = require('../../cli-commands/commands/test/options/resource-usage-option/report-table'); const definition = require('../../cli-commands/commands/test/definition'); const PathOption = require('../../cli-commands/commands/test/options/path-option'); @@ -32,7 +31,6 @@ describe('TestCommand', function () { let mochaSetDescribeArgs; let mochaRunTestsSpy; let providerFactorySpy; - let reportTableSpy; before(async () => { initialDir = process.cwd(); @@ -50,7 +48,6 @@ describe('TestCommand', function () { mochaSetDescribeArgs = sinon.spy(MochaFramework.prototype, "setDescribeArgs"); mochaRunTestsSpy = sinon.spy(MochaFramework.prototype, "runTests"); providerFactorySpy = sinon.spy(ProviderFactory.prototype, "reset"); - reportTableSpy = sinon.spy(ReportTable.prototype, "draw"); preloadMockedTests(); }); @@ -154,7 +151,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(resourceReportOptionSpy, 'true'); sinon.assert.calledOnce(mochaRunTestsSpy); - // sinon.assert.calledOnce(reportTableSpy); }); it('Should execute tests and display resource usage report', async () => { @@ -162,7 +158,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(resourceReportOptionSpy, 'false'); sinon.assert.calledOnce(mochaRunTestsSpy); - // sinon.assert.notCalled(reportTableSpy); }); }); \ No newline at end of file From 787aa8263c0e705b3b4b71e022c19a401aa79b80 Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Wed, 10 Jun 2020 20:44:04 +0300 Subject: [PATCH 10/35] cli command nodeos - tests --- tests/cli-commands/mocks/nodeos-data/eosd.pid | 1 + .../cli-commands/mocks/nodeos-data/nodeos.log | 142 +++++++++ tests/cli-commands/nodeos-tests.js | 300 ++++++++++++++++++ 3 files changed, 443 insertions(+) create mode 100644 tests/cli-commands/mocks/nodeos-data/eosd.pid create mode 100644 tests/cli-commands/mocks/nodeos-data/nodeos.log create mode 100644 tests/cli-commands/nodeos-tests.js diff --git a/tests/cli-commands/mocks/nodeos-data/eosd.pid b/tests/cli-commands/mocks/nodeos-data/eosd.pid new file mode 100644 index 0000000..66ab9d5 --- /dev/null +++ b/tests/cli-commands/mocks/nodeos-data/eosd.pid @@ -0,0 +1 @@ +50267 diff --git a/tests/cli-commands/mocks/nodeos-data/nodeos.log b/tests/cli-commands/mocks/nodeos-data/nodeos.log new file mode 100644 index 0000000..83357f4 --- /dev/null +++ b/tests/cli-commands/mocks/nodeos-data/nodeos.log @@ -0,0 +1,142 @@ +info 2020-06-02T16:53:28.358 thread-0 chain_plugin.cpp:594 plugin_initialize ] initializing chain plugin +info 2020-06-02T16:53:28.362 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'WTMSIG_BLOCK_SIGNATURES' (with digest of '299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707') is enabled with preactivation required +info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'WTMSIG_BLOCK_SIGNATURES' (with digest of '299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-WTMSIG_BLOCK_SIGNATURES.json +info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'WEBAUTHN_KEY' (with digest of '4fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2') is enabled with preactivation required +info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'WEBAUTHN_KEY' (with digest of '4fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-WEBAUTHN_KEY.json +info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'RAM_RESTRICTIONS' (with digest of '4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d67') is enabled with preactivation required +info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'RAM_RESTRICTIONS' (with digest of '4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d67') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-RAM_RESTRICTIONS.json +info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'GET_SENDER' (with digest of 'f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d') is enabled with preactivation required +info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'GET_SENDER' (with digest of 'f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-GET_SENDER.json +info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'FORWARD_SETCODE' (with digest of '2652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25') is enabled with preactivation required +info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'FORWARD_SETCODE' (with digest of '2652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-FORWARD_SETCODE.json +info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'ONLY_BILL_FIRST_AUTHORIZER' (with digest of '8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a405') is enabled with preactivation required +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'ONLY_BILL_FIRST_AUTHORIZER' (with digest of '8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a405') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-ONLY_BILL_FIRST_AUTHORIZER.json +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'RESTRICT_ACTION_TO_SELF' (with digest of 'ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43') is enabled with preactivation required +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'RESTRICT_ACTION_TO_SELF' (with digest of 'ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-RESTRICT_ACTION_TO_SELF.json +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'DISALLOW_EMPTY_PRODUCER_SCHEDULE' (with digest of '68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428') is enabled with preactivation required +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'DISALLOW_EMPTY_PRODUCER_SCHEDULE' (with digest of '68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-DISALLOW_EMPTY_PRODUCER_SCHEDULE.json +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'FIX_LINKAUTH_RESTRICTION' (with digest of 'e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526') is enabled with preactivation required +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'FIX_LINKAUTH_RESTRICTION' (with digest of 'e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-FIX_LINKAUTH_RESTRICTION.json +info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'REPLACE_DEFERRED' (with digest of 'ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99') is enabled with preactivation required +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'REPLACE_DEFERRED' (with digest of 'ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-REPLACE_DEFERRED.json +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'NO_DUPLICATE_DEFERRED_ID' (with digest of '4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f') is enabled with preactivation required +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'NO_DUPLICATE_DEFERRED_ID' (with digest of '4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-NO_DUPLICATE_DEFERRED_ID.json +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'ONLY_LINK_TO_EXISTING_PERMISSION' (with digest of '1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241') is enabled with preactivation required +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'ONLY_LINK_TO_EXISTING_PERMISSION' (with digest of '1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-ONLY_LINK_TO_EXISTING_PERMISSION.json +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:425 operator() ] Support for builtin protocol feature 'PREACTIVATE_FEATURE' (with digest of '0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd') is enabled without activation restrictions +info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'PREACTIVATE_FEATURE' (with digest of '0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-PREACTIVATE_FEATURE.json +info 2020-06-02T16:53:28.370 thread-0 chain_plugin.cpp:988 plugin_initialize ] Starting fresh blockchain state using default genesis state. +info 2020-06-02T16:53:28.845 thread-0 platform_timer_accurac:62 compute_and_print_ti ] Checktime timer accuracy: min:-1us max:1059us mean:35us stddev:45us +info 2020-06-02T16:53:28.855 thread-0 http_plugin.cpp:525 plugin_initialize ] configured http to listen on 127.0.0.1:8888 +warn 2020-06-02T16:53:28.855 thread-0 history_plugin.cpp:317 plugin_initialize ] --filter-on * enabled. This can fill shared_mem, causing nodeos to stop. +info 2020-06-02T16:53:28.856 thread-0 main.cpp:106 main ] nodeos version v2.0.4 v2.0.4-b39b6bd3c74ff47fc53fa80aafa2c1941ba82879 +info 2020-06-02T16:53:28.856 thread-0 main.cpp:107 main ] nodeos using configuration file /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/config.ini +info 2020-06-02T16:53:28.856 thread-0 main.cpp:108 main ] nodeos data directory is /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/data +warn 2020-06-02T16:53:28.857 thread-0 controller.cpp:580 startup ] No existing chain state or fork database. Initializing fresh blockchain state and resetting fork database. +warn 2020-06-02T16:53:28.857 thread-0 controller.cpp:450 initialize_blockchai ] Initializing new blockchain with genesis state +info 2020-06-02T16:53:28.895 thread-0 chain_plugin.cpp:1121 plugin_startup ] starting chain in read/write mode +info 2020-06-02T16:53:28.895 thread-0 chain_plugin.cpp:1126 plugin_startup ] Blockchain started; head block is #1, genesis timestamp is 2018-06-01T12:00:00.000 +info 2020-06-02T16:53:28.895 thread-0 producer_plugin.cpp:947 plugin_startup ] producer plugin: plugin_startup() begin +info 2020-06-02T16:53:28.898 thread-0 producer_plugin.cpp:972 plugin_startup ] Launching block production for 1 producers at 2020-06-02T16:53:28.898. +info 2020-06-02T16:53:28.908 thread-0 producer_plugin.cpp:983 plugin_startup ] producer plugin: plugin_startup() end +info 2020-06-02T16:53:28.909 thread-0 http_plugin.cpp:603 plugin_startup ] start listening for http requests +info 2020-06-02T16:53:28.911 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/node/get_supported_apis +info 2020-06-02T16:53:28.911 thread-0 producer_api_plugin.cp:89 plugin_startup ] starting producer_api_plugin +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/add_greylist_accounts +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/create_snapshot +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_account_ram_corrections +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_greylist +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_integrity_hash +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_runtime_options +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_scheduled_protocol_feature_activations +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_supported_protocol_features +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_whitelist_blacklist +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/pause +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/paused +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/remove_greylist_accounts +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/resume +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/schedule_protocol_feature_activations +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/set_whitelist_blacklist +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/update_runtime_options +info 2020-06-02T16:53:28.912 thread-0 chain_api_plugin.cpp:73 plugin_startup ] starting chain_api_plugin +info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_info +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/abi_bin_to_json +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/abi_json_to_bin +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_abi +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_account +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_activated_protocol_features +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_block +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_block_header_state +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_code +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_code_hash +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_currency_balance +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_currency_stats +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_producer_schedule +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_producers +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_raw_abi +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_raw_code_and_abi +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_required_keys +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_scheduled_transactions +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_table_by_scope +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_table_rows +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_transaction_id +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/push_block +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/push_transaction +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/push_transactions +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/send_transaction +info 2020-06-02T16:53:28.913 thread-0 history_api_plugin.cpp:34 plugin_startup ] starting history_api_plugin +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_actions +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_controlled_accounts +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_key_accounts +info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_transaction +info 2020-06-02T16:53:28.914 thread-0 net_plugin.cpp:3403 plugin_startup ] my node_id is d6af73ab613beb58ae2f8198d7b45caff86a4ec32864cfcf60ebe5ba626edd1f +info 2020-06-02T16:53:28.914 thread-0 net_plugin.cpp:3459 plugin_startup ] starting listener, max clients is 25 +info 2020-06-02T16:53:28.921 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block fe1c10543bdf6388... #2 @ 2020-06-02T16:53:29.000 signed by eosio [trxs: 0, lib: 1, confirmed: 0] +info 2020-06-02T16:53:29.301 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block bbf94510ee93e36f... #3 @ 2020-06-02T16:53:29.500 signed by eosio [trxs: 0, lib: 2, confirmed: 0] +info 2020-06-02T16:53:29.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9163c80ad40be876... #4 @ 2020-06-02T16:53:30.000 signed by eosio [trxs: 0, lib: 3, confirmed: 0] +info 2020-06-02T16:53:30.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 8d8cd47d3c53c522... #5 @ 2020-06-02T16:53:30.500 signed by eosio [trxs: 0, lib: 4, confirmed: 0] +info 2020-06-02T16:53:30.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 2dcc6c67370207cd... #6 @ 2020-06-02T16:53:31.000 signed by eosio [trxs: 3, lib: 5, confirmed: 0] +info 2020-06-02T16:53:31.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 2d955fb69c7913c5... #7 @ 2020-06-02T16:53:31.500 signed by eosio [trxs: 0, lib: 6, confirmed: 0] +info 2020-06-02T16:53:31.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 08deeb52bdc2f57b... #8 @ 2020-06-02T16:53:32.000 signed by eosio [trxs: 0, lib: 7, confirmed: 0] +info 2020-06-02T16:53:32.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 3eca474b4931495a... #9 @ 2020-06-02T16:53:32.500 signed by eosio [trxs: 0, lib: 8, confirmed: 0] +info 2020-06-02T16:53:32.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9f94c1259c5533e7... #10 @ 2020-06-02T16:53:33.000 signed by eosio [trxs: 0, lib: 9, confirmed: 0] +info 2020-06-02T16:53:33.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block d04368ec346189f4... #11 @ 2020-06-02T16:53:33.500 signed by eosio [trxs: 0, lib: 10, confirmed: 0] +info 2020-06-02T16:53:33.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dbfb48b6e45def7b... #12 @ 2020-06-02T16:53:34.000 signed by eosio [trxs: 0, lib: 11, confirmed: 0] +info 2020-06-02T16:53:34.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 877d0f3ea0403ddb... #13 @ 2020-06-02T16:53:34.500 signed by eosio [trxs: 0, lib: 12, confirmed: 0] +info 2020-06-02T16:53:34.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 3b6265897808e444... #14 @ 2020-06-02T16:53:35.000 signed by eosio [trxs: 0, lib: 13, confirmed: 0] +info 2020-06-02T16:53:35.301 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 324272a5719d6dde... #15 @ 2020-06-02T16:53:35.500 signed by eosio [trxs: 0, lib: 14, confirmed: 0] +info 2020-06-02T16:53:35.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dcb045f5b910108b... #16 @ 2020-06-02T16:53:36.000 signed by eosio [trxs: 0, lib: 15, confirmed: 0] +info 2020-06-02T16:53:36.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 6d57007a9bc414c4... #17 @ 2020-06-02T16:53:36.500 signed by eosio [trxs: 0, lib: 16, confirmed: 0] +info 2020-06-02T16:53:36.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9cf4b1dedea4a87c... #18 @ 2020-06-02T16:53:37.000 signed by eosio [trxs: 0, lib: 17, confirmed: 0] +info 2020-06-02T16:53:37.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block ecf3e3bd602c4b25... #19 @ 2020-06-02T16:53:37.500 signed by eosio [trxs: 0, lib: 18, confirmed: 0] +info 2020-06-02T16:53:37.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block a69e8ae427a37eab... #20 @ 2020-06-02T16:53:38.000 signed by eosio [trxs: 0, lib: 19, confirmed: 0] +info 2020-06-02T16:53:38.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9f712a1a5cbae701... #21 @ 2020-06-02T16:53:38.500 signed by eosio [trxs: 0, lib: 20, confirmed: 0] +info 2020-06-02T16:53:38.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dfa419c3434fbc96... #22 @ 2020-06-02T16:53:39.000 signed by eosio [trxs: 0, lib: 21, confirmed: 0] +info 2020-06-02T16:53:39.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 4e3a456c90fe3f05... #23 @ 2020-06-02T16:53:39.500 signed by eosio [trxs: 0, lib: 22, confirmed: 0] +info 2020-06-02T16:53:39.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 37bbbb907a3012e6... #24 @ 2020-06-02T16:53:40.000 signed by eosio [trxs: 0, lib: 23, confirmed: 0] +info 2020-06-02T16:53:40.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block d9c9bc196e484c6f... #25 @ 2020-06-02T16:53:40.500 signed by eosio [trxs: 0, lib: 24, confirmed: 0] +info 2020-06-02T16:53:40.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 14a0134de6aa4a9f... #26 @ 2020-06-02T16:53:41.000 signed by eosio [trxs: 0, lib: 25, confirmed: 0] +info 2020-06-02T16:53:41.301 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 6891df85da1d0dbd... #27 @ 2020-06-02T16:53:41.500 signed by eosio [trxs: 0, lib: 26, confirmed: 0] +info 2020-06-02T16:53:41.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block a2d96781ca99cf73... #28 @ 2020-06-02T16:53:42.000 signed by eosio [trxs: 0, lib: 27, confirmed: 0] +info 2020-06-02T16:53:42.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block a0f08892405baa74... #29 @ 2020-06-02T16:53:42.500 signed by eosio [trxs: 0, lib: 28, confirmed: 0] +info 2020-06-02T16:53:42.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 7a521eea79001ab9... #30 @ 2020-06-02T16:53:43.000 signed by eosio [trxs: 0, lib: 29, confirmed: 0] +info 2020-06-02T16:53:43.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 5ec522129c56c7b9... #31 @ 2020-06-02T16:53:43.500 signed by eosio [trxs: 0, lib: 30, confirmed: 0] +info 2020-06-02T16:53:43.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 8f9a663fa151ed26... #32 @ 2020-06-02T16:53:44.000 signed by eosio [trxs: 0, lib: 31, confirmed: 0] +info 2020-06-02T16:53:44.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block aeaa2470e300de15... #33 @ 2020-06-02T16:53:44.500 signed by eosio [trxs: 0, lib: 32, confirmed: 0] +info 2020-06-02T16:53:44.904 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block ae96aa66268353c5... #34 @ 2020-06-02T16:53:45.000 signed by eosio [trxs: 0, lib: 33, confirmed: 0] +info 2020-06-02T16:53:45.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 852975eed72f8db0... #35 @ 2020-06-02T16:53:45.500 signed by eosio [trxs: 0, lib: 34, confirmed: 0] +info 2020-06-02T16:53:45.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 8dfdad739e190319... #36 @ 2020-06-02T16:53:46.000 signed by eosio [trxs: 0, lib: 35, confirmed: 0] +info 2020-06-02T16:53:46.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dc645e1bc35125e6... #37 @ 2020-06-02T16:53:46.500 signed by eosio [trxs: 0, lib: 36, confirmed: 0] +info 2020-06-02T16:53:46.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block f77abd5ca578b59c... #38 @ 2020-06-02T16:53:47.000 signed by eosio [trxs: 0, lib: 37, confirmed: 0] +info 2020-06-02T16:53:47.302 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 4ff9c5550bb2a101... #39 @ 2020-06-02T16:53:47.500 signed by eosio [trxs: 0, lib: 38, confirmed: 0] +info 2020-06-02T16:53:47.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block cecafa607d864ac9... #40 @ 2020-06-02T16:53:48.000 signed by eosio [trxs: 0, lib: 39, confirmed: 0] +info 2020-06-02T16:53:48.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 6d64e9fa642958d8... #41 @ 2020-06-02T16:53:48.500 signed by eosio [trxs: 0, lib: 40, confirmed: 0] +info 2020-06-02T16:53:48.904 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 839e68ea2b39db92... #42 @ 2020-06-02T16:53:49.000 signed by eosio [trxs: 0, lib: 41, confirmed: 0] +info 2020-06-02T16:53:49.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 568a5ea58679944f... #43 @ 2020-06-02T16:53:49.500 signed by eosio [trxs: 0, lib: 42, confirmed: 0] +info 2020-06-02T16:53:49.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block cb440088ebf1df19... #44 @ 2020-06-02T16:53:50.000 signed by eosio [trxs: 0, lib: 43, confirmed: 0] +info 2020-06-02T16:53:50.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 63b3da1229666dc9... #45 @ 2020-06-02T16:53:50.500 signed by eosio [trxs: 0, lib: 44, confirmed: 0] +info 2020-06-02T16:53:50.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block f7da5fe0b8c1c890... #46 @ 2020-06-02T16:53:51.000 signed by eosio [trxs: 0, lib: 45, confirmed: 0] +info 2020-06-02T16:53:51.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 31461a4e3d3e11c8... #47 @ 2020-06-02T16:53:51.500 signed by eosio [trxs: 0, lib: 46, confirmed: 0] +info 2020-06-02T16:53:51.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 443bcd8a32f98516... #48 @ 2020-06-02T16:53:52.000 signed by eosio [trxs: 0, lib: 47, confirmed: 0] +info 2020-06-02T16:53:52.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block ec476b085c6a01f3... #49 @ 2020-06-02T16:53:52.500 signed by eosio [trxs: 0, lib: 48, confirmed: 0] +info 2020-06-02T16:53:52.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 65e71b794437e123... #50 @ 2020-06-02T16:53:53.000 signed by eosio [trxs: 0, lib: 49, confirmed: 0] \ No newline at end of file diff --git a/tests/cli-commands/nodeos-tests.js b/tests/cli-commands/nodeos-tests.js new file mode 100644 index 0000000..5591afc --- /dev/null +++ b/tests/cli-commands/nodeos-tests.js @@ -0,0 +1,300 @@ +const path = require('path'); +const fs = require('fs-extra'); +const sinon = require('sinon'); +const assert = require('assert'); + +const Command = require('../../cli-commands/commands/command'); +const GroupCommand = require('../../cli-commands/commands/group-command'); +const NodeosCommand = require('../../cli-commands/commands/nodeos/index'); +const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); +const AccountFactory = require('../../src/account/normal-account/account-factory'); +const LogsCommand = require('../../cli-commands/commands/nodeos/subcommands/logs/index'); +const StopCommand = require('../../cli-commands/commands/nodeos/subcommands/stop/index'); +const StartCommand = require('../../cli-commands/commands/nodeos/subcommands/start/index'); +const AccountsCommand = require('../../cli-commands/commands/nodeos/subcommands/accounts/index'); +const AccountsTable = require('../../cli-commands/commands/nodeos/subcommands/accounts/specific/accounts-table'); + +const nodeosDefinition = require('../../cli-commands/commands/nodeos/definition'); +const logsDefinition = require('../../cli-commands/commands/nodeos/subcommands/logs/definition'); +const stopDefinition = require('../../cli-commands/commands/nodeos/subcommands/stop/definition'); +const startDefinition = require('../../cli-commands/commands/nodeos/subcommands/start/definition'); +const accountsDefinition = require('../../cli-commands/commands/nodeos/subcommands/accounts/definition'); +const PathOption = require('../../cli-commands/commands/nodeos/subcommands/start/options/path-option'); +const LinesOption = require('../../cli-commands/commands/nodeos/subcommands/logs/options/lines-option'); + +const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); +const template = require('../../cli-commands/commands/nodeos/subcommands/start/specific/template'); +const predefinedAccounts = require('../../cli-commands/commands/nodeos/subcommands/common/accounts'); +const nodoesDataManager = require('../../cli-commands/commands/nodeos/specific/nodeos-data/data-manager'); + +describe('NodeosCommand', function () { + const TEST_DIR = './cli-commands-test'; + const NODEOS_DATA_DIR = 'nodeos-data'; + + let initialDir; + let nodeosDataPath; + + before(async () => { + initialDir = process.cwd(); + fs.mkdirSync(TEST_DIR); + process.chdir(TEST_DIR); + nodeosDataPath = path.resolve(process.cwd(), NODEOS_DATA_DIR); + fs.mkdirSync('./nodeos-data'); + }); + + afterEach(async () => { + sinon.restore(); + cleanNodeosDataFolder(); + }); + + after(async () => { + process.chdir(initialDir); + fs.removeSync(TEST_DIR); + }); + + function preloadNodeosData () { + fs.mkdir('./nodeos-data/data'); + fs.mkdir('./nodeos-data/config'); + fs.copyFileSync('../tests/cli-commands/mocks/nodeos-data/eosd.pid', './nodeos-data/eosd.pid'); + fs.copyFileSync('../tests/cli-commands/mocks/nodeos-data/nodeos.log', './nodeos-data/nodeos.log'); + } + + function cleanNodeosDataFolder () { + fs.removeSync('./nodeos-data/data'); + fs.removeSync('./nodeos-data/config'); + fs.removeSync('./nodeos-data/eosd.pid'); + fs.removeSync('./nodeos-data/nodeos.log'); + } + + it('Should initialize command properly', async () => { + let nodeosCommand = new NodeosCommand(); + + assert(nodeosCommand instanceof GroupCommand); + assert(nodeosCommand.template == nodeosDefinition.template); + assert(nodeosCommand.description = nodeosDefinition.description); + assert(nodeosCommand.options == nodeosDefinition.options); + assert(nodeosCommand.subcommands.length > 0); + }); + + describe('StartCommand', function () { + let startCommand; + let templateSpy; + let pathOptionSpy; + let setNodeosPathSpy; + let predefinedAccountsSpy; + + beforeEach(async () => { + startCommand = new StartCommand(); + + sinon.stub(fileSystemUtil, "writeFile"); + sinon.stub(AsyncSoftExec.prototype, "exec"); + sinon.stub(AccountFactory.prototype, "create"); + sinon.stub(nodoesDataManager, "defaultPath").returns(nodeosDataPath); + + templateSpy = sinon.spy(template, "build"); + pathOptionSpy = sinon.spy(PathOption, "process"); + predefinedAccountsSpy = sinon.spy(predefinedAccounts, "load"); + setNodeosPathSpy = sinon.spy(nodoesDataManager, "setNodeosPath"); + }); + + it('Should initialize command properly', async () => { + assert(startCommand instanceof Command); + assert(startCommand.template == startDefinition.template); + assert(startCommand.description = startDefinition.description); + assert(startCommand.options == startDefinition.options); + assert(startCommand.subcommands.length == 0); + }); + + it('Should break when there is already running nodeos', async () => { + sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + + assert(await startCommand.execute({ path: nodeosDataPath })); + + sinon.assert.notCalled(pathOptionSpy); + sinon.assert.notCalled(setNodeosPathSpy); + }); + + it('Should start nodeos successfully', async () => { + let stub = sinon.stub(nodoesDataManager, "nodeosIsRunning"); + stub.onFirstCall().returns(false); + stub.onSecondCall().returns(true); + + assert(await startCommand.execute({ path: nodeosDataPath })); + + sinon.assert.calledWith(pathOptionSpy, nodeosDataPath); + sinon.assert.calledWith(setNodeosPathSpy, nodeosDataPath); + sinon.assert.calledWith(templateSpy, nodeosDataPath); + sinon.assert.calledOnce(predefinedAccountsSpy); + }); + + it('Should throw when unexpected error occurred', async () => { + let stub = sinon.stub(nodoesDataManager, "nodeosIsRunning"); + stub.onFirstCall().returns(false); + stub.onSecondCall().returns(false); + + assert(await startCommand.execute({ path: nodeosDataPath })); + + sinon.assert.calledWith(pathOptionSpy, nodeosDataPath); + sinon.assert.calledWith(setNodeosPathSpy, nodeosDataPath); + sinon.assert.calledWith(templateSpy, nodeosDataPath); + sinon.assert.notCalled(predefinedAccountsSpy); + }); + + }); + + describe('StopCommand', function () { + let stopCommand; + let fsReadFileSpy; + let fsRemoveDirSpy; + let fsRemoveFileSpy; + + beforeEach(async () => { + stopCommand = new StopCommand(); + sinon.stub(AsyncSoftExec.prototype, "exec"); + sinon.stub(nodoesDataManager, "nodeosPath").returns(nodeosDataPath); + fsReadFileSpy = sinon.spy(fileSystemUtil, "readFile"); + fsRemoveFileSpy = sinon.spy(fileSystemUtil, "rmFile"); + fsRemoveDirSpy = sinon.spy(fileSystemUtil, "recursivelyDeleteDir"); + }); + + it('Should initialize command properly', async () => { + assert(stopCommand instanceof Command); + assert(stopCommand.template == stopDefinition.template); + assert(stopCommand.description = stopDefinition.description); + assert(stopCommand.options == stopDefinition.options); + assert(stopCommand.subcommands.length == 0); + }); + + it('Should clean only the data when there is no running nodeos', async () => { + preloadNodeosData(); + + sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(false); + + assert(await stopCommand.execute()); + + sinon.assert.notCalled(fsReadFileSpy); + sinon.assert.calledTwice(fsRemoveFileSpy); + sinon.assert.calledTwice(fsRemoveDirSpy); + }); + + it('Should stop nodeos and clean the data', async () => { + preloadNodeosData(); + + sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + + assert(await stopCommand.execute()); + + sinon.assert.calledWith(fsReadFileSpy, nodeosDataPath + '/eosd.pid'); + sinon.assert.calledTwice(fsRemoveFileSpy); + fsRemoveFileSpy.firstCall.calledWith(nodeosDataPath + '/eosd.pid'); + fsRemoveFileSpy.firstCall.calledWith(nodeosDataPath + '/nodeos.log'); + sinon.assert.calledTwice(fsRemoveDirSpy); + fsRemoveDirSpy.firstCall.calledWith(nodeosDataPath + '/data'); + fsRemoveDirSpy.firstCall.calledWith(nodeosDataPath + '/config'); + }); + + it('Should throw when unexpected error occured', async () => { + preloadNodeosData(); + + sinon.stub(nodoesDataManager, "nodeosIsRunning").throws(); + + assert(await stopCommand.execute()); + + sinon.assert.notCalled(fsRemoveFileSpy); + sinon.assert.notCalled(fsRemoveDirSpy); + }); + + }); + + describe('LogsCommand', function () { + let logsCommand; + let linesOptionSpy; + + beforeEach(async () => { + logsCommand = new LogsCommand(); + sinon.stub(AsyncSoftExec.prototype, "exec"); + sinon.stub(nodoesDataManager, "nodeosPath").returns(nodeosDataPath); + linesOptionSpy = sinon.spy(LinesOption, "process"); + }); + + it('Should initialize command properly', async () => { + assert(logsCommand instanceof Command); + assert(logsCommand.template == logsDefinition.template); + assert(logsCommand.description = logsDefinition.description); + assert(logsCommand.options == logsDefinition.options); + assert(logsCommand.subcommands.length == 0); + }); + + it('Should break when there is no running nodeos', async () => { + preloadNodeosData(); + + sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(false); + let processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); + + assert(await logsCommand.execute({ lines: 10 })); + + sinon.assert.notCalled(processOptionsSpy); + }); + + it('Should display nodeos logs successfully', async () => { + preloadNodeosData(); + + sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + + assert(await logsCommand.execute({ lines: 10 })); + + sinon.assert.calledWith(linesOptionSpy, 10); + }); + + it('Should throw when processing command options failed', async () => { + preloadNodeosData(); + + sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + sinon.stub(Command.prototype, "processOptions").throws('Test: Process Options Failure'); + + assert(await logsCommand.execute({ lines: 10 })); + + sinon.assert.notCalled(linesOptionSpy); + }); + + }); + + describe('AccountsCommand', function () { + let accountsCommand; + + beforeEach(async () => { + accountsCommand = new AccountsCommand(); + }); + + it('Should initialize command properly', async () => { + assert(accountsCommand instanceof Command); + assert(accountsCommand.template == accountsDefinition.template); + assert(accountsCommand.description = accountsDefinition.description); + assert(accountsCommand.options == accountsDefinition.options); + assert(accountsCommand.subcommands.length == 0); + }); + + it('Should display preloaded accounts', async () => { + let addTableRowSpy = sinon.spy(AccountsTable.prototype, "addRow"); + let drawTableSpy = sinon.spy(AccountsTable.prototype, "draw"); + + assert(await accountsCommand.execute()); + + const accounts = predefinedAccounts.accounts(); + + sinon.assert.calledThrice(addTableRowSpy); + addTableRowSpy.firstCall.calledWith(accounts[0].name, accounts[0].publicKey, accounts[0].privateKey); + addTableRowSpy.firstCall.calledWith(accounts[1].name, accounts[1].publicKey, accounts[1].privateKey); + addTableRowSpy.firstCall.calledWith(accounts[2].name, accounts[2].publicKey, accounts[2].privateKey); + sinon.assert.calledOnce(drawTableSpy); + }); + + it('Should throw when table drawing failed', async () => { + sinon.stub(AccountsTable.prototype, "draw").throws(); + + assert(await accountsCommand.execute()); + }); + + }); + +}); \ No newline at end of file From 0dfb96b5873e1902a7f397fbf4aa84b83e9ba30d Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 12 Jun 2020 10:29:18 +0300 Subject: [PATCH 11/35] added tests for nodeos data manager --- tests/cli-commands/nodeos-tests.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/cli-commands/nodeos-tests.js b/tests/cli-commands/nodeos-tests.js index 5591afc..735a0f8 100644 --- a/tests/cli-commands/nodeos-tests.js +++ b/tests/cli-commands/nodeos-tests.js @@ -297,4 +297,32 @@ describe('NodeosCommand', function () { }); + describe('DataManager', function () { + + describe('nodeosIsRunning', function () { + + it('Should return false when eosd.pid file does not exists', async () => { + assert(!nodoesDataManager.nodeosIsRunning(nodeosDataPath)); + }); + + it('Should return false when eosd.pid file exists, but nodeos is not running', async () => { + preloadNodeosData(); + + sinon.stub(process, "kill").throws(); + + assert(!nodoesDataManager.nodeosIsRunning(nodeosDataPath)); + }); + + it('Should return true when eosd.pid file exists and nodeos is running', async () => { + preloadNodeosData(); + + sinon.stub(process, "kill").returns(true); + + assert(nodoesDataManager.nodeosIsRunning(nodeosDataPath)); + }); + + }); + + }) + }); \ No newline at end of file From bdce930bad7e0f0c8a1352c88a37511322f4c9dd Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Mon, 15 Jun 2020 22:31:02 +0300 Subject: [PATCH 12/35] added logger --- cli-commands/commands/compile/messages.js | 17 ++++++----------- cli-commands/commands/deploy/messages.js | 15 +++++---------- cli-commands/commands/init/messages.js | 10 ++++------ .../nodeos/subcommands/accounts/messages.js | 8 +++----- .../nodeos/subcommands/logs/messages.js | 15 ++++----------- .../nodeos/subcommands/start/messages.js | 12 +++++------- .../nodeos/subcommands/stop/messages.js | 10 ++++------ cli-commands/commands/shape/messages.js | 12 +++++------- cli-commands/commands/test/index.js | 7 ++++++- cli-commands/commands/test/messages.js | 14 ++++---------- cli-commands/common/logger.js | 11 +++++++++++ cli-commands/helpers/file-system-util.js | 3 --- package.json | 1 + tests/cli-commands/compile-tests.js | 3 +++ tests/cli-commands/deploy-tests.js | 3 +++ tests/cli-commands/init-tests.js | 3 +++ tests/cli-commands/nodeos-tests.js | 6 ++++++ tests/cli-commands/shape-tests.js | 3 +++ tests/cli-commands/test-tests.js | 5 ++++- 19 files changed, 80 insertions(+), 78 deletions(-) create mode 100644 cli-commands/common/logger.js diff --git a/cli-commands/commands/compile/messages.js b/cli-commands/commands/compile/messages.js index af94aff..29789b6 100644 --- a/cli-commands/commands/compile/messages.js +++ b/cli-commands/commands/compile/messages.js @@ -1,15 +1,10 @@ const chalk = require('chalk'); +const logger = require('../../common/logger'); module.exports = { - 'StartCompilation': () => { console.log(chalk.magentaBright('===== Compilation has started... =====')); }, - 'UnsuccessfulCompilation': (error) => { - console.log(chalk.redBright(`===== Unsuccessful compilation =====`)); - console.log(error); - }, - 'SuccessfulCompilationOfContract': (contract) => { console.log(chalk.greenBright(`===== Successfully compilation of ${contract} =====`)); }, - 'UnsuccessfulCompilationOfContract': (error, file) => { - console.log(chalk.redBright(`===== Unsuccessful compilation of ${file} =====`)); - console.log(error); - }, - 'ContractNotExisting': () => { console.log(chalk.redBright(`===== There is not a contract to compile =====`)); } + 'StartCompilation': () => { logger.info(chalk.magentaBright('===== Compilation has started... =====')); }, + 'UnsuccessfulCompilation': (error) => { logger.error(chalk.redBright(`===== Unsuccessful compilation =====`), error); }, + 'SuccessfulCompilationOfContract': (contract) => { logger.info(chalk.greenBright(`===== Successfully compilation of ${contract} =====`)); }, + 'UnsuccessfulCompilationOfContract': (error, file) => { logger.error(chalk.redBright(`===== Unsuccessful compilation of ${file} =====`), error); }, + 'ContractNotExisting': () => { logger.info(chalk.redBright(`===== There is not a contract to compile =====`)); } } \ No newline at end of file diff --git a/cli-commands/commands/deploy/messages.js b/cli-commands/commands/deploy/messages.js index b76746d..522b0ed 100644 --- a/cli-commands/commands/deploy/messages.js +++ b/cli-commands/commands/deploy/messages.js @@ -1,14 +1,9 @@ const chalk = require('chalk'); +const logger = require('../../common/logger'); module.exports = { - 'StartDeployment': () => { console.log(chalk.magentaBright('===== Deployment has started... =====')); }, - 'SuccessfulDeploymentOfScript': (script) => { console.log(chalk.greenBright(`===== Successful deployment of ${script} =====`)); }, - 'UnsuccessfulDeploymentOfScript': (script, error) => { - console.log(chalk.redBright(`===== Unsuccessful deployment of ${script} =====`)); - console.log(error); - }, - 'UnsuccessfulDeployment': (error) => { - console.log(chalk.redBright(`===== Unsuccessful deployment =====`)); - console.log(error); - } + 'StartDeployment': () => { logger.info(chalk.magentaBright('===== Deployment has started... =====')); }, + 'SuccessfulDeploymentOfScript': (script) => { logger.info(chalk.greenBright(`===== Successful deployment of ${script} =====`)); }, + 'UnsuccessfulDeploymentOfScript': (script, error) => { logger.error(chalk.redBright(`===== Unsuccessful deployment of ${script} =====`), error); }, + 'UnsuccessfulDeployment': (error) => { logger.error(chalk.redBright(`===== Unsuccessful deployment =====`), error); } } \ No newline at end of file diff --git a/cli-commands/commands/init/messages.js b/cli-commands/commands/init/messages.js index 7b3967a..362f1f1 100644 --- a/cli-commands/commands/init/messages.js +++ b/cli-commands/commands/init/messages.js @@ -1,10 +1,8 @@ const chalk = require('chalk'); +const logger = require('../../common/logger'); module.exports = { - 'Installation': () => { console.log(chalk.magentaBright('===== Installing eoslime... =====')); }, - 'SuccessfulInstallation': () => { console.log(chalk.greenBright('===== Successfully installed =====')); }, - 'UnsuccessfulInstallation': (error) => { - console.log(chalk.redBright('===== Unsuccessful installation =====')); - console.log(error); - } + 'Installation': () => { logger.info(chalk.magentaBright('===== Installing eoslime... =====')); }, + 'SuccessfulInstallation': () => { logger.info(chalk.greenBright('===== Successfully installed =====')); }, + 'UnsuccessfulInstallation': (error) => { logger.error(chalk.redBright('===== Unsuccessful installation ====='), error); } } \ No newline at end of file diff --git a/cli-commands/commands/nodeos/subcommands/accounts/messages.js b/cli-commands/commands/nodeos/subcommands/accounts/messages.js index eb325c6..edb3d32 100644 --- a/cli-commands/commands/nodeos/subcommands/accounts/messages.js +++ b/cli-commands/commands/nodeos/subcommands/accounts/messages.js @@ -1,9 +1,7 @@ const chalk = require('chalk'); +const logger = require('../../../../common/logger'); module.exports = { - 'PreloadedAccounts': () => { console.log(chalk.magentaBright('===== Preloaded accounts =====')); }, - 'UnsuccessfulShowing': (error) => { - console.log(chalk.redBright(`===== Accounts has not been shown =====`)); - console.log(error); - } + 'PreloadedAccounts': () => { logger.info(chalk.magentaBright('===== Preloaded accounts =====')); }, + 'UnsuccessfulShowing': (error) => { logger.error(chalk.redBright(`===== Accounts has not been shown =====`), error); } } \ No newline at end of file diff --git a/cli-commands/commands/nodeos/subcommands/logs/messages.js b/cli-commands/commands/nodeos/subcommands/logs/messages.js index 2ea92a0..52fb42f 100644 --- a/cli-commands/commands/nodeos/subcommands/logs/messages.js +++ b/cli-commands/commands/nodeos/subcommands/logs/messages.js @@ -1,15 +1,8 @@ const chalk = require('chalk'); +const logger = require('../../../../common/logger'); module.exports = { - 'NodeosLogs': (logs) => { - console.log(chalk.magentaBright('===== Nodeos logs =====')); - console.log(logs); - }, - 'EmptyLogs': () => { - console.log(chalk.blueBright('===== Empty logs =====')); - }, - 'UnsuccessfulShowing': (error) => { - console.log(chalk.redBright(`===== Logs has not been shown =====`)); - console.log(error); - } + 'NodeosLogs': (logs) => { logger.info(chalk.magentaBright(`===== Nodeos logs ===== \n ${logs}`)); }, + 'EmptyLogs': () => { logger.info(chalk.blueBright('===== Empty logs =====')); }, + 'UnsuccessfulShowing': (error) => { logger.error(chalk.redBright(`===== Logs has not been shown =====`), error); } } \ No newline at end of file diff --git a/cli-commands/commands/nodeos/subcommands/start/messages.js b/cli-commands/commands/nodeos/subcommands/start/messages.js index 94119a1..f2a1356 100644 --- a/cli-commands/commands/nodeos/subcommands/start/messages.js +++ b/cli-commands/commands/nodeos/subcommands/start/messages.js @@ -1,11 +1,9 @@ const chalk = require('chalk'); +const logger = require('../../../../common/logger'); module.exports = { - 'StartingNodeos': () => { console.log(chalk.magentaBright('===== Starting nodeos ... =====')); }, - 'NodeosAlreadyRunning': () => { console.log(chalk.blueBright(`===== Nodeos is already running =====`)); }, - 'SuccessfullyStarted': () => { console.log(chalk.greenBright(`===== Successfully started =====`)); }, - 'UnsuccessfulStarting': (error) => { - console.log(chalk.redBright(`===== Nodeos has not been started =====`)); - console.log(error); - } + 'StartingNodeos': () => { logger.info(chalk.magentaBright('===== Starting nodeos ... =====')); }, + 'NodeosAlreadyRunning': () => { logger.info(chalk.blueBright(`===== Nodeos is already running =====`)); }, + 'SuccessfullyStarted': () => { logger.info(chalk.greenBright(`===== Successfully started =====`)); }, + 'UnsuccessfulStarting': (error) => { logger.error(chalk.redBright(`===== Nodeos has not been started =====`), error); } } \ No newline at end of file diff --git a/cli-commands/commands/nodeos/subcommands/stop/messages.js b/cli-commands/commands/nodeos/subcommands/stop/messages.js index 7af7b3f..2c043f3 100644 --- a/cli-commands/commands/nodeos/subcommands/stop/messages.js +++ b/cli-commands/commands/nodeos/subcommands/stop/messages.js @@ -1,10 +1,8 @@ const chalk = require('chalk'); +const logger = require('../../../../common/logger'); module.exports = { - 'StoppingNodeos': () => { console.log(chalk.magentaBright('===== Stopping nodeos ... =====')); }, - 'SuccessfullyStopped': () => { console.log(chalk.greenBright(`===== Successfully stopped =====`)); }, - 'UnsuccessfulStopping': (error) => { - console.log(chalk.redBright(`===== Nodeos has not been stopped =====`)); - console.log(error); - } + 'StoppingNodeos': () => { logger.info(chalk.magentaBright('===== Stopping nodeos ... =====')); }, + 'SuccessfullyStopped': () => { logger.info(chalk.greenBright(`===== Successfully stopped =====`)); }, + 'UnsuccessfulStopping': (error) => { logger.error(chalk.redBright(`===== Nodeos has not been stopped =====`), error); } } \ No newline at end of file diff --git a/cli-commands/commands/shape/messages.js b/cli-commands/commands/shape/messages.js index 975c899..5254b51 100644 --- a/cli-commands/commands/shape/messages.js +++ b/cli-commands/commands/shape/messages.js @@ -1,11 +1,9 @@ const chalk = require('chalk'); +const logger = require('../../common/logger'); module.exports = { - 'StartShaping': () => { console.log(chalk.magentaBright('===== Shaping of DApp has started... =====')); }, - 'SuccessfulShaping': () => { console.log(chalk.greenBright(`===== Successful shaping =====`)); }, - 'UnsuccessfulShaping': (error) => { - console.log(chalk.redBright(`===== Unsuccessful shaping =====`)); - console.log(error); - }, - 'InvalidShapeName': (name) => { console.log(chalk.redBright(`===== Invalid shape name ${name} =====`)); }, + 'StartShaping': () => { logger.info(chalk.magentaBright('===== Shaping of DApp has started... =====')); }, + 'SuccessfulShaping': () => { logger.info(chalk.greenBright(`===== Successful shaping =====`)); }, + 'UnsuccessfulShaping': (error) => { logger.error(chalk.redBright(`===== Unsuccessful shaping =====`), error); }, + 'InvalidShapeName': (name) => { logger.info(chalk.redBright(`===== Invalid shape name ${name} =====`)); }, } \ No newline at end of file diff --git a/cli-commands/commands/test/index.js b/cli-commands/commands/test/index.js index 64777db..025f963 100644 --- a/cli-commands/commands/test/index.js +++ b/cli-commands/commands/test/index.js @@ -1,4 +1,5 @@ const Command = require('../command'); +const commandMessages = require('./messages'); const testCommandDefinition = require('./definition'); const eoslime = require('../../../index'); @@ -16,6 +17,8 @@ class TestCommand extends Command { async execute (args) { try { + commandMessages.StartTesting(); + args.eoslime = eoslime.init(); args.testFramework = new this.TestFramework(); @@ -25,8 +28,10 @@ class TestCommand extends Command { args.testFramework.setDescribeArgs(args.eoslime); args.testFramework.runTests(); + + commandMessages.SuccessfulTesting(); } catch (error) { - console.log(error); + commandMessages.UnsuccessfulTesting(error); } return true; diff --git a/cli-commands/commands/test/messages.js b/cli-commands/commands/test/messages.js index 3f29265..b04d5b3 100644 --- a/cli-commands/commands/test/messages.js +++ b/cli-commands/commands/test/messages.js @@ -1,14 +1,8 @@ const chalk = require('chalk'); +const logger = require('../../common/logger'); module.exports = { - 'StartTesting': () => { console.log(chalk.magentaBright('===== Deployment has started... =====')); }, - 'SuccessfulDeploymentOfScript': (script) => { console.log(chalk.greenBright(`===== Successful deployment of ${script} =====`)); }, - 'UnsuccessfulDeploymentOfScript': (script, error) => { - console.log(chalk.redBright(`===== Unsuccessful deployment of ${script} =====`)); - console.log(error); - }, - 'UnsuccessfulDeployment': (error) => { - console.log(chalk.redBright(`===== Unsuccessful deployment =====`)); - console.log(error); - } + 'StartTesting': () => { logger.info(chalk.magentaBright('===== Testing has started... =====')); }, + 'SuccessfulTesting': () => { logger.info(chalk.greenBright(`===== Testing completed successfully =====`)); }, + 'UnsuccessfulTesting': (error) => { logger.error(chalk.redBright(`===== Testing failed =====`), error); } } \ No newline at end of file diff --git a/cli-commands/common/logger.js b/cli-commands/common/logger.js new file mode 100644 index 0000000..3962835 --- /dev/null +++ b/cli-commands/common/logger.js @@ -0,0 +1,11 @@ +const logger = { + info: (message) => { + console.log(message); + }, + error: (message, error) => { + console.log(message); + console.log(error); + } +} + +module.exports = logger; \ No newline at end of file diff --git a/cli-commands/helpers/file-system-util.js b/cli-commands/helpers/file-system-util.js index 71d9c84..f468849 100644 --- a/cli-commands/helpers/file-system-util.js +++ b/cli-commands/helpers/file-system-util.js @@ -75,9 +75,6 @@ const fileSystemUtils = { }); }); }, - exists: (path) => { - return fs.existsSync(path); - }, readFile: (filePath) => { return fs.readFileSync(filePath); }, diff --git a/package.json b/package.json index 329d5b8..856cdf0 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "eoslime": "./cli.js" }, "devDependencies": { + "fs-extra": "9.0.1", "nyc": "14.1.1", "sinon": "9.0.2" } diff --git a/tests/cli-commands/compile-tests.js b/tests/cli-commands/compile-tests.js index 301bd73..c5f96aa 100644 --- a/tests/cli-commands/compile-tests.js +++ b/tests/cli-commands/compile-tests.js @@ -9,6 +9,7 @@ const fileSysUtils = require('../../cli-commands/helpers/file-system-util'); const definition = require('../../cli-commands/commands/compile/definition'); const PathOption = require('../../cli-commands/commands/compile/options/path-option'); const directories = require('../../cli-commands/commands/compile/specific/directories.json'); +const logger = require('../../cli-commands/common/logger'); describe('CompileCommand', function () { const TEST_DIR = './cli-commands-test'; @@ -29,6 +30,8 @@ describe('CompileCommand', function () { beforeEach(async () => { compileCommand = new CompileCommand(); + sinon.stub(logger, "info"); + sinon.stub(logger, "error"); sinon.stub(AsyncSoftExec.prototype, "exec"); pathOptionSpy = sinon.spy(PathOption, "process"); fsCreateDirSpy = sinon.spy(fileSysUtils, "createDir"); diff --git a/tests/cli-commands/deploy-tests.js b/tests/cli-commands/deploy-tests.js index dbf67d8..17d49d4 100644 --- a/tests/cli-commands/deploy-tests.js +++ b/tests/cli-commands/deploy-tests.js @@ -4,6 +4,7 @@ const assert = require('assert'); const prompts = require('prompts'); const eoslime = require('../../index'); +const logger = require('../../cli-commands/common/logger'); const Command = require('../../cli-commands/commands/command'); const Account = require('../../src/account/normal-account/account'); const DeployCommand = require('../../cli-commands/commands/deploy/index'); @@ -43,6 +44,8 @@ describe('DeployCommand', function () { pathOptionSpy = sinon.spy(PathOption, "process"); networkOptionSpy = sinon.spy(NetworkOption, "process"); deployerOptionSpy = sinon.spy(DeployerOption, "process"); + sinon.stub(logger, "info"); + sinon.stub(logger, "error"); preloadDeploymentScript(); }); diff --git a/tests/cli-commands/init-tests.js b/tests/cli-commands/init-tests.js index 6707c97..5697575 100644 --- a/tests/cli-commands/init-tests.js +++ b/tests/cli-commands/init-tests.js @@ -2,6 +2,7 @@ const fs = require('fs-extra'); const sinon = require('sinon'); const assert = require('assert'); +const logger = require('../../cli-commands/common/logger'); const Command = require('../../cli-commands/commands/command'); const InitCommand = require('../../cli-commands/commands/init/index'); const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); @@ -28,6 +29,8 @@ describe('InitCommand', function () { beforeEach(async () => { initCommand = new InitCommand(); + sinon.stub(logger, "info"); + sinon.stub(logger, "error"); sinon.stub(AsyncSoftExec.prototype, "exec"); exampleOptionSpy = sinon.spy(WithExampleOption, "process"); fileSystemUtilSpy = sinon.spy(fileSystemUtil, "copyAllFilesFromDirTo"); diff --git a/tests/cli-commands/nodeos-tests.js b/tests/cli-commands/nodeos-tests.js index 735a0f8..f7dab92 100644 --- a/tests/cli-commands/nodeos-tests.js +++ b/tests/cli-commands/nodeos-tests.js @@ -22,6 +22,7 @@ const accountsDefinition = require('../../cli-commands/commands/nodeos/subcomman const PathOption = require('../../cli-commands/commands/nodeos/subcommands/start/options/path-option'); const LinesOption = require('../../cli-commands/commands/nodeos/subcommands/logs/options/lines-option'); +const logger = require('../../cli-commands/common/logger'); const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); const template = require('../../cli-commands/commands/nodeos/subcommands/start/specific/template'); const predefinedAccounts = require('../../cli-commands/commands/nodeos/subcommands/common/accounts'); @@ -42,6 +43,11 @@ describe('NodeosCommand', function () { fs.mkdirSync('./nodeos-data'); }); + beforeEach(async () => { + sinon.stub(logger, "info"); + sinon.stub(logger, "error"); + }); + afterEach(async () => { sinon.restore(); cleanNodeosDataFolder(); diff --git a/tests/cli-commands/shape-tests.js b/tests/cli-commands/shape-tests.js index 5f237d9..9c6574f 100644 --- a/tests/cli-commands/shape-tests.js +++ b/tests/cli-commands/shape-tests.js @@ -2,6 +2,7 @@ const sinon = require('sinon'); const assert = require('assert'); const Git = require('simple-git/src/git'); +const logger = require('../../cli-commands/common/logger'); const Command = require('../../cli-commands/commands/command'); const ShapeCommand = require('../../cli-commands/commands/shape/index'); const definition = require('../../cli-commands/commands/shape/definition'); @@ -19,6 +20,8 @@ describe('ShapeCommand', function () { beforeEach(async () => { shapeCommand = new ShapeCommand(); frameworkOptionSpy = sinon.spy(FrameworkOption, "process"); + sinon.stub(logger, "info"); + sinon.stub(logger, "error"); }); afterEach(async () => { diff --git a/tests/cli-commands/test-tests.js b/tests/cli-commands/test-tests.js index 173e4fe..dca8998 100644 --- a/tests/cli-commands/test-tests.js +++ b/tests/cli-commands/test-tests.js @@ -8,6 +8,7 @@ const TestCommand = require('../../cli-commands/commands/test/index'); const ProviderFactory = require('../../src/network-providers/provider-factory'); const MochaFramework = require('../../cli-commands/commands/test/specific/test-frameworks/mocha'); +const logger = require('../../cli-commands/common/logger'); const definition = require('../../cli-commands/commands/test/definition'); const PathOption = require('../../cli-commands/commands/test/options/path-option'); const NetworkOption = require('../../cli-commands/commands/test/options/network-option'); @@ -39,6 +40,8 @@ describe('TestCommand', function () { }); beforeEach(async () => { + sinon.stub(logger, "info"); + sinon.stub(logger, "error"); eoslimeSpy = sinon.spy(eoslime, "init"); testCommand = new TestCommand(MochaFramework); pathOptionSpy = sinon.spy(PathOption, "process"); @@ -153,7 +156,7 @@ describe('TestCommand', function () { sinon.assert.calledOnce(mochaRunTestsSpy); }); - it('Should execute tests and display resource usage report', async () => { + it('Should execute tests and not display resource usage report', async () => { assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'false' })); sinon.assert.calledWith(resourceReportOptionSpy, 'false'); From f3887c3b15e8781aae7e675dec7e754af109359a Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 26 Jun 2020 11:15:05 +0300 Subject: [PATCH 13/35] updated src helpers tests --- tests/helpers-tests.js | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/tests/helpers-tests.js b/tests/helpers-tests.js index 45dedde..fb21563 100644 --- a/tests/helpers-tests.js +++ b/tests/helpers-tests.js @@ -1,4 +1,6 @@ +const sinon = require('sinon'); const assert = require('assert'); +const cryptoJS = require('crypto-js'); const is = require('./../src/helpers/is'); const crypto = require('./../src/helpers/crypto'); const EventClass = require('./../src/helpers/event-class'); @@ -13,22 +15,33 @@ describe('Helpers scripts', function () { const expectedHash = 'a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e'; const resultedHash = crypto.hash('Hello World'); - assert(expectedHash == resultedHash) + assert(expectedHash == resultedHash); }); it('Should encrypt and decrypt a data', async () => { const encryptedData = crypto.encrypt('Hello World', '123'); const decryptedData = crypto.decrypt(encryptedData, '123'); - assert(decryptedData == 'Hello World') + assert(decryptedData == 'Hello World'); + }); + + it('Should throw if it is not able to hash the data', async () => { + sinon.stub(cryptoJS, "SHA256").throws(); + + try { + crypto.hash(); + } catch (error) { + assert(error.message.includes('Couldn\'t hash the data')); + } + + sinon.restore(); }); it('Should throw if it is not able to encrypt a data', async () => { try { crypto.encrypt('Hello World', { fake: 'FAKE' }); - assert(false); } catch (error) { - assert(error.message.includes('Couldn\'t encrypt the data')) + assert(error.message.includes('Couldn\'t encrypt the data')); } }); @@ -36,9 +49,8 @@ describe('Helpers scripts', function () { try { const encryptedData = crypto.encrypt('Hello World', '123'); crypto.decrypt(encryptedData, { fake: 'FAKE' }); - assert(false); } catch (error) { - assert(error.message.includes('Couldn\'t decrypt the data')) + assert(error.message.includes('Couldn\'t decrypt the data')); } }); }); @@ -58,8 +70,24 @@ describe('Helpers scripts', function () { eventClass.on('created', () => x++); eventClass.emit('created'); - assert(x == 2) + assert(x == 2); }); + + it('Should not be able to subscribe for unknown event', async () => { + const eventClass = new EventClass({ }); + eventClass.on('created', () => { }); + + assert(eventClass.eventsHooks['created'] == undefined); + }); + + it('Should be able to subscribe twice for an event', async () => { + const eventClass = new EventClass({ 'created': 'created' }); + eventClass.on('created', () => { }); + eventClass.on('created', () => { }); + + assert(eventClass.eventsHooks['created'].length == 2); + }); + }); describe('is.js', function () { From 4b30fb8629cb5502d6c2a3831ee6d7db20c6cf9d Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 26 Jun 2020 11:39:23 +0300 Subject: [PATCH 14/35] updated cli commands tests --- .../commands/deploy/options/network-option.js | 4 +- src/helpers/crypto.js | 2 +- tests/cli-commands/command-tests.js | 20 +++++++++ tests/cli-commands/common/logger-tests.js | 30 +++++++++++++ tests/cli-commands/compile-tests.js | 10 ++++- tests/cli-commands/deploy-tests.js | 14 +++++++ tests/cli-commands/group-command-tests.js | 26 ++++++++++++ .../helpers/async-soft-exec-tests.js | 42 +++++++++++++++++++ tests/cli-commands/init-tests.js | 9 +++- tests/cli-commands/mocks/test-option.js | 19 +++++++++ 10 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 tests/cli-commands/command-tests.js create mode 100644 tests/cli-commands/common/logger-tests.js create mode 100644 tests/cli-commands/group-command-tests.js create mode 100644 tests/cli-commands/helpers/async-soft-exec-tests.js create mode 100644 tests/cli-commands/mocks/test-option.js diff --git a/cli-commands/commands/deploy/options/network-option.js b/cli-commands/commands/deploy/options/network-option.js index e8b11a8..abaf971 100644 --- a/cli-commands/commands/deploy/options/network-option.js +++ b/cli-commands/commands/deploy/options/network-option.js @@ -15,9 +15,7 @@ class NetworkOption extends Option { } process (optionValue) { - if (optionValue) { - return eoslime.init(optionValue); - } + return eoslime.init(optionValue); } } diff --git a/src/helpers/crypto.js b/src/helpers/crypto.js index 1396330..e6f1075 100644 --- a/src/helpers/crypto.js +++ b/src/helpers/crypto.js @@ -6,7 +6,7 @@ module.exports = { let dataHash = cryptoJS.SHA256(data).toString(cryptoJS.enc.Hex); return dataHash; } catch (error) { - throw new Error('Couldn\'t hash the data') + throw new Error('Couldn\'t hash the data'); } }, encrypt: function (data, password) { diff --git a/tests/cli-commands/command-tests.js b/tests/cli-commands/command-tests.js new file mode 100644 index 0000000..60fb798 --- /dev/null +++ b/tests/cli-commands/command-tests.js @@ -0,0 +1,20 @@ +const assert = require('assert'); + +const Command = require('../../cli-commands/commands/command'); + +describe('Command', function () { + let command; + + beforeEach(async () => { + command = new Command({}); + }); + + it('Should initialize command properly', async () => { + assert(command instanceof Command); + assert(command.template == ''); + assert(command.description == ''); + assert(command.options.length == 0); + assert(command.subcommands.length == 0); + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/common/logger-tests.js b/tests/cli-commands/common/logger-tests.js new file mode 100644 index 0000000..5872602 --- /dev/null +++ b/tests/cli-commands/common/logger-tests.js @@ -0,0 +1,30 @@ +const sinon = require('sinon'); +const logger = require('../../../cli-commands/common/logger'); + +describe('Logger', function () { + + let consoleLogSpy; + + beforeEach(async () => { + consoleLogSpy = sinon.spy(console, "log"); + }); + + afterEach(async () => { + sinon.restore(); + }); + + it('Should log info message', async () => { + logger.info('Test info message'); + + sinon.assert.calledWith(consoleLogSpy, 'Test info message'); + }); + + it('Should log error message', async () => { + logger.error('Test error message', 'Error'); + + sinon.assert.calledTwice(consoleLogSpy); + consoleLogSpy.firstCall.calledWith('Test error message'); + consoleLogSpy.secondCall.calledWith('Error'); + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/compile-tests.js b/tests/cli-commands/compile-tests.js index c5f96aa..f38b946 100644 --- a/tests/cli-commands/compile-tests.js +++ b/tests/cli-commands/compile-tests.js @@ -7,6 +7,7 @@ const CompileCommand = require('../../cli-commands/commands/compile/index'); const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); const fileSysUtils = require('../../cli-commands/helpers/file-system-util'); const definition = require('../../cli-commands/commands/compile/definition'); +const commandMessages = require('../../cli-commands/commands/compile/messages'); const PathOption = require('../../cli-commands/commands/compile/options/path-option'); const directories = require('../../cli-commands/commands/compile/specific/directories.json'); const logger = require('../../cli-commands/common/logger'); @@ -32,7 +33,6 @@ describe('CompileCommand', function () { compileCommand = new CompileCommand(); sinon.stub(logger, "info"); sinon.stub(logger, "error"); - sinon.stub(AsyncSoftExec.prototype, "exec"); pathOptionSpy = sinon.spy(PathOption, "process"); fsCreateDirSpy = sinon.spy(fileSysUtils, "createDir"); }); @@ -70,6 +70,10 @@ describe('CompileCommand', function () { }); it('Should compile when valid contracts folder is specified', async () => { + sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { + commandMessages.SuccessfulCompilationOfContract(); + }); + createContractsFolder(); preloadContracts(); @@ -100,6 +104,10 @@ describe('CompileCommand', function () { }); it('Should compile when valid contract path is specified', async () => { + sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { + commandMessages.UnsuccessfulCompilationOfContract(); + }); + createContractsFolder(); preloadContracts(); diff --git a/tests/cli-commands/deploy-tests.js b/tests/cli-commands/deploy-tests.js index 17d49d4..07996ed 100644 --- a/tests/cli-commands/deploy-tests.js +++ b/tests/cli-commands/deploy-tests.js @@ -162,6 +162,20 @@ describe('DeployCommand', function () { assert(result.executiveAuthority.permission == DEPLOYER_PERMISSION); }); + it('Should deploy when deployer is provided [permission missing]', async () => { + prompts.inject(`${DEPLOYER_PRIVATE_KEY}`); + + assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, deployer: DEPLOYER_NAME })); + + sinon.assert.calledWith(deployerOptionSpy, DEPLOYER_NAME); + + let result = await deployerOptionSpy.returnValues[0]; + assert(result instanceof Account); + assert(result.name == DEPLOYER_NAME); + assert(result.privateKey == DEPLOYER_PRIVATE_KEY); + assert(result.executiveAuthority.permission == 'active'); + }); + it('Should throw when invalid deployer private key is provided', async () => { prompts.inject(`${INVALID_PRIVATE_KEY}@${DEPLOYER_PERMISSION}`); diff --git a/tests/cli-commands/group-command-tests.js b/tests/cli-commands/group-command-tests.js new file mode 100644 index 0000000..f4d0331 --- /dev/null +++ b/tests/cli-commands/group-command-tests.js @@ -0,0 +1,26 @@ +const assert = require('assert'); + +const TestOption = require('../cli-commands/mocks/test-option'); +const GroupCommand = require('../../cli-commands/commands/group-command'); + +describe('GroupCommand', function () { + let groupCommand; + + beforeEach(async () => { + groupCommand = new GroupCommand({ options: [ TestOption ] }); + }); + + it('Should initialize group command properly', async () => { + assert(groupCommand instanceof GroupCommand); + assert(groupCommand.options.length > 0); + }); + + it('Should process valid option', async () => { + assert(await groupCommand.execute({ test: 'true' })); + }); + + it('Should return if not found valid option', async () => { + assert(!(await groupCommand.execute({ sample: 'abc' }))); + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/helpers/async-soft-exec-tests.js b/tests/cli-commands/helpers/async-soft-exec-tests.js new file mode 100644 index 0000000..469268b --- /dev/null +++ b/tests/cli-commands/helpers/async-soft-exec-tests.js @@ -0,0 +1,42 @@ +const assert = require('assert'); + +const AsyncSoftExec = require('../../../cli-commands/helpers/async-soft-exec'); + +const COMMAND_ECHO = 'echo' +const INVALID_COMMAND = 'ech'; + +describe('AsyncSoftExec', function () { + let asyncSoftExec; + + it('Should execute sample command [default success callback]', async () => { + asyncSoftExec = new AsyncSoftExec(COMMAND_ECHO); + + assert(await asyncSoftExec.exec()); + }); + + it('Should execute sample command', async () => { + asyncSoftExec = new AsyncSoftExec(COMMAND_ECHO); + asyncSoftExec.onSuccess(() => {}); + + assert(await asyncSoftExec.exec()); + }); + + it('Should throw when command is unknown [default error callback]', async () => { + asyncSoftExec = new AsyncSoftExec(INVALID_COMMAND); + + try { + await asyncSoftExec.exec(); + } catch (error) { + assert(true); + } + }); + + it('Should throw when command is unknown', async () => { + asyncSoftExec = new AsyncSoftExec(INVALID_COMMAND); + asyncSoftExec.onError(() => {}); + + await asyncSoftExec.exec(); + assert(true); + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/init-tests.js b/tests/cli-commands/init-tests.js index 5697575..3f19fae 100644 --- a/tests/cli-commands/init-tests.js +++ b/tests/cli-commands/init-tests.js @@ -7,6 +7,7 @@ const Command = require('../../cli-commands/commands/command'); const InitCommand = require('../../cli-commands/commands/init/index'); const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); const definition = require('../../cli-commands/commands/init/definition'); +const commandMessages = require('../../cli-commands/commands/init/messages'); const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); const directories = require('../../cli-commands/commands/init/specific/directories.json'); const WithExampleOption = require('../../cli-commands/commands/init/options/with-example/with-example-option'); @@ -31,7 +32,7 @@ describe('InitCommand', function () { initCommand = new InitCommand(); sinon.stub(logger, "info"); sinon.stub(logger, "error"); - sinon.stub(AsyncSoftExec.prototype, "exec"); + sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { commandMessages.SuccessfulInstallation(); }); exampleOptionSpy = sinon.spy(WithExampleOption, "process"); fileSystemUtilSpy = sinon.spy(fileSystemUtil, "copyAllFilesFromDirTo"); }); @@ -97,4 +98,10 @@ describe('InitCommand', function () { checkExampleFiles(); }); + it('Should throw when processing command options failed', async () => { + sinon.stub(Command.prototype, "processOptions").throws('Test: Process Options Failure'); + + assert(await initCommand.execute({})); + }); + }); \ No newline at end of file diff --git a/tests/cli-commands/mocks/test-option.js b/tests/cli-commands/mocks/test-option.js new file mode 100644 index 0000000..36d487f --- /dev/null +++ b/tests/cli-commands/mocks/test-option.js @@ -0,0 +1,19 @@ +const Option = require('../../../cli-commands/commands/option'); + +class TestOption extends Option { + constructor() { + super( + 'test', + { + "describe": "Test option description", + "type": "boolean" + } + ); + } + + process (optionValue) { + return optionValue; + } +} + +module.exports = new TestOption(); From 5250b38436f39b71b8b425f51216bc3235a3dd6e Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 26 Jun 2020 13:19:54 +0300 Subject: [PATCH 15/35] updated cli commands tests --- .../helpers/file-system-util-tests.js | 71 +++++++++++++++++++ tests/cli-commands/nodeos-tests.js | 15 ++-- tests/cli-commands/test-tests.js | 13 +--- 3 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 tests/cli-commands/helpers/file-system-util-tests.js diff --git a/tests/cli-commands/helpers/file-system-util-tests.js b/tests/cli-commands/helpers/file-system-util-tests.js new file mode 100644 index 0000000..36183fa --- /dev/null +++ b/tests/cli-commands/helpers/file-system-util-tests.js @@ -0,0 +1,71 @@ +const fs = require('fs-extra'); +const sinon = require('sinon'); +const assert = require('assert'); + +const fileSystemUtil = require('../../../cli-commands/helpers/file-system-util'); + +describe('FileSystemUtil', function () { + const TEST_DIR = './cli-commands-test'; + + before(async () => { + initialDir = process.cwd(); + fs.mkdirSync(TEST_DIR); + process.chdir(TEST_DIR); + }); + + beforeEach(async () => { + fs.mkdirSync('./root'); + fs.mkdirSync('./root/child'); + fs.createFileSync('./root/sample.txt'); + }); + + afterEach(async () => { + sinon.restore(); + }); + + after(async () => { + process.chdir(initialDir); + fs.removeSync(TEST_DIR); + }); + + // it('Should throw when trying to copy all files from not existing folder', async () => { + // try { + // await fileSystemUtil.copyAllFilesFromDirTo('./a', './b'); + // } catch (error) { + // assert(error.message.includes('Example files can not be copied')); + // } + // }); + + it('Should recursively read dir content', async () => { + let files = await fileSystemUtil.recursivelyReadDir('root'); + + assert(files.length == 1); + assert(files[0].fileName == 'sample.txt'); + assert(files[0].fullPath == 'root/sample.txt');fs.removeSync('./root'); + + fs.removeSync('./root'); + }); + + it('Should recursively delete dir content', async () => { + await fileSystemUtil.recursivelyDeleteDir('root'); + + assert(!fs.existsSync('./root')); + }); + + it('Should create new file', async () => { + fileSystemUtil.writeFile('sample.txt', 'text'); + + assert(fs.existsSync('sample.txt')); + + fs.removeSync('sample.txt'); + }); + + it('Should throw when trying to create undefined file', async () => { + try { + fileSystemUtil.writeFile(undefined, 'text'); + } catch (error) { + assert(error.message.includes('Storing content failed')); + } + }); + +}); \ No newline at end of file diff --git a/tests/cli-commands/nodeos-tests.js b/tests/cli-commands/nodeos-tests.js index f7dab92..2defef2 100644 --- a/tests/cli-commands/nodeos-tests.js +++ b/tests/cli-commands/nodeos-tests.js @@ -19,6 +19,7 @@ const logsDefinition = require('../../cli-commands/commands/nodeos/subcommands/l const stopDefinition = require('../../cli-commands/commands/nodeos/subcommands/stop/definition'); const startDefinition = require('../../cli-commands/commands/nodeos/subcommands/start/definition'); const accountsDefinition = require('../../cli-commands/commands/nodeos/subcommands/accounts/definition'); +const logsCommandMessages = require('../../cli-commands/commands/nodeos/subcommands/logs/messages'); const PathOption = require('../../cli-commands/commands/nodeos/subcommands/start/options/path-option'); const LinesOption = require('../../cli-commands/commands/nodeos/subcommands/logs/options/lines-option'); @@ -193,10 +194,10 @@ describe('NodeosCommand', function () { sinon.assert.calledWith(fsReadFileSpy, nodeosDataPath + '/eosd.pid'); sinon.assert.calledTwice(fsRemoveFileSpy); fsRemoveFileSpy.firstCall.calledWith(nodeosDataPath + '/eosd.pid'); - fsRemoveFileSpy.firstCall.calledWith(nodeosDataPath + '/nodeos.log'); + fsRemoveFileSpy.secondCall.calledWith(nodeosDataPath + '/nodeos.log'); sinon.assert.calledTwice(fsRemoveDirSpy); fsRemoveDirSpy.firstCall.calledWith(nodeosDataPath + '/data'); - fsRemoveDirSpy.firstCall.calledWith(nodeosDataPath + '/config'); + fsRemoveDirSpy.secondCall.calledWith(nodeosDataPath + '/config'); }); it('Should throw when unexpected error occured', async () => { @@ -218,7 +219,6 @@ describe('NodeosCommand', function () { beforeEach(async () => { logsCommand = new LogsCommand(); - sinon.stub(AsyncSoftExec.prototype, "exec"); sinon.stub(nodoesDataManager, "nodeosPath").returns(nodeosDataPath); linesOptionSpy = sinon.spy(LinesOption, "process"); }); @@ -234,6 +234,7 @@ describe('NodeosCommand', function () { it('Should break when there is no running nodeos', async () => { preloadNodeosData(); + sinon.stub(AsyncSoftExec.prototype, "exec"); sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(false); let processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); @@ -245,6 +246,7 @@ describe('NodeosCommand', function () { it('Should display nodeos logs successfully', async () => { preloadNodeosData(); + sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { logsCommandMessages.NodeosLogs(); }); sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); assert(await logsCommand.execute({ lines: 10 })); @@ -282,7 +284,7 @@ describe('NodeosCommand', function () { it('Should display preloaded accounts', async () => { let addTableRowSpy = sinon.spy(AccountsTable.prototype, "addRow"); - let drawTableSpy = sinon.spy(AccountsTable.prototype, "draw"); + sinon.stub(AccountsTable.prototype, "draw"); assert(await accountsCommand.execute()); @@ -290,9 +292,8 @@ describe('NodeosCommand', function () { sinon.assert.calledThrice(addTableRowSpy); addTableRowSpy.firstCall.calledWith(accounts[0].name, accounts[0].publicKey, accounts[0].privateKey); - addTableRowSpy.firstCall.calledWith(accounts[1].name, accounts[1].publicKey, accounts[1].privateKey); - addTableRowSpy.firstCall.calledWith(accounts[2].name, accounts[2].publicKey, accounts[2].privateKey); - sinon.assert.calledOnce(drawTableSpy); + addTableRowSpy.secondCall.calledWith(accounts[1].name, accounts[1].publicKey, accounts[1].privateKey); + addTableRowSpy.thirdCall.calledWith(accounts[2].name, accounts[2].publicKey, accounts[2].privateKey); }); it('Should throw when table drawing failed', async () => { diff --git a/tests/cli-commands/test-tests.js b/tests/cli-commands/test-tests.js index dca8998..47a4c85 100644 --- a/tests/cli-commands/test-tests.js +++ b/tests/cli-commands/test-tests.js @@ -30,7 +30,6 @@ describe('TestCommand', function () { let resourceReportOptionSpy; let mochaAddTestFilesSpy; let mochaSetDescribeArgs; - let mochaRunTestsSpy; let providerFactorySpy; before(async () => { @@ -42,6 +41,7 @@ describe('TestCommand', function () { beforeEach(async () => { sinon.stub(logger, "info"); sinon.stub(logger, "error"); + sinon.stub(MochaFramework.prototype, "runTests"); eoslimeSpy = sinon.spy(eoslime, "init"); testCommand = new TestCommand(MochaFramework); pathOptionSpy = sinon.spy(PathOption, "process"); @@ -49,7 +49,6 @@ describe('TestCommand', function () { resourceReportOptionSpy = sinon.spy(ResourceReportOption, "process"); mochaAddTestFilesSpy = sinon.spy(MochaFramework.prototype, "addTestFiles"); mochaSetDescribeArgs = sinon.spy(MochaFramework.prototype, "setDescribeArgs"); - mochaRunTestsSpy = sinon.spy(MochaFramework.prototype, "runTests"); providerFactorySpy = sinon.spy(ProviderFactory.prototype, "reset"); preloadMockedTests(); @@ -84,7 +83,6 @@ describe('TestCommand', function () { sinon.assert.calledOnce(eoslimeSpy); sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); sinon.assert.calledWith(mochaAddTestFilesSpy, ["./tests/tests-mock.js"]); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should throw when invalid tests folder is specified', async () => { @@ -92,7 +90,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); sinon.assert.notCalled(mochaAddTestFilesSpy); - sinon.assert.notCalled(mochaRunTestsSpy); }); it('Should not throw when tests folder is empty', async () => { @@ -102,7 +99,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); sinon.assert.calledWith(mochaAddTestFilesSpy, []); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should execute tests when path to file with tests is provided', async () => { @@ -110,7 +106,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/tests-mock.js`); sinon.assert.calledOnce(mochaAddTestFilesSpy); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should not throw when file without tests is provided', async () => { @@ -120,7 +115,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(pathOptionSpy, './tests.txt'); sinon.assert.calledOnce(mochaAddTestFilesSpy); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should execute tests when valid network is specified', async () => { @@ -129,7 +123,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(networkOptionSpy, DEFAULT_NETWORK); sinon.assert.calledOnce(providerFactorySpy); sinon.assert.calledOnce(mochaSetDescribeArgs); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should execute tests when custom network url and chainId are provided', async () => { @@ -138,7 +131,6 @@ describe('TestCommand', function () { sinon.assert.calledWith(networkOptionSpy, CUSTOM_NETWORK); sinon.assert.calledOnce(providerFactorySpy); sinon.assert.calledOnce(mochaSetDescribeArgs); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should throw when invalid network is specified', async () => { @@ -146,21 +138,18 @@ describe('TestCommand', function () { sinon.assert.calledWith(networkOptionSpy, INVALID_NETWORK); sinon.assert.notCalled(providerFactorySpy); - sinon.assert.notCalled(mochaRunTestsSpy); }); it('Should execute tests and display resource usage report', async () => { assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'true' })); sinon.assert.calledWith(resourceReportOptionSpy, 'true'); - sinon.assert.calledOnce(mochaRunTestsSpy); }); it('Should execute tests and not display resource usage report', async () => { assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'false' })); sinon.assert.calledWith(resourceReportOptionSpy, 'false'); - sinon.assert.calledOnce(mochaRunTestsSpy); }); }); \ No newline at end of file From a88c322bfaa04074556f4ef93157c94fb6f3c66d Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 26 Jun 2020 16:08:40 +0300 Subject: [PATCH 16/35] updated fs util tests --- tests/cli-commands/helpers/file-system-util-tests.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/cli-commands/helpers/file-system-util-tests.js b/tests/cli-commands/helpers/file-system-util-tests.js index 36183fa..ae83fe8 100644 --- a/tests/cli-commands/helpers/file-system-util-tests.js +++ b/tests/cli-commands/helpers/file-system-util-tests.js @@ -21,6 +21,7 @@ describe('FileSystemUtil', function () { afterEach(async () => { sinon.restore(); + fs.removeSync('./root'); }); after(async () => { @@ -28,22 +29,12 @@ describe('FileSystemUtil', function () { fs.removeSync(TEST_DIR); }); - // it('Should throw when trying to copy all files from not existing folder', async () => { - // try { - // await fileSystemUtil.copyAllFilesFromDirTo('./a', './b'); - // } catch (error) { - // assert(error.message.includes('Example files can not be copied')); - // } - // }); - it('Should recursively read dir content', async () => { let files = await fileSystemUtil.recursivelyReadDir('root'); assert(files.length == 1); assert(files[0].fileName == 'sample.txt'); assert(files[0].fullPath == 'root/sample.txt');fs.removeSync('./root'); - - fs.removeSync('./root'); }); it('Should recursively delete dir content', async () => { From b8bf0f7ab012ca2f9c744afa4d9d5d8c8454c18d Mon Sep 17 00:00:00 2001 From: vladimirhristov Date: Fri, 26 Jun 2020 16:09:36 +0300 Subject: [PATCH 17/35] updated scripts --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 856cdf0..86dc5d2 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,8 @@ "description": "eoslime is an EOS development and deployment framework based on eosjs.js", "main": "index.js", "scripts": { - "test": "bash ./tests/testing-contracts/compile.sh && nyc --check-coverage mocha './tests/*.js'", + "test": "bash ./tests/testing-contracts/compile.sh && nyc --check-coverage mocha './tests/**/*.js'", "test-dev": "bash ./tests/testing-contracts/compile.sh && mocha './tests/*.js'", - "test-cli": "mocha './tests/cli-commands/*.js'", "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" }, "author": "Lyubomir Kiprov (Limechain)", From d17ecb3f33e2e6a4e5f9bdfacccb14af31ec5058 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Tue, 30 Jun 2020 16:55:25 +0300 Subject: [PATCH 18/35] [BREAKING CHANGES] Move all contract actions from contract.smth to contract.actions.smth; Move all contract tables from contract.sometable to contract.tables.sometable --- src/contract/contract.js | 7 ++++-- tests/contract-tests.js | 54 ++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/contract/contract.js b/src/contract/contract.js index cf21ab1..2404670 100644 --- a/src/contract/contract.js +++ b/src/contract/contract.js @@ -8,6 +8,9 @@ class Contract { this.provider = provider; this.executor = contractExecutorAccount; + this.actions = {}; + this.tables = {}; + declareFunctionsFromABI.call(this, abi); declareTableGetters.call(this, abi); } @@ -37,7 +40,7 @@ let declareFunctionsFromABI = function (abi) { for (let i = 0; i < contractActions.length; i++) { const functionName = contractActions[i].name; const functionType = contractActions[i].type; - this[functionName] = FunctionsFactory.createFunction(this, functionName, contractStructs[functionType].fields) + this.actions[functionName] = FunctionsFactory.createFunction(this, functionName, contractStructs[functionType].fields) } }; @@ -46,7 +49,7 @@ let declareTableGetters = function (abi) { for (let i = 0; i < contractTables.length; i++) { const tableName = contractTables[i].name; - this[tableName] = new Proxy({}, { + this.tables[tableName] = new Proxy({}, { get: (target, name) => { return this.provider.select(tableName).from(this.name)[name]; } diff --git a/tests/contract-tests.js b/tests/contract-tests.js index 15d9c37..ba9af00 100644 --- a/tests/contract-tests.js +++ b/tests/contract-tests.js @@ -10,7 +10,7 @@ const FAUCET_WASM_PATH = "./tests/testing-contracts/compiled/faucet.wasm"; You should have running local nodeos in order to run tests */ -describe("Contract", function () { +describe.only("Contract", function () { // Increase mocha(testing framework) time, otherwise tests fails this.timeout(15000); @@ -29,7 +29,7 @@ describe("Contract", function () { try { const tokenAccount = await eoslime.Account.createRandom(); tokenContract = await eoslime.Contract.deployOnAccount(TOKEN_WASM_PATH, TOKEN_ABI_PATH, tokenAccount); - await tokenContract.create(faucetAccount.name, TOTAL_SUPPLY); + await tokenContract.actions.create(faucetAccount.name, TOTAL_SUPPLY); } catch (error) { console.log(error); } @@ -60,8 +60,8 @@ describe("Contract", function () { it("Should instantiate correct instance of Contract from ABI file", async () => { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); - assert(typeof faucetContract.produce == "function"); - assert(typeof faucetContract.withdraw == "function"); + assert(typeof faucetContract.actions.produce == "function"); + assert(typeof faucetContract.actions.withdraw == "function"); assert(faucetContract.name == faucetAccount.name); assert(JSON.stringify(faucetContract.executor) == JSON.stringify(faucetAccount)); @@ -71,8 +71,8 @@ describe("Contract", function () { it("Should instantiate correct instance of Contract from blockchain account name", async () => { const faucetContract = await eoslime.Contract.at(faucetAccount.name, faucetAccount); - assert(typeof faucetContract.produce == "function"); - assert(typeof faucetContract.withdraw == "function"); + assert(typeof faucetContract.actions.produce == "function"); + assert(typeof faucetContract.actions.withdraw == "function"); assert(faucetContract.name == faucetAccount.name); assert(JSON.stringify(faucetContract.executor) == JSON.stringify(faucetAccount)); @@ -92,7 +92,7 @@ describe("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, "INVALID"); eoslime.Provider.defaultAccount = ''; - await faucetContract.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); assert(false, "Should throw"); } catch (error) { @@ -157,9 +157,9 @@ describe("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor - await faucetContract.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); - const result = await faucetContract.withdrawers.limit(1).equal(tokensHolder.name).find(); + const result = await faucetContract.tables.withdrawers.limit(1).equal(tokensHolder.name).find(); assert(result[0].quantity == PRODUCED_TOKENS_AMOUNT); assert(result[0].token_name == tokenContract.name); @@ -170,7 +170,7 @@ describe("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); const executor = await eoslime.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo", { from: executor }); + await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo", { from: executor }); // After the execution, the contract executor should be the same as the initially provided one assert(faucetContract.executor.name == faucetAccount.name); @@ -181,8 +181,8 @@ describe("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); const executor = await eoslime.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor, unique: true }); - await faucetContract.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor, unique: true }); + await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor, unique: true }); + await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor, unique: true }); assert(true); }); @@ -192,8 +192,8 @@ describe("Contract", function () { const executor = await eoslime.Account.createRandom(); try { - await faucetContract.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor }); - await faucetContract.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor }); + await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor }); + await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor }); } catch (error) { assert(error.includes('duplicate transaction')); } @@ -203,7 +203,7 @@ describe("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); const tokensHolder = await eoslime.Account.createRandom(); - const rawActionTx = await faucetContract.produce.getRawTransaction(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); + const rawActionTx = await faucetContract.actions.produce.getRawTransaction(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); assert(rawActionTx.expiration != undefined); assert(rawActionTx.ref_block_num != undefined); assert(rawActionTx.ref_block_prefix != undefined); @@ -223,7 +223,7 @@ describe("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); const signer = await eoslime.Account.createRandom(); - const signedActionTx = await faucetContract.produce.sign(signer, tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); + const signedActionTx = await faucetContract.actions.produce.sign(signer, tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); assert(signedActionTx.signatures.length == 1); assert(signedActionTx.transaction.expiration != undefined); @@ -245,7 +245,7 @@ describe("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); const tokensHolder = await eoslime.Account.createRandom(); - await faucetContract.produce.sign('Fake signer', tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); + await faucetContract.actions.produce.sign('Fake signer', tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); } catch (error) { assert(error.message.includes('String is not an instance of BaseAccount')); } @@ -258,7 +258,7 @@ describe("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); // withdrawers is a table in the contract - assert(faucetContract.withdrawers); + assert(faucetContract.tables.withdrawers); }); it("Should apply the default query params if none provided", async () => { @@ -266,9 +266,9 @@ describe("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor - await faucetContract.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); - const allWithdrawers = await faucetContract.withdrawers.find(); + const allWithdrawers = await faucetContract.tables.withdrawers.find(); assert(allWithdrawers[0].quantity == PRODUCED_TOKENS_AMOUNT); assert(allWithdrawers[0].token_name == tokenContract.name); @@ -279,27 +279,27 @@ describe("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor - await faucetContract.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); // With equal criteria - const equalResult = await faucetContract.withdrawers.equal(tokensHolder.name).find(); + const equalResult = await faucetContract.tables.withdrawers.equal(tokensHolder.name).find(); assert(equalResult[0].quantity == PRODUCED_TOKENS_AMOUNT); assert(equalResult[0].token_name == tokenContract.name); // With range criteria - const rangeResult = await faucetContract.withdrawers.range(0, 100 * TOKEN_PRECISION).index(2).find(); + const rangeResult = await faucetContract.tables.withdrawers.range(0, 100 * TOKEN_PRECISION).index(2).find(); assert(rangeResult[0].quantity == PRODUCED_TOKENS_AMOUNT); assert(rangeResult[0].token_name == tokenContract.name); // With limit // There is only one withdrawer - const allWithdrawers = await faucetContract.withdrawers.limit(10).find(); + const allWithdrawers = await faucetContract.tables.withdrawers.limit(10).find(); assert(allWithdrawers.length == 1); assert(allWithdrawers[0].quantity == PRODUCED_TOKENS_AMOUNT); assert(allWithdrawers[0].token_name == tokenContract.name); // With different index (By Balance) - const balanceWithdrawers = await faucetContract.withdrawers.equal(100 * TOKEN_PRECISION).index(2).find(); + const balanceWithdrawers = await faucetContract.tables.withdrawers.equal(100 * TOKEN_PRECISION).index(2).find(); assert(balanceWithdrawers[0].quantity == PRODUCED_TOKENS_AMOUNT); assert(balanceWithdrawers[0].token_name == tokenContract.name); }); @@ -311,13 +311,13 @@ describe("Contract", function () { await faucetContract.makeInline(); const tokensHolder = await eoslime.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); + await faucetContract.actions.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); const tokensHolderBeforeBalance = await tokensHolder.getBalance("TKNS", tokenContract.name); assert(tokensHolderBeforeBalance.length == 0); // withdraw method behind the scene calls token's contract issue method - await faucetContract.withdraw(tokensHolder.name); + await faucetContract.actions.withdraw(tokensHolder.name); const tokensHolderAfterBalance = await tokensHolder.getBalance("TKNS", tokenContract.name); assert(tokensHolderAfterBalance[0] == PRODUCED_TOKENS_AMOUNT); From 97ec57c92eeec5feacad0bf9b44ec9740f053b28 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Tue, 30 Jun 2020 17:05:31 +0300 Subject: [PATCH 19/35] [BREAKING CHANGES] Contract actions now accepts parameters in form of array instead of ...params --- .../contract-function/contract-function.js | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/contract/contract-function/contract-function.js b/src/contract/contract-function/contract-function.js index d2571a7..e7b3a3a 100644 --- a/src/contract/contract-function/contract-function.js +++ b/src/contract/contract-function/contract-function.js @@ -8,7 +8,7 @@ const EVENTS = { class ContractFunction extends EventClass { - constructor(contract, functionName, functionFields) { + constructor (contract, functionName, functionFields) { super(EVENTS); this.contract = contract; this.functionName = functionName; @@ -16,15 +16,14 @@ class ContractFunction extends EventClass { this.isTransactional = true; } - async broadcast(...params) { + async broadcast (params, options) { is(this.contract.executor).instanceOf('BaseAccount', 'executor is missing'); - const functionParamsCount = this.functionFields.length; - const functionParams = params.slice(0, functionParamsCount); + const functionParams = params.slice(0, this.functionFields.length); const functionRawTxData = buildFunctionRawTxData.call(this, this.contract.executor, functionParams); // Optionals starts from the last function parameter position - const optionals = params[functionParamsCount] instanceof Object ? params[functionParamsCount] : null; + const optionals = options instanceof Object ? options : null; for (let i = 0; i < optionalsFunctions.all.length; i++) { const optionalFunction = optionalsFunctions.all[i]; optionalFunction(optionals, functionRawTxData); @@ -40,8 +39,17 @@ class ContractFunction extends EventClass { return txReceipt; } - async getRawTransaction(...params) { - const functionRawTxData = buildFunctionRawTxData.call(this, this.contract.executor, params); + async getRawTransaction (params, options) { + const functionRawTxData = buildFunctionRawTxData.call(this, this.contract.executor, ...params); + + // Optionals starts from the last function parameter position + // const optionals = options instanceof Object ? options : null; + // for (let i = 0; i < optionalsFunctions.all.length; i++) { + // const optionalFunction = optionalsFunctions.all[i]; + // optionalFunction(optionals, functionRawTxData); + // } + + const rawTransaction = await executeFunction( this.contract.provider.eos, functionRawTxData, @@ -51,10 +59,19 @@ class ContractFunction extends EventClass { return rawTransaction.transaction.transaction; } - async sign(signer, ...params) { + async sign (params, options) { is(signer).instanceOf('BaseAccount'); - const functionRawTxData = buildFunctionRawTxData.call(this, signer, params); + const functionRawTxData = buildFunctionRawTxData.call(this, signer, ...params); + + // Optionals starts from the last function parameter position + // const optionals = options instanceof Object ? options : null; + // for (let i = 0; i < optionalsFunctions.all.length; i++) { + // const optionalFunction = optionalsFunctions.all[i]; + // optionalFunction(optionals, functionRawTxData); + // } + + const rawTransaction = await executeFunction( this.contract.provider.eos, functionRawTxData, From df14d1dd2e27a903a732e5f5980089164592622c Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Tue, 30 Jun 2020 19:06:48 +0300 Subject: [PATCH 20/35] [BREAKING CHANGES] Contract actions accept parameteres in form of array now; actions.sign([params],{options}) and actoins.getRawTransaction([params], {options}) accepts params in form of array and supports options like the normal action does; --- .../with-example/tests-example/tests.js | 10 +- .../multi-signature-account/account.js | 2 +- .../contract-function/contract-function.js | 119 ++++++++---------- .../function-optionals/from.js | 4 +- .../contract-function/functions-factory.js | 2 +- src/helpers/is.js | 2 +- tests/account-tests.js | 14 ++- tests/contract-tests.js | 32 ++--- tests/multisig-account-test.js | 28 ++--- tests/providers-tests.js | 16 +-- 10 files changed, 111 insertions(+), 118 deletions(-) diff --git a/cli-commands/commands/init/options/with-example/tests-example/tests.js b/cli-commands/commands/init/options/with-example/tests-example/tests.js index d850183..b34f78b 100644 --- a/cli-commands/commands/init/options/with-example/tests-example/tests.js +++ b/cli-commands/commands/init/options/with-example/tests-example/tests.js @@ -32,7 +32,7 @@ describe("EOSIO Token", function (eoslime) { }); it("Should create a new token", async () => { - await tokenContract.create(tokensIssuer.name, TOTAL_SUPPLY); + await tokenContract.actions.create([tokensIssuer.name, TOTAL_SUPPLY]); /* You have access to the EOS(eosjs) instance: @@ -45,25 +45,25 @@ describe("EOSIO Token", function (eoslime) { }); it("Should issue tokens", async () => { - await tokenContract.create(tokensIssuer.name, TOTAL_SUPPLY); + await tokenContract.actions.create([tokensIssuer.name, TOTAL_SUPPLY]); /* On each contract method you can provide an optional object -> { from: account } If you don't provide a 'from' object, the method will be executed from the contract account authority */ - await tokenContract.issue(tokensHolder.name, HOLDER_SUPPLY, "memo", { from: tokensIssuer }); + await tokenContract.actions.issue([tokensHolder.name, HOLDER_SUPPLY, "memo"], { from: tokensIssuer }); let holderBalance = await tokensHolder.getBalance("SYS", tokenContract.name); assert.equal(holderBalance[0], HOLDER_SUPPLY, "Incorrect holder balance"); }); it("Should throw if tokens quantity is negative", async () => { - await tokenContract.create(tokensIssuer.name, TOTAL_SUPPLY); + await tokenContract.actions.create([tokensIssuer.name, TOTAL_SUPPLY]); const INVALID_ISSUING_AMOUNT = "-100.0000 SYS"; /* For easier testing, eoslime provides you ('utils.test') with helper functions */ - await eoslime.tests.expectAssert(tokenContract.issue(tokensHolder.name, INVALID_ISSUING_AMOUNT, "memo", { from: tokensIssuer })); + await eoslime.tests.expectAssert(tokenContract.actions.issue([tokensHolder.name, INVALID_ISSUING_AMOUNT, "memo"], { from: tokensIssuer })); let holderBalance = await tokensHolder.getBalance("SYS", tokenContract.name); assert.equal(holderBalance.length, 0, "Incorrect holder balance"); diff --git a/src/account/multi-signature-account/account.js b/src/account/multi-signature-account/account.js index 6cb6ef4..14392b1 100644 --- a/src/account/multi-signature-account/account.js +++ b/src/account/multi-signature-account/account.js @@ -26,7 +26,7 @@ class MultiSignatureAccount extends BaseAccount { async propose (contractAction, actionData) { is(contractAction).instanceOf('ContractFunction'); - const actionTx = await contractAction.sign(this, ...actionData); + const actionTx = await contractAction.sign(actionData, { from: this }); const proposalId = Date.now(); this.proposals[proposalId] = actionTx; diff --git a/src/contract/contract-function/contract-function.js b/src/contract/contract-function/contract-function.js index e7b3a3a..2d46f3f 100644 --- a/src/contract/contract-function/contract-function.js +++ b/src/contract/contract-function/contract-function.js @@ -10,6 +10,7 @@ class ContractFunction extends EventClass { constructor (contract, functionName, functionFields) { super(EVENTS); + this.contract = contract; this.functionName = functionName; this.functionFields = functionFields; @@ -19,77 +20,75 @@ class ContractFunction extends EventClass { async broadcast (params, options) { is(this.contract.executor).instanceOf('BaseAccount', 'executor is missing'); - const functionParams = params.slice(0, this.functionFields.length); - const functionRawTxData = buildFunctionRawTxData.call(this, this.contract.executor, functionParams); - - // Optionals starts from the last function parameter position - const optionals = options instanceof Object ? options : null; - for (let i = 0; i < optionalsFunctions.all.length; i++) { - const optionalFunction = optionalsFunctions.all[i]; - optionalFunction(optionals, functionRawTxData); + const txOptions = { + broadcast: true, + sign: true } - const txReceipt = await executeFunction( - this.contract.provider.eos, - functionRawTxData, - { broadcast: true, sign: true, keyProvider: functionRawTxData.defaultExecutor.privateKey } - ); + const txReceipt = await executeFunction.call(this, params, options, txOptions); - this.emit(EVENTS.processed, txReceipt, functionParams); + this.emit(EVENTS.processed, txReceipt, params); return txReceipt; } async getRawTransaction (params, options) { - const functionRawTxData = buildFunctionRawTxData.call(this, this.contract.executor, ...params); - - // Optionals starts from the last function parameter position - // const optionals = options instanceof Object ? options : null; - // for (let i = 0; i < optionalsFunctions.all.length; i++) { - // const optionalFunction = optionalsFunctions.all[i]; - // optionalFunction(optionals, functionRawTxData); - // } - - - const rawTransaction = await executeFunction( - this.contract.provider.eos, - functionRawTxData, - { broadcast: false, sign: false } - ); + const txOptions = { + broadcast: false, + sign: false + } + const rawTransaction = await executeFunction.call(this, params, options, txOptions); return rawTransaction.transaction.transaction; } async sign (params, options) { - is(signer).instanceOf('BaseAccount'); - - const functionRawTxData = buildFunctionRawTxData.call(this, signer, ...params); - - // Optionals starts from the last function parameter position - // const optionals = options instanceof Object ? options : null; - // for (let i = 0; i < optionalsFunctions.all.length; i++) { - // const optionalFunction = optionalsFunctions.all[i]; - // optionalFunction(optionals, functionRawTxData); - // } - - - const rawTransaction = await executeFunction( - this.contract.provider.eos, - functionRawTxData, - { broadcast: false, sign: true, keyProvider: signer.privateKey } - ); + const txOptions = { + broadcast: false, + sign: true + } + const rawTransaction = await executeFunction.call(this, params, options, txOptions); return rawTransaction.transaction; } } -const buildFunctionRawTxData = function (authorizer, params) { +async function executeFunction (params, fnOptions, txOptions) { + const functionRawTxData = buildFunctionRawTxData.call( + this, + this.contract.executor, + params, + fnOptions + ); + + return this.contract.provider.eos.transaction( + { + actions: functionRawTxData.actions + }, + { ...txOptions, keyProvider: functionRawTxData.defaultExecutor.privateKey } + ); +}; + +function buildFunctionRawTxData (authorizer, params, options) { const structuredParams = structureParamsToExpectedLook(params, this.functionFields); const functionTx = buildMainFunctionTx(this.contract.name, this.functionName, structuredParams, authorizer); + processOptions(options, functionTx); + return functionTx; } -const buildMainFunctionTx = function (contractName, actionName, data, authorizationAccount) { +function structureParamsToExpectedLook (params, expectedParamsLook) { + let structuredParams = {}; + + for (let i = 0; i < expectedParamsLook.length; i++) { + let expectedParam = expectedParamsLook[i].name; + structuredParams[expectedParam] = params[i]; + } + + return structuredParams; +}; + +function buildMainFunctionTx (contractName, actionName, data, authorizationAccount) { return { defaultExecutor: authorizationAccount, actions: [ @@ -103,24 +102,12 @@ const buildMainFunctionTx = function (contractName, actionName, data, authorizat }; }; -const structureParamsToExpectedLook = function (params, expectedParamsLook) { - let structuredParams = {}; - - for (let i = 0; i < expectedParamsLook.length; i++) { - let expectedParam = expectedParamsLook[i].name; - structuredParams[expectedParam] = params[i]; +function processOptions (options, functionRawTxData) { + const optionals = options instanceof Object ? options : null; + for (let i = 0; i < optionalsFunctions.all.length; i++) { + const optionalFunction = optionalsFunctions.all[i]; + optionalFunction(optionals, functionRawTxData); } - - return structuredParams; -}; - -const executeFunction = async function (eos, functionRawTx, options) { - return eos.transaction( - { - actions: functionRawTx.actions - }, - options - ); -}; +} module.exports = ContractFunction; diff --git a/src/contract/contract-function/function-optionals/from.js b/src/contract/contract-function/function-optionals/from.js index 44de550..21ed93a 100644 --- a/src/contract/contract-function/function-optionals/from.js +++ b/src/contract/contract-function/function-optionals/from.js @@ -1,7 +1,7 @@ -const Account = require("./../../../account/normal-account/account"); +const is = require('../../../helpers/is') const fromOption = function (optionals, rawTransaction) { - if (optionals && optionals.from instanceof Account) { + if (optionals && is(optionals.from).instanceOf('BaseAccount')) { rawTransaction.defaultExecutor = optionals.from; for (let i = 0; i < rawTransaction.actions.length; i++) { rawTransaction.actions[i].authorization = [rawTransaction.defaultExecutor.executiveAuthority]; diff --git a/src/contract/contract-function/functions-factory.js b/src/contract/contract-function/functions-factory.js index 461413c..695a7a6 100644 --- a/src/contract/contract-function/functions-factory.js +++ b/src/contract/contract-function/functions-factory.js @@ -2,7 +2,7 @@ const ContractFunction = require('./contract-function'); class FunctionsFactory { - static createFunction(contract, functionName, functionFields) { + static createFunction (contract, functionName, functionFields) { const contractFunction = new ContractFunction(contract, functionName, functionFields); const proxyHandler = { diff --git a/src/helpers/is.js b/src/helpers/is.js index 8a43457..313378f 100644 --- a/src/helpers/is.js +++ b/src/helpers/is.js @@ -6,7 +6,7 @@ module.exports = function (data) { throw new Error(errorMessage); } - recursivelyCheckIfInstance(data, ofObject); + return recursivelyCheckIfInstance(data, ofObject); } } } diff --git a/tests/account-tests.js b/tests/account-tests.js index c798241..278ff90 100644 --- a/tests/account-tests.js +++ b/tests/account-tests.js @@ -72,8 +72,8 @@ describe('Account', function () { try { const tokenAccount = await Account.createFromName('eosio.token'); const tokenContract = await eoslimeTool.Contract.deployOnAccount(TOKEN_WASM_PATH, TOKEN_ABI_PATH, tokenAccount); - await tokenContract.create(tokenAccount.name, TOTAL_SUPPLY); - await tokenContract.issue(ACCOUNT_NAME, TOTAL_SUPPLY, 'memo'); + await tokenContract.actions.create([tokenAccount.name, TOTAL_SUPPLY]); + await tokenContract.actions.issue([ACCOUNT_NAME, TOTAL_SUPPLY, 'memo']); } catch (error) { } } @@ -484,7 +484,10 @@ describe('Account', function () { const accountRandomAuth = await account.createSubAuthority('random'); try { - await faucetContract.produce(account.name, "100.0000 TKNS", account.name, "memo", { from: accountRandomAuth }); + await faucetContract.actions.produce( + [account.name, "100.0000 TKNS", account.name, "memo"], + { from: accountRandomAuth } + ); } catch (error) { assert(error.includes('action declares irrelevant authority')); } @@ -496,7 +499,10 @@ describe('Account', function () { } ]); - await faucetContract.produce(account.name, "100.0000 TKNS", account.name, "memo", { from: accountRandomAuth }); + await faucetContract.actions.produce( + [account.name, "100.0000 TKNS", account.name, "memo"], + { from: accountRandomAuth } + ); }); it('Should throw if one does not provide array as abilities', async () => { diff --git a/tests/contract-tests.js b/tests/contract-tests.js index ba9af00..c9b44fc 100644 --- a/tests/contract-tests.js +++ b/tests/contract-tests.js @@ -10,7 +10,7 @@ const FAUCET_WASM_PATH = "./tests/testing-contracts/compiled/faucet.wasm"; You should have running local nodeos in order to run tests */ -describe.only("Contract", function () { +describe("Contract", function () { // Increase mocha(testing framework) time, otherwise tests fails this.timeout(15000); @@ -29,7 +29,7 @@ describe.only("Contract", function () { try { const tokenAccount = await eoslime.Account.createRandom(); tokenContract = await eoslime.Contract.deployOnAccount(TOKEN_WASM_PATH, TOKEN_ABI_PATH, tokenAccount); - await tokenContract.actions.create(faucetAccount.name, TOTAL_SUPPLY); + await tokenContract.actions.create([faucetAccount.name, TOTAL_SUPPLY]); } catch (error) { console.log(error); } @@ -92,7 +92,7 @@ describe.only("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, "INVALID"); eoslime.Provider.defaultAccount = ''; - await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"]); assert(false, "Should throw"); } catch (error) { @@ -157,7 +157,7 @@ describe.only("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor - await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"]); const result = await faucetContract.tables.withdrawers.limit(1).equal(tokensHolder.name).find(); @@ -170,7 +170,7 @@ describe.only("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); const executor = await eoslime.Account.createRandom(); - await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo", { from: executor }); + await faucetContract.actions.produce([tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"], { from: executor }); // After the execution, the contract executor should be the same as the initially provided one assert(faucetContract.executor.name == faucetAccount.name); @@ -181,8 +181,8 @@ describe.only("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); const executor = await eoslime.Account.createRandom(); - await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor, unique: true }); - await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor, unique: true }); + await faucetContract.actions.produce([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: executor, unique: true }); + await faucetContract.actions.produce([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: executor, unique: true }); assert(true); }); @@ -192,8 +192,8 @@ describe.only("Contract", function () { const executor = await eoslime.Account.createRandom(); try { - await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor }); - await faucetContract.actions.produce(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo', { from: executor }); + await faucetContract.actions.produce([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: executor }); + await faucetContract.actions.produce([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: executor }); } catch (error) { assert(error.includes('duplicate transaction')); } @@ -203,7 +203,7 @@ describe.only("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); const tokensHolder = await eoslime.Account.createRandom(); - const rawActionTx = await faucetContract.actions.produce.getRawTransaction(tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); + const rawActionTx = await faucetContract.actions.produce.getRawTransaction([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo']); assert(rawActionTx.expiration != undefined); assert(rawActionTx.ref_block_num != undefined); assert(rawActionTx.ref_block_prefix != undefined); @@ -223,7 +223,7 @@ describe.only("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); const signer = await eoslime.Account.createRandom(); - const signedActionTx = await faucetContract.actions.produce.sign(signer, tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); + const signedActionTx = await faucetContract.actions.produce.sign([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: signer }); assert(signedActionTx.signatures.length == 1); assert(signedActionTx.transaction.expiration != undefined); @@ -245,7 +245,7 @@ describe.only("Contract", function () { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); const tokensHolder = await eoslime.Account.createRandom(); - await faucetContract.actions.produce.sign('Fake signer', tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'); + await faucetContract.actions.produce.sign([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: 'Fake signer' }); } catch (error) { assert(error.message.includes('String is not an instance of BaseAccount')); } @@ -266,7 +266,7 @@ describe.only("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor - await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"]); const allWithdrawers = await faucetContract.tables.withdrawers.find(); @@ -279,7 +279,7 @@ describe.only("Contract", function () { const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor - await faucetContract.actions.produce(tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, "100.0000 TKNS", tokenContract.name, "memo"]); // With equal criteria const equalResult = await faucetContract.tables.withdrawers.equal(tokensHolder.name).find(); @@ -311,13 +311,13 @@ describe.only("Contract", function () { await faucetContract.makeInline(); const tokensHolder = await eoslime.Account.createRandom(); - await faucetContract.actions.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"]); const tokensHolderBeforeBalance = await tokensHolder.getBalance("TKNS", tokenContract.name); assert(tokensHolderBeforeBalance.length == 0); // withdraw method behind the scene calls token's contract issue method - await faucetContract.actions.withdraw(tokensHolder.name); + await faucetContract.actions.withdraw([tokensHolder.name]); const tokensHolderAfterBalance = await tokensHolder.getBalance("TKNS", tokenContract.name); assert(tokensHolderAfterBalance[0] == PRODUCED_TOKENS_AMOUNT); diff --git a/tests/multisig-account-test.js b/tests/multisig-account-test.js index 59c7d2a..031229d 100644 --- a/tests/multisig-account-test.js +++ b/tests/multisig-account-test.js @@ -27,11 +27,11 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); - await multiSigAccount.approve(multiSigAccount.accounts[0].publicKey, proposalId); + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + await multiSigAccount.approve(multiSigAccount.accounts[1].publicKey, proposalId); await multiSigAccount.processProposal(proposalId); - const withdrawers = (await faucetContract.withdrawers.find())[0]; + const withdrawers = (await faucetContract.tables.withdrawers.find())[0]; assert(eoslime.utils.toName(withdrawers.account) == account.name); assert(withdrawers.quantity == "100.0000 TKNS"); }); @@ -51,11 +51,11 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadKeys(keys.map((key) => { return key.privateKey })); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [multiSigAccount.name, "100.0000 TKNS", multiSigAccount.name, "memo"]) + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [multiSigAccount.name, "100.0000 TKNS", multiSigAccount.name, "memo"]) await multiSigAccount.approve(multiSigAccount.accounts[1].publicKey, proposalId) await multiSigAccount.processProposal(proposalId); - const withdrawers = (await faucetContract.withdrawers.find())[0]; + const withdrawers = (await faucetContract.tables.withdrawers.find())[0]; assert(eoslime.utils.toName(withdrawers.account) == account.name); assert(withdrawers.quantity == "100.0000 TKNS"); }); @@ -71,7 +71,7 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); for (let i = 0; i < accounts.length; i++) { await multiSigAccount.approve(accounts[i].publicKey, proposalId); } @@ -80,7 +80,7 @@ describe('Multi signature account', function () { await multiSigAccount.processProposal(proposalId); - const withdrawers = (await faucetContract.withdrawers.find())[0]; + const withdrawers = (await faucetContract.tables.withdrawers.find())[0]; assert(eoslime.utils.toName(withdrawers.account) == account.name); assert(withdrawers.quantity == "100.0000 TKNS"); }); @@ -119,7 +119,7 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); await multiSigAccount.approve('Fake account', proposalId); assert(false); @@ -140,8 +140,8 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); - await multiSigAccount.approve(multiSigAccount.accounts[0].publicKey, 'Fake proposal'); + await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + await multiSigAccount.approve(multiSigAccount.accounts[1].publicKey, 'Fake proposal'); assert(false); } catch (error) { @@ -164,8 +164,8 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); - await multiSigAccount.approve(multiSigAccount.accounts[0].publicKey, proposalId); + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + await multiSigAccount.approve(multiSigAccount.accounts[1].publicKey, proposalId); await multiSigAccount.processProposal('Fake proposal'); @@ -187,7 +187,7 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); for (let i = 0; i < accounts.length; i++) { await multiSigAccount.approve(accounts[i].publicKey, proposalId); } @@ -212,7 +212,7 @@ describe('Multi signature account', function () { const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); multiSigAccount.loadAccounts(accounts); - const proposalId = await multiSigAccount.propose(faucetContract.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); + const proposalId = await multiSigAccount.propose(faucetContract.actions.produce, [account.name, "100.0000 TKNS", account.name, "memo"]); await multiSigAccount.processProposal(proposalId); assert(false); diff --git a/tests/providers-tests.js b/tests/providers-tests.js index dc6641e..bdd70fc 100644 --- a/tests/providers-tests.js +++ b/tests/providers-tests.js @@ -240,9 +240,9 @@ describe('Providers', function () { const tokenContract = await eoslimeInstance.Contract.deploy(TOKEN_WASM_PATH, TOKEN_ABI_PATH); const faucetContract = await eoslimeInstance.Contract.deploy(FAUCET_WASM_PATH, FAUCET_ABI_PATH); - await tokenContract.create(faucetContract.name, TOTAL_SUPPLY); + await tokenContract.actions.create([faucetContract.name, TOTAL_SUPPLY]); const tokensHolder = await eoslimeInstance.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"]); // With equal criteria const equalResult = await Provider.select('withdrawers').from(faucetContract.name).equal(tokensHolder.name).find(); @@ -281,9 +281,9 @@ describe('Providers', function () { const tokenContract = await eoslimeInstance.Contract.deploy(TOKEN_WASM_PATH, TOKEN_ABI_PATH); const faucetContract = await eoslimeInstance.Contract.deploy(FAUCET_WASM_PATH, FAUCET_ABI_PATH); - await tokenContract.create(faucetContract.name, TOTAL_SUPPLY); + await tokenContract.actions.create([faucetContract.name, TOTAL_SUPPLY]); const tokensHolder = await eoslimeInstance.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"]); await Provider.select().find(); } catch (error) { @@ -299,9 +299,9 @@ describe('Providers', function () { const tokenContract = await eoslimeInstance.Contract.deploy(TOKEN_WASM_PATH, TOKEN_ABI_PATH); const faucetContract = await eoslimeInstance.Contract.deploy(FAUCET_WASM_PATH, FAUCET_ABI_PATH); - await tokenContract.create(faucetContract.name, TOTAL_SUPPLY); + await tokenContract.actions.create([faucetContract.name, TOTAL_SUPPLY]); const tokensHolder = await eoslimeInstance.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"]); await Provider.select('withdrawers').from().find(); } catch (error) { @@ -317,9 +317,9 @@ describe('Providers', function () { const tokenContract = await eoslimeInstance.Contract.deploy(TOKEN_WASM_PATH, TOKEN_ABI_PATH); const faucetContract = await eoslimeInstance.Contract.deploy(FAUCET_WASM_PATH, FAUCET_ABI_PATH); - await tokenContract.create(faucetContract.name, TOTAL_SUPPLY); + await tokenContract.actions.create([faucetContract.name, TOTAL_SUPPLY]); const tokensHolder = await eoslimeInstance.Account.createRandom(); - await faucetContract.produce(tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"); + await faucetContract.actions.produce([tokensHolder.name, PRODUCED_TOKENS_AMOUNT, tokenContract.name, "memo"]); await Provider.select('withdrawers').from(faucetContract.name).scope().find(); } catch (error) { From ce3d3bd099da12c721f624ec19b3af21bcb50049 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 1 Jul 2020 12:08:45 +0300 Subject: [PATCH 21/35] =?UTF-8?q?[BREAKING=20CHANGES]=20Contract:=20Change?= =?UTF-8?q?=20[=20on(=E2=80=98deploy=E2=80=99,=20(tx,=20contract)=20=3D>?= =?UTF-8?q?=20{})=20to=20be=20on(=E2=80=98deploy=E2=80=99,=20(contract,=20?= =?UTF-8?q?tx)=20=3D>=20{})=20];=20Account:=20[=20Rename=20addAuthorityKey?= =?UTF-8?q?=20to=20be=20addOnBehalfKey,=20Rename=20executiveAuthority=20to?= =?UTF-8?q?=20be=20simply=20authority,=20Remove=20AuthorityAccount,=20=20A?= =?UTF-8?q?dd=20setAuthorityAbilities=20to=20account,=20Modify=20setAuthor?= =?UTF-8?q?ityAbilities=20to=20accept=20authName=20and=20abilities,=20Fix?= =?UTF-8?q?=20from=20option=20to=20check=20for=20baseAccount=20properly=20?= =?UTF-8?q?]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resource-usage-option.js | 4 +- package.json | 2 +- src/account/authority-account/account.js | 30 --------- src/account/base-account.js | 6 +- .../multi-signature-account/account.js | 2 +- src/account/normal-account/account-factory.js | 4 +- src/account/normal-account/account.js | 49 ++++++++++---- src/contract/contract-factory.js | 4 +- .../contract-function/contract-function.js | 2 +- .../function-optionals/from.js | 4 +- .../function-optionals/tokens.js | 3 +- .../function-optionals/unique.js | 2 +- tests/account-tests.js | 64 +++++++++++++------ tests/multisig-account-test.js | 4 +- tests/types/account.ts | 32 +++++----- tests/types/contract.ts | 18 +++--- types/account/index.d.ts | 25 +++----- 17 files changed, 131 insertions(+), 124 deletions(-) delete mode 100644 src/account/authority-account/account.js diff --git a/cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js b/cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js index 27a13b1..cece88c 100644 --- a/cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js +++ b/cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js @@ -2,7 +2,7 @@ const Option = require('../../../option'); const ReportTable = require('./report-table'); class ResourceReportOption extends Option { - constructor() { + constructor () { super( 'resource-usage', { @@ -60,7 +60,7 @@ class ResourceReportOption extends Option { } const fillDeploymentsResources = function (eoslime, deploymentsResources) { - eoslime.Contract.on('deploy', (txReceipts, contract) => { + eoslime.Contract.on('deploy', (contract, txReceipts) => { const setCodeResources = extractResourcesCostsFromReceipt(txReceipts[0]); const setABIResources = extractResourcesCostsFromReceipt(txReceipts[1]); diff --git a/package.json b/package.json index dbe78ed..29aab47 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "tsc", "test": "bash ./tests/testing-contracts/compile.sh && mocha './tests/*.js'", "test-prod": "bash ./tests/testing-contracts/compile.sh && nyc --check-coverage mocha './tests/*.js'", - "test-types": "bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/contract.ts", + "test-types": "bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/*.ts", "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" }, "author": "Lyubomir Kiprov (Limechain)", diff --git a/src/account/authority-account/account.js b/src/account/authority-account/account.js deleted file mode 100644 index a17b20a..0000000 --- a/src/account/authority-account/account.js +++ /dev/null @@ -1,30 +0,0 @@ -const is = require('../../helpers/is'); -const Account = require('../normal-account/account'); - -class AuthorityAccount extends Account { - - constructor (parentPermission, name, privateKey, provider, permission) { - super(name, privateKey, provider, permission); - this.parentPermission = parentPermission; - } - - async setAuthorityAbilities (abilities) { - is(abilities).instanceOf('Array'); - - const txReceipt = await this.provider.eos.transaction(tr => { - for (let i = 0; i < abilities.length; i++) { - const ability = abilities[i]; - tr.linkauth({ - account: this.name, - code: ability.contract, - type: ability.action, - requirement: this.executiveAuthority.permission - }, { authorization: [`${this.name}@${parentAuthority}`] }); - } - }, { broadcast: true, sign: true, keyProvider: this.privateKey }); - - return txReceipt; - } -} - -module.exports = AuthorityAccount; diff --git a/src/account/base-account.js b/src/account/base-account.js index dd4fb37..ca7c1ac 100644 --- a/src/account/base-account.js +++ b/src/account/base-account.js @@ -2,10 +2,10 @@ const eosECC = require('eosjs').modules.ecc; class BaseAccount { - constructor(name, privateKey, provider, permission) { + constructor (name, privateKey, provider, permission) { this.name = name; this.provider = provider; - this.executiveAuthority = { + this.authority = { actor: name, permission: permission } @@ -15,7 +15,7 @@ class BaseAccount { } async getAuthorityInfo () { - const authority = arguments[0] ? arguments[0] : this.executiveAuthority.permission; + const authority = arguments[0] ? arguments[0] : this.authority.permission; const accountInfo = await this.provider.eos.getAccount(this.name); const authorityInfo = accountInfo.permissions.find((permission) => { diff --git a/src/account/multi-signature-account/account.js b/src/account/multi-signature-account/account.js index 14392b1..a347f4e 100644 --- a/src/account/multi-signature-account/account.js +++ b/src/account/multi-signature-account/account.js @@ -12,7 +12,7 @@ class MultiSignatureAccount extends BaseAccount { loadKeys (privateKeys) { for (let i = 0; i < privateKeys.length; i++) { - this.accounts.push(new BaseAccount(this.name, privateKeys[i], this.provider, this.executiveAuthority.permission)); + this.accounts.push(new BaseAccount(this.name, privateKeys[i], this.provider, this.authority.permission)); } } diff --git a/src/account/normal-account/account-factory.js b/src/account/normal-account/account-factory.js index 08416aa..86f42af 100644 --- a/src/account/normal-account/account-factory.js +++ b/src/account/normal-account/account-factory.js @@ -7,7 +7,7 @@ const utils = require('../../utils'); const DEFAULT_AUTHORITY = 'active'; class AccountFactory { - constructor(provider) { + constructor (provider) { this.provider = provider; } @@ -66,7 +66,7 @@ class AccountFactory { const dataToBeEncrypted = { name: newAccount.name, network: newAccount.provider.network, - authority: newAccount.executiveAuthority + authority: newAccount.authority }; const dataHash = crypto.hash(JSON.stringify({ ...dataToBeEncrypted, privateKey: newAccount.privateKey })); diff --git a/src/account/normal-account/account.js b/src/account/normal-account/account.js index 523ed35..1889d21 100644 --- a/src/account/normal-account/account.js +++ b/src/account/normal-account/account.js @@ -1,7 +1,6 @@ const is = require('../../helpers/is') const eosECC = require('eosjs').modules.ecc; const BaseAccount = require('../base-account'); -// const AuthorityAccount = require('../authority-account/account'); class Account extends BaseAccount { @@ -42,25 +41,49 @@ class Account extends BaseAccount { this.name, receiver.name, `${amount} ${symbol}`, - this.executiveAuthority, + this.authority, { broadcast: true, sign: true, keyProvider: this.privateKey } ); } - async createSubAuthority (authorityName, threshold = 1) { + async addAuthority (authorityName, threshold = 1) { const authorization = { threshold, keys: [{ key: this.publicKey, weight: threshold }] } - await updateAuthority.call(this, authorityName, this.executiveAuthority.permission, authorization); - // return new AuthorityAccount( - // this.executiveAuthority.permission, - // this.name, - // this.privateKey, - // this.provider, - // authorityName - // ); + return updateAuthority.call(this, authorityName, this.authority.permission, authorization); + } + + async setAuthorityAbilities (authorityName, abilities) { + is(abilities).instanceOf('Array'); + + const accountInfo = await this.provider.eos.getAccount(this.name); + const hasAuthName = accountInfo.permissions.find((permissions) => { + return permissions.perm_name == authorityName; + }); + + if (!hasAuthName) { + throw new Error(` + Account does not have authority with name: [${authorityName}]. + You could add it by using [addAuthority] function. + For details check [Set authority abilities] suite in account-tests.js + `); + } + + const txReceipt = await this.provider.eos.transaction(tr => { + for (let i = 0; i < abilities.length; i++) { + const ability = abilities[i]; + tr.linkauth({ + account: this.name, + code: ability.contract, + type: ability.action, + requirement: authorityName + }, { authorization: [this.authority] }); + } + }, { broadcast: true, sign: true, keyProvider: this.privateKey }); + + return txReceipt; } async increaseThreshold (threshold) { @@ -86,7 +109,7 @@ class Account extends BaseAccount { } } - async addAuthorityKey (publicKey, weight = 1) { + async addOnBehalfKey (publicKey, weight = 1) { if (!eosECC.isValidPublic(publicKey)) { throw new Error('Provided public key is not a valid one'); } @@ -125,7 +148,7 @@ const updateAuthority = async function (authorityName, parent, auth) { permission: authorityName, parent: parent, auth: auth - }, { authorization: [this.executiveAuthority] }); + }, { authorization: [this.authority] }); }, { broadcast: true, sign: true, keyProvider: this.privateKey }); diff --git a/src/contract/contract-factory.js b/src/contract/contract-factory.js index 7d2b71a..d087c1b 100644 --- a/src/contract/contract-factory.js +++ b/src/contract/contract-factory.js @@ -22,7 +22,7 @@ const EVENTS = { class ContractFactory extends ContractInitializator { - constructor(provider) { + constructor (provider) { super(provider); Object.assign(this.events, EVENTS); } @@ -77,7 +77,7 @@ class ContractFactory extends ContractInitializator { options = Object.assign(defaultDeployOptions, options); await executeOptions(contract, options); - this.emit(EVENTS.deploy, [setCodeTxReceipt, setAbiTxReceipt], contract); + this.emit(EVENTS.deploy, contract, [setCodeTxReceipt, setAbiTxReceipt]); return contract; } diff --git a/src/contract/contract-function/contract-function.js b/src/contract/contract-function/contract-function.js index 2d46f3f..b6b8171 100644 --- a/src/contract/contract-function/contract-function.js +++ b/src/contract/contract-function/contract-function.js @@ -95,7 +95,7 @@ function buildMainFunctionTx (contractName, actionName, data, authorizationAccou { account: contractName, name: actionName, - authorization: [authorizationAccount.executiveAuthority], + authorization: [authorizationAccount.authority], data: data } ] diff --git a/src/contract/contract-function/function-optionals/from.js b/src/contract/contract-function/function-optionals/from.js index 21ed93a..4009bd7 100644 --- a/src/contract/contract-function/function-optionals/from.js +++ b/src/contract/contract-function/function-optionals/from.js @@ -1,10 +1,10 @@ const is = require('../../../helpers/is') const fromOption = function (optionals, rawTransaction) { - if (optionals && is(optionals.from).instanceOf('BaseAccount')) { + if (optionals && optionals.from && is(optionals.from).instanceOf('BaseAccount')) { rawTransaction.defaultExecutor = optionals.from; for (let i = 0; i < rawTransaction.actions.length; i++) { - rawTransaction.actions[i].authorization = [rawTransaction.defaultExecutor.executiveAuthority]; + rawTransaction.actions[i].authorization = [rawTransaction.defaultExecutor.authority]; } } }; diff --git a/src/contract/contract-function/function-optionals/tokens.js b/src/contract/contract-function/function-optionals/tokens.js index ca3eeda..6d74287 100644 --- a/src/contract/contract-function/function-optionals/tokens.js +++ b/src/contract/contract-function/function-optionals/tokens.js @@ -1,10 +1,9 @@ const tokensOptional = function (optionals, rawTransaction) { if (optionals && optionals.tokens) { - rawTransaction.actions.push({ account: "eosio.token", name: "transfer", - authorization: [rawTransaction.defaultExecutor.executiveAuthority], + authorization: [rawTransaction.defaultExecutor.authority], data: { from: rawTransaction.defaultExecutor.name, to: rawTransaction.actions[0].account, diff --git a/src/contract/contract-function/function-optionals/unique.js b/src/contract/contract-function/function-optionals/unique.js index e8efff4..fee30ee 100644 --- a/src/contract/contract-function/function-optionals/unique.js +++ b/src/contract/contract-function/function-optionals/unique.js @@ -3,7 +3,7 @@ const uniqueOptional = function (optionals, rawTransaction) { rawTransaction.actions.push({ account: "eosio.null", name: "nonce", - authorization: [rawTransaction.defaultExecutor.executiveAuthority], + authorization: [rawTransaction.defaultExecutor.authority], data: { value: `${Date.now()}` } diff --git a/tests/account-tests.js b/tests/account-tests.js index 278ff90..f9336b0 100644 --- a/tests/account-tests.js +++ b/tests/account-tests.js @@ -86,8 +86,8 @@ describe('Account', function () { assert(account.name == ACCOUNT_NAME, 'Incorrect name'); assert(account.privateKey == ACCOUNT_PRIVATE_KEY, 'Incorrect private key'); assert(account.publicKey == ACCOUNT_PUBLIC_KEY, 'Incorrect public key'); - assert(account.executiveAuthority.actor == EXECUTIVE_AUTHORITY.actor, 'Incorrect executive authority actor'); - assert(account.executiveAuthority.permission == EXECUTIVE_AUTHORITY.permission, 'Incorrect executive authority permission'); + assert(account.authority.actor == EXECUTIVE_AUTHORITY.actor, 'Incorrect executive authority actor'); + assert(account.authority.permission == EXECUTIVE_AUTHORITY.permission, 'Incorrect executive authority permission'); const network = account.provider.network; assert(JSON.stringify(account.provider.network) == JSON.stringify(Networks[network.name])) @@ -237,20 +237,22 @@ describe('Account', function () { }); }); - describe('Create authority', function () { + describe('Add authority', function () { const AUTHORITY = 'contracts'; const PARENT_AUTHORITY = 'active'; - it('Should create authority', async () => { + it('Should add authority', async () => { let account = await Account.createRandom(); let authority = await getAuthorityForAccount(AUTHORITY, account.name); assert(authority == undefined); - const newAuthorityAccount = await account.createSubAuthority(AUTHORITY); + await account.addAuthority(AUTHORITY); + + const newAuthorityAccount = Account.load(account.name, account.privateKey, AUTHORITY); assert(newAuthorityAccount.name == account.name); - assert(newAuthorityAccount.executiveAuthority.actor == newAuthorityAccount.name); - assert(newAuthorityAccount.executiveAuthority.permission == AUTHORITY); + assert(newAuthorityAccount.authority.actor == newAuthorityAccount.name); + assert(newAuthorityAccount.authority.permission == AUTHORITY); authority = await getAuthorityForAccount(AUTHORITY, newAuthorityAccount.name); assert(authority.parent == PARENT_AUTHORITY); @@ -288,7 +290,7 @@ describe('Account', function () { it('Should throw if one try to create a permission for non-existing authority', async () => { try { let account = await Account.createRandom(); - account.executiveAuthority.permission = 'FAKE'; + account.authority.permission = 'FAKE'; await account.addPermission(PERMISSION); @@ -321,7 +323,7 @@ describe('Account', function () { assert(authorityInfo.required_auth.keys.length == 1); const keysPair = await eoslime.utils.generateKeys(); - await account.addAuthorityKey(keysPair.publicKey); + await account.addOnBehalfKey(keysPair.publicKey); authorityInfo = await account.getAuthorityInfo(); assert(authorityInfo.required_auth.keys.find((keyData) => { return keyData.key == keysPair.publicKey })); @@ -336,7 +338,7 @@ describe('Account', function () { assert(authorityInfo.required_auth.keys[0].weight == 1); const keysPair = await eoslime.utils.generateKeys(); - await account.addAuthorityKey(keysPair.publicKey, WEIGHT); + await account.addOnBehalfKey(keysPair.publicKey, WEIGHT); authorityInfo = await account.getAuthorityInfo(); assert(authorityInfo.required_auth.keys.find((key) => { return key.weight == WEIGHT })); @@ -349,8 +351,8 @@ describe('Account', function () { assert(authorityInfo.required_auth.keys.length == 1); const keysPair = await eoslime.utils.generateKeys(); - await account.addAuthorityKey(keysPair.publicKey); - await account.addAuthorityKey(keysPair.publicKey); + await account.addOnBehalfKey(keysPair.publicKey); + await account.addOnBehalfKey(keysPair.publicKey); authorityInfo = await account.getAuthorityInfo(); @@ -361,7 +363,7 @@ describe('Account', function () { it('Should throw if one provide an invalid public key', async () => { try { const account = await Account.createRandom(); - await account.addAuthorityKey('Invalid public key'); + await account.addOnBehalfKey('Invalid public key'); assert(false); } catch (error) { @@ -428,7 +430,7 @@ describe('Account', function () { assert(authorityInfo.required_auth.threshold == 1); const keysPair = await eoslime.utils.generateKeys(); - await account.addAuthorityKey(keysPair.publicKey); + await account.addOnBehalfKey(keysPair.publicKey); await account.increaseThreshold(THRESHOLD); authorityInfo = await account.getAuthorityInfo(); @@ -481,7 +483,8 @@ describe('Account', function () { const faucetContract = await eoslimeTool.Contract.deploy(FAUCET_WASM_PATH, FAUCET_ABI_PATH); const account = await eoslimeTool.Account.createRandom(); - const accountRandomAuth = await account.createSubAuthority('random'); + await account.addAuthority('random'); + const accountRandomAuth = eoslimeTool.Account.load(account.name, account.privateKey, 'random'); try { await faucetContract.actions.produce( @@ -492,7 +495,7 @@ describe('Account', function () { assert(error.includes('action declares irrelevant authority')); } - await accountRandomAuth.setAuthorityAbilities([ + await account.setAuthorityAbilities('random', [ { action: 'produce', contract: faucetContract.name @@ -508,15 +511,34 @@ describe('Account', function () { it('Should throw if one does not provide array as abilities', async () => { try { const account = await eoslimeTool.Account.createRandom(); - const authorityAccount = await account.createSubAuthority('random'); - - await authorityAccount.setAuthorityAbilities('Fake ability'); + await account.addAuthority('random'); + await account.setAuthorityAbilities('random', 'Fake ability'); assert(false); } catch (error) { assert(error.message.includes('Provided String is not an instance of Array')); } }); + + it('Should throw if one does not provide existing authority', async () => { + try { + const account = await eoslimeTool.Account.createRandom(); + + // Random does not exists + await account.setAuthorityAbilities('random', [ + { + action: 'test', + contract: 'eosio' + } + ]); + + assert(false); + } catch (error) { + assert(error.message.includes('Account does not have authority with name: [random].')); + assert(error.message.includes('You could add it by using [addAuthority] function.')); + assert(error.message.includes('For details check [Set authority abilities] suite in account-tests.js')); + } + }); }); const getAuthorityForAccount = async function (authorityName, accountName) { @@ -721,8 +743,8 @@ describe('Account', function () { assert(decryptedJSONAccount.name, 'Incorrect name'); assert(decryptedJSONAccount.privateKey, 'Incorrect private key'); assert(decryptedJSONAccount.publicKey, 'Incorrect public key'); - assert(decryptedJSONAccount.executiveAuthority.actor == decryptedJSONAccount.name, 'Incorrect authority actor'); - assert(decryptedJSONAccount.executiveAuthority.permission == 'active', 'Incorrect authority permission'); + assert(decryptedJSONAccount.authority.actor == decryptedJSONAccount.name, 'Incorrect authority actor'); + assert(decryptedJSONAccount.authority.permission == 'active', 'Incorrect authority permission'); assert(JSON.stringify(decryptedJSONAccount.provider.network) == JSON.stringify(Networks['local']), 'Incorrect network'); }); diff --git a/tests/multisig-account-test.js b/tests/multisig-account-test.js index 031229d..35a1cb5 100644 --- a/tests/multisig-account-test.js +++ b/tests/multisig-account-test.js @@ -44,8 +44,8 @@ describe('Multi signature account', function () { await eoslime.utils.generateKeys() ] - await account.addAuthorityKey(keys[0].publicKey) - await account.addAuthorityKey(keys[1].publicKey) + await account.addOnBehalfKey(keys[0].publicKey) + await account.addOnBehalfKey(keys[1].publicKey) await account.increaseThreshold(2); const multiSigAccount = eoslime.MultiSigAccount.load(account.name, account.privateKey); diff --git a/tests/types/account.ts b/tests/types/account.ts index 8cd2826..0cd84e0 100644 --- a/tests/types/account.ts +++ b/tests/types/account.ts @@ -27,20 +27,21 @@ describe('All types of accounts', function () { assert(account.provider !== undefined); assert(account.publicKey !== undefined); assert(account.privateKey !== undefined); - assert(account.executiveAuthority.actor !== undefined); - assert(account.executiveAuthority.permission !== undefined); + assert(account.authority.actor !== undefined); + assert(account.authority.permission !== undefined); assert(typeof (account.send) == 'function'); assert(typeof (account.buyRam) == 'function'); assert(typeof (account.setWeight) == 'function'); assert(typeof (account.getBalance) == 'function'); assert(typeof (account.buyBandwidth) == 'function'); + assert(typeof (account.addAuthority) == 'function'); assert(typeof (account.addPermission) == 'function'); - assert(typeof (account.addAuthorityKey) == 'function'); + assert(typeof (account.addOnBehalfKey) == 'function'); assert(typeof (account.getAuthorityInfo) == 'function'); assert(typeof (account.increaseThreshold) == 'function'); - assert(typeof (account.createSubAuthority) == 'function'); assert(typeof (account.addOnBehalfAccount) == 'function'); + assert(typeof (account.setAuthorityAbilities) == 'function'); } describe('Static functions', function () { @@ -184,12 +185,10 @@ describe('All types of accounts', function () { assertTransactionResult(tx); }); - it('Should create authority', async () => { + it('Should add authority', async () => { const account = await eoslime.Account.createRandom(); - const newAuthorityAccount = await account.createSubAuthority('custom'); - - assertAccount(newAuthorityAccount); - assert(typeof (newAuthorityAccount.setAuthorityAbilities) == 'function'); + const tx = await account.addAuthority('custom'); + assertTransactionResult(tx); }); it('Should create permission for active authority', async () => { @@ -210,7 +209,7 @@ describe('All types of accounts', function () { const account = await eoslime.Account.createRandom(); const keysPair = await utils.generateKeys(); - const tx = await account.addAuthorityKey(keysPair.publicKey); + const tx = await account.addOnBehalfKey(keysPair.publicKey); assertTransactionResult(tx); }); @@ -220,7 +219,7 @@ describe('All types of accounts', function () { const account = await eoslime.Account.createRandom(); const keysPair = await utils.generateKeys(); - const tx = await account.addAuthorityKey(keysPair.publicKey, WEIGHT); + const tx = await account.addOnBehalfKey(keysPair.publicKey, WEIGHT); assertTransactionResult(tx); }); @@ -249,7 +248,7 @@ describe('All types of accounts', function () { const account = await eoslime.Account.createRandom(); const keysPair = await utils.generateKeys(); - await account.addAuthorityKey(keysPair.publicKey); + await account.addOnBehalfKey(keysPair.publicKey); const tx = await account.increaseThreshold(THRESHOLD); assertTransactionResult(tx); @@ -290,9 +289,8 @@ describe('All types of accounts', function () { const { name } = await eoslime.Contract.deploy(WASM_PATH, ABI_PATH); const account = await eoslime.Account.createRandom(); - const authorityAccount = await account.createSubAuthority('custom'); - - const tx = await authorityAccount.setAuthorityAbilities([ + await account.addAuthority('custom'); + const tx = await account.setAuthorityAbilities('custom', [ { action: 'test', contract: name @@ -311,8 +309,8 @@ describe('All types of accounts', function () { assert(account.proposals); assert(account.publicKey); assert(account.privateKey); - assert(account.executiveAuthority.actor); - assert(account.executiveAuthority.permission); + assert(account.authority.actor); + assert(account.authority.permission); assert(typeof (account.loadKeys) == 'function'); assert(typeof (account.loadAccounts) == 'function'); diff --git a/tests/types/contract.ts b/tests/types/contract.ts index 080a1d8..492643e 100644 --- a/tests/types/contract.ts +++ b/tests/types/contract.ts @@ -16,7 +16,9 @@ const WASM_PATH = "./tests/testing-contracts/compiled/faucet.wasm"; describe("Contract", function () { - const account = eoslime.Account.load('eosio', '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'); + this.timeout(20000); + + const eosio = eoslime.Account.load('eosio', '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'); const assertContract = function (contract: Contract): void { assert(typeof contract.actions.produce == "function"); @@ -30,17 +32,17 @@ describe("Contract", function () { describe("Instantiation", function () { it("Should have fromFile function", async () => { - const contract = eoslime.Contract.fromFile(ABI_PATH, 'contracttest', account); + const contract = eoslime.Contract.fromFile(ABI_PATH, 'contracttest', eosio); assertContract(contract); }); it("Should have at function", async () => { const { name } = await eoslime.Contract.deploy(WASM_PATH, ABI_PATH); - const contract = await eoslime.Contract.at(name, account); + const contract = await eoslime.Contract.at(name, eosio); assertContract(contract); }); - it("Should set default account as executor if none is provided", async () => { + it("Should set default eosio as executor if none is provided", async () => { const contract = eoslime.Contract.fromFile(ABI_PATH, 'contracttest'); assertContract(contract); }); @@ -91,7 +93,7 @@ describe("Contract", function () { it("Should execute a blockchain action from another executor", async () => { const contract = await eoslime.Contract.deploy(WASM_PATH, ABI_PATH); - await contract.actions.test([], { from: account }); + await contract.actions.test([], { from: eosio }); }); it('Should process nonce-action', async () => { @@ -101,7 +103,7 @@ describe("Contract", function () { it('Should process token action', async () => { const contract = await eoslime.Contract.deploy(WASM_PATH, ABI_PATH); - await contract.actions.test([], { tokens: '5.0000 SYS' }); + await contract.actions.test([], { from: eosio, tokens: '5.0000 SYS' }); }); describe("Methods", function () { @@ -115,7 +117,7 @@ describe("Contract", function () { it('Should get a raw transaction from an action with options', async () => { const contract = await eoslime.Contract.deploy(WASM_PATH, ABI_PATH); - const rawActionTx = await contract.actions.test.getRawTransaction([], { from: account }); + const rawActionTx = await contract.actions.test.getRawTransaction([], { from: eosio }); assertRawTransaction(rawActionTx); }); @@ -186,7 +188,7 @@ describe("Contract", function () { const { name } = await eoslime.Contract.deploy(WASM_PATH, ABI_PATH); eoslime.Contract.on('init', (contract: Contract) => { assertContract(contract) }); - eoslime.Contract.fromFile(ABI_PATH, name, account); + eoslime.Contract.fromFile(ABI_PATH, name, eosio); }); it("Should have deploy event", async () => { diff --git a/types/account/index.d.ts b/types/account/index.d.ts index d2e6cb9..54b16de 100644 --- a/types/account/index.d.ts +++ b/types/account/index.d.ts @@ -32,12 +32,17 @@ declare class BaseAccount { public publicKey: string; public privateKey: string; public provider: BaseProvider; - public executiveAuthority: ExecutiveAuthority; + public authority: ExecutiveAuthority; constructor (name: string, privateKey: string, provider: BaseProvider, permission: string); public getAuthorityInfo (): Promise; } +interface AuthorityAbilities { + action: string; + contract: string; +} + export class Account extends BaseAccount { constructor (name: string, privateKey: string, provider: BaseProvider, permission: string); @@ -45,28 +50,16 @@ export class Account extends BaseAccount { public buyRam (bytes: number, payer?: Account): Promise; public buyBandwidth (cpu: string, net: string, payer?: Account): Promise; public send (receiver: Account, amount: string, symbol: string): Promise; - public createSubAuthority (authorityName: string, threshold?: number): Promise; + public addAuthority (authorityName: string, threshold?: number): Promise; + public setAuthorityAbilities (authorityName: string, abilities: Array): Promise; public increaseThreshold (threshold: number): Promise; public addPermission (authorityName: string, weight?: number): Promise; public addOnBehalfAccount (accountName: string, authority?: string, weight?: number): Promise; - public addAuthorityKey (publicKey: string, weight?: number): Promise; + public addOnBehalfKey (publicKey: string, weight?: number): Promise; public setWeight (weight: number): Promise; public getBalance (symbol?: string, code?: string): Promise>; } -interface AuthorityAbilities { - action: string; - contract: string; -} - -declare class AuthorityAccount extends Account { - - public parentPermission: string; - - constructor (parentPermission: string, name: string, privateKey: string, provider: BaseProvider, permission: string); - public setAuthorityAbilities (abilities: Array): Promise; -} - export class MultiSignatureFactory { constructor (provider: BaseProvider); From 2b828d2dfb1284264d2b016bd59bc3a475eed419 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 1 Jul 2020 13:30:41 +0300 Subject: [PATCH 22/35] Add tests for action.sign and action.getRawTransaction with options --- .../multi-signature-account/account.js | 2 +- tests/contract-tests.js | 104 +++++++++++++----- tests/types/account.ts | 7 +- 3 files changed, 84 insertions(+), 29 deletions(-) diff --git a/src/account/multi-signature-account/account.js b/src/account/multi-signature-account/account.js index a347f4e..1e14040 100644 --- a/src/account/multi-signature-account/account.js +++ b/src/account/multi-signature-account/account.js @@ -6,7 +6,7 @@ class MultiSignatureAccount extends BaseAccount { constructor (name, privateKey, provider, authority) { super(name, privateKey, provider, authority) - this.accounts = [this]; + this.accounts = []; this.proposals = {}; } diff --git a/tests/contract-tests.js b/tests/contract-tests.js index c9b44fc..6383c54 100644 --- a/tests/contract-tests.js +++ b/tests/contract-tests.js @@ -199,45 +199,95 @@ describe("Contract", function () { } }); - it('Should get the raw transaction from the action', async () => { + function assertRawTransaction (tx, contractName) { + assert(tx.expiration != undefined); + assert(tx.ref_block_num != undefined); + assert(tx.ref_block_prefix != undefined); + assert(tx.max_net_usage_words != undefined); + assert(tx.max_cpu_usage_ms != undefined); + assert(tx.delay_sec != undefined); + assert(tx.context_free_actions != undefined); + assert(tx.actions != undefined); + assert(tx.actions[0].name == 'produce'); + assert(tx.actions[0].account == contractName); + assert(tx.actions[0].data != undefined); + assert(tx.actions[0].authorization != undefined); + } + + it('Should get a raw transaction from action', async () => { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); const tokensHolder = await eoslime.Account.createRandom(); const rawActionTx = await faucetContract.actions.produce.getRawTransaction([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo']); - assert(rawActionTx.expiration != undefined); - assert(rawActionTx.ref_block_num != undefined); - assert(rawActionTx.ref_block_prefix != undefined); - assert(rawActionTx.max_net_usage_words != undefined); - assert(rawActionTx.max_cpu_usage_ms != undefined); - assert(rawActionTx.delay_sec != undefined); - assert(rawActionTx.context_free_actions != undefined); - assert(rawActionTx.actions != undefined); - assert(rawActionTx.actions[0].name == 'produce'); - assert(rawActionTx.actions[0].account == faucetContract.name); - assert(rawActionTx.actions[0].data != undefined); - assert(rawActionTx.actions[0].authorization != undefined); + assertRawTransaction(rawActionTx, faucetContract.name); + }); + + it('Should get a raw transaction from payable action', async () => { + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const tokensHolder = await eoslime.Account.createRandom(); + + const rawActionTx = await faucetContract.actions.produce.getRawTransaction([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { tokens: '5.0000 SYS' }); + + assertRawTransaction(rawActionTx, faucetContract.name); + assert(rawActionTx.actions[1].name == 'transfer'); + assert(rawActionTx.actions[1].account == 'eosio.token'); + assert(rawActionTx.actions[1].data != undefined); + assert(rawActionTx.actions[1].authorization != undefined); }); - it('Should sign the action without broadcasting it', async () => { + it('Should get a raw transaction from unique action', async () => { + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const tokensHolder = await eoslime.Account.createRandom(); + + const rawActionTx = await faucetContract.actions.produce.getRawTransaction([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { unique: true }); + + assertRawTransaction(rawActionTx, faucetContract.name); + assert(rawActionTx.actions[1].name == 'nonce'); + assert(rawActionTx.actions[1].account == 'eosio.null'); + assert(rawActionTx.actions[1].data != undefined); + assert(rawActionTx.actions[1].authorization != undefined); + }); + + function assertSignedTransaction (tx, contractName) { + assert(tx.signatures.length == 1); + assertRawTransaction(tx.transaction, contractName); + } + + it('Should sign an action without broadcasting it', async () => { const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); const tokensHolder = await eoslime.Account.createRandom(); const signer = await eoslime.Account.createRandom(); const signedActionTx = await faucetContract.actions.produce.sign([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { from: signer }); + assertSignedTransaction(signedActionTx, faucetContract.name); + }); + + it('Should sign a payable action ', async () => { + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const tokensHolder = await eoslime.Account.createRandom(); + const signer = await eoslime.Account.createRandom(); + + const signedActionTx = await faucetContract.actions.produce.sign([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { tokens: '5.0000 SYS', from: signer }); + + assertSignedTransaction(signedActionTx, faucetContract.name); + assert(signedActionTx.transaction.actions[1].name == 'transfer'); + assert(signedActionTx.transaction.actions[1].account == 'eosio.token'); + assert(signedActionTx.transaction.actions[1].data != undefined); + assert(signedActionTx.transaction.actions[1].authorization != undefined); + }); + + it('Should sign unique action ', async () => { + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const tokensHolder = await eoslime.Account.createRandom(); + const signer = await eoslime.Account.createRandom(); + + const signedActionTx = await faucetContract.actions.produce.sign([tokensHolder.name, '100.0000 TKNS', tokenContract.name, 'memo'], { unique: true, from: signer }); - assert(signedActionTx.signatures.length == 1); - assert(signedActionTx.transaction.expiration != undefined); - assert(signedActionTx.transaction.ref_block_num != undefined); - assert(signedActionTx.transaction.ref_block_prefix != undefined); - assert(signedActionTx.transaction.max_net_usage_words != undefined); - assert(signedActionTx.transaction.max_cpu_usage_ms != undefined); - assert(signedActionTx.transaction.delay_sec != undefined); - assert(signedActionTx.transaction.context_free_actions != undefined); - assert(signedActionTx.transaction.actions != undefined); - assert(signedActionTx.transaction.actions[0].name == 'produce'); - assert(signedActionTx.transaction.actions[0].account == faucetContract.name); - assert(signedActionTx.transaction.actions[0].data != undefined); - assert(signedActionTx.transaction.actions[0].authorization != undefined); + assertSignedTransaction(signedActionTx, faucetContract.name); + assert(signedActionTx.transaction.actions[1].name == 'nonce'); + assert(signedActionTx.transaction.actions[1].account == 'eosio.null'); + assert(signedActionTx.transaction.actions[1].data != undefined); + assert(signedActionTx.transaction.actions[1].authorization != undefined); }); it('Should throw if trying to sign the action with an invalid signer', async () => { diff --git a/tests/types/account.ts b/tests/types/account.ts index 0cd84e0..83fe9cd 100644 --- a/tests/types/account.ts +++ b/tests/types/account.ts @@ -14,6 +14,8 @@ import { assertTransactionResult } from './utils'; describe('All types of accounts', function () { + this.timeout(20000); + const ACCOUNT_NAME = 'eosio'; const ACCOUNT_PRIVATE_KEY = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'; @@ -361,10 +363,13 @@ describe('All types of accounts', function () { }); it('Should approve a transaction for broadcasting', async () => { + const accounts = await eoslime.Account.createRandoms(2); const multiSigAccount = eoslime.MultiSigAccount.load(ACCOUNT_NAME, ACCOUNT_PRIVATE_KEY); + multiSigAccount.loadAccounts(accounts); + const proposalId = await multiSigAccount.propose(contract.actions.test, []); - await multiSigAccount.approve(multiSigAccount.publicKey, proposalId); + await multiSigAccount.approve(multiSigAccount.accounts[0].publicKey, proposalId); }); it('Should broadcast a proposed transaction', async () => { From 575d3271e6e4342f38fcdf9464f9f89c0807feee Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 1 Jul 2020 14:45:16 +0300 Subject: [PATCH 23/35] Add functions description --- types/account/index.d.ts | 194 +++++++++++++++++++++++++++++++++++++- types/contract/index.d.ts | 84 ++++++++++++++++- types/provider/index.d.ts | 31 ++++++ types/utils/index.d.ts | 31 ++++++ 4 files changed, 337 insertions(+), 3 deletions(-) diff --git a/types/account/index.d.ts b/types/account/index.d.ts index 54b16de..d937ac9 100644 --- a/types/account/index.d.ts +++ b/types/account/index.d.ts @@ -18,12 +18,68 @@ export class AccountFactory { constructor (provider: BaseProvider); + /** + * + * @description Load existing network account + * @param {string} name Account name + * @param {string} privateKey Private key of the signer + * @param {string} [authorityName] Authority account will act from + * @returns {Account} Loaded account + */ public load (name: string, privateKey: string, authorityName?: string): Account; + + /** + * @description Creates a fresh new account for a given name and private key + * + * @param {string} accountName Desired name of the created account + * @param {string} privateKey Desired private key of the created account + * @param {Account} [accountCreator] Another account responsible for paying creation fees + * @returns {Promise} Created account + */ public create (accountName: string, privateKey: string, accountCreator?: Account): Promise; + + /** + * @description Creates a fresh new account for a given name + * + * @param {string} accountName Desired name of the created account + * @param {Account} [accountCreator] Another account responsible for paying creation fees + * @returns {Promise} Created account + */ public createFromName (accountName: string, accountCreator?: Account): Promise; + + /** + * @description Create new random account + * + * @param {Account} [accountCreator] Another account responsible for paying creation fees + * @returns {Promise} Created account + */ public createRandom (accountCreator?: Account): Promise; + + /** + * @description Create new random accounts + * + * @param {number} accountsCount Number of accounts to be created + * @param {Account} [accountCreator] Another account responsible for paying creation fees + * @returns {Promise>} Array of created account + */ public createRandoms (accountsCount: number, accountCreator?: Account): Promise> + + /** + * @description Create a new random account and encrypt it. + * + * @param {string} password Password the account data will be encrypted with + * @param {Account} [accountCreator] Another account responsible for paying creation fees + * @returns {Promise} Encrypted account data in JSON + */ public createEncrypted (password: string, accountCreator?: Account): Promise; + + /** + * @description Decrypt an encrypted account + * + * @param {EncryptedAccount} encryptedAccount JSON format of the encrypted account + * @param {string} password Password for decrypting + * @returns {Account} Decrypted account + */ public fromEncrypted (encryptedAccount: EncryptedAccount, password: string): Account; } @@ -35,6 +91,12 @@ declare class BaseAccount { public authority: ExecutiveAuthority; constructor (name: string, privateKey: string, provider: BaseProvider, permission: string); + + /** + * @description Returns information for loaded authority + * + * @returns {Promise} + */ public getAuthorityInfo (): Promise; } @@ -47,22 +109,119 @@ export class Account extends BaseAccount { constructor (name: string, privateKey: string, provider: BaseProvider, permission: string); + /** + * @description Buy ram for this account + * + * @param {number} bytes Number of RAM bytes + * @param {Account} [payer] Another account responsible for paying + * @returns {Promise} Transaction receipt + */ public buyRam (bytes: number, payer?: Account): Promise; + + /** + * @description Buy cpu and network for this account + * + * @param {string} cpu Amount of tokens one want to buy cpu for + * @param {string} net Amount of tokens one want to buy net for + * @param {Account} [payer] Another account responsible for paying + * @returns {Promise} Transaction receipt + */ public buyBandwidth (cpu: string, net: string, payer?: Account): Promise; + + /** + * @description Send tokens to another account + * + * @param {Account} receiver Account tokens will be sent to + * @param {string} amount Tokens amount + * @param {string} symbol Token symbol + * @returns {Promise} Transaction receipt + */ public send (receiver: Account, amount: string, symbol: string): Promise; + + /** + * @description Add sub authority to the current account's one + * + * @param {string} authorityName Desired name of the new authority + * @param {number} [threshold] Desired threshold of the new authority + * @returns {Promise} Transaction receipt + */ public addAuthority (authorityName: string, threshold?: number): Promise; + + /** + * @description Define what actions of which contracts the authority has permissions to execute + * + * @param {string} authorityName Sub authority one is setting permissions for + * @param {Array} abilities Array of permissions + * @returns {Promise} Transaction receipt + */ public setAuthorityAbilities (authorityName: string, abilities: Array): Promise; + + /** + * @description Increase authority's threshold + * + * @param {number} threshold Number of desired threshold + * @returns {Promise} Transaction receipt + */ public increaseThreshold (threshold: number): Promise; + + /** + * @description Add permission to authority such as eosio.code + * + * @param {string} authorityName Permissions [eosio.code] + * @param {number} [weight] Weight of the permission + * @returns {Promise} Transaction receipt + */ public addPermission (authorityName: string, weight?: number): Promise; + + /** + * @description Allow another account to act from your account's authority + * + * @param {string} accountName Name of another account + * @param {string} [authority] Authority of another account + * @param {number} [weight] Weight another account has + * @returns {Promise} Transaction receipt + */ public addOnBehalfAccount (accountName: string, authority?: string, weight?: number): Promise; + + /** + * @description Allow more keys to sign transactions from your account + * + * @param {string} publicKey Allowed public key + * @param {number} [weight] Weight the key has + * @returns {Promise} Transaction receipt + */ public addOnBehalfKey (publicKey: string, weight?: number): Promise; + + /** + * @description Set weight on account public key. + * + * @param {number} weight Number of weight + * @returns {Promise} Transaction receipt + */ public setWeight (weight: number): Promise; + + /** + * @description Returns the balance of provided token account has + * + * @param {string} [symbol] Token symbol + * @param {string} [code] Token contract name + * @returns {Promise>} Balance + */ public getBalance (symbol?: string, code?: string): Promise>; } export class MultiSignatureFactory { constructor (provider: BaseProvider); + + /** + * @description Load existing network multisig account + * + * @param {string} name Account name + * @param {string} privateKey Private key of one of the multisig owners + * @param {string} [authorityName] Authority the multisig will act from + * @returns {MultiSignatureAccount} Loaded multisig account + */ public load (name: string, privateKey: string, authorityName?: string): MultiSignatureAccount; } @@ -73,10 +232,43 @@ export class MultiSignatureAccount extends BaseAccount { constructor (name: string, privateKey: string, provider: BaseProvider, authority: string); + /** + * @description Load the private keys of the authority public keys in order to approve transactions with them + * + * @param {Array} privateKeys Private keys one will use to approve transactions with + */ public loadKeys (privateKeys: Array): void; + + /** + * @description Load the accounts configured to act on behalf of the multisig authority + * + * @param {Array} accounts Accounts one will use to approve transactions with + */ public loadAccounts (accounts: Array): void; + + /** + * @description Propose a transaction to be executed + * + * @param {ContractFunction} contractAction Action one propose to be broadcasted + * @param {Array} actionData Action data + * @returns {Promise} Proposal id + */ public propose (contractAction: ContractFunction, actionData: Array): Promise; + + /** + * @description Sign a proposed transaction + * + * @param {string} publicKey Key of the approver + * @param {number} proposalId ID of proposal + * @returns {Promise} + */ public approve (publicKey: string, proposalId: number): Promise; + + /** + * @description Broadcast proposal in case of enough approvals + * + * @param {number} proposalId ID of proposal + * @returns {Promise} Transaction receipt + */ public processProposal (proposalId: number): Promise; } - diff --git a/types/contract/index.d.ts b/types/contract/index.d.ts index ccf0279..3cee94d 100644 --- a/types/contract/index.d.ts +++ b/types/contract/index.d.ts @@ -17,8 +17,23 @@ declare class ContractInitializator extends EventClass { constructor (provider: BaseProvider); - /* Own functions */ + /** + * @description Instantiate a contract by retrieving the ABI from the chain + * + * @param {string} contractName Contract name of the contract one wants to instantiate + * @param {Account} [contractExecutorAccount] Account responsible for signing and broadcasting transactions + * @returns {Promise} Instantiated contract + */ public at (contractName: string, contractExecutorAccount?: Account): Promise; + + /** + * @description Instantiate a contract by providing the ABI + * + * @param {*} abi Contract ABI + * @param {string} contractName Contract name of the contract one wants to instantiate + * @param {Account} [contractExecutorAccount] Account responsible for signing and broadcasting transactions + * @returns {Contract} Instantiated contract + */ public fromFile (abi: any, contractName: string, contractExecutorAccount?: Account): Contract } @@ -36,9 +51,47 @@ export class ContractFactory extends ContractInitializator { public on (eventName: 'deploy', callback: (contract: Contract, deployTx: [TransactionResult, TransactionResult]) => void): void; /* Own functions */ + + /** + * @description Deploy a new contract on a random generated account from ABI and WASM files + * + * @param {string} wasmPath Path to the contract WASM file + * @param {string} abiPath Path to the contract ABI file + * @param {DeployOptions} [options] + * @returns {Promise} Deployed contract + */ public deploy (wasmPath: string, abiPath: string, options?: DeployOptions): Promise; + + /** + * @description Deploy a new contract on a random generated account from loaded ABI and WASM + * + * @param {string} wasm Contract WASM + * @param {*} abi Contract ABI + * @param {DeployOptions} [options] + * @returns {Promise} Deployed contract + */ public deployRaw (wasm: string, abi: any, options?: DeployOptions): Promise; + + /** + * @description Deploy a new contract on the provided account from ABI and WASM files + * + * @param {string} wasmPath Path to the contract WASM file + * @param {string} abiPath Path to the contract ABI file + * @param {Account} contractAccount Account the contract will be deployed on + * @param {DeployOptions} [options] + * @returns {Promise} Deployed contract + */ public deployOnAccount (wasmPath: string, abiPath: string, contractAccount: Account, options?: DeployOptions): Promise; + + /** + * @description Deploy a new contract on the provided account from loaded ABI and WASM + * + * @param {string} wasm Contract WASM + * @param {*} abi Contract ABI + * @param {Account} contractAccount Account the contract will be deployed on + * @param {DeployOptions} [options] + * @returns {Promise} Deployed contract + */ public deployRawOnAccount (wasm: string, abi: any, contractAccount: Account, options?: DeployOptions): Promise; } @@ -56,7 +109,23 @@ export interface ContractFunction extends EventClass { on (eventName: 'processed', callback: (txResult: TransactionResult, ...fnParams: any[]) => void): void; /* Own functions */ + + /** + * @description Construct raw transaction for an action + * + * @param {any[]} params Action arguments + * @param {ContractFunctionOptions} [options] + * @returns {Promise} Raw transaction + */ getRawTransaction (params: any[], options?: ContractFunctionOptions): Promise; + + /** + * @description Sign action and return a ready-to-broadcast transaction + * + * @param {any[]} params Action arguments + * @param {ContractFunctionOptions} [options] + * @returns {Promise} Ready to broadcast transaction + */ sign (params: any[], options?: ContractFunctionOptions): Promise; } @@ -79,6 +148,17 @@ export class Contract { constructor (provider: BaseProvider, abi: any, contractName: string, contractExecutorAccount: Account); + /** + * @description Enable the contract to make inline actions + * + * @returns {Promise} void + */ public makeInline (): Promise; - public getRawWASM (): Promise; + + /** + * @description Retrieve contract raw WASM + * + * @returns {Promise} Contract raw WASM + */ + public getRawWASM (): Promise; } diff --git a/types/provider/index.d.ts b/types/provider/index.d.ts index 43fa113..4bc7372 100644 --- a/types/provider/index.d.ts +++ b/types/provider/index.d.ts @@ -19,8 +19,28 @@ export class BaseProvider { constructor (networkConfig: NetworkData); + /** + * @description Table query chain + * + * @param {string} table Contract table one wants to read data from + * @returns {SelectQuery} Select query chain + */ public select (table: string): SelectQuery; + + /** + * @description Retrieve contract ABI from the chain + * + * @param {string} contractName Name of the contract + * @returns {Promise} Contract ABI + */ public getABI (contractName: string): Promise; + + /** + * @description Retrieve contract WASM from the chain. Useful for deploying another contract directly + * + * @param {string} contractName Name of the contract + * @returns {Promise} Contract WASM + */ public getRawWASM (contractName: string): Promise; } @@ -31,7 +51,18 @@ declare class ProviderFactory { constructor (network: string, config: NetworkDetails); + /** + * @description Reset provider to another one + * + * @param {BaseProvider} newProvider New provider the current one will be set to + */ public reset (newProvider: BaseProvider): void; + + /** + * @description List of eoslime supported networks + * + * @returns {Array} Supported networks names + */ public availableNetworks (): Array; } diff --git a/types/utils/index.d.ts b/types/utils/index.d.ts index 6b29301..2bc90fc 100644 --- a/types/utils/index.d.ts +++ b/types/utils/index.d.ts @@ -4,9 +4,40 @@ interface KeysPair { } export interface utils { + /** + * @description Convert account name from uint64 to string + * + * @param {string} encodedName Uint64 format of account name + * @returns {string} String format of account name + */ toName (encodedName: string): string; + + /** + * @description Generate a random account name + * + * @returns {Promise} Account name + */ randomName (): Promise; + + /** + * @description Generate a random public/private keys pair + * + * @returns {Promise} Keys pair + */ generateKeys (): Promise; + + /** + * @description Generate a random private key + * + * @returns {Promise} Random key + */ randomPrivateKey (): Promise; + + /** + * @description Construct an account name from a private key. The name is constructed in a custom way, it is not related to the private key in any manner + * + * @param {string} privateKey Private key + * @returns {Promise} Account name + */ nameFromPrivateKey (privateKey: string): Promise; } From 6329bc5e85f638d36ede7d7d80129b404d734b9a Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Tue, 18 Aug 2020 17:31:36 +0300 Subject: [PATCH 24/35] Refactor CLI commands tests and cover 97% percent of the code --- README.md | 2 +- cli-commands/command-definer.js | 6 +- cli-commands/commands/command.js | 7 +- cli-commands/commands/compile/index.js | 31 +- cli-commands/commands/compile/messages.js | 15 +- .../commands/compile/options/path-option.js | 4 +- cli-commands/commands/deploy/index.js | 16 +- cli-commands/commands/deploy/messages.js | 13 +- .../commands/deploy/options/network-option.js | 2 +- .../commands/deploy/options/path-option.js | 12 +- cli-commands/commands/group-command.js | 8 +- cli-commands/commands/init/index.js | 32 +- cli-commands/commands/init/messages.js | 11 +- .../with-example/with-example-option.js | 2 +- .../specific/nodeos-data/data-manager.js | 3 +- .../nodeos/subcommands/accounts/index.js | 10 +- .../nodeos/subcommands/accounts/messages.js | 7 +- .../nodeos/subcommands/common/accounts.js | 8 +- .../commands/nodeos/subcommands/logs/index.js | 18 +- .../nodeos/subcommands/logs/messages.js | 13 +- .../nodeos/subcommands/start/index.js | 19 +- .../nodeos/subcommands/start/messages.js | 13 +- .../subcommands/start/options/path-option.js | 7 +- .../commands/nodeos/subcommands/stop/index.js | 13 +- .../nodeos/subcommands/stop/messages.js | 11 +- cli-commands/commands/shape/index.js | 21 +- cli-commands/commands/shape/messages.js | 12 +- .../shape/options/framework-option.js | 6 +- cli-commands/commands/test/definition.js | 2 +- cli-commands/commands/test/index.js | 18 +- cli-commands/commands/test/messages.js | 11 +- .../commands/test/options/network-option.js | 2 +- .../commands/test/options/path-option.js | 2 +- .../{resource-usage-option.js => index.js} | 2 +- .../commands/test/specific/utils/index.js | 11 +- cli-commands/common/logger.js | 14 +- cli-commands/common/table.js | 6 +- cli-commands/helpers/async-soft-exec.js | 28 +- nyc.config.js | 8 + package.json | 6 +- src/contract/contract-initializator.js | 6 +- tests/cli-commands/command-tests.js | 20 - tests/cli-commands/common/logger-tests.js | 30 - tests/cli-commands/compile-tests.js | 283 +++++--- tests/cli-commands/deploy-tests.js | 354 ++++----- tests/cli-commands/group-command-tests.js | 26 - .../helpers/async-soft-exec-tests.js | 42 -- .../helpers/file-system-util-tests.js | 62 -- tests/cli-commands/init-tests.js | 235 +++--- tests/cli-commands/miscellaneous.js | 398 +++++++++++ tests/cli-commands/mocks/deployment.js | 3 - tests/cli-commands/mocks/nodeos-data/eosd.pid | 1 - .../cli-commands/mocks/nodeos-data/nodeos.log | 142 ---- tests/cli-commands/mocks/test-option.js | 19 - tests/cli-commands/mocks/tests-mock.js | 7 - tests/cli-commands/nodeos-tests.js | 669 +++++++++++------- tests/cli-commands/shape-tests.js | 120 ++-- tests/cli-commands/test-tests.js | 380 +++++++--- tests/cli-commands/utils/logger.js | 55 ++ tests/contract-tests.js | 6 +- tests/helpers-tests.js | 34 +- 61 files changed, 1966 insertions(+), 1358 deletions(-) rename cli-commands/commands/test/options/resource-usage-option/{resource-usage-option.js => index.js} (99%) create mode 100644 nyc.config.js delete mode 100644 tests/cli-commands/command-tests.js delete mode 100644 tests/cli-commands/common/logger-tests.js delete mode 100644 tests/cli-commands/group-command-tests.js delete mode 100644 tests/cli-commands/helpers/async-soft-exec-tests.js delete mode 100644 tests/cli-commands/helpers/file-system-util-tests.js create mode 100644 tests/cli-commands/miscellaneous.js delete mode 100644 tests/cli-commands/mocks/deployment.js delete mode 100644 tests/cli-commands/mocks/nodeos-data/eosd.pid delete mode 100644 tests/cli-commands/mocks/nodeos-data/nodeos.log delete mode 100644 tests/cli-commands/mocks/test-option.js delete mode 100644 tests/cli-commands/mocks/tests-mock.js create mode 100644 tests/cli-commands/utils/logger.js diff --git a/README.md b/README.md index 0145c14..b7d644b 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ EOSLIME was able to be initialized only with pre-configured providers connection const eoslime = require('eoslime').init('bos', { url: 'Your url', chainId: 'Your chainId' }); // ... any other supported netwok ... ``` -* **Allow read-only contracts** - You are able now to instantiate a contract withouth a signer/executor and read the contract's tables +* **Allow read-only contracts** - You are able now to instantiate a contract without a signer/executor and read the contract's tables * **Add Tutorial section in the documentation** * **Describe how examples in the documentation could be run** * **Increase the code coverage from 46% to 90+ %** diff --git a/cli-commands/command-definer.js b/cli-commands/command-definer.js index 8a0c821..b774242 100644 --- a/cli-commands/command-definer.js +++ b/cli-commands/command-definer.js @@ -1,5 +1,5 @@ class CommandDefiner { - constructor(yargs) { + constructor (yargs) { this.yargs = yargs; } @@ -28,8 +28,8 @@ class CommandDefiner { handle (command) { return async (args) => { - const result = await command.execute(args); - if (!result) { + await command.execute(args); + if (!command.hasBeenExecuted) { this.yargs.showHelp(); } } diff --git a/cli-commands/commands/command.js b/cli-commands/commands/command.js index 2aa7277..495a562 100644 --- a/cli-commands/commands/command.js +++ b/cli-commands/commands/command.js @@ -1,9 +1,11 @@ class Command { - constructor(commandDefinition) { + constructor (commandDefinition) { this.subcommands = []; this.options = commandDefinition.options || []; this.template = commandDefinition.template || ''; this.description = commandDefinition.description || ''; + + this.hasBeenExecuted = true; } async processOptions (args) { @@ -21,8 +23,7 @@ class Command { return optionResults; } - execute (args) { } - + async execute (args) { } } module.exports = Command; diff --git a/cli-commands/commands/compile/index.js b/cli-commands/commands/compile/index.js index 675a712..b68ef82 100644 --- a/cli-commands/commands/compile/index.js +++ b/cli-commands/commands/compile/index.js @@ -1,7 +1,9 @@ const AsyncSoftExec = require('../../helpers/async-soft-exec'); const Command = require('../command'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; +const MESSAGE_CONTRACT = require('./messages').CONTRACT; + const compiledDirectories = require('./specific/directories.json'); const compileCommandDefinition = require('./definition'); @@ -11,13 +13,13 @@ const fileSysUtils = require('../../helpers/file-system-util'); class CompileCommand extends Command { - constructor() { + constructor () { super(compileCommandDefinition); } async execute (args) { try { - commandMessages.StartCompilation(); + MESSAGE_COMMAND.Start(); const optionsResults = await super.processOptions(args); @@ -26,20 +28,25 @@ class CompileCommand extends Command { for (let i = 0; i < optionsResults.path.length; i++) { const contractPath = optionsResults.path[i]; - // Todo: Check how to compile without using eosio-cpp - const asyncSoftExec = new AsyncSoftExec(`eosio-cpp -I . -o ./compiled/${contractPath.fileName}.wasm ${contractPath.fullPath} --abigen`); - asyncSoftExec.onError((error) => commandMessages.UnsuccessfulCompilationOfContract(error, contractPath.fileName)); - asyncSoftExec.onSuccess(() => commandMessages.SuccessfulCompilationOfContract(contractPath.fileName)); - - await asyncSoftExec.exec(); + await processCompilation(contractPath) } } else { - commandMessages.ContractNotExisting(); + MESSAGE_CONTRACT.NotFound(); } } catch (error) { - commandMessages.UnsuccessfulCompilation(error); + MESSAGE_COMMAND.Error(error); } - return true; + } +} + +const processCompilation = async function (contract) { + try { + const asyncSoftExec = new AsyncSoftExec(`eosio-cpp -I . -o ./compiled/${contract.fileName}.wasm ${contract.fullPath} --abigen`); + await asyncSoftExec.exec(); + + MESSAGE_CONTRACT.Compiled(contract.fileName); + } catch (error) { + MESSAGE_CONTRACT.NotCompiled(error, contract.fileName); } } diff --git a/cli-commands/commands/compile/messages.js b/cli-commands/commands/compile/messages.js index 29789b6..f5f04b2 100644 --- a/cli-commands/commands/compile/messages.js +++ b/cli-commands/commands/compile/messages.js @@ -1,10 +1,13 @@ -const chalk = require('chalk'); const logger = require('../../common/logger'); module.exports = { - 'StartCompilation': () => { logger.info(chalk.magentaBright('===== Compilation has started... =====')); }, - 'UnsuccessfulCompilation': (error) => { logger.error(chalk.redBright(`===== Unsuccessful compilation =====`), error); }, - 'SuccessfulCompilationOfContract': (contract) => { logger.info(chalk.greenBright(`===== Successfully compilation of ${contract} =====`)); }, - 'UnsuccessfulCompilationOfContract': (error, file) => { logger.error(chalk.redBright(`===== Unsuccessful compilation of ${file} =====`), error); }, - 'ContractNotExisting': () => { logger.info(chalk.redBright(`===== There is not a contract to compile =====`)); } + 'CONTRACT': { + 'Compiled': (contract) => { logger.success(`===== ${contract} has been successfully compiled =====`); }, + 'NotCompiled': (error, file) => { logger.error(`===== Unsuccessful compilation of ${file} =====`, error); }, + 'NotFound': () => { logger.info(`===== There is not a contract to compile =====`); } + }, + 'COMMAND': { + 'Start': () => { logger.info('===== Compilation has started... ====='); }, + 'Error': (error) => { logger.error(`===== Unsuccessful compilation =====`, error); }, + } } \ No newline at end of file diff --git a/cli-commands/commands/compile/options/path-option.js b/cli-commands/commands/compile/options/path-option.js index 07afd15..1035a88 100644 --- a/cli-commands/commands/compile/options/path-option.js +++ b/cli-commands/commands/compile/options/path-option.js @@ -5,7 +5,7 @@ const Option = require('../../option'); class PathOption extends Option { - constructor() { + constructor () { super( 'path', { @@ -29,7 +29,7 @@ class PathOption extends Option { }); } - return optionValue.endsWith('.cpp') ? [ { fullPath: optionValue, fileName: path.basename(optionValue, '.cpp') } ] : []; + return optionValue.endsWith('.cpp') ? [{ fullPath: optionValue, fileName: path.basename(optionValue, '.cpp') }] : []; } } diff --git a/cli-commands/commands/deploy/index.js b/cli-commands/commands/deploy/index.js index 4c0afdb..217bda2 100644 --- a/cli-commands/commands/deploy/index.js +++ b/cli-commands/commands/deploy/index.js @@ -1,24 +1,26 @@ const Command = require('../command'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; +const MESSAGE_SCRIPT = require('./messages').SCRIPT; + const deployCommandDefinition = require('./definition'); // eoslime deploy --path --network --deployer class DeployCommand extends Command { - constructor() { + constructor () { super(deployCommandDefinition); } async execute (args) { try { - commandMessages.StartDeployment(); + MESSAGE_COMMAND.Start(); + const optionsResults = await super.processOptions(args); await runDeploymentScripts(optionsResults.path, optionsResults.network, optionsResults.deployer); } catch (error) { - commandMessages.UnsuccessfulDeployment(error); + MESSAGE_COMMAND.Error(error); } - return true; } } @@ -27,9 +29,9 @@ const runDeploymentScripts = async function (deploymentScripts, ...configuration const deploymentScript = deploymentScripts[i]; try { await deploymentScript.deploy(...configuration); - commandMessages.SuccessfulDeploymentOfScript(deploymentScript.fileName); + MESSAGE_SCRIPT.Processed(deploymentScript.fileName); } catch (error) { - commandMessages.UnsuccessfulDeploymentOfScript(deploymentScript.fileName, error); + MESSAGE_SCRIPT.NotProcessed(deploymentScript.fileName, error); } } } diff --git a/cli-commands/commands/deploy/messages.js b/cli-commands/commands/deploy/messages.js index 522b0ed..7df124d 100644 --- a/cli-commands/commands/deploy/messages.js +++ b/cli-commands/commands/deploy/messages.js @@ -1,9 +1,12 @@ -const chalk = require('chalk'); const logger = require('../../common/logger'); module.exports = { - 'StartDeployment': () => { logger.info(chalk.magentaBright('===== Deployment has started... =====')); }, - 'SuccessfulDeploymentOfScript': (script) => { logger.info(chalk.greenBright(`===== Successful deployment of ${script} =====`)); }, - 'UnsuccessfulDeploymentOfScript': (script, error) => { logger.error(chalk.redBright(`===== Unsuccessful deployment of ${script} =====`), error); }, - 'UnsuccessfulDeployment': (error) => { logger.error(chalk.redBright(`===== Unsuccessful deployment =====`), error); } + 'SCRIPT': { + 'Processed': (script) => { logger.success(`===== Successful deployment of ${script} =====`); }, + 'NotProcessed': (script, error) => { logger.error(`===== Unsuccessful deployment of ${script} =====`, error); }, + }, + 'COMMAND': { + 'Start': () => { logger.info('===== Deployment has started... ====='); }, + 'Error': (error) => { logger.error(`===== Unsuccessful deployment =====`, error); } + } } \ No newline at end of file diff --git a/cli-commands/commands/deploy/options/network-option.js b/cli-commands/commands/deploy/options/network-option.js index abaf971..4f9cc94 100644 --- a/cli-commands/commands/deploy/options/network-option.js +++ b/cli-commands/commands/deploy/options/network-option.js @@ -2,7 +2,7 @@ const Option = require('../../option'); const eoslime = require('../../../../index'); class NetworkOption extends Option { - constructor() { + constructor () { super( 'network', { diff --git a/cli-commands/commands/deploy/options/path-option.js b/cli-commands/commands/deploy/options/path-option.js index 710849d..c98f262 100644 --- a/cli-commands/commands/deploy/options/path-option.js +++ b/cli-commands/commands/deploy/options/path-option.js @@ -4,7 +4,7 @@ const Option = require('../../option'); const fileSystemUtil = require('../../../helpers/file-system-util'); class PathOption extends Option { - constructor() { + constructor () { super( 'path', { @@ -16,8 +16,8 @@ class PathOption extends Option { } async process (optionValue) { - let deploymentFilesFunctions = []; if (fileSystemUtil.isDir(optionValue)) { + const deploymentFilesFunctions = []; const dirFiles = await fileSystemUtil.recursivelyReadDir(optionValue); for (let i = 0; i < dirFiles.length; i++) { @@ -27,16 +27,16 @@ class PathOption extends Option { deploy: require(path.resolve('./', dirFile.fullPath)) }); } + + return deploymentFilesFunctions; } if (fileSystemUtil.isFile(optionValue)) { - deploymentFilesFunctions.push({ + return [{ fileName: optionValue, deploy: require(path.resolve('./', optionValue)) - }); + }]; } - - return deploymentFilesFunctions; } } diff --git a/cli-commands/commands/group-command.js b/cli-commands/commands/group-command.js index 639564d..ab6ffde 100644 --- a/cli-commands/commands/group-command.js +++ b/cli-commands/commands/group-command.js @@ -1,17 +1,17 @@ const Command = require('./command'); class GroupCommand extends Command { - constructor(commandDefinition) { + constructor (commandDefinition) { super(commandDefinition); } async execute (args) { if (optionsProvided(args, this.options)) { await super.processOptions(args); - return true; + this.hasBeenExecuted = true; + } else { + this.hasBeenExecuted = false; } - - return false; } } diff --git a/cli-commands/commands/init/index.js b/cli-commands/commands/init/index.js index 3385bbe..05d47e3 100644 --- a/cli-commands/commands/init/index.js +++ b/cli-commands/commands/init/index.js @@ -4,7 +4,7 @@ const Command = require('../command'); const initDirectories = require('./specific/directories.json'); const initCommandDefinition = require('./definition'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; const fileSystemUtil = require('../../helpers/file-system-util'); const defaultPackageJsonDestination = `${__dirname}/specific/default-package.json`; @@ -13,47 +13,45 @@ const defaultPackageJsonDestination = `${__dirname}/specific/default-package.jso class InitCommand extends Command { - constructor() { + constructor () { super(initCommandDefinition); } async execute (args) { + try { - commandMessages.Installation(); + MESSAGE_COMMAND.Start(); createContractsDir(); createDeploymentDir(); createTestsDir(); copyDefaultPackageJson(); - super.processOptions(args); - } catch (error) { - commandMessages.UnsuccessfulInstallation(error); - } - - const asyncSoftExec = new AsyncSoftExec('npm install eoslime'); - asyncSoftExec.onError((error) => { commandMessages.UnsuccessfulInstallation(error); }); - asyncSoftExec.onSuccess(() => { commandMessages.SuccessfulInstallation(); }); + await super.processOptions(args); - await asyncSoftExec.exec(); + const asyncSoftExec = new AsyncSoftExec('npm install eoslime'); + await asyncSoftExec.exec(); - return true; + MESSAGE_COMMAND.Success(); + } catch (error) { + MESSAGE_COMMAND.Error(error); + } } } -let createContractsDir = function () { +const createContractsDir = function () { fileSystemUtil.createDir(initDirectories.CONTRACTS); } -let createDeploymentDir = function () { +const createDeploymentDir = function () { fileSystemUtil.createDir(initDirectories.DEPLOYMENT); } -let createTestsDir = function () { +const createTestsDir = function () { fileSystemUtil.createDir(initDirectories.TESTS); } -let copyDefaultPackageJson = function () { +const copyDefaultPackageJson = function () { fileSystemUtil.copyFileFromTo(defaultPackageJsonDestination, initDirectories.PACKAGE_JSON); } diff --git a/cli-commands/commands/init/messages.js b/cli-commands/commands/init/messages.js index 362f1f1..7fbee95 100644 --- a/cli-commands/commands/init/messages.js +++ b/cli-commands/commands/init/messages.js @@ -1,8 +1,9 @@ -const chalk = require('chalk'); const logger = require('../../common/logger'); module.exports = { - 'Installation': () => { logger.info(chalk.magentaBright('===== Installing eoslime... =====')); }, - 'SuccessfulInstallation': () => { logger.info(chalk.greenBright('===== Successfully installed =====')); }, - 'UnsuccessfulInstallation': (error) => { logger.error(chalk.redBright('===== Unsuccessful installation ====='), error); } -} \ No newline at end of file + 'COMMAND': { + 'Start': () => { logger.info('===== Installing eoslime... ====='); }, + 'Success': () => { logger.success('===== Successfully installed ====='); }, + 'Error': (error) => { logger.error('===== Unsuccessful installation =====', error); } + } +} diff --git a/cli-commands/commands/init/options/with-example/with-example-option.js b/cli-commands/commands/init/options/with-example/with-example-option.js index ee57beb..e41a75c 100644 --- a/cli-commands/commands/init/options/with-example/with-example-option.js +++ b/cli-commands/commands/init/options/with-example/with-example-option.js @@ -5,7 +5,7 @@ const Option = require('../../../option'); class WithExampleOption extends Option { - constructor() { + constructor () { super( 'with-example', { diff --git a/cli-commands/commands/nodeos/specific/nodeos-data/data-manager.js b/cli-commands/commands/nodeos/specific/nodeos-data/data-manager.js index e259471..e641c34 100644 --- a/cli-commands/commands/nodeos/specific/nodeos-data/data-manager.js +++ b/cli-commands/commands/nodeos/specific/nodeos-data/data-manager.js @@ -14,8 +14,7 @@ const nodeosDataManager = { try { const pid = fileSystemUtil.readFile(path + '/eosd.pid').toString(); return process.kill(pid, 0); - } - catch (e) { + } catch (e) { return e.code === 'EPERM' } }, diff --git a/cli-commands/commands/nodeos/subcommands/accounts/index.js b/cli-commands/commands/nodeos/subcommands/accounts/index.js index 046b74f..04a301e 100644 --- a/cli-commands/commands/nodeos/subcommands/accounts/index.js +++ b/cli-commands/commands/nodeos/subcommands/accounts/index.js @@ -1,7 +1,7 @@ const AccountsTable = require('./specific/accounts-table'); const Command = require('../../../command'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; const accountsCommandDefinition = require('./definition'); const predefinedAccounts = require('../common/accounts'); @@ -9,13 +9,13 @@ const predefinedAccounts = require('../common/accounts'); // eoslime nodeos show accounts class AccountsCommand extends Command { - constructor() { + constructor () { super(accountsCommandDefinition); } async execute (args) { try { - commandMessages.PreloadedAccounts(); + MESSAGE_COMMAND.Start(); const accountsTable = new AccountsTable(); const accounts = predefinedAccounts.accounts(); @@ -26,10 +26,8 @@ class AccountsCommand extends Command { accountsTable.draw(); } catch (error) { - commandMessages.UnsuccessfulShowing(error); + MESSAGE_COMMAND.Error(error); } - - return true; } } diff --git a/cli-commands/commands/nodeos/subcommands/accounts/messages.js b/cli-commands/commands/nodeos/subcommands/accounts/messages.js index edb3d32..a80bd08 100644 --- a/cli-commands/commands/nodeos/subcommands/accounts/messages.js +++ b/cli-commands/commands/nodeos/subcommands/accounts/messages.js @@ -1,7 +1,8 @@ -const chalk = require('chalk'); const logger = require('../../../../common/logger'); module.exports = { - 'PreloadedAccounts': () => { logger.info(chalk.magentaBright('===== Preloaded accounts =====')); }, - 'UnsuccessfulShowing': (error) => { logger.error(chalk.redBright(`===== Accounts has not been shown =====`), error); } + 'COMMAND': { + 'Start': () => { logger.info('===== Preloaded accounts ====='); }, + 'Error': (error) => { logger.error(`===== Accounts has not been shown =====`, error); } + } } \ No newline at end of file diff --git a/cli-commands/commands/nodeos/subcommands/common/accounts.js b/cli-commands/commands/nodeos/subcommands/common/accounts.js index c778d90..2f9692c 100644 --- a/cli-commands/commands/nodeos/subcommands/common/accounts.js +++ b/cli-commands/commands/nodeos/subcommands/common/accounts.js @@ -8,13 +8,13 @@ const preDefinedAccounts = [ }, { "name": "eoslimekevin", - "publicKey": "EOS7UyV15G2t47MqRm4WpUP6KTfy9sNU3HHGu9aAgR2A3ktxoBTLv", - "privateKey": "5KS9t8LGsaQZxLP6Ln5WK6XwYU8M3AYHcfx1x6zoGmbs34vQsPT" + "publicKey": "EOS6Zz4iPbjm6FNys1zUMaRE4zPXrHcX3SRG65YWneVbdXQTSiqDp", + "privateKey": "5KieRy975NgHk5XQfn8r6o3pcqJDF2vpeV9bDiuB5uF4xKCTwRF" }, { "name": "eoslimemarty", - "publicKey": "EOS7UyV15G2t47MqRm4WpUP6KTfy9sNU3HHGu9aAgR2A3ktxoBTLv", - "privateKey": "5KS9t8LGsaQZxLP6Ln5WK6XwYU8M3AYHcfx1x6zoGmbs34vQsPT" + "publicKey": "EOS7FDeYdY3G8yMNxtrU8MSYnAJc3ZogYHgL7RG3rBf8ZDYA3xthi", + "privateKey": "5JtbCXgK5NERDdFdrmxb8rpYMkoxVfSyH1sR6TYxHBG5zNLHfj5" } ] diff --git a/cli-commands/commands/nodeos/subcommands/logs/index.js b/cli-commands/commands/nodeos/subcommands/logs/index.js index d1f1ef9..106f2f8 100644 --- a/cli-commands/commands/nodeos/subcommands/logs/index.js +++ b/cli-commands/commands/nodeos/subcommands/logs/index.js @@ -3,20 +3,21 @@ const nodoesDataManager = require('../../specific/nodeos-data/data-manager'); const Command = require('../../../command'); const AsyncSoftExec = require('../../../../helpers/async-soft-exec'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; +const MESSAGE_LOGS = require('./messages').LOGS; const logsCommandDefinition = require('./definition'); // eoslime nodeos show logs --lines class LogsCommand extends Command { - constructor() { + constructor () { super(logsCommandDefinition); } async execute (args) { if (!nodoesDataManager.nodeosIsRunning(nodoesDataManager.nodeosPath())) { - commandMessages.EmptyLogs(); - return true; + MESSAGE_LOGS.Empty(); + return void 0; } try { @@ -24,13 +25,12 @@ class LogsCommand extends Command { const nodeosLogFile = nodoesDataManager.nodeosPath() + '/nodeos.log'; const asyncSoftExec = new AsyncSoftExec(`tail -n ${optionsResults.lines} ${nodeosLogFile}`); - asyncSoftExec.onSuccess((logs) => { commandMessages.NodeosLogs(logs); }); - await asyncSoftExec.exec(); + const logs = await asyncSoftExec.exec(); + + MESSAGE_COMMAND.Success(logs); } catch (error) { - commandMessages.UnsuccessfulShowing(error); + MESSAGE_COMMAND.Error(error); } - - return true; } } diff --git a/cli-commands/commands/nodeos/subcommands/logs/messages.js b/cli-commands/commands/nodeos/subcommands/logs/messages.js index 52fb42f..adeaae1 100644 --- a/cli-commands/commands/nodeos/subcommands/logs/messages.js +++ b/cli-commands/commands/nodeos/subcommands/logs/messages.js @@ -1,8 +1,11 @@ -const chalk = require('chalk'); const logger = require('../../../../common/logger'); module.exports = { - 'NodeosLogs': (logs) => { logger.info(chalk.magentaBright(`===== Nodeos logs ===== \n ${logs}`)); }, - 'EmptyLogs': () => { logger.info(chalk.blueBright('===== Empty logs =====')); }, - 'UnsuccessfulShowing': (error) => { logger.error(chalk.redBright(`===== Logs has not been shown =====`), error); } -} \ No newline at end of file + 'COMMAND': { + 'Success': (logs) => { logger.success(`===== Nodeos logs ===== \n ${logs}`); }, + 'Error': (error) => { logger.error(`===== Logs has not been shown =====`, error); } + }, + 'LOGS': { + 'Empty': () => { logger.info('===== Empty logs ====='); }, + } +} diff --git a/cli-commands/commands/nodeos/subcommands/start/index.js b/cli-commands/commands/nodeos/subcommands/start/index.js index 242fcb1..6a6f7fc 100644 --- a/cli-commands/commands/nodeos/subcommands/start/index.js +++ b/cli-commands/commands/nodeos/subcommands/start/index.js @@ -1,7 +1,8 @@ const Command = require('../../../command'); const AsyncSoftExec = require('../../../../helpers/async-soft-exec'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; +const MESSAGE_NODEOS = require('./messages').NODEOS; const startCommandDefinition = require('./definition'); const template = require('./specific/template'); @@ -12,20 +13,20 @@ const nodeosDataManager = require('../../specific/nodeos-data/data-manager'); // eoslime nodeos start --path class StartCommand extends Command { - constructor() { + constructor () { super(startCommandDefinition); } async execute (args) { if (nodeosDataManager.nodeosIsRunning(nodeosDataManager.nodeosPath())) { - commandMessages.NodeosAlreadyRunning(); - return true; + MESSAGE_NODEOS.AlreadyRunning(); + return void 0; } try { - commandMessages.StartingNodeos(); - const optionsResults = await super.processOptions(args); + MESSAGE_COMMAND.Start(); + const optionsResults = await super.processOptions(args); nodeosDataManager.setNodeosPath(optionsResults.path); const asyncSoftExec = new AsyncSoftExec(template.build(optionsResults.path)); @@ -34,12 +35,10 @@ class StartCommand extends Command { nodeosDataManager.requireRunningNodeos(optionsResults.path); await predefinedAccounts.load(); - commandMessages.SuccessfullyStarted(); + MESSAGE_COMMAND.Success(); } catch (error) { - commandMessages.UnsuccessfulStarting(error); + MESSAGE_COMMAND.Error(error); } - - return true; } } diff --git a/cli-commands/commands/nodeos/subcommands/start/messages.js b/cli-commands/commands/nodeos/subcommands/start/messages.js index f2a1356..9009b50 100644 --- a/cli-commands/commands/nodeos/subcommands/start/messages.js +++ b/cli-commands/commands/nodeos/subcommands/start/messages.js @@ -1,9 +1,12 @@ -const chalk = require('chalk'); const logger = require('../../../../common/logger'); module.exports = { - 'StartingNodeos': () => { logger.info(chalk.magentaBright('===== Starting nodeos ... =====')); }, - 'NodeosAlreadyRunning': () => { logger.info(chalk.blueBright(`===== Nodeos is already running =====`)); }, - 'SuccessfullyStarted': () => { logger.info(chalk.greenBright(`===== Successfully started =====`)); }, - 'UnsuccessfulStarting': (error) => { logger.error(chalk.redBright(`===== Nodeos has not been started =====`), error); } + 'COMMAND': { + 'Start': () => { logger.info('===== Starting nodeos ... ====='); }, + 'Success': () => { logger.success(`===== Successfully started =====`); }, + 'Error': (error) => { logger.error(`===== Nodeos has not been started =====`, error); } + }, + 'NODEOS': { + 'AlreadyRunning': () => { logger.info(`===== Nodeos is already running =====`); }, + } } \ No newline at end of file diff --git a/cli-commands/commands/nodeos/subcommands/start/options/path-option.js b/cli-commands/commands/nodeos/subcommands/start/options/path-option.js index d4a7a43..617e842 100644 --- a/cli-commands/commands/nodeos/subcommands/start/options/path-option.js +++ b/cli-commands/commands/nodeos/subcommands/start/options/path-option.js @@ -1,9 +1,10 @@ const Option = require('../../../../option'); +const fileSystemUtils = require('../../../../../helpers/file-system-util'); const nodeosDataManager = require('../../../specific/nodeos-data/data-manager'); class PathOption extends Option { - constructor() { + constructor () { super( 'path', { @@ -15,7 +16,9 @@ class PathOption extends Option { } async process (optionValue) { - return optionValue; + if (fileSystemUtils.isDir(optionValue)) { + return optionValue; + } } } diff --git a/cli-commands/commands/nodeos/subcommands/stop/index.js b/cli-commands/commands/nodeos/subcommands/stop/index.js index 816ef51..9313fdb 100644 --- a/cli-commands/commands/nodeos/subcommands/stop/index.js +++ b/cli-commands/commands/nodeos/subcommands/stop/index.js @@ -4,19 +4,19 @@ const nodeosDataManager = require('../../specific/nodeos-data/data-manager'); const Command = require('../../../command'); const AsyncSoftExec = require('../../../../helpers/async-soft-exec'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; const stopCommandDefinition = require('./definition'); // eoslime nodeos stop class StopCommand extends Command { - constructor() { + constructor () { super(stopCommandDefinition); } async execute (args) { try { - commandMessages.StoppingNodeos(); + MESSAGE_COMMAND.Start(); if (nodeosDataManager.nodeosIsRunning(nodeosDataManager.nodeosPath())) { const nodeosPid = fileSystemUtil.readFile( @@ -27,12 +27,11 @@ class StopCommand extends Command { } await clearNodeosData(nodeosDataManager.nodeosPath()); - commandMessages.SuccessfullyStopped(); + + MESSAGE_COMMAND.Success(); } catch (error) { - commandMessages.UnsuccessfulStopping(error); + MESSAGE_COMMAND.Error(error); } - - return true; } } diff --git a/cli-commands/commands/nodeos/subcommands/stop/messages.js b/cli-commands/commands/nodeos/subcommands/stop/messages.js index 2c043f3..4bd98c8 100644 --- a/cli-commands/commands/nodeos/subcommands/stop/messages.js +++ b/cli-commands/commands/nodeos/subcommands/stop/messages.js @@ -1,8 +1,9 @@ -const chalk = require('chalk'); const logger = require('../../../../common/logger'); module.exports = { - 'StoppingNodeos': () => { logger.info(chalk.magentaBright('===== Stopping nodeos ... =====')); }, - 'SuccessfullyStopped': () => { logger.info(chalk.greenBright(`===== Successfully stopped =====`)); }, - 'UnsuccessfulStopping': (error) => { logger.error(chalk.redBright(`===== Nodeos has not been stopped =====`), error); } -} \ No newline at end of file + 'COMMAND': { + 'Start': () => { logger.info('===== Stopping nodeos ... ====='); }, + 'Success': () => { logger.success(`===== Successfully stopped =====`); }, + 'Error': (error) => { logger.error(`===== Nodeos has not been stopped =====`, error); } + } +} diff --git a/cli-commands/commands/shape/index.js b/cli-commands/commands/shape/index.js index 1026b44..83169d1 100644 --- a/cli-commands/commands/shape/index.js +++ b/cli-commands/commands/shape/index.js @@ -2,33 +2,28 @@ const git = require('simple-git/promise')() const Command = require('../command'); -const commandMessages = require('./messages'); +const MESSAGE_COMMAND = require('./messages').COMMAND; + const shapeCommandDefinition = require('./definition'); // eoslime shape --name class ShapeCommand extends Command { - constructor() { + constructor () { super(shapeCommandDefinition); } async execute (args) { try { - commandMessages.StartShaping(); - const optionsResults = await super.processOptions(args); - - if (!optionsResults.framework) { - commandMessages.InvalidShapeName(args.framework); - return true; - } + MESSAGE_COMMAND.Start(); + const optionsResults = await super.processOptions(args); await git.clone(optionsResults.framework); - commandMessages.SuccessfulShaping(); + + MESSAGE_COMMAND.Success(); } catch (error) { - commandMessages.UnsuccessfulShaping(error); + MESSAGE_COMMAND.Error(error); } - - return true; } } diff --git a/cli-commands/commands/shape/messages.js b/cli-commands/commands/shape/messages.js index 5254b51..8fa1288 100644 --- a/cli-commands/commands/shape/messages.js +++ b/cli-commands/commands/shape/messages.js @@ -1,9 +1,9 @@ -const chalk = require('chalk'); const logger = require('../../common/logger'); module.exports = { - 'StartShaping': () => { logger.info(chalk.magentaBright('===== Shaping of DApp has started... =====')); }, - 'SuccessfulShaping': () => { logger.info(chalk.greenBright(`===== Successful shaping =====`)); }, - 'UnsuccessfulShaping': (error) => { logger.error(chalk.redBright(`===== Unsuccessful shaping =====`), error); }, - 'InvalidShapeName': (name) => { logger.info(chalk.redBright(`===== Invalid shape name ${name} =====`)); }, -} \ No newline at end of file + 'COMMAND': { + 'Start': () => { logger.info('===== Shaping of DApp has started... ====='); }, + 'Success': () => { logger.success(`===== Successful shaping =====`); }, + 'Error': (error) => { logger.error(`===== Unsuccessful shaping =====`, error); }, + } +} diff --git a/cli-commands/commands/shape/options/framework-option.js b/cli-commands/commands/shape/options/framework-option.js index 763b762..3f19d2d 100644 --- a/cli-commands/commands/shape/options/framework-option.js +++ b/cli-commands/commands/shape/options/framework-option.js @@ -2,7 +2,7 @@ const Option = require('../../option'); const repositories = require('../specific/repositories.json'); class FrameworkOption extends Option { - constructor() { + constructor () { super( 'framework', { @@ -14,6 +14,10 @@ class FrameworkOption extends Option { } process (optionValue) { + if (!repositories[optionValue]) { + throw new Error('Invalid Shape framework'); + } + return repositories[optionValue]; } } diff --git a/cli-commands/commands/test/definition.js b/cli-commands/commands/test/definition.js index 165c588..d3d0ec1 100644 --- a/cli-commands/commands/test/definition.js +++ b/cli-commands/commands/test/definition.js @@ -1,6 +1,6 @@ const pathOption = require('./options/path-option'); const networkOption = require('./options/network-option'); -const resourceReportOption = require('./options/resource-usage-option/resource-usage-option'); +const resourceReportOption = require('./options/resource-usage-option'); module.exports = { "template": "test", diff --git a/cli-commands/commands/test/index.js b/cli-commands/commands/test/index.js index 025f963..f975306 100644 --- a/cli-commands/commands/test/index.js +++ b/cli-commands/commands/test/index.js @@ -1,23 +1,23 @@ const Command = require('../command'); -const commandMessages = require('./messages'); + +const MESSAGE_COMMAND = require('./messages').COMMAND; const testCommandDefinition = require('./definition'); const eoslime = require('../../../index'); - const testUtils = require('./specific/utils'); // eoslime test --path --network --resource-usage= class TestCommand extends Command { - constructor(TestFramework) { + constructor (TestFramework) { super(testCommandDefinition); this.TestFramework = TestFramework; } async execute (args) { try { - commandMessages.StartTesting(); + MESSAGE_COMMAND.Start(); args.eoslime = eoslime.init(); args.testFramework = new this.TestFramework(); @@ -27,14 +27,12 @@ class TestCommand extends Command { setTestsHelpers(args.eoslime); args.testFramework.setDescribeArgs(args.eoslime); - args.testFramework.runTests(); - - commandMessages.SuccessfulTesting(); + await args.testFramework.runTests(); + + MESSAGE_COMMAND.Success(); } catch (error) { - commandMessages.UnsuccessfulTesting(error); + MESSAGE_COMMAND.Error(error); } - - return true; } } diff --git a/cli-commands/commands/test/messages.js b/cli-commands/commands/test/messages.js index b04d5b3..5c0618a 100644 --- a/cli-commands/commands/test/messages.js +++ b/cli-commands/commands/test/messages.js @@ -1,8 +1,9 @@ -const chalk = require('chalk'); const logger = require('../../common/logger'); module.exports = { - 'StartTesting': () => { logger.info(chalk.magentaBright('===== Testing has started... =====')); }, - 'SuccessfulTesting': () => { logger.info(chalk.greenBright(`===== Testing completed successfully =====`)); }, - 'UnsuccessfulTesting': (error) => { logger.error(chalk.redBright(`===== Testing failed =====`), error); } -} \ No newline at end of file + 'COMMAND': { + 'Start': () => { logger.info('===== Testing has started... ====='); }, + 'Success': () => { logger.success(`===== Testing completed successfully =====`); }, + 'Error': (error) => { logger.error(`===== Testing failed =====`, error); } + } +} diff --git a/cli-commands/commands/test/options/network-option.js b/cli-commands/commands/test/options/network-option.js index a37eeb8..f375839 100644 --- a/cli-commands/commands/test/options/network-option.js +++ b/cli-commands/commands/test/options/network-option.js @@ -1,7 +1,7 @@ const Option = require('../../option'); class NetworkOption extends Option { - constructor() { + constructor () { super( 'network', { diff --git a/cli-commands/commands/test/options/path-option.js b/cli-commands/commands/test/options/path-option.js index d4465ee..d8fd653 100644 --- a/cli-commands/commands/test/options/path-option.js +++ b/cli-commands/commands/test/options/path-option.js @@ -4,7 +4,7 @@ const Option = require('../../option'); const fileSystemUtil = require('../../../helpers/file-system-util'); class PathOption extends Option { - constructor() { + constructor () { super( 'path', { diff --git a/cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js b/cli-commands/commands/test/options/resource-usage-option/index.js similarity index 99% rename from cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js rename to cli-commands/commands/test/options/resource-usage-option/index.js index 27a13b1..f5d3bf0 100644 --- a/cli-commands/commands/test/options/resource-usage-option/resource-usage-option.js +++ b/cli-commands/commands/test/options/resource-usage-option/index.js @@ -2,7 +2,7 @@ const Option = require('../../../option'); const ReportTable = require('./report-table'); class ResourceReportOption extends Option { - constructor() { + constructor () { super( 'resource-usage', { diff --git a/cli-commands/commands/test/specific/utils/index.js b/cli-commands/commands/test/specific/utils/index.js index 79c592d..94635c6 100644 --- a/cli-commands/commands/test/specific/utils/index.js +++ b/cli-commands/commands/test/specific/utils/index.js @@ -1,10 +1,10 @@ const assert = require('assert'); module.exports = { - expectAssert: function (promise) { + expectAssert: async function (promise) { return expectEOSError(promise, 'eosio_assert_message_exception', 'assert'); }, - expectMissingAuthority: function (promise) { + expectMissingAuthority: async function (promise) { return expectEOSError(promise, 'missing_auth_exception', 'missing authority'); }, // Todo: Uncomment it once test cli command is ready @@ -20,12 +20,13 @@ module.exports = { // } } -let expectEOSError = async function (promise, errorType, errorInfo) { +const expectEOSError = async function (promise, errorType, errorInfo) { try { await promise; } catch (error) { - const expectedError = error.search(errorType) >= 0; - assert(expectedError, `Expected ${errorInfo}, got '${error}' instead`); + const message = error.message || error; + const expectedError = message.search(errorType) >= 0; + assert(expectedError, `Expected ${errorInfo}, got '${message}' instead`); return; } assert.fail(`Expected ${errorInfo} not received`); diff --git a/cli-commands/common/logger.js b/cli-commands/common/logger.js index 3962835..001726d 100644 --- a/cli-commands/common/logger.js +++ b/cli-commands/common/logger.js @@ -1,11 +1,19 @@ +const chalk = require('chalk'); + const logger = { - info: (message) => { + log: (message) => { console.log(message); }, + success: (message) => { + console.log(chalk.greenBright(message)); + }, + info: (message) => { + console.log(chalk.blueBright(message)); + }, error: (message, error) => { - console.log(message); + console.log(chalk.redBright(message)); console.log(error); } } -module.exports = logger; \ No newline at end of file +module.exports = logger; diff --git a/cli-commands/common/table.js b/cli-commands/common/table.js index b232557..7fc3d7e 100644 --- a/cli-commands/common/table.js +++ b/cli-commands/common/table.js @@ -1,8 +1,10 @@ const chalk = require('chalk').default; const CLITable = require('cli-table'); +const logger = require('./logger'); + class Table { - constructor(tableHead) { + constructor (tableHead) { this.table = new CLITable(tableHead); } @@ -25,7 +27,7 @@ class Table { } draw () { - console.log(this.table.toString()); + logger.log(this.table.toString()); } } diff --git a/cli-commands/helpers/async-soft-exec.js b/cli-commands/helpers/async-soft-exec.js index f1f44de..6176d9f 100644 --- a/cli-commands/helpers/async-soft-exec.js +++ b/cli-commands/helpers/async-soft-exec.js @@ -1,39 +1,21 @@ const exec = require('child_process').exec; class AsyncSoftExec { - constructor(command) { + constructor (command) { this.command = command; - - this.successCallback = () => { }; - this.errorCallback = (error) => { throw new Error(error) }; - } - - onError (callback) { - this.errorCallback = callback; - } - - onSuccess (callback) { - this.successCallback = callback; } async exec () { return new Promise((resolve, reject) => { exec(this.command, async (error, stdout) => { - try { - if (error) { - await this.errorCallback(error); - return void resolve(true); - } - - await this.successCallback(stdout); - return void resolve(true); - } catch (error) { - reject(error); + if (error) { + return void reject(error); } + + return void resolve(stdout); }); }); } } - module.exports = AsyncSoftExec; diff --git a/nyc.config.js b/nyc.config.js new file mode 100644 index 0000000..98bbcd0 --- /dev/null +++ b/nyc.config.js @@ -0,0 +1,8 @@ +module.exports = { + "include": [ + "**/*.js" + ], + "exclude": [ + "tests/**/*.js" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 86dc5d2..6b1f55c 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "crypto-js": "3.1.9-1", "eosjs": "16.0.9", "is-invalid-path": "1.0.2", - "mocha": "5.2.0", "prompts": "2.2.1", "simple-git": "1.132.0", "yargs": "12.0.5" @@ -42,8 +41,9 @@ "eoslime": "./cli.js" }, "devDependencies": { - "fs-extra": "9.0.1", "nyc": "14.1.1", - "sinon": "9.0.2" + "mocha": "5.2.0", + "sinon": "^9.0.2", + "proxyquire": "^2.1.3" } } \ No newline at end of file diff --git a/src/contract/contract-initializator.js b/src/contract/contract-initializator.js index ab2eedb..3892e09 100644 --- a/src/contract/contract-initializator.js +++ b/src/contract/contract-initializator.js @@ -10,14 +10,12 @@ const EVENTS = { class ContractInitializator extends EventClass { - constructor(provider) { + constructor (provider) { super(EVENTS); this.provider = provider; } fromFile (abi, contractName, contractExecutorAccount = this.provider.defaultAccount) { - is(contractExecutorAccount).instanceOf('BaseAccount'); - let abiInterface = abi; if (contractFilesReader.doesAbiExists(abi)) { abiInterface = contractFilesReader.readABIFromFile(abi); @@ -30,8 +28,6 @@ class ContractInitializator extends EventClass { } async at (contractName, contractExecutorAccount = this.provider.defaultAccount) { - is(contractExecutorAccount).instanceOf('BaseAccount'); - const abiInterface = (await this.provider.eos.getAbi(contractName)).abi; const contract = new Contract(this.provider, abiInterface, contractName, contractExecutorAccount); diff --git a/tests/cli-commands/command-tests.js b/tests/cli-commands/command-tests.js deleted file mode 100644 index 60fb798..0000000 --- a/tests/cli-commands/command-tests.js +++ /dev/null @@ -1,20 +0,0 @@ -const assert = require('assert'); - -const Command = require('../../cli-commands/commands/command'); - -describe('Command', function () { - let command; - - beforeEach(async () => { - command = new Command({}); - }); - - it('Should initialize command properly', async () => { - assert(command instanceof Command); - assert(command.template == ''); - assert(command.description == ''); - assert(command.options.length == 0); - assert(command.subcommands.length == 0); - }); - -}); \ No newline at end of file diff --git a/tests/cli-commands/common/logger-tests.js b/tests/cli-commands/common/logger-tests.js deleted file mode 100644 index 5872602..0000000 --- a/tests/cli-commands/common/logger-tests.js +++ /dev/null @@ -1,30 +0,0 @@ -const sinon = require('sinon'); -const logger = require('../../../cli-commands/common/logger'); - -describe('Logger', function () { - - let consoleLogSpy; - - beforeEach(async () => { - consoleLogSpy = sinon.spy(console, "log"); - }); - - afterEach(async () => { - sinon.restore(); - }); - - it('Should log info message', async () => { - logger.info('Test info message'); - - sinon.assert.calledWith(consoleLogSpy, 'Test info message'); - }); - - it('Should log error message', async () => { - logger.error('Test error message', 'Error'); - - sinon.assert.calledTwice(consoleLogSpy); - consoleLogSpy.firstCall.calledWith('Test error message'); - consoleLogSpy.secondCall.calledWith('Error'); - }); - -}); \ No newline at end of file diff --git a/tests/cli-commands/compile-tests.js b/tests/cli-commands/compile-tests.js index f38b946..6189abf 100644 --- a/tests/cli-commands/compile-tests.js +++ b/tests/cli-commands/compile-tests.js @@ -1,140 +1,203 @@ -const fs = require('fs-extra'); const sinon = require('sinon'); const assert = require('assert'); +const proxyquire = require('proxyquire').noCallThru(); + +const definition = require('../../cli-commands/commands/compile/definition'); const Command = require('../../cli-commands/commands/command'); const CompileCommand = require('../../cli-commands/commands/compile/index'); -const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); -const fileSysUtils = require('../../cli-commands/helpers/file-system-util'); -const definition = require('../../cli-commands/commands/compile/definition'); -const commandMessages = require('../../cli-commands/commands/compile/messages'); -const PathOption = require('../../cli-commands/commands/compile/options/path-option'); -const directories = require('../../cli-commands/commands/compile/specific/directories.json'); -const logger = require('../../cli-commands/common/logger'); - -describe('CompileCommand', function () { - const TEST_DIR = './cli-commands-test'; - const DEFAULT_PATH = './contracts'; - const INVALID_PATH = './unknown_folder'; - const INVALID_FILE = 'file.txt'; - - let initialDir; - let compileCommand; - let pathOptionSpy; - let fsCreateDirSpy; - - before(async () => { - initialDir = process.cwd(); - fs.mkdirSync(TEST_DIR); - process.chdir(TEST_DIR); - }); + +const Logger = require('./utils/logger'); +const logger = new Logger(); + +describe('Compile Command', function () { beforeEach(async () => { - compileCommand = new CompileCommand(); - sinon.stub(logger, "info"); - sinon.stub(logger, "error"); - pathOptionSpy = sinon.spy(PathOption, "process"); - fsCreateDirSpy = sinon.spy(fileSysUtils, "createDir"); + logger.hide(sinon); }); afterEach(async () => { sinon.restore(); - cleanDirectories(); }); - after(async () => { - process.chdir(initialDir); - fs.removeSync(TEST_DIR); - }); - - function cleanDirectories () { - fs.removeSync('./contracts'); - fs.removeSync('./compiled'); - } - - function createContractsFolder () { - fs.mkdirSync('./contracts'); - } - - function preloadContracts() { - fs.copyFileSync('../tests/testing-contracts/eosio.token.cpp', './contracts/eosio.token.cpp'); - fs.copyFileSync('../tests/testing-contracts/eosio.token.hpp', './contracts/eosio.token.hpp'); - } - - it('Should initialize command properly', async () => { - assert(compileCommand instanceof Command); - assert(compileCommand.template == definition.template); - assert(compileCommand.description = definition.description); - assert(compileCommand.options == definition.options); - assert(compileCommand.subcommands.length == 0); - }); + describe('Command', function () { - it('Should compile when valid contracts folder is specified', async () => { - sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { - commandMessages.SuccessfulCompilationOfContract(); + it('Should initialize command properly', async () => { + const compileCommand = new CompileCommand(); + assert(compileCommand instanceof Command); + assert(compileCommand.template == definition.template); + assert(compileCommand.description = definition.description); + assert(compileCommand.options == definition.options); + assert(compileCommand.subcommands.length == 0); }); - createContractsFolder(); - preloadContracts(); - - assert(await compileCommand.execute({ path: DEFAULT_PATH })); - - sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); - sinon.assert.calledOnceWithExactly(fsCreateDirSpy, directories.COMPILED); - - let result = await pathOptionSpy.returnValues[0]; - assert(result[0].fullPath == `${DEFAULT_PATH}/eosio.token.cpp`); - assert(result[0].fileName == 'eosio.token'); - }); - - it('Should throw when invalid contracts folder is specified', async () => { - assert(await compileCommand.execute({ path: INVALID_PATH })); - - sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); - sinon.assert.notCalled(fsCreateDirSpy); - }); + function stubBaseCommand (cb) { + return { + '../command': class FakeBaseCommand { + processOptions () { + return cb(); + } + } + } + } + + function stubAsyncSoftExec (checkPoints) { + return { + '../../helpers/async-soft-exec': class FakeAsyncSoftExec { + exec () { checkPoints.exec--; } + } + } + } + + it('Should compile ', async () => { + const checkPoints = { + exec: 1, + createDir: 1 + } + + const compileCommand = new (proxyquire( + '../../cli-commands/commands/compile/index', + { + ...stubBaseCommand(() => { + return { path: [{ fileName: 'test', fullPath: './test.cpp' }] } + }), + ...stubAsyncSoftExec(checkPoints), + '../../helpers/file-system-util': { + createDir: () => { + checkPoints.createDir--; + } + } + } + )); + + await compileCommand.execute(); + + assert(checkPoints.exec == 0); + assert(checkPoints.createDir == 0); + }); - it('Should throw when specified contracts folder is empty', async () => { - createContractsFolder(); + it('Should log in case any contracts was found', async () => { + const compileCommand = new (proxyquire('../../cli-commands/commands/compile/index', + { ...stubBaseCommand(() => { return { path: [] } }) } + )); - assert(await compileCommand.execute({ path: DEFAULT_PATH })); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('info', (message) => { + if (message.includes('There is not a contract to compile')) { + return resolve(true); + } + }); - sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); - sinon.assert.notCalled(fsCreateDirSpy); - }); + await compileCommand.execute({ 'path': './' }); + }); - it('Should compile when valid contract path is specified', async () => { - sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { - commandMessages.UnsuccessfulCompilationOfContract(); + await waitToPass; }); - createContractsFolder(); - preloadContracts(); - - assert(await compileCommand.execute({ path: `${DEFAULT_PATH}/eosio.token.cpp` })); + it('Should log in case a contract could not be compiled', async () => { + const compileCommand = new (proxyquire( + '../../cli-commands/commands/compile/index', + { + ...stubBaseCommand(() => { + return { path: [{ fileName: 'test', fullPath: './test.cpp' }] } + }), + '../../helpers/async-soft-exec': class FakeAsyncSoftExec { + exec () { throw new Error('Fake error'); } + }, + '../../helpers/file-system-util': { + createDir: () => { } + } + } + )); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Unsuccessful compilation of')) { + return resolve(true); + } + }); + + await compileCommand.execute({ 'path': './' }); + }); + + await waitToPass; + }); - sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/eosio.token.cpp`); - sinon.assert.calledOnceWithExactly(fsCreateDirSpy, directories.COMPILED); + it('Should log in case of an error', async () => { + const compileCommand = new (proxyquire('../../cli-commands/commands/compile/index', + { ...stubBaseCommand(() => { throw new Error('Fake Error') }) } + )); - let result = await pathOptionSpy.returnValues[0]; - assert(result[0].fullPath == `${DEFAULT_PATH}/eosio.token.cpp`); - assert(result[0].fileName == 'eosio.token'); - }); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Unsuccessful compilation')) { + return resolve(true); + } + }); - it('Should throw when invalid contract path is specified', async () => { - assert(await compileCommand.execute({ path: `${INVALID_PATH}/eosio.token.cpp` })); + await compileCommand.execute({ 'path': './' }); + }); - sinon.assert.calledWith(pathOptionSpy, `${INVALID_PATH}/eosio.token.cpp`); - sinon.assert.notCalled(fsCreateDirSpy); + await waitToPass; + }); }); - it('Should throw when invalid contract name is specified', async () => { - fs.createFileSync(INVALID_FILE); - assert(await compileCommand.execute({ path: `./${INVALID_FILE}` })); - - sinon.assert.calledWith(pathOptionSpy, `./${INVALID_FILE}`); - sinon.assert.notCalled(fsCreateDirSpy); + describe('Options', function () { + describe('Path', function () { + + function stubFileSystemUtils (isFolder) { + const Option = proxyquire( + '../../cli-commands/commands/compile/options/path-option', + { + '../../../helpers/file-system-util': { + isDir: () => { + return isFolder; + }, + recursivelyReadDir: () => { + return [ + { + fileName: 'test1.cpp', + fullPath: `./custom/test1.cpp` + }, { + fileName: 'test2.cpp', + fullPath: `./custom/test2.cpp` + } + ] + } + } + } + ); + + return Option; + } + + it('Should return contract path', async () => { + const pathOption = stubFileSystemUtils(false); + const result = await pathOption.process('./test.cpp'); + + assert(result.length == 1); + assert(result[0].fileName == 'test'); + assert(result[0].fullPath == './test.cpp'); + }); + + it('Should return contracts paths from a folder', async () => { + const pathOption = stubFileSystemUtils(true); + const result = await pathOption.process('./test.cpp'); + + assert(result.length == 2); + assert(result[0].fileName == 'test1'); + assert(result[1].fileName == 'test2'); + assert(result[0].fullPath == './custom/test1.cpp'); + assert(result[1].fullPath == './custom/test2.cpp'); + }); + + it('Should return empty array if any contracts was found', async () => { + const pathOption = stubFileSystemUtils(false); + const result = await pathOption.process('./test.fake'); + + assert(result.length == 0); + }); + }); }); - -}); \ No newline at end of file +}); diff --git a/tests/cli-commands/deploy-tests.js b/tests/cli-commands/deploy-tests.js index 07996ed..e87b2db 100644 --- a/tests/cli-commands/deploy-tests.js +++ b/tests/cli-commands/deploy-tests.js @@ -1,187 +1,199 @@ -const fs = require('fs-extra'); +const path = require('path') const sinon = require('sinon'); const assert = require('assert'); -const prompts = require('prompts'); -const eoslime = require('../../index'); +const proxyquire = require('proxyquire').noCallThru(); + +const definition = require('../../cli-commands/commands/deploy/definition'); -const logger = require('../../cli-commands/common/logger'); const Command = require('../../cli-commands/commands/command'); -const Account = require('../../src/account/normal-account/account'); const DeployCommand = require('../../cli-commands/commands/deploy/index'); -const definition = require('../../cli-commands/commands/deploy/definition'); -const PathOption = require('../../cli-commands/commands/deploy/options/path-option'); + const NetworkOption = require('../../cli-commands/commands/deploy/options/network-option'); -const DeployerOption = require('../../cli-commands/commands/deploy/options/deployer-option'); - -describe('DeployCommand', function () { - const TEST_DIR = './cli-commands-test'; - const DEFAULT_PATH = './deployment'; - const INVALID_PATH = './unknown_folder'; - const DEFAULT_NETWORK = 'local'; - const INVALID_NETWORK = 'invalid_network'; - const CUSTOM_NETWORK = { url: "https://test.custom.net", chainId: "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f" }; - const DEPLOYER_NAME = 'alice'; - const DEPLOYER_PERMISSION = 'active'; - const DEPLOYER_PRIVATE_KEY = '5JymWHYohPMXTckmmFkmiRZZDQTWN91eDFSnEvjYAbXTd6oFtie'; - const INVALID_PRIVATE_KEY = 'invalid_private_key'; - - let initialDir; + +const Logger = require('./utils/logger'); +const logger = new Logger(); + +describe('Deploy Command', function () { + let deployCommand; - let eoslimeSpy; - let pathOptionSpy; - let networkOptionSpy; - let deployerOptionSpy; - - before(async () => { - initialDir = process.cwd(); - fs.mkdirSync(TEST_DIR); - process.chdir(TEST_DIR); - }); beforeEach(async () => { + logger.hide(sinon); deployCommand = new DeployCommand(); - eoslimeSpy = sinon.spy(eoslime, "init"); - pathOptionSpy = sinon.spy(PathOption, "process"); - networkOptionSpy = sinon.spy(NetworkOption, "process"); - deployerOptionSpy = sinon.spy(DeployerOption, "process"); - sinon.stub(logger, "info"); - sinon.stub(logger, "error"); - - preloadDeploymentScript(); }); afterEach(async () => { sinon.restore(); - fs.removeSync('./contracts'); - fs.removeSync('./deployment'); - }); - - after(async () => { - process.chdir(initialDir); - fs.removeSync(TEST_DIR); - }); - - function preloadDeploymentScript () { - fs.mkdirSync('./deployment'); - fs.copyFileSync('../tests/cli-commands/mocks/deployment.js', './deployment/deployment.js'); - } - - it('Should initialize command properly', async () => { - assert(deployCommand instanceof Command); - assert(deployCommand.template == definition.template); - assert(deployCommand.description = definition.description); - assert(deployCommand.options == definition.options); - assert(deployCommand.subcommands.length == 0); - }); - - it('Should deploy when valid deployment folder is specified', async () => { - assert(await deployCommand.execute({ path: DEFAULT_PATH })); - - sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); - - let result = await pathOptionSpy.returnValues[0]; - assert(result[0].fileName, 'deployment.js'); - assert(typeof(result[0].deploy) == 'function'); - assert(result[0].deploy.name, 'deploy'); - }); - - it('Should throw when invalid deployment folder is specified', async () => { - assert(await deployCommand.execute({ path: INVALID_PATH })); - - sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); - }); - - it('Should not throw when deployment folder is empty', async () => { - assert(await deployCommand.execute({ path: DEFAULT_PATH })); - - sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); - }); - - it('Should deploy when valid deployment script is specified', async () => { - assert(await deployCommand.execute({ path: `${DEFAULT_PATH}/deployment.js` })); - - sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/deployment.js`); - - let result = await pathOptionSpy.returnValues[0]; - assert(result[0].fileName, 'deployment.js'); - assert(typeof(result[0].deploy) == 'function'); - assert(result[0].deploy.name, 'deploy'); - }); - - it('Should throw when invalid deployment script is specified', async () => { - fs.createFileSync('./test.txt'); - - assert(await deployCommand.execute({ path: './test.txt', network: DEFAULT_NETWORK })); - - sinon.assert.calledWith(pathOptionSpy, './test.txt'); - }); - - it('Should deploy when valid deployment network is specified', async () => { - assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK })); - - sinon.assert.calledWith(networkOptionSpy, DEFAULT_NETWORK); - sinon.assert.calledWith(eoslimeSpy, DEFAULT_NETWORK); - - let result = await networkOptionSpy.returnValues[0]; - assert(result.hasOwnProperty('Account')); - assert(result.hasOwnProperty('Contract')); - assert(result.hasOwnProperty('Provider')); - assert(result.hasOwnProperty('MultiSigAccount')); - }); - - it('Should deploy when custom network url and chainId are provided', async () => { - assert(await deployCommand.execute({ path: DEFAULT_PATH, network: CUSTOM_NETWORK })); - - sinon.assert.calledWith(networkOptionSpy, CUSTOM_NETWORK); - sinon.assert.calledWith(eoslimeSpy, CUSTOM_NETWORK); - - let result = await networkOptionSpy.returnValues[0]; - assert(result.hasOwnProperty('Account')); - assert(result.hasOwnProperty('Contract')); - assert(result.hasOwnProperty('Provider')); - assert(result.hasOwnProperty('MultiSigAccount')); - }); - - it('Should throw when invalid network is specified', async () => { - assert(await deployCommand.execute({ path: DEFAULT_PATH, network: INVALID_NETWORK })); - - sinon.assert.calledWith(networkOptionSpy, INVALID_NETWORK); - }); - - it('Should deploy when deployer is provided', async () => { - prompts.inject(`${DEPLOYER_PRIVATE_KEY}@${DEPLOYER_PERMISSION}`); - - assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, deployer: DEPLOYER_NAME })); - - sinon.assert.calledWith(deployerOptionSpy, DEPLOYER_NAME); - - let result = await deployerOptionSpy.returnValues[0]; - assert(result instanceof Account); - assert(result.name == DEPLOYER_NAME); - assert(result.privateKey == DEPLOYER_PRIVATE_KEY); - assert(result.executiveAuthority.permission == DEPLOYER_PERMISSION); - }); - - it('Should deploy when deployer is provided [permission missing]', async () => { - prompts.inject(`${DEPLOYER_PRIVATE_KEY}`); - - assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, deployer: DEPLOYER_NAME })); - - sinon.assert.calledWith(deployerOptionSpy, DEPLOYER_NAME); - - let result = await deployerOptionSpy.returnValues[0]; - assert(result instanceof Account); - assert(result.name == DEPLOYER_NAME); - assert(result.privateKey == DEPLOYER_PRIVATE_KEY); - assert(result.executiveAuthority.permission == 'active'); - }); - - it('Should throw when invalid deployer private key is provided', async () => { - prompts.inject(`${INVALID_PRIVATE_KEY}@${DEPLOYER_PERMISSION}`); - - assert(await deployCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, deployer: DEPLOYER_NAME })); - - sinon.assert.calledWith(deployerOptionSpy, DEPLOYER_NAME); }); -}); \ No newline at end of file + describe('Command', function () { + + it('Should initialize command properly', async () => { + assert(deployCommand instanceof Command); + assert(deployCommand.template == definition.template); + assert(deployCommand.description = definition.description); + assert(deployCommand.options == definition.options); + assert(deployCommand.subcommands.length == 0); + }); + + function stubBaseCommand (cb) { + return { + '../command': class FakeBaseCommand { + processOptions (args) { + return cb(args); + } + } + } + } + + it('Should deploy contracts', async () => { + const deployCommand = new (proxyquire('../../cli-commands/commands/deploy/index', { + ...stubBaseCommand(() => { + return { + path: [{ + deploy: (network, deployer) => { + assert(network == 'custom'); + assert(deployer == 'deployer'); + } + }], + network: 'custom', + deployer: 'deployer' + } + }) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('success', (message) => { + if (message.includes('Successful deployment of')) { + return resolve(true); + } + }); + + await deployCommand.execute(); + }); + + await waitToPass; + }); + + it('Should log error message in case a deployment script throws', async () => { + const deployCommand = new (proxyquire('../../cli-commands/commands/deploy/index', { + ...stubBaseCommand(() => { + return { + path: [{ + deploy: () => { + throw new Error('Fake Error'); + } + }] + } + }) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Unsuccessful deployment of')) { + return resolve(true); + } + }); + + await deployCommand.execute(); + }); + + await waitToPass; + }); + + it('Should log error message in case of error', async () => { + const deployCommand = new (proxyquire('../../cli-commands/commands/deploy/index', { + ...stubBaseCommand(() => { throw new Error('Fake error'); }) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes(' Unsuccessful deployment')) { + return resolve(true); + } + }); + + await deployCommand.execute(); + }); + + await waitToPass; + }); + }); + + + describe('Options', function () { + + describe('Path', function () { + + function stubFileSystemUtils (isFolder) { + return { + '../../../helpers/file-system-util': { + isDir: () => { return isFolder; }, + isFile: () => { return !isFolder; }, + recursivelyReadDir: (dir) => { + return [{ fileName: 'fileName', fullPath: `${dir}/fullPath` }]; + } + } + } + } + + function stubDeploymentPath (deploymentPath) { + return { + [deploymentPath]: 'deploymentPath' + } + } + + it('Should return a deployment script', async () => { + const pathOption = proxyquire('../../cli-commands/commands/deploy/options/path-option', { + ...stubFileSystemUtils(false), + ...stubDeploymentPath(path.resolve('./', './custom.js')) + }); + + const deploymentScript = (await pathOption.process('./custom.js'))[0]; + assert(deploymentScript.fileName == './custom.js'); + assert(deploymentScript.deploy == 'deploymentPath'); + }); + + it('Should return all deployment scripts from folder', async () => { + const pathOption = proxyquire('../../cli-commands/commands/deploy/options/path-option', { + ...stubFileSystemUtils(true), + ...stubDeploymentPath(path.resolve('./', './custom/fullPath')) + }); + + const deploymentScripts = await pathOption.process('./custom'); + assert(deploymentScripts[0].fileName == 'fileName'); + assert(deploymentScripts[0].deploy == 'deploymentPath'); + }); + }); + + describe('Network', function () { + + it('Should return an instance of eoslime', async () => { + const eoslimeInstance = NetworkOption.process('local'); + assert(eoslimeInstance.Provider.network.name == 'local'); + }); + }); + + describe('Deployer', function () { + + function stubPrompts (output) { + return { + 'prompts': () => { return { value: output } } + } + } + + it('Should return an instance of eoslime.Account', async () => { + const deployerOption = proxyquire('../../cli-commands/commands/deploy/options/deployer-option', { + ...stubPrompts('5KieRy975NgHk5XQfn8r6o3pcqJDF2vpeV9bDiuB5uF4xKCTwRF@active') + }); + + const deployer = await deployerOption.process('name', { network: 'local' }); + + assert(deployer.constructor.name == 'Account'); + assert(deployer.name == 'name'); + assert(deployer.privateKey == '5KieRy975NgHk5XQfn8r6o3pcqJDF2vpeV9bDiuB5uF4xKCTwRF'); + assert(deployer.executiveAuthority.permission == 'active'); + }); + }); + }); +}); diff --git a/tests/cli-commands/group-command-tests.js b/tests/cli-commands/group-command-tests.js deleted file mode 100644 index f4d0331..0000000 --- a/tests/cli-commands/group-command-tests.js +++ /dev/null @@ -1,26 +0,0 @@ -const assert = require('assert'); - -const TestOption = require('../cli-commands/mocks/test-option'); -const GroupCommand = require('../../cli-commands/commands/group-command'); - -describe('GroupCommand', function () { - let groupCommand; - - beforeEach(async () => { - groupCommand = new GroupCommand({ options: [ TestOption ] }); - }); - - it('Should initialize group command properly', async () => { - assert(groupCommand instanceof GroupCommand); - assert(groupCommand.options.length > 0); - }); - - it('Should process valid option', async () => { - assert(await groupCommand.execute({ test: 'true' })); - }); - - it('Should return if not found valid option', async () => { - assert(!(await groupCommand.execute({ sample: 'abc' }))); - }); - -}); \ No newline at end of file diff --git a/tests/cli-commands/helpers/async-soft-exec-tests.js b/tests/cli-commands/helpers/async-soft-exec-tests.js deleted file mode 100644 index 469268b..0000000 --- a/tests/cli-commands/helpers/async-soft-exec-tests.js +++ /dev/null @@ -1,42 +0,0 @@ -const assert = require('assert'); - -const AsyncSoftExec = require('../../../cli-commands/helpers/async-soft-exec'); - -const COMMAND_ECHO = 'echo' -const INVALID_COMMAND = 'ech'; - -describe('AsyncSoftExec', function () { - let asyncSoftExec; - - it('Should execute sample command [default success callback]', async () => { - asyncSoftExec = new AsyncSoftExec(COMMAND_ECHO); - - assert(await asyncSoftExec.exec()); - }); - - it('Should execute sample command', async () => { - asyncSoftExec = new AsyncSoftExec(COMMAND_ECHO); - asyncSoftExec.onSuccess(() => {}); - - assert(await asyncSoftExec.exec()); - }); - - it('Should throw when command is unknown [default error callback]', async () => { - asyncSoftExec = new AsyncSoftExec(INVALID_COMMAND); - - try { - await asyncSoftExec.exec(); - } catch (error) { - assert(true); - } - }); - - it('Should throw when command is unknown', async () => { - asyncSoftExec = new AsyncSoftExec(INVALID_COMMAND); - asyncSoftExec.onError(() => {}); - - await asyncSoftExec.exec(); - assert(true); - }); - -}); \ No newline at end of file diff --git a/tests/cli-commands/helpers/file-system-util-tests.js b/tests/cli-commands/helpers/file-system-util-tests.js deleted file mode 100644 index ae83fe8..0000000 --- a/tests/cli-commands/helpers/file-system-util-tests.js +++ /dev/null @@ -1,62 +0,0 @@ -const fs = require('fs-extra'); -const sinon = require('sinon'); -const assert = require('assert'); - -const fileSystemUtil = require('../../../cli-commands/helpers/file-system-util'); - -describe('FileSystemUtil', function () { - const TEST_DIR = './cli-commands-test'; - - before(async () => { - initialDir = process.cwd(); - fs.mkdirSync(TEST_DIR); - process.chdir(TEST_DIR); - }); - - beforeEach(async () => { - fs.mkdirSync('./root'); - fs.mkdirSync('./root/child'); - fs.createFileSync('./root/sample.txt'); - }); - - afterEach(async () => { - sinon.restore(); - fs.removeSync('./root'); - }); - - after(async () => { - process.chdir(initialDir); - fs.removeSync(TEST_DIR); - }); - - it('Should recursively read dir content', async () => { - let files = await fileSystemUtil.recursivelyReadDir('root'); - - assert(files.length == 1); - assert(files[0].fileName == 'sample.txt'); - assert(files[0].fullPath == 'root/sample.txt');fs.removeSync('./root'); - }); - - it('Should recursively delete dir content', async () => { - await fileSystemUtil.recursivelyDeleteDir('root'); - - assert(!fs.existsSync('./root')); - }); - - it('Should create new file', async () => { - fileSystemUtil.writeFile('sample.txt', 'text'); - - assert(fs.existsSync('sample.txt')); - - fs.removeSync('sample.txt'); - }); - - it('Should throw when trying to create undefined file', async () => { - try { - fileSystemUtil.writeFile(undefined, 'text'); - } catch (error) { - assert(error.message.includes('Storing content failed')); - } - }); - -}); \ No newline at end of file diff --git a/tests/cli-commands/init-tests.js b/tests/cli-commands/init-tests.js index 3f19fae..2e88182 100644 --- a/tests/cli-commands/init-tests.js +++ b/tests/cli-commands/init-tests.js @@ -1,107 +1,160 @@ -const fs = require('fs-extra'); const sinon = require('sinon'); const assert = require('assert'); +const proxyquire = require('proxyquire').noCallThru(); + +const definition = require('../../cli-commands/commands/init/definition'); -const logger = require('../../cli-commands/common/logger'); const Command = require('../../cli-commands/commands/command'); const InitCommand = require('../../cli-commands/commands/init/index'); -const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); -const definition = require('../../cli-commands/commands/init/definition'); -const commandMessages = require('../../cli-commands/commands/init/messages'); -const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); -const directories = require('../../cli-commands/commands/init/specific/directories.json'); -const WithExampleOption = require('../../cli-commands/commands/init/options/with-example/with-example-option'); - -describe('InitCommand', function () { - const TEST_DIR = "./cli-commands-test"; - const WITH_EXAMPLE = { 'with-example': true }; - const WITHOUT_EXAMPLE = { 'with-example': false }; - - let initialDir; - let initCommand; - let exampleOptionSpy; - let fileSystemUtilSpy; - - before(async () => { - initialDir = process.cwd(); - fs.mkdirSync(TEST_DIR); - process.chdir(TEST_DIR); - }); + +const Logger = require('./utils/logger'); +const logger = new Logger(); + +describe('Init Command', function () { beforeEach(async () => { - initCommand = new InitCommand(); - sinon.stub(logger, "info"); - sinon.stub(logger, "error"); - sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { commandMessages.SuccessfulInstallation(); }); - exampleOptionSpy = sinon.spy(WithExampleOption, "process"); - fileSystemUtilSpy = sinon.spy(fileSystemUtil, "copyAllFilesFromDirTo"); + logger.hide(sinon); }); afterEach(async () => { sinon.restore(); - cleanDirectories(); }); - after(async () => { - process.chdir(initialDir); - fs.removeSync(TEST_DIR); + describe('Command', function () { + + it('Should initialize command properly', async () => { + const initCommand = new InitCommand(); + assert(initCommand instanceof Command); + assert(initCommand.template == definition.template); + assert(initCommand.description = definition.description); + assert(initCommand.options == definition.options); + assert(initCommand.subcommands.length == 0); + }); + + function stubBaseCommand (cb) { + return { + '../command': class FakeBaseCommand { + processOptions (args) { + return cb(args); + } + } + } + } + + function stubFileSystemUtils (checkPoints) { + return { + '../../helpers/file-system-util': { + createDir: () => { + checkPoints.createDir--; + }, + copyFileFromTo: () => { + checkPoints.copyFileFromTo--; + } + } + } + } + + function stubAsyncSoftExec (checkPoints) { + return { + '../../helpers/async-soft-exec': class FakeAsyncSoftExec { + exec () { checkPoints.exec--; } + } + } + } + + it('Should init project structure', async () => { + const checkPoints = { + exec: 1, + createDir: 3, + copyFileFromTo: 1, + } + + const initCommand = new (proxyquire('../../cli-commands/commands/init/index', { + ...stubAsyncSoftExec(checkPoints), + ...stubFileSystemUtils(checkPoints), + ...stubBaseCommand((args) => { + return new Promise((resolve) => { + assert(args['with-example'] == false); + resolve(true); + }); + }) + })); + + await initCommand.execute({ 'with-example': false }); + + assert(checkPoints.exec == 0); + assert(checkPoints.createDir == 0); + assert(checkPoints.copyFileFromTo == 0); + }); + + it('Should log error message in case an option throws', async () => { + const initCommand = new (proxyquire('../../cli-commands/commands/init/index', { + ...stubAsyncSoftExec(), + ...stubFileSystemUtils(), + ...stubBaseCommand(() => { throw new Error('Fake error'); }) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Unsuccessful installation')) { + return resolve(true); + } + }); + + await initCommand.execute({ 'with-example': true }); + }); + + await waitToPass; + }); }); - function checkDirectories() { - assert(fs.existsSync(directories.DEPLOYMENT)); - assert(fs.existsSync(directories.CONTRACTS)); - assert(fs.existsSync(directories.TESTS)); - assert(fs.existsSync(directories.PACKAGE_JSON)); - } - - function checkExampleFiles () { - assert(fs.existsSync(`${directories.DEPLOYMENT}/example-deploy.js`)); - assert(fs.existsSync(`${directories.CONTRACTS}/example`)); - assert(fs.existsSync(`${directories.TESTS}/example-tests.js`)); - } - - function cleanDirectories () { - fs.removeSync(directories.DEPLOYMENT); - fs.removeSync(directories.CONTRACTS); - fs.removeSync(directories.TESTS); - fs.removeSync(directories.PACKAGE_JSON); - } - - it('Should initialize command properly', async () => { - assert(initCommand instanceof Command); - assert(initCommand.template == definition.template); - assert(initCommand.description = definition.description); - assert(initCommand.options == definition.options); - assert(initCommand.subcommands.length == 0); + describe('Options', function () { + describe('With-example', function () { + + const checkPoints = { + createDir: 1, + copyFileFromTo: 2, + copyAllFilesFromDirTo: 1 + } + + function stubFileSystemUtils () { + const Option = proxyquire( + '../../cli-commands/commands/init/options/with-example/with-example-option', + { + '../../../../helpers/file-system-util': { + createDir: () => { + checkPoints.createDir--; + }, + copyFileFromTo: () => { + checkPoints.copyFileFromTo--; + }, + copyAllFilesFromDirTo: () => { + checkPoints.copyAllFilesFromDirTo--; + } + } + } + ); + + return Option; + } + + it('Should init project structure [with-example = false]', async () => { + const checkPointUntouched = JSON.stringify(checkPoints); + + const exampleOption = stubFileSystemUtils(); + exampleOption.process(false); + + assert(checkPointUntouched == JSON.stringify(checkPoints)); + }); + + it('Should init project structure and provide example files', async () => { + const exampleOption = stubFileSystemUtils(); + exampleOption.process(true); + + assert(checkPoints.createDir == 0); + assert(checkPoints.copyFileFromTo == 0); + assert(checkPoints.copyAllFilesFromDirTo == 0); + }); + }); }); - - it('Should init project structure', async () => { - assert(await initCommand.execute({})); - - sinon.assert.notCalled(exampleOptionSpy); - checkDirectories(); - }); - - it('Should init project structure [with-example = false]', async () => { - assert(await initCommand.execute(WITHOUT_EXAMPLE)); - - sinon.assert.calledWith(exampleOptionSpy, false); - sinon.assert.notCalled(fileSystemUtilSpy); - checkDirectories(); - }); - - it('Should init project structure and provide example files', async () => { - assert(await initCommand.execute(WITH_EXAMPLE)); - - sinon.assert.calledWith(exampleOptionSpy, true); - checkDirectories(); - checkExampleFiles(); - }); - - it('Should throw when processing command options failed', async () => { - sinon.stub(Command.prototype, "processOptions").throws('Test: Process Options Failure'); - - assert(await initCommand.execute({})); - }); - -}); \ No newline at end of file +}); diff --git a/tests/cli-commands/miscellaneous.js b/tests/cli-commands/miscellaneous.js new file mode 100644 index 0000000..dbf4608 --- /dev/null +++ b/tests/cli-commands/miscellaneous.js @@ -0,0 +1,398 @@ +const sinon = require('sinon'); +const assert = require('assert'); +const proxyquire = require('proxyquire').noCallThru(); + +const Command = require('../../cli-commands/commands/command'); +const GroupCommand = require('../../cli-commands/commands/group-command'); + +// Commons +const logger = require('../../cli-commands/common/logger'); +const Table = require('../../cli-commands/common/table'); + +const Logger = require('./utils/logger'); +const testLogger = new Logger(); + +describe('Miscellaneous', function () { + + describe('CLI Command', function () { + it('Should process command options', async () => { + const commandOptions = [{ name: 'test', process: (args) => { return args; } }]; + const command = new Command({ options: commandOptions }); + + const result = await command.processOptions({ 'test': true }); + assert(result.test); + }); + + it('Should execute command', async () => { + const command = new Command({}); + await command.execute(); + }); + }); + + describe('Group Command', function () { + it('Should execute provided option', async () => { + const commandOptions = [{ name: 'test', process: (args) => { return args; } }]; + const groupCommand = new GroupCommand({ options: commandOptions }); + await groupCommand.execute({ 'test': true }); + + assert(groupCommand.hasBeenExecuted); + }); + + it('Should set hasBeenExecuted to false if no option has been provided', async () => { + const groupCommand = new GroupCommand({}); + await groupCommand.execute(); + + assert(!groupCommand.hasBeenExecuted); + }); + }); + + describe('Common', function () { + describe('Logger', function () { + it('Should print all type of logs', async () => { + const originalConsoleLog = console.log; + console.log = () => { assert(true); } + + logger.log('message'); + logger.success('message'); + logger.info('message'); + logger.error('message', 'error'); + + console.log = originalConsoleLog; + }); + }); + + describe('Table', function () { + + it('Should add a row', async () => { + const table = new Table(['head']); + table.addRow(['row']); + + assert(table.table[0][0] == 'row'); + }); + + it('Should add a section', async () => { + const table = new Table(['head']); + table.addSection('sectionName', [['row']]); + + assert(Array.isArray(table.table[0]['\u001b[96msectionName\u001b[39m'])); + assert(table.table[1][0] == ''); + assert(table.table[1][1] == 'row'); + }); + + it('Should visualize the table', async () => { + testLogger.hide(sinon); + testLogger.on('log', (table) => { + console.log(table) + assert(table.includes('sectionName')); + assert(table.includes('row')); + }); + + const table = new Table(['head']); + table.addSection('sectionName', [['row']]); + table.draw(); + + sinon.restore(); + }); + }); + }); + + describe('Helpers', function () { + describe('AsyncSoftExec', function () { + function stubChildProcess (error, stdout) { + const AsyncSoftExec = proxyquire('../../cli-commands/helpers/async-soft-exec', { + 'child_process': { + exec: (command, cb) => { cb(error, stdout); } + } + }); + + return AsyncSoftExec; + } + + it('Should execute command', async () => { + const AsyncSoftExec = stubChildProcess('', 'executed'); + const asyncSoftExec = new AsyncSoftExec('Test'); + + const result = await asyncSoftExec.exec(); + assert(result == 'executed'); + }); + + it('Should throw error when executing', async () => { + const AsyncSoftExec = stubChildProcess('Fake error', ''); + const asyncSoftExec = new AsyncSoftExec('Test'); + + try { + await asyncSoftExec.exec(); + assert(false); + } catch (error) { + assert(error == 'Fake error'); + } + }); + }); + + describe('File system utils', function () { + + function stubExistsSync (output) { + return { existsSync: () => { return output; } } + } + + function stubMkdirSync (cb) { + return { mkdirSync: (dirName) => { cb(dirName); } } + } + + function stubCopyFileSync (cb) { + return { copyFileSync: (source, destination) => { cb(source, destination); } } + } + + function stubReaddir (...output) { + return { readdir: (dir, cb) => { cb(...output); } } + } + + function stubLstatSyncFile (output) { + return { lstatSync: () => { return { isFile: () => { return output; } } } } + } + + function stubLstatSyncDir (output) { + return { lstatSync: () => { return { isDirectory: () => { return output; } } } } + } + + function stubReadFileSync () { + return { readFileSync: (filePath) => { return filePath; } } + } + + function stubWriteFileSync (cb) { + return { writeFileSync: (filePath, fileContent) => { cb(filePath, fileContent); } } + } + + function stubUnlinkSync () { + return { unlinkSync: () => { } } + } + + function stubRmdirSync () { + return { rmdirSync: () => { } } + } + + it('Should create dir', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubExistsSync(false), + ...stubMkdirSync((dir) => { + assert(dir == 'dir'); + }) + } + }); + + utils.createDir('dir'); + }); + + it('Should copy file', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubExistsSync(false), + ...stubCopyFileSync((src, dst) => { + assert(src == 'here'); + assert(dst == 'there'); + }) + } + }); + + utils.copyFileFromTo('here', 'there'); + }); + + it('Should copy all files in dir', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubExistsSync(true), + ...stubCopyFileSync((src, dst) => { + assert(src == 'src/file'); + assert(dst == 'dst/file'); + }), + ...stubReaddir(undefined, ['file']) + } + }); + + utils.copyAllFilesFromDirTo('src/', 'dst/'); + }); + + it('Should throw if coping of all files in dir fails', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubExistsSync(true), + ...stubCopyFileSync((src, dst) => { + assert(src == 'src/file'); + assert(dst == 'dst/file'); + }), + ...stubReaddir('error', []) + } + }); + + try { + utils.copyAllFilesFromDirTo('src/', 'dst/'); + assert(false); + } catch (error) { + assert(error.message == 'Example files can not be copied'); + } + + }); + + it('Should return if path is dir', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubLstatSyncDir(true), + } + }); + + assert(utils.isDir('dir')); + }); + + it('Should return if path is a file', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubLstatSyncFile(true) + } + }); + + assert(utils.isFile('path')); + }); + + it('Should read file', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubReadFileSync(true), + } + }); + + assert(utils.readFile('file') == 'file'); + }); + + it('Should read dir with files only', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubLstatSyncDir(false), + ...stubReaddir(undefined, ['file']) + } + }); + + const result = await utils.recursivelyReadDir('dir'); + assert(result[0].fullPath == 'dir/file'); + }); + + it('Should read dir with sub dirs', async () => { + let dirLevel = 2; + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + lstatSync: () => { return { isDirectory: () => { dirLevel--; return dirLevel; } } }, + ...stubReaddir(undefined, ['file']) + } + }); + + const result = await utils.recursivelyReadDir('dir') + assert(result[0].fullPath == 'dir/file/file'); + }); + + it('Should throw if reading of a dir fails', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubReaddir({ message: 'Error' }, []) + } + }); + + try { + await utils.recursivelyReadDir('dir'); + assert(false); + } catch (error) { + assert(error == 'Error'); + } + }); + + it('Should clean up dir containing only files', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubLstatSyncDir(false), + ...stubReaddir(undefined, ['file']), + ...stubUnlinkSync(), + ...stubRmdirSync() + } + }); + + await utils.recursivelyDeleteDir('dir'); + }); + + it('Should clean up dir containing sub dirs', async () => { + let dirLevel = 2; + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + lstatSync: () => { return { isDirectory: () => { dirLevel--; return dirLevel; } } }, + ...stubReaddir(undefined, ['file']), + ...stubRmdirSync(), + ...stubUnlinkSync() + } + }); + + await utils.recursivelyDeleteDir('dir'); + }); + + it('Should throw if deletion of a dir fails', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubReaddir({ message: 'Error' }, []) + } + }); + + try { + await utils.recursivelyDeleteDir('dir'); + assert(false); + } catch (error) { + assert(error == 'Error'); + } + }); + + it('Should write to a file', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubWriteFileSync((path, content) => { + assert(path == 'path'); + assert(content == 'content'); + }), + } + }); + + utils.writeFile('path', 'content'); + }); + + it('Should throw if writing to a file fails', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubWriteFileSync(() => { throw new Error('Fake error'); }) + } + }); + + try { + utils.writeFile('path', 'content'); + assert(false); + } catch (error) { + assert(error.message.includes('Fake error')); + } + }); + + it('Should remove a file', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubUnlinkSync(true), + } + }); + + utils.rmFile('file'); + }); + + it('Should remove dir', async () => { + const utils = proxyquire('../../cli-commands/helpers/file-system-util', { + 'fs': { + ...stubRmdirSync(true), + } + }); + + utils.rmDir('dir'); + }); + }); + }); +}); diff --git a/tests/cli-commands/mocks/deployment.js b/tests/cli-commands/mocks/deployment.js deleted file mode 100644 index f114235..0000000 --- a/tests/cli-commands/mocks/deployment.js +++ /dev/null @@ -1,3 +0,0 @@ -let deploy = async function (eoslime, deployer) {} - -module.exports = deploy; \ No newline at end of file diff --git a/tests/cli-commands/mocks/nodeos-data/eosd.pid b/tests/cli-commands/mocks/nodeos-data/eosd.pid deleted file mode 100644 index 66ab9d5..0000000 --- a/tests/cli-commands/mocks/nodeos-data/eosd.pid +++ /dev/null @@ -1 +0,0 @@ -50267 diff --git a/tests/cli-commands/mocks/nodeos-data/nodeos.log b/tests/cli-commands/mocks/nodeos-data/nodeos.log deleted file mode 100644 index 83357f4..0000000 --- a/tests/cli-commands/mocks/nodeos-data/nodeos.log +++ /dev/null @@ -1,142 +0,0 @@ -info 2020-06-02T16:53:28.358 thread-0 chain_plugin.cpp:594 plugin_initialize ] initializing chain plugin -info 2020-06-02T16:53:28.362 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'WTMSIG_BLOCK_SIGNATURES' (with digest of '299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707') is enabled with preactivation required -info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'WTMSIG_BLOCK_SIGNATURES' (with digest of '299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-WTMSIG_BLOCK_SIGNATURES.json -info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'WEBAUTHN_KEY' (with digest of '4fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2') is enabled with preactivation required -info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'WEBAUTHN_KEY' (with digest of '4fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-WEBAUTHN_KEY.json -info 2020-06-02T16:53:28.364 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'RAM_RESTRICTIONS' (with digest of '4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d67') is enabled with preactivation required -info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'RAM_RESTRICTIONS' (with digest of '4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d67') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-RAM_RESTRICTIONS.json -info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'GET_SENDER' (with digest of 'f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d') is enabled with preactivation required -info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'GET_SENDER' (with digest of 'f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-GET_SENDER.json -info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'FORWARD_SETCODE' (with digest of '2652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25') is enabled with preactivation required -info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'FORWARD_SETCODE' (with digest of '2652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-FORWARD_SETCODE.json -info 2020-06-02T16:53:28.365 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'ONLY_BILL_FIRST_AUTHORIZER' (with digest of '8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a405') is enabled with preactivation required -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'ONLY_BILL_FIRST_AUTHORIZER' (with digest of '8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a405') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-ONLY_BILL_FIRST_AUTHORIZER.json -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'RESTRICT_ACTION_TO_SELF' (with digest of 'ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43') is enabled with preactivation required -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'RESTRICT_ACTION_TO_SELF' (with digest of 'ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-RESTRICT_ACTION_TO_SELF.json -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'DISALLOW_EMPTY_PRODUCER_SCHEDULE' (with digest of '68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428') is enabled with preactivation required -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'DISALLOW_EMPTY_PRODUCER_SCHEDULE' (with digest of '68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-DISALLOW_EMPTY_PRODUCER_SCHEDULE.json -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'FIX_LINKAUTH_RESTRICTION' (with digest of 'e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526') is enabled with preactivation required -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'FIX_LINKAUTH_RESTRICTION' (with digest of 'e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-FIX_LINKAUTH_RESTRICTION.json -info 2020-06-02T16:53:28.366 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'REPLACE_DEFERRED' (with digest of 'ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99') is enabled with preactivation required -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'REPLACE_DEFERRED' (with digest of 'ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-REPLACE_DEFERRED.json -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'NO_DUPLICATE_DEFERRED_ID' (with digest of '4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f') is enabled with preactivation required -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'NO_DUPLICATE_DEFERRED_ID' (with digest of '4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-NO_DUPLICATE_DEFERRED_ID.json -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:412 operator() ] Support for builtin protocol feature 'ONLY_LINK_TO_EXISTING_PERMISSION' (with digest of '1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241') is enabled with preactivation required -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'ONLY_LINK_TO_EXISTING_PERMISSION' (with digest of '1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-ONLY_LINK_TO_EXISTING_PERMISSION.json -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:425 operator() ] Support for builtin protocol feature 'PREACTIVATE_FEATURE' (with digest of '0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd') is enabled without activation restrictions -info 2020-06-02T16:53:28.367 thread-0 chain_plugin.cpp:522 operator() ] Saved default specification for builtin protocol feature 'PREACTIVATE_FEATURE' (with digest of '0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd') to: /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/protocol_features/BUILTIN-PREACTIVATE_FEATURE.json -info 2020-06-02T16:53:28.370 thread-0 chain_plugin.cpp:988 plugin_initialize ] Starting fresh blockchain state using default genesis state. -info 2020-06-02T16:53:28.845 thread-0 platform_timer_accurac:62 compute_and_print_ti ] Checktime timer accuracy: min:-1us max:1059us mean:35us stddev:45us -info 2020-06-02T16:53:28.855 thread-0 http_plugin.cpp:525 plugin_initialize ] configured http to listen on 127.0.0.1:8888 -warn 2020-06-02T16:53:28.855 thread-0 history_plugin.cpp:317 plugin_initialize ] --filter-on * enabled. This can fill shared_mem, causing nodeos to stop. -info 2020-06-02T16:53:28.856 thread-0 main.cpp:106 main ] nodeos version v2.0.4 v2.0.4-b39b6bd3c74ff47fc53fa80aafa2c1941ba82879 -info 2020-06-02T16:53:28.856 thread-0 main.cpp:107 main ] nodeos using configuration file /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/config/config.ini -info 2020-06-02T16:53:28.856 thread-0 main.cpp:108 main ] nodeos data directory is /usr/local/lib/node_modules/eoslime/cli-commands/commands/nodeos/specific/nodeos-data/data -warn 2020-06-02T16:53:28.857 thread-0 controller.cpp:580 startup ] No existing chain state or fork database. Initializing fresh blockchain state and resetting fork database. -warn 2020-06-02T16:53:28.857 thread-0 controller.cpp:450 initialize_blockchai ] Initializing new blockchain with genesis state -info 2020-06-02T16:53:28.895 thread-0 chain_plugin.cpp:1121 plugin_startup ] starting chain in read/write mode -info 2020-06-02T16:53:28.895 thread-0 chain_plugin.cpp:1126 plugin_startup ] Blockchain started; head block is #1, genesis timestamp is 2018-06-01T12:00:00.000 -info 2020-06-02T16:53:28.895 thread-0 producer_plugin.cpp:947 plugin_startup ] producer plugin: plugin_startup() begin -info 2020-06-02T16:53:28.898 thread-0 producer_plugin.cpp:972 plugin_startup ] Launching block production for 1 producers at 2020-06-02T16:53:28.898. -info 2020-06-02T16:53:28.908 thread-0 producer_plugin.cpp:983 plugin_startup ] producer plugin: plugin_startup() end -info 2020-06-02T16:53:28.909 thread-0 http_plugin.cpp:603 plugin_startup ] start listening for http requests -info 2020-06-02T16:53:28.911 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/node/get_supported_apis -info 2020-06-02T16:53:28.911 thread-0 producer_api_plugin.cp:89 plugin_startup ] starting producer_api_plugin -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/add_greylist_accounts -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/create_snapshot -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_account_ram_corrections -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_greylist -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_integrity_hash -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_runtime_options -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_scheduled_protocol_feature_activations -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_supported_protocol_features -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/get_whitelist_blacklist -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/pause -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/paused -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/remove_greylist_accounts -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/resume -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/schedule_protocol_feature_activations -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/set_whitelist_blacklist -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/producer/update_runtime_options -info 2020-06-02T16:53:28.912 thread-0 chain_api_plugin.cpp:73 plugin_startup ] starting chain_api_plugin -info 2020-06-02T16:53:28.912 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_info -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/abi_bin_to_json -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/abi_json_to_bin -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_abi -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_account -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_activated_protocol_features -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_block -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_block_header_state -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_code -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_code_hash -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_currency_balance -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_currency_stats -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_producer_schedule -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_producers -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_raw_abi -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_raw_code_and_abi -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_required_keys -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_scheduled_transactions -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_table_by_scope -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_table_rows -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/get_transaction_id -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/push_block -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/push_transaction -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/push_transactions -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/chain/send_transaction -info 2020-06-02T16:53:28.913 thread-0 history_api_plugin.cpp:34 plugin_startup ] starting history_api_plugin -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_actions -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_controlled_accounts -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_key_accounts -info 2020-06-02T16:53:28.913 thread-0 http_plugin.cpp:700 add_handler ] add api url: /v1/history/get_transaction -info 2020-06-02T16:53:28.914 thread-0 net_plugin.cpp:3403 plugin_startup ] my node_id is d6af73ab613beb58ae2f8198d7b45caff86a4ec32864cfcf60ebe5ba626edd1f -info 2020-06-02T16:53:28.914 thread-0 net_plugin.cpp:3459 plugin_startup ] starting listener, max clients is 25 -info 2020-06-02T16:53:28.921 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block fe1c10543bdf6388... #2 @ 2020-06-02T16:53:29.000 signed by eosio [trxs: 0, lib: 1, confirmed: 0] -info 2020-06-02T16:53:29.301 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block bbf94510ee93e36f... #3 @ 2020-06-02T16:53:29.500 signed by eosio [trxs: 0, lib: 2, confirmed: 0] -info 2020-06-02T16:53:29.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9163c80ad40be876... #4 @ 2020-06-02T16:53:30.000 signed by eosio [trxs: 0, lib: 3, confirmed: 0] -info 2020-06-02T16:53:30.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 8d8cd47d3c53c522... #5 @ 2020-06-02T16:53:30.500 signed by eosio [trxs: 0, lib: 4, confirmed: 0] -info 2020-06-02T16:53:30.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 2dcc6c67370207cd... #6 @ 2020-06-02T16:53:31.000 signed by eosio [trxs: 3, lib: 5, confirmed: 0] -info 2020-06-02T16:53:31.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 2d955fb69c7913c5... #7 @ 2020-06-02T16:53:31.500 signed by eosio [trxs: 0, lib: 6, confirmed: 0] -info 2020-06-02T16:53:31.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 08deeb52bdc2f57b... #8 @ 2020-06-02T16:53:32.000 signed by eosio [trxs: 0, lib: 7, confirmed: 0] -info 2020-06-02T16:53:32.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 3eca474b4931495a... #9 @ 2020-06-02T16:53:32.500 signed by eosio [trxs: 0, lib: 8, confirmed: 0] -info 2020-06-02T16:53:32.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9f94c1259c5533e7... #10 @ 2020-06-02T16:53:33.000 signed by eosio [trxs: 0, lib: 9, confirmed: 0] -info 2020-06-02T16:53:33.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block d04368ec346189f4... #11 @ 2020-06-02T16:53:33.500 signed by eosio [trxs: 0, lib: 10, confirmed: 0] -info 2020-06-02T16:53:33.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dbfb48b6e45def7b... #12 @ 2020-06-02T16:53:34.000 signed by eosio [trxs: 0, lib: 11, confirmed: 0] -info 2020-06-02T16:53:34.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 877d0f3ea0403ddb... #13 @ 2020-06-02T16:53:34.500 signed by eosio [trxs: 0, lib: 12, confirmed: 0] -info 2020-06-02T16:53:34.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 3b6265897808e444... #14 @ 2020-06-02T16:53:35.000 signed by eosio [trxs: 0, lib: 13, confirmed: 0] -info 2020-06-02T16:53:35.301 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 324272a5719d6dde... #15 @ 2020-06-02T16:53:35.500 signed by eosio [trxs: 0, lib: 14, confirmed: 0] -info 2020-06-02T16:53:35.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dcb045f5b910108b... #16 @ 2020-06-02T16:53:36.000 signed by eosio [trxs: 0, lib: 15, confirmed: 0] -info 2020-06-02T16:53:36.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 6d57007a9bc414c4... #17 @ 2020-06-02T16:53:36.500 signed by eosio [trxs: 0, lib: 16, confirmed: 0] -info 2020-06-02T16:53:36.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9cf4b1dedea4a87c... #18 @ 2020-06-02T16:53:37.000 signed by eosio [trxs: 0, lib: 17, confirmed: 0] -info 2020-06-02T16:53:37.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block ecf3e3bd602c4b25... #19 @ 2020-06-02T16:53:37.500 signed by eosio [trxs: 0, lib: 18, confirmed: 0] -info 2020-06-02T16:53:37.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block a69e8ae427a37eab... #20 @ 2020-06-02T16:53:38.000 signed by eosio [trxs: 0, lib: 19, confirmed: 0] -info 2020-06-02T16:53:38.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 9f712a1a5cbae701... #21 @ 2020-06-02T16:53:38.500 signed by eosio [trxs: 0, lib: 20, confirmed: 0] -info 2020-06-02T16:53:38.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dfa419c3434fbc96... #22 @ 2020-06-02T16:53:39.000 signed by eosio [trxs: 0, lib: 21, confirmed: 0] -info 2020-06-02T16:53:39.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 4e3a456c90fe3f05... #23 @ 2020-06-02T16:53:39.500 signed by eosio [trxs: 0, lib: 22, confirmed: 0] -info 2020-06-02T16:53:39.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 37bbbb907a3012e6... #24 @ 2020-06-02T16:53:40.000 signed by eosio [trxs: 0, lib: 23, confirmed: 0] -info 2020-06-02T16:53:40.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block d9c9bc196e484c6f... #25 @ 2020-06-02T16:53:40.500 signed by eosio [trxs: 0, lib: 24, confirmed: 0] -info 2020-06-02T16:53:40.900 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 14a0134de6aa4a9f... #26 @ 2020-06-02T16:53:41.000 signed by eosio [trxs: 0, lib: 25, confirmed: 0] -info 2020-06-02T16:53:41.301 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 6891df85da1d0dbd... #27 @ 2020-06-02T16:53:41.500 signed by eosio [trxs: 0, lib: 26, confirmed: 0] -info 2020-06-02T16:53:41.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block a2d96781ca99cf73... #28 @ 2020-06-02T16:53:42.000 signed by eosio [trxs: 0, lib: 27, confirmed: 0] -info 2020-06-02T16:53:42.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block a0f08892405baa74... #29 @ 2020-06-02T16:53:42.500 signed by eosio [trxs: 0, lib: 28, confirmed: 0] -info 2020-06-02T16:53:42.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 7a521eea79001ab9... #30 @ 2020-06-02T16:53:43.000 signed by eosio [trxs: 0, lib: 29, confirmed: 0] -info 2020-06-02T16:53:43.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 5ec522129c56c7b9... #31 @ 2020-06-02T16:53:43.500 signed by eosio [trxs: 0, lib: 30, confirmed: 0] -info 2020-06-02T16:53:43.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 8f9a663fa151ed26... #32 @ 2020-06-02T16:53:44.000 signed by eosio [trxs: 0, lib: 31, confirmed: 0] -info 2020-06-02T16:53:44.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block aeaa2470e300de15... #33 @ 2020-06-02T16:53:44.500 signed by eosio [trxs: 0, lib: 32, confirmed: 0] -info 2020-06-02T16:53:44.904 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block ae96aa66268353c5... #34 @ 2020-06-02T16:53:45.000 signed by eosio [trxs: 0, lib: 33, confirmed: 0] -info 2020-06-02T16:53:45.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 852975eed72f8db0... #35 @ 2020-06-02T16:53:45.500 signed by eosio [trxs: 0, lib: 34, confirmed: 0] -info 2020-06-02T16:53:45.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 8dfdad739e190319... #36 @ 2020-06-02T16:53:46.000 signed by eosio [trxs: 0, lib: 35, confirmed: 0] -info 2020-06-02T16:53:46.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block dc645e1bc35125e6... #37 @ 2020-06-02T16:53:46.500 signed by eosio [trxs: 0, lib: 36, confirmed: 0] -info 2020-06-02T16:53:46.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block f77abd5ca578b59c... #38 @ 2020-06-02T16:53:47.000 signed by eosio [trxs: 0, lib: 37, confirmed: 0] -info 2020-06-02T16:53:47.302 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 4ff9c5550bb2a101... #39 @ 2020-06-02T16:53:47.500 signed by eosio [trxs: 0, lib: 38, confirmed: 0] -info 2020-06-02T16:53:47.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block cecafa607d864ac9... #40 @ 2020-06-02T16:53:48.000 signed by eosio [trxs: 0, lib: 39, confirmed: 0] -info 2020-06-02T16:53:48.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 6d64e9fa642958d8... #41 @ 2020-06-02T16:53:48.500 signed by eosio [trxs: 0, lib: 40, confirmed: 0] -info 2020-06-02T16:53:48.904 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 839e68ea2b39db92... #42 @ 2020-06-02T16:53:49.000 signed by eosio [trxs: 0, lib: 41, confirmed: 0] -info 2020-06-02T16:53:49.400 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 568a5ea58679944f... #43 @ 2020-06-02T16:53:49.500 signed by eosio [trxs: 0, lib: 42, confirmed: 0] -info 2020-06-02T16:53:49.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block cb440088ebf1df19... #44 @ 2020-06-02T16:53:50.000 signed by eosio [trxs: 0, lib: 43, confirmed: 0] -info 2020-06-02T16:53:50.401 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 63b3da1229666dc9... #45 @ 2020-06-02T16:53:50.500 signed by eosio [trxs: 0, lib: 44, confirmed: 0] -info 2020-06-02T16:53:50.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block f7da5fe0b8c1c890... #46 @ 2020-06-02T16:53:51.000 signed by eosio [trxs: 0, lib: 45, confirmed: 0] -info 2020-06-02T16:53:51.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 31461a4e3d3e11c8... #47 @ 2020-06-02T16:53:51.500 signed by eosio [trxs: 0, lib: 46, confirmed: 0] -info 2020-06-02T16:53:51.902 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 443bcd8a32f98516... #48 @ 2020-06-02T16:53:52.000 signed by eosio [trxs: 0, lib: 47, confirmed: 0] -info 2020-06-02T16:53:52.402 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block ec476b085c6a01f3... #49 @ 2020-06-02T16:53:52.500 signed by eosio [trxs: 0, lib: 48, confirmed: 0] -info 2020-06-02T16:53:52.901 thread-0 producer_plugin.cpp:2093 produce_block ] Produced block 65e71b794437e123... #50 @ 2020-06-02T16:53:53.000 signed by eosio [trxs: 0, lib: 49, confirmed: 0] \ No newline at end of file diff --git a/tests/cli-commands/mocks/test-option.js b/tests/cli-commands/mocks/test-option.js deleted file mode 100644 index 36d487f..0000000 --- a/tests/cli-commands/mocks/test-option.js +++ /dev/null @@ -1,19 +0,0 @@ -const Option = require('../../../cli-commands/commands/option'); - -class TestOption extends Option { - constructor() { - super( - 'test', - { - "describe": "Test option description", - "type": "boolean" - } - ); - } - - process (optionValue) { - return optionValue; - } -} - -module.exports = new TestOption(); diff --git a/tests/cli-commands/mocks/tests-mock.js b/tests/cli-commands/mocks/tests-mock.js deleted file mode 100644 index a9dbf1e..0000000 --- a/tests/cli-commands/mocks/tests-mock.js +++ /dev/null @@ -1,7 +0,0 @@ -describe("Mocked Tests", function () { - - it("Should execute mocked test", async () => { - - }); - -}); diff --git a/tests/cli-commands/nodeos-tests.js b/tests/cli-commands/nodeos-tests.js index 2defef2..b3692dd 100644 --- a/tests/cli-commands/nodeos-tests.js +++ b/tests/cli-commands/nodeos-tests.js @@ -1,335 +1,524 @@ -const path = require('path'); -const fs = require('fs-extra'); const sinon = require('sinon'); const assert = require('assert'); +const proxyquire = require('proxyquire').noCallThru(); const Command = require('../../cli-commands/commands/command'); const GroupCommand = require('../../cli-commands/commands/group-command'); const NodeosCommand = require('../../cli-commands/commands/nodeos/index'); -const AsyncSoftExec = require('../../cli-commands/helpers/async-soft-exec'); -const AccountFactory = require('../../src/account/normal-account/account-factory'); + +// Sub-commands const LogsCommand = require('../../cli-commands/commands/nodeos/subcommands/logs/index'); const StopCommand = require('../../cli-commands/commands/nodeos/subcommands/stop/index'); const StartCommand = require('../../cli-commands/commands/nodeos/subcommands/start/index'); const AccountsCommand = require('../../cli-commands/commands/nodeos/subcommands/accounts/index'); -const AccountsTable = require('../../cli-commands/commands/nodeos/subcommands/accounts/specific/accounts-table'); -const nodeosDefinition = require('../../cli-commands/commands/nodeos/definition'); +// Definitions const logsDefinition = require('../../cli-commands/commands/nodeos/subcommands/logs/definition'); const stopDefinition = require('../../cli-commands/commands/nodeos/subcommands/stop/definition'); const startDefinition = require('../../cli-commands/commands/nodeos/subcommands/start/definition'); +const nodeosDefinition = require('../../cli-commands/commands/nodeos/definition'); const accountsDefinition = require('../../cli-commands/commands/nodeos/subcommands/accounts/definition'); -const logsCommandMessages = require('../../cli-commands/commands/nodeos/subcommands/logs/messages'); + +// Options const PathOption = require('../../cli-commands/commands/nodeos/subcommands/start/options/path-option'); const LinesOption = require('../../cli-commands/commands/nodeos/subcommands/logs/options/lines-option'); -const logger = require('../../cli-commands/common/logger'); -const fileSystemUtil = require('../../cli-commands/helpers/file-system-util'); -const template = require('../../cli-commands/commands/nodeos/subcommands/start/specific/template'); +// Common & Specifics +const dataManager = require('../../cli-commands/commands/nodeos/specific/nodeos-data/data-manager'); const predefinedAccounts = require('../../cli-commands/commands/nodeos/subcommands/common/accounts'); -const nodoesDataManager = require('../../cli-commands/commands/nodeos/specific/nodeos-data/data-manager'); -describe('NodeosCommand', function () { - const TEST_DIR = './cli-commands-test'; - const NODEOS_DATA_DIR = 'nodeos-data'; +const Logger = require('./utils/logger'); +const logger = new Logger(); - let initialDir; - let nodeosDataPath; - - before(async () => { - initialDir = process.cwd(); - fs.mkdirSync(TEST_DIR); - process.chdir(TEST_DIR); - nodeosDataPath = path.resolve(process.cwd(), NODEOS_DATA_DIR); - fs.mkdirSync('./nodeos-data'); - }); +describe('Nodeos Command', function () { beforeEach(async () => { - sinon.stub(logger, "info"); - sinon.stub(logger, "error"); + logger.hide(sinon); }); afterEach(async () => { sinon.restore(); - cleanNodeosDataFolder(); - }); - - after(async () => { - process.chdir(initialDir); - fs.removeSync(TEST_DIR); }); - function preloadNodeosData () { - fs.mkdir('./nodeos-data/data'); - fs.mkdir('./nodeos-data/config'); - fs.copyFileSync('../tests/cli-commands/mocks/nodeos-data/eosd.pid', './nodeos-data/eosd.pid'); - fs.copyFileSync('../tests/cli-commands/mocks/nodeos-data/nodeos.log', './nodeos-data/nodeos.log'); - } - - function cleanNodeosDataFolder () { - fs.removeSync('./nodeos-data/data'); - fs.removeSync('./nodeos-data/config'); - fs.removeSync('./nodeos-data/eosd.pid'); - fs.removeSync('./nodeos-data/nodeos.log'); - } - - it('Should initialize command properly', async () => { - let nodeosCommand = new NodeosCommand(); - - assert(nodeosCommand instanceof GroupCommand); - assert(nodeosCommand.template == nodeosDefinition.template); - assert(nodeosCommand.description = nodeosDefinition.description); - assert(nodeosCommand.options == nodeosDefinition.options); - assert(nodeosCommand.subcommands.length > 0); - }); - - describe('StartCommand', function () { - let startCommand; - let templateSpy; - let pathOptionSpy; - let setNodeosPathSpy; - let predefinedAccountsSpy; - - beforeEach(async () => { - startCommand = new StartCommand(); - - sinon.stub(fileSystemUtil, "writeFile"); - sinon.stub(AsyncSoftExec.prototype, "exec"); - sinon.stub(AccountFactory.prototype, "create"); - sinon.stub(nodoesDataManager, "defaultPath").returns(nodeosDataPath); - - templateSpy = sinon.spy(template, "build"); - pathOptionSpy = sinon.spy(PathOption, "process"); - predefinedAccountsSpy = sinon.spy(predefinedAccounts, "load"); - setNodeosPathSpy = sinon.spy(nodoesDataManager, "setNodeosPath"); - }); + describe('Command', function () { it('Should initialize command properly', async () => { - assert(startCommand instanceof Command); - assert(startCommand.template == startDefinition.template); - assert(startCommand.description = startDefinition.description); - assert(startCommand.options == startDefinition.options); - assert(startCommand.subcommands.length == 0); + let nodeosCommand = new NodeosCommand(); + + assert(nodeosCommand instanceof GroupCommand); + assert(nodeosCommand.template == nodeosDefinition.template); + assert(nodeosCommand.description = nodeosDefinition.description); + assert(nodeosCommand.options == nodeosDefinition.options); + assert(nodeosCommand.subcommands.length > 0); }); + }); - it('Should break when there is already running nodeos', async () => { - sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + describe('Sub-commands', function () { + + function stubNodeosDataManager (nodeosRunning, checkPoints) { + return { + '../../specific/nodeos-data/data-manager': { + nodeosIsRunning: () => { return nodeosRunning; }, + nodeosPath: () => { return ''; }, + setNodeosPath: (path) => { checkPoints.setNodeosPath--; }, + requireRunningNodeos: () => { checkPoints.requireRunningNodeos--; } + } + } + } + + function stubAsyncSoftExec (checkPoints) { + return { + '../../../../helpers/async-soft-exec': class FakeAsyncSoftExec { + constructor () { } + exec () { checkPoints.exec--; } + } + } + } + + describe('Start Command', function () { + let startCommand; + + beforeEach(async () => { + startCommand = new StartCommand(); + }); - assert(await startCommand.execute({ path: nodeosDataPath })); + it('Should initialize command properly', async () => { + assert(startCommand instanceof Command); + assert(startCommand.template == startDefinition.template); + assert(startCommand.description = startDefinition.description); + assert(startCommand.options == startDefinition.options); + assert(startCommand.subcommands.length == 0); + }); - sinon.assert.notCalled(pathOptionSpy); - sinon.assert.notCalled(setNodeosPathSpy); - }); + function stubPredefinedAccounts (checkPoints) { + return { + '../common/accounts': { + load: () => { checkPoints.load--; } + } + } + } + + it('Should log in case another nodeos instance has been run already', async () => { + const startCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/start/index', { + ...stubNodeosDataManager(true) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('info', (message) => { + if (message.includes('Nodeos is already running')) { + return resolve(true); + } + }); + + await startCommand.execute(); + }); + + await waitToPass; + }); - it('Should start nodeos successfully', async () => { - let stub = sinon.stub(nodoesDataManager, "nodeosIsRunning"); - stub.onFirstCall().returns(false); - stub.onSecondCall().returns(true); + it('Should start nodeos', async () => { + const checkPoints = { + setNodeosPath: 1, + requireRunningNodeos: 1, + load: 1, + exec: 1 + } + + const startCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/start/index', { + ...stubNodeosDataManager(false, checkPoints), + ...stubAsyncSoftExec(checkPoints), + ...stubPredefinedAccounts(checkPoints) + })); + + await startCommand.execute({ path: './' }); + + assert(checkPoints.setNodeosPath == 0); + assert(checkPoints.requireRunningNodeos == 0); + assert(checkPoints.load == 0); + assert(checkPoints.exec == 0); + }); - assert(await startCommand.execute({ path: nodeosDataPath })); + it('Should log in case of an error', async () => { + const startCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/start/index', { + '../../command': class FakeCommand { + processOptions () { throw new Error('Fake error') } + }, + ...stubNodeosDataManager(false) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Nodeos has not been started')) { + return resolve(true); + } + }); + + await startCommand.execute(); + }); + + await waitToPass; + }); - sinon.assert.calledWith(pathOptionSpy, nodeosDataPath); - sinon.assert.calledWith(setNodeosPathSpy, nodeosDataPath); - sinon.assert.calledWith(templateSpy, nodeosDataPath); - sinon.assert.calledOnce(predefinedAccountsSpy); + describe('Options', function () { + describe('Path', function () { + it('Should return provided path', async () => { + const path = await PathOption.process('./'); + assert(path == './'); + }); + + it('Should throw in case the provided path is an incorrect one', async () => { + try { + await PathOption.process('./custom'); + assert(false); + } catch (error) { + assert(error.message.includes('no such file or directory, lstat \'./custom\'')); + } + }); + }); + }); }); - it('Should throw when unexpected error occurred', async () => { - let stub = sinon.stub(nodoesDataManager, "nodeosIsRunning"); - stub.onFirstCall().returns(false); - stub.onSecondCall().returns(false); + describe('Stop Command', function () { + let stopCommand; - assert(await startCommand.execute({ path: nodeosDataPath })); + beforeEach(async () => { + stopCommand = new StopCommand(); + }); - sinon.assert.calledWith(pathOptionSpy, nodeosDataPath); - sinon.assert.calledWith(setNodeosPathSpy, nodeosDataPath); - sinon.assert.calledWith(templateSpy, nodeosDataPath); - sinon.assert.notCalled(predefinedAccountsSpy); - }); + function stubFileSystemUtils (checkPoints) { + return { + '../../../../helpers/file-system-util': { + rmFile: (dirPath) => { checkPoints.rmFile--; }, + recursivelyDeleteDir: () => { checkPoints.recursivelyDeleteDir--; }, + readFile: () => { checkPoints.readFile--; } + } + } + } + + it('Should initialize command properly', async () => { + assert(stopCommand instanceof Command); + assert(stopCommand.template == stopDefinition.template); + assert(stopCommand.description = stopDefinition.description); + assert(stopCommand.options == stopDefinition.options); + assert(stopCommand.subcommands.length == 0); + }); - }); + it('Should stop nodeos instance', async () => { + const checkPoints = { + exec: 1, + rmFile: 2, + readFile: 1, + recursivelyDeleteDir: 2 + } + + const dataManager = stubNodeosDataManager(true); + const asyncSoftExec = stubAsyncSoftExec(checkPoints); + const fileSystemUtils = stubFileSystemUtils(checkPoints); + + const stopCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/stop/index', { + ...dataManager, + ...fileSystemUtils, + ...asyncSoftExec + })); + + await stopCommand.execute(); + assert(checkPoints.exec == 0); + assert(checkPoints.rmFile == 0); + assert(checkPoints.readFile == 0); + assert(checkPoints.recursivelyDeleteDir == 0); + }); - describe('StopCommand', function () { - let stopCommand; - let fsReadFileSpy; - let fsRemoveDirSpy; - let fsRemoveFileSpy; - - beforeEach(async () => { - stopCommand = new StopCommand(); - sinon.stub(AsyncSoftExec.prototype, "exec"); - sinon.stub(nodoesDataManager, "nodeosPath").returns(nodeosDataPath); - fsReadFileSpy = sinon.spy(fileSystemUtil, "readFile"); - fsRemoveFileSpy = sinon.spy(fileSystemUtil, "rmFile"); - fsRemoveDirSpy = sinon.spy(fileSystemUtil, "recursivelyDeleteDir"); - }); + it('Should clear only nodeos data in case of no running nodeos instance', async () => { + const checkPoints = { + exec: 1, + rmFile: 2, + readFile: 1, + recursivelyDeleteDir: 2 + } + + const dataManager = stubNodeosDataManager(false); + const asyncSoftExec = stubAsyncSoftExec(checkPoints); + const fileSystemUtils = stubFileSystemUtils(checkPoints); + + const stopCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/stop/index', { + ...dataManager, + ...fileSystemUtils, + ...asyncSoftExec + })); + + await stopCommand.execute(); + assert(checkPoints.exec == 1); + assert(checkPoints.rmFile == 0); + assert(checkPoints.readFile == 1); + assert(checkPoints.recursivelyDeleteDir == 0); + }); - it('Should initialize command properly', async () => { - assert(stopCommand instanceof Command); - assert(stopCommand.template == stopDefinition.template); - assert(stopCommand.description = stopDefinition.description); - assert(stopCommand.options == stopDefinition.options); - assert(stopCommand.subcommands.length == 0); - }); + it('Should log in case of an error', async () => { + const stopCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/stop/index', { + '../../specific/nodeos-data/data-manager': { + nodeosIsRunning: () => { throw new Error(); }, + nodeosPath: () => { return ''; } + } + })); - it('Should clean only the data when there is no running nodeos', async () => { - preloadNodeosData(); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Nodeos has not been stopped')) { + return resolve(true); + } + }); - sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(false); - assert(await stopCommand.execute()); + await stopCommand.execute(); + }); - sinon.assert.notCalled(fsReadFileSpy); - sinon.assert.calledTwice(fsRemoveFileSpy); - sinon.assert.calledTwice(fsRemoveDirSpy); + await waitToPass; + }); }); - it('Should stop nodeos and clean the data', async () => { - preloadNodeosData(); + describe('Logs Command', function () { + let logsCommand; - sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + beforeEach(async () => { + logsCommand = new LogsCommand(); + }); - assert(await stopCommand.execute()); + it('Should initialize command properly', async () => { + assert(logsCommand instanceof Command); + assert(logsCommand.template == logsDefinition.template); + assert(logsCommand.description = logsDefinition.description); + assert(logsCommand.options == logsDefinition.options); + assert(logsCommand.subcommands.length == 0); + }); - sinon.assert.calledWith(fsReadFileSpy, nodeosDataPath + '/eosd.pid'); - sinon.assert.calledTwice(fsRemoveFileSpy); - fsRemoveFileSpy.firstCall.calledWith(nodeosDataPath + '/eosd.pid'); - fsRemoveFileSpy.secondCall.calledWith(nodeosDataPath + '/nodeos.log'); - sinon.assert.calledTwice(fsRemoveDirSpy); - fsRemoveDirSpy.firstCall.calledWith(nodeosDataPath + '/data'); - fsRemoveDirSpy.secondCall.calledWith(nodeosDataPath + '/config'); - }); + it('Should log in case of no running nodeos instance', async () => { + const dataManager = stubNodeosDataManager(false); - it('Should throw when unexpected error occured', async () => { - preloadNodeosData(); + const logsCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/logs/index', { + ...dataManager + })); - sinon.stub(nodoesDataManager, "nodeosIsRunning").throws(); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('info', (message) => { + if (message.includes('Empty logs')) { + return resolve(true); + } + }); - assert(await stopCommand.execute()); + await logsCommand.execute(); + }); - sinon.assert.notCalled(fsRemoveFileSpy); - sinon.assert.notCalled(fsRemoveDirSpy); - }); + await waitToPass; + }); - }); + it('Should display nodeos logs', async () => { + const checkPoints = { + exec: 1 + } - describe('LogsCommand', function () { - let logsCommand; - let linesOptionSpy; + const dataManager = stubNodeosDataManager(true); + const asyncSoftExec = stubAsyncSoftExec(checkPoints); - beforeEach(async () => { - logsCommand = new LogsCommand(); - sinon.stub(nodoesDataManager, "nodeosPath").returns(nodeosDataPath); - linesOptionSpy = sinon.spy(LinesOption, "process"); - }); + const logsCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/logs/index', { + ...dataManager, + ...asyncSoftExec + })); - it('Should initialize command properly', async () => { - assert(logsCommand instanceof Command); - assert(logsCommand.template == logsDefinition.template); - assert(logsCommand.description = logsDefinition.description); - assert(logsCommand.options == logsDefinition.options); - assert(logsCommand.subcommands.length == 0); - }); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('success', (message) => { + if (message.includes('Nodeos logs')) { + return resolve(true); + } + }); - it('Should break when there is no running nodeos', async () => { - preloadNodeosData(); + await logsCommand.execute({ lines: 1 }); + }); - sinon.stub(AsyncSoftExec.prototype, "exec"); - sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(false); - let processOptionsSpy = sinon.spy(Command.prototype, "processOptions"); + await waitToPass; + assert(checkPoints.exec == 0); + }); - assert(await logsCommand.execute({ lines: 10 })); + it('Should log in case of an error', async () => { + const logsCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/logs/index', { + '../../../command': class FakeCommand { + processOptions () { + throw new Error(); + } + }, + ...stubNodeosDataManager(true) + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Logs has not been shown')) { + return resolve(true); + } + }); + + await logsCommand.execute({ lines: 1 }); + }); + + await waitToPass; + }); - sinon.assert.notCalled(processOptionsSpy); + describe('Options', function () { + describe('Number of lines', function () { + it('Should return provided number of lines', async () => { + assert(LinesOption.process(1) == 1); + assert(LinesOption.definition.default == 10); + }); + }); + }); }); - it('Should display nodeos logs successfully', async () => { - preloadNodeosData(); + describe('Accounts Command', function () { + let accountsCommand; - sinon.stub(AsyncSoftExec.prototype, "exec").callsFake(() => { logsCommandMessages.NodeosLogs(); }); - sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); + beforeEach(async () => { + accountsCommand = new AccountsCommand(); + }); - assert(await logsCommand.execute({ lines: 10 })); + it('Should initialize command properly', async () => { + assert(accountsCommand instanceof Command); + assert(accountsCommand.template == accountsDefinition.template); + assert(accountsCommand.description = accountsDefinition.description); + assert(accountsCommand.options == accountsDefinition.options); + assert(accountsCommand.subcommands.length == 0); + }); - sinon.assert.calledWith(linesOptionSpy, 10); - }); + it('Should display a table with preloaded accounts', async () => { + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('log', (message) => { + for (let i = 0; i < predefinedAccounts.length; i++) { + assert(message.includes(predefinedAccounts[i].name)); + assert(message.includes(predefinedAccounts[i].publicKey)); + assert(message.includes(predefinedAccounts[i].privateKey)); + } - it('Should throw when processing command options failed', async () => { - preloadNodeosData(); + return resolve(true); + }); - sinon.stub(nodoesDataManager, "nodeosIsRunning").returns(true); - sinon.stub(Command.prototype, "processOptions").throws('Test: Process Options Failure'); + await accountsCommand.execute(); + }); - assert(await logsCommand.execute({ lines: 10 })); + await waitToPass; + }); - sinon.assert.notCalled(linesOptionSpy); - }); + it('Should log in case of an error', async () => { + const accountsCommand = new (proxyquire('../../cli-commands/commands/nodeos/subcommands/accounts/index', { + '../common/accounts': { + accounts () { + throw new Error(); + } + } + })); + + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Accounts has not been shown')) { + return resolve(true); + } + }); + + await accountsCommand.execute(); + }); + + await waitToPass; + }); + }); }); - describe('AccountsCommand', function () { - let accountsCommand; + describe('Command specifics', function () { - beforeEach(async () => { - accountsCommand = new AccountsCommand(); - }); + describe('Data Manager', function () { - it('Should initialize command properly', async () => { - assert(accountsCommand instanceof Command); - assert(accountsCommand.template == accountsDefinition.template); - assert(accountsCommand.description = accountsDefinition.description); - assert(accountsCommand.options == accountsDefinition.options); - assert(accountsCommand.subcommands.length == 0); - }); + function stubFileSystemUtilsCallback (cb) { + return proxyquire('../../cli-commands/commands/nodeos/specific/nodeos-data/data-manager', { + '../../../../helpers/file-system-util': { + writeFile: (path, content) => { return cb(path, content) }, + readFile: (filePath) => { return cb(filePath) } + } + }); + } - it('Should display preloaded accounts', async () => { - let addTableRowSpy = sinon.spy(AccountsTable.prototype, "addRow"); - sinon.stub(AccountsTable.prototype, "draw"); + it('Should return dirname as default path', async () => { + assert(dataManager.defaultPath().includes('eoslime/cli-commands/commands/nodeos/specific/nodeos-data')); + }); - assert(await accountsCommand.execute()); + it('Should return the last set nodeosPath', async () => { + assert(dataManager.nodeosPath()); + }); - const accounts = predefinedAccounts.accounts(); + it('Should set new nodeos path', async () => { + const dataManagerStub = stubFileSystemUtilsCallback((path, content) => { + assert(path.includes('eoslime/cli-commands/commands/nodeos/specific/nodeos-data/nodeos.json')); + assert(content == '{"nodeosPath":"./custom"}'); + }); - sinon.assert.calledThrice(addTableRowSpy); - addTableRowSpy.firstCall.calledWith(accounts[0].name, accounts[0].publicKey, accounts[0].privateKey); - addTableRowSpy.secondCall.calledWith(accounts[1].name, accounts[1].publicKey, accounts[1].privateKey); - addTableRowSpy.thirdCall.calledWith(accounts[2].name, accounts[2].publicKey, accounts[2].privateKey); - }); + dataManagerStub.setNodeosPath('./custom'); + }); - it('Should throw when table drawing failed', async () => { - sinon.stub(AccountsTable.prototype, "draw").throws(); + it('Should return if nodeos is running', async () => { + const dataManagerStub = stubFileSystemUtilsCallback((path) => { + assert('./custom/eosd.pid' == path); + return { toString: () => { return 'content'; } } + }); - assert(await accountsCommand.execute()); + dataManagerStub.nodeosIsRunning('./custom'); + }); + + it('Should throw in case nodeos is not running', async () => { + try { + dataManager.nodeosIsRunning = () => { return false; } + dataManager.requireRunningNodeos('./custom'); + + assert(false); + } catch (error) { + assert(error.message === 'Check if another nodeos has been started already'); + } + }); }); - }); + describe('Pre-defined accounts', function () { - describe('DataManager', function () { + it('Should return pre-defined accounts', async () => { + const accounts = predefinedAccounts.accounts(); - describe('nodeosIsRunning', function () { + assert(accounts.length == 3); - it('Should return false when eosd.pid file does not exists', async () => { - assert(!nodoesDataManager.nodeosIsRunning(nodeosDataPath)); - }); - - it('Should return false when eosd.pid file exists, but nodeos is not running', async () => { - preloadNodeosData(); - - sinon.stub(process, "kill").throws(); - - assert(!nodoesDataManager.nodeosIsRunning(nodeosDataPath)); - }); + assert(accounts[0].name == 'eoslimedavid'); + assert(accounts[0].publicKey == 'EOS7UyV15G2t47MqRm4WpUP6KTfy9sNU3HHGu9aAgR2A3ktxoBTLv'); + assert(accounts[0].privateKey == '5KS9t8LGsaQZxLP6Ln5WK6XwYU8M3AYHcfx1x6zoGmbs34vQsPT'); + + assert(accounts[1].name == 'eoslimekevin'); + assert(accounts[1].publicKey == 'EOS6Zz4iPbjm6FNys1zUMaRE4zPXrHcX3SRG65YWneVbdXQTSiqDp'); + assert(accounts[1].privateKey == '5KieRy975NgHk5XQfn8r6o3pcqJDF2vpeV9bDiuB5uF4xKCTwRF'); - it('Should return true when eosd.pid file exists and nodeos is running', async () => { - preloadNodeosData(); - - sinon.stub(process, "kill").returns(true); - - assert(nodoesDataManager.nodeosIsRunning(nodeosDataPath)); + assert(accounts[2].name == 'eoslimemarty'); + assert(accounts[2].publicKey == 'EOS7FDeYdY3G8yMNxtrU8MSYnAJc3ZogYHgL7RG3rBf8ZDYA3xthi'); + assert(accounts[2].privateKey == '5JtbCXgK5NERDdFdrmxb8rpYMkoxVfSyH1sR6TYxHBG5zNLHfj5'); }); + it('Should create pre-defined accounts on the chain', async () => { + let numberOfCreations = 3; + + // Stub eoslime + const accounts = proxyquire('../../cli-commands/commands/nodeos/subcommands/common/accounts', { + '../../../../../': { + init: () => { + return { + Account: { + create: () => { numberOfCreations--; } + } + } + } + } + }); + + await accounts.load(); + assert(numberOfCreations == 0); + }); }); - - }) - -}); \ No newline at end of file + }); +}); diff --git a/tests/cli-commands/shape-tests.js b/tests/cli-commands/shape-tests.js index 9c6574f..6743980 100644 --- a/tests/cli-commands/shape-tests.js +++ b/tests/cli-commands/shape-tests.js @@ -1,66 +1,104 @@ const sinon = require('sinon'); const assert = require('assert'); +const proxyquire = require('proxyquire').noCallThru(); + +const definition = require('../../cli-commands/commands/shape/definition'); -const Git = require('simple-git/src/git'); -const logger = require('../../cli-commands/common/logger'); const Command = require('../../cli-commands/commands/command'); const ShapeCommand = require('../../cli-commands/commands/shape/index'); -const definition = require('../../cli-commands/commands/shape/definition'); -const repositories = require('../../cli-commands/commands/shape/specific/repositories.json'); + const FrameworkOption = require('../../cli-commands/commands/shape/options/framework-option'); -describe('ShapeCommand', function () { - const FRAMEWORK_REACT = "react"; - const NOT_SUPPORTED_FRAMEWORK = "angular"; +const Logger = require('./utils/logger'); +const logger = new Logger(); - let shapeCommand; - let simpleGitSpy; - let frameworkOptionSpy; +describe('Shape Command', function () { beforeEach(async () => { - shapeCommand = new ShapeCommand(); - frameworkOptionSpy = sinon.spy(FrameworkOption, "process"); - sinon.stub(logger, "info"); - sinon.stub(logger, "error"); + logger.hide(sinon); }); afterEach(async () => { sinon.restore(); }); - it('Should initialize command properly', async () => { - assert(shapeCommand instanceof Command); - assert(shapeCommand.template == definition.template); - assert(shapeCommand.description = definition.description); - assert(shapeCommand.options == definition.options); - assert(shapeCommand.subcommands.length == 0); - }); + describe('Command', function () { - it('Should prepare React project', async () => { - sinon.stub(Git.prototype, 'clone').callsFake(function (command, callback) { - callback.call(); + it('Should initialize command properly', async () => { + const shapeCommand = new ShapeCommand(); + + assert(shapeCommand instanceof Command); + assert(shapeCommand.template == definition.template); + assert(shapeCommand.description = definition.description); + assert(shapeCommand.options == definition.options); + assert(shapeCommand.subcommands.length == 0); }); - - assert(await shapeCommand.execute({ framework: FRAMEWORK_REACT })); - sinon.assert.calledWith(frameworkOptionSpy, FRAMEWORK_REACT); - assert(frameworkOptionSpy.returnValues[0] == repositories[FRAMEWORK_REACT]); - }); + it('Should prepare shape ', async () => { + const shapeCommand = new (proxyquire('../../cli-commands/commands/shape/index', + { + 'simple-git/promise': () => { + return { clone: () => { assert(true); } } + } + } + )); - it('Should throw when an unknown front-end framework is specified', async () => { - simpleGitSpy = sinon.spy(Git.prototype, 'clone'); - - assert(await shapeCommand.execute({ framework: NOT_SUPPORTED_FRAMEWORK })); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('success', (message) => { + if (message.includes('Successful shaping')) { + return resolve(true); + } + }); - sinon.assert.calledWith(frameworkOptionSpy, NOT_SUPPORTED_FRAMEWORK); - assert(frameworkOptionSpy.returnValues[0] == repositories[NOT_SUPPORTED_FRAMEWORK]); - sinon.assert.notCalled(simpleGitSpy); - }); + await shapeCommand.execute({ framework: 'react' }); + }); + + await waitToPass; + }); + + function stubBaseCommand (cb) { + return { + '../command': class FakeBaseCommand { + processOptions () { + return cb(); + } + } + } + } + + it('Should log in case of an error', async () => { + const shapeCommand = new (proxyquire('../../cli-commands/commands/shape/index', { + ...stubBaseCommand(() => { throw new Error('Fake error'); }) + })); - it('Should throw when repository cloning fails', async () => { - sinon.stub(Git.prototype, 'clone').throws('Test: Cloning Repo Failure'); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message) => { + if (message.includes('Unsuccessful shaping')) { + return resolve(true); + } + }); - assert(await shapeCommand.execute({ framework: FRAMEWORK_REACT })); + await shapeCommand.execute(); + }); + + await waitToPass; + }); }); -}); \ No newline at end of file + describe('Options', function () { + describe('Framework', function () { + + it('Should return the repository of the shape framework', async () => { + assert(FrameworkOption.process('react') == 'https://github.com/LimeChain/eoslime-shape-react.git'); + }); + + it('Should throw in case the provided framework is not supported', async () => { + try { + await FrameworkOption.process('Not supported'); + } catch (error) { + assert(error.message == 'Invalid Shape framework'); + } + }); + }); + }); +}); diff --git a/tests/cli-commands/test-tests.js b/tests/cli-commands/test-tests.js index 47a4c85..8bf1c66 100644 --- a/tests/cli-commands/test-tests.js +++ b/tests/cli-commands/test-tests.js @@ -1,155 +1,303 @@ -const fs = require('fs-extra'); +const path = require('path'); const sinon = require('sinon'); const assert = require('assert'); -const eoslime = require('../../index'); +const proxyquire = require('proxyquire').noCallThru(); const Command = require('../../cli-commands/commands/command'); const TestCommand = require('../../cli-commands/commands/test/index'); -const ProviderFactory = require('../../src/network-providers/provider-factory'); -const MochaFramework = require('../../cli-commands/commands/test/specific/test-frameworks/mocha'); -const logger = require('../../cli-commands/common/logger'); -const definition = require('../../cli-commands/commands/test/definition'); -const PathOption = require('../../cli-commands/commands/test/options/path-option'); const NetworkOption = require('../../cli-commands/commands/test/options/network-option'); -const ResourceReportOption = require('../../cli-commands/commands/test/options/resource-usage-option/resource-usage-option'); - -describe('TestCommand', function () { - const TEST_DIR = './cli-commands-test'; - const DEFAULT_PATH = './tests'; - const INVALID_PATH = './unknown_folder'; - const DEFAULT_NETWORK = 'local'; - const INVALID_NETWORK = 'invalid_network'; - const CUSTOM_NETWORK = { url: "https://test.custom.net", chainId: "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f" }; - - let initialDir; - let testCommand; - let eoslimeSpy; - let pathOptionSpy; - let networkOptionSpy; - let resourceReportOptionSpy; - let mochaAddTestFilesSpy; - let mochaSetDescribeArgs; - let providerFactorySpy; - - before(async () => { - initialDir = process.cwd(); - fs.mkdirSync(TEST_DIR); - process.chdir(TEST_DIR); - }); +const ResourceReportOption = require('../../cli-commands/commands/test/options/resource-usage-option'); + +const definition = require('../../cli-commands/commands/test/definition'); + +const Logger = require('./utils/logger'); +const logger = new Logger(); + +const eoslime = require('../../').init(); +const MochaFramework = require('../../cli-commands/commands/test/specific/test-frameworks/mocha'); + +describe('Test Command', function () { + + this.timeout(10000); beforeEach(async () => { - sinon.stub(logger, "info"); - sinon.stub(logger, "error"); - sinon.stub(MochaFramework.prototype, "runTests"); - eoslimeSpy = sinon.spy(eoslime, "init"); - testCommand = new TestCommand(MochaFramework); - pathOptionSpy = sinon.spy(PathOption, "process"); - networkOptionSpy = sinon.spy(NetworkOption, "process"); - resourceReportOptionSpy = sinon.spy(ResourceReportOption, "process"); - mochaAddTestFilesSpy = sinon.spy(MochaFramework.prototype, "addTestFiles"); - mochaSetDescribeArgs = sinon.spy(MochaFramework.prototype, "setDescribeArgs"); - providerFactorySpy = sinon.spy(ProviderFactory.prototype, "reset"); - - preloadMockedTests(); + logger.hide(sinon); }); afterEach(async () => { sinon.restore(); - fs.removeSync('./tests'); - }); - - after(async () => { - process.chdir(initialDir); - fs.removeSync(TEST_DIR); }); - function preloadMockedTests () { - fs.mkdirSync('./tests'); - fs.copyFileSync('../tests/cli-commands/mocks/tests-mock.js', './tests/tests-mock.js'); - } - - it('Should initialize command properly', async () => { - assert(testCommand instanceof Command); - assert(testCommand.template == definition.template); - assert(testCommand.description = definition.description); - assert(testCommand.options == definition.options); - assert(testCommand.subcommands.length == 0); - }); + describe('Command', function () { - it('Should execute tests when valid tests folder is specified', async () => { - assert(await testCommand.execute({ path: DEFAULT_PATH })); + it('Should initialize command properly', async () => { + const testCommand = new TestCommand(); + assert(testCommand instanceof Command); + assert(testCommand.template == definition.template); + assert(testCommand.description = definition.description); + assert(testCommand.options == definition.options); + assert(testCommand.subcommands.length == 0); + }); - sinon.assert.calledOnce(eoslimeSpy); - sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); - sinon.assert.calledWith(mochaAddTestFilesSpy, ["./tests/tests-mock.js"]); - }); + it('Should run test scripts', async () => { + const checkPoint = { + runTests: 1, + setDescribeArgs: 1 + } - it('Should throw when invalid tests folder is specified', async () => { - assert(await testCommand.execute({ path: INVALID_PATH })); + const testCommand = new TestCommand(class TestFramework { + setDescribeArgs () { checkPoint.setDescribeArgs--; } + runTests () { checkPoint.runTests--; } + }); - sinon.assert.calledWith(pathOptionSpy, INVALID_PATH); - sinon.assert.notCalled(mochaAddTestFilesSpy); - }); + const args = {}; + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('success', (message) => { + if (message.includes('Testing completed successfully')) { + return resolve(true); + } + }); - it('Should not throw when tests folder is empty', async () => { - fs.removeSync('./tests/tests-mock.js'); + await testCommand.execute(args); + }); - assert(await testCommand.execute({ path: DEFAULT_PATH })); + await waitToPass; - sinon.assert.calledWith(pathOptionSpy, DEFAULT_PATH); - sinon.assert.calledWith(mochaAddTestFilesSpy, []); - }); + assert(args.eoslime.tests); + assert(checkPoint.runTests == 0); + assert(checkPoint.setDescribeArgs == 0); + }); - it('Should execute tests when path to file with tests is provided', async () => { - assert(await testCommand.execute({ path: `${DEFAULT_PATH}/tests-mock.js` })); - - sinon.assert.calledWith(pathOptionSpy, `${DEFAULT_PATH}/tests-mock.js`); - sinon.assert.calledOnce(mochaAddTestFilesSpy); - }); + it('Should log error message in case of error', async () => { + // Because no test framework has been provided, it will throw + const testCommand = new TestCommand(); - it('Should not throw when file without tests is provided', async () => { - fs.createFileSync('./tests.txt'); + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('error', (message, error) => { + if (message.includes('Testing failed')) { + return resolve(true); + } + }); - assert(await testCommand.execute({ path: './tests.txt' })); + await testCommand.execute(); + }); - sinon.assert.calledWith(pathOptionSpy, './tests.txt'); - sinon.assert.calledOnce(mochaAddTestFilesSpy); + await waitToPass; + }); }); - it('Should execute tests when valid network is specified', async () => { - assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK })); + describe('Options', function () { - sinon.assert.calledWith(networkOptionSpy, DEFAULT_NETWORK); - sinon.assert.calledOnce(providerFactorySpy); - sinon.assert.calledOnce(mochaSetDescribeArgs); - }); + describe('Path', function () { - it('Should execute tests when custom network url and chainId are provided', async () => { - assert(await testCommand.execute({ path: DEFAULT_PATH, network: CUSTOM_NETWORK })); + function stubFileSystemUtils (isFolder) { + return { + '../../../helpers/file-system-util': { + isDir: () => { return isFolder; }, + isFile: () => { return !isFolder; }, + recursivelyReadDir: (dir) => { + return [{ fullPath: `${dir}/fullPath` }]; + } + } + } + } - sinon.assert.calledWith(networkOptionSpy, CUSTOM_NETWORK); - sinon.assert.calledOnce(providerFactorySpy); - sinon.assert.calledOnce(mochaSetDescribeArgs); - }); + it('Should load a test script into the test framework', async () => { + const pathOption = proxyquire('../../cli-commands/commands/test/options/path-option', { + ...stubFileSystemUtils(false) + }); - it('Should throw when invalid network is specified', async () => { - assert(await testCommand.execute({ path: DEFAULT_PATH, network: INVALID_NETWORK })); + await pathOption.process('./custom.js', + { + testFramework: { + addTestFiles: (files) => { assert(files[0] == path.resolve('./', './custom.js')); } + } + } + ); + }); - sinon.assert.calledWith(networkOptionSpy, INVALID_NETWORK); - sinon.assert.notCalled(providerFactorySpy); - }); + it('Should load all test scripts from a folder into the test framework', async () => { + const pathOption = proxyquire('../../cli-commands/commands/test/options/path-option', { + ...stubFileSystemUtils(true) + }); + + await pathOption.process('./custom', + { + testFramework: { + addTestFiles: (files) => { assert(files[0] == './custom/fullPath'); } + } + } + ); + }); + }); + + describe('Network', function () { + const eoslime = require('../../').init(); + + it('Should set eoslime on provided network', async () => { + NetworkOption.process('jungle', { eoslime }); + assert(eoslime.Provider.network.name == 'jungle'); + }); - it('Should execute tests and display resource usage report', async () => { - assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'true' })); + it('Should set eoslime on local network none has been provided', async () => { + NetworkOption.process(undefined, { eoslime }); + assert(eoslime.Provider.network.name == 'local'); + }); + }); - sinon.assert.calledWith(resourceReportOptionSpy, 'true'); + describe('Resources usage report', function () { + + const FAUCET_ABI_PATH = "./tests/testing-contracts/compiled/faucet.abi"; + const FAUCET_WASM_PATH = "./tests/testing-contracts/compiled/faucet.wasm"; + + it('Should show correct data in report', async () => { + const testFramework = new MochaFramework(); + + const waitToPass = new Promise(async (resolve, reject) => { + const contractAccount = await eoslime.Account.createRandom(); + + // The result is a table visualization + // Because of that we are waiting for the table output to check if all good + logger.on('log', () => { + const table = ResourceReportOption.reportTable.table; + assert(JSON.stringify(table.options.head) == JSON.stringify([ + '', + 'Contract', + 'Action', + 'CPU ( MIN | MAX )', + 'NET ( MIN | MAX )', + 'RAM ( MIN | MAX )', + 'Calls' + ])); + + assert(table[1][1] == contractAccount.name); + assert(table[3][1] == contractAccount.name); + assert(table[4][2] == 'withdraw'); + + return resolve(true); + }); + + ResourceReportOption.process(true, { eoslime, testFramework }); + + const contract = await eoslime.Contract.deployOnAccount(FAUCET_WASM_PATH, FAUCET_ABI_PATH, contractAccount); + await contract.withdraw('testtesttest'); + + testFramework.eventsHooks[0].callback(); + }); + + await waitToPass; + }); + }); }); - it('Should execute tests and not display resource usage report', async () => { - assert(await testCommand.execute({ path: DEFAULT_PATH, network: DEFAULT_NETWORK, 'resource-usage': 'false' })); + describe('Command specifics', function () { + + describe('Test utils', function () { + + async function expect (promise, expectCb) { + const testCommand = new TestCommand(class TestFramework { + setDescribeArgs () { } + runTests () { } + }); + + const args = {}; + const waitToPass = new Promise(async (resolve, reject) => { + logger.on('success', async () => { + try { + await args.eoslime.tests[expectCb]( + promise + ) + resolve(true); + } catch (error) { + reject(error); + } + }); + + await testCommand.execute(args); + }); + + await waitToPass; + } + + describe('expectAssert', function () { + + it('Should expectAssert', async () => { + await expect( + new Promise((reject, resolve) => { + throw new Error('eosio_assert_message_exception'); + }), + 'expectAssert' + ); + }); + + it('Should throw in case unexpected error happens', async () => { + try { + await expect( + new Promise((resolve, reject) => { + throw new Error('Another error'); + }), + 'expectAssert' + ); + assert(false); + } catch (error) { + assert(error.message.includes('Expected assert, got \'Another error\' instead')); + } + }); + + it('Should throw in case assert error has not been received', async () => { + try { + await expect( + new Promise((resolve, reject) => { + resolve(true); + }), + 'expectAssert' + ); + assert(false); + } catch (error) { + assert(error.message.includes('Expected assert not received')); + } + }); + }); + + describe('expectMissingAuthority', function () { + it('Should expectMissingAuthority', async () => { + await expect( + new Promise((reject, resolve) => { + throw new Error('missing_auth_exception'); + }), + 'expectMissingAuthority' + ); + }); + + it('Should throw in case unexpected error happens', async () => { + try { + await expect( + new Promise((resolve, reject) => { + throw new Error('Another error'); + }), + 'expectMissingAuthority' + ); + assert(false); + } catch (error) { + assert(error.message.includes('Expected missing authority, got \'Another error\' instead')); + } + }); - sinon.assert.calledWith(resourceReportOptionSpy, 'false'); + it('Should throw in case missing authority error has not been received', async () => { + try { + await expect( + new Promise((resolve, reject) => { + resolve(true); + }), + 'expectMissingAuthority' + ); + assert(false); + } catch (error) { + assert(error.message.includes('Expected missing authority not received')); + } + }); + }); + }); }); - -}); \ No newline at end of file +}); diff --git a/tests/cli-commands/utils/logger.js b/tests/cli-commands/utils/logger.js new file mode 100644 index 0000000..38ad196 --- /dev/null +++ b/tests/cli-commands/utils/logger.js @@ -0,0 +1,55 @@ +const logger = require('../../../cli-commands/common/logger'); + +class Logger { + + constructor () { + this.events = {} + } + + hide (sinon) { + const self = this + sinon.stub(logger, 'log').callsFake((message) => { + if (self.events['log']) { + for (let i = 0; i < self.events['log'].length; i++) { + self.events['log'][i](message) + } + } + }); + + sinon.stub(logger, 'info').callsFake((message) => { + if (self.events['info']) { + for (let i = 0; i < self.events['info'].length; i++) { + self.events['info'][i](message) + } + } + }); + + sinon.stub(logger, 'success').callsFake((message) => { + if (self.events['success']) { + for (let i = 0; i < self.events['success'].length; i++) { + self.events['success'][i](message) + } + } + }); + + sinon.stub(logger, 'error').callsFake((message, error) => { + if (self.events['error']) { + for (let i = 0; i < self.events['error'].length; i++) { + self.events['error'][i](message) + } + } + + // throw new Error(error.message); + }); + } + + on (event, cb) { + if (!this.events[event]) { + this.events[event] = []; + } + + this.events[event].push(cb) + } +} + +module.exports = Logger; diff --git a/tests/contract-tests.js b/tests/contract-tests.js index 15d9c37..d6cf8a8 100644 --- a/tests/contract-tests.js +++ b/tests/contract-tests.js @@ -255,14 +255,14 @@ describe("Contract", function () { describe("Blockchain tables", function () { it("Should have a default table getter", async () => { - const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name); // withdrawers is a table in the contract assert(faucetContract.withdrawers); }); it("Should apply the default query params if none provided", async () => { - const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name); const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor @@ -275,7 +275,7 @@ describe("Contract", function () { }); it("Should query a table", async () => { - const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name, faucetAccount); + const faucetContract = eoslime.Contract.fromFile(FAUCET_ABI_PATH, faucetAccount.name); const tokensHolder = await eoslime.Account.createRandom(); // faucetAccount is the executor diff --git a/tests/helpers-tests.js b/tests/helpers-tests.js index fb21563..b81c3a6 100644 --- a/tests/helpers-tests.js +++ b/tests/helpers-tests.js @@ -1,6 +1,5 @@ -const sinon = require('sinon'); const assert = require('assert'); -const cryptoJS = require('crypto-js'); + const is = require('./../src/helpers/is'); const crypto = require('./../src/helpers/crypto'); const EventClass = require('./../src/helpers/event-class'); @@ -18,26 +17,15 @@ describe('Helpers scripts', function () { assert(expectedHash == resultedHash); }); - it('Should encrypt and decrypt a data', async () => { + it('Should encrypt and decrypt data', async () => { const encryptedData = crypto.encrypt('Hello World', '123'); const decryptedData = crypto.decrypt(encryptedData, '123'); assert(decryptedData == 'Hello World'); }); - it('Should throw if it is not able to hash the data', async () => { - sinon.stub(cryptoJS, "SHA256").throws(); - - try { - crypto.hash(); - } catch (error) { - assert(error.message.includes('Couldn\'t hash the data')); - } - - sinon.restore(); - }); - it('Should throw if it is not able to encrypt a data', async () => { + it('Should throw if it is not able to encrypt data', async () => { try { crypto.encrypt('Hello World', { fake: 'FAKE' }); } catch (error) { @@ -45,7 +33,7 @@ describe('Helpers scripts', function () { } }); - it('Should throw if it is not able to decrypt a data', async () => { + it('Should throw if it is not able to decrypt data', async () => { try { const encryptedData = crypto.encrypt('Hello World', '123'); crypto.decrypt(encryptedData, { fake: 'FAKE' }); @@ -73,19 +61,19 @@ describe('Helpers scripts', function () { assert(x == 2); }); - it('Should not be able to subscribe for unknown event', async () => { - const eventClass = new EventClass({ }); + it('Should be able for more than one subscriber to listen for an event', async () => { + const eventClass = new EventClass({ 'created': 'created' }); + eventClass.on('created', () => { }); eventClass.on('created', () => { }); - assert(eventClass.eventsHooks['created'] == undefined); + assert(eventClass.eventsHooks['created'].length == 2); }); - it('Should be able to subscribe twice for an event', async () => { - const eventClass = new EventClass({ 'created': 'created' }); - eventClass.on('created', () => { }); + it('Should not be able to subscribe for unknown event', async () => { + const eventClass = new EventClass({}); eventClass.on('created', () => { }); - assert(eventClass.eventsHooks['created'].length == 2); + assert(eventClass.eventsHooks['created'] == undefined); }); }); From 7c950c2aad66a5b5fa2b282e669b33938ba6c792 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 19 Aug 2020 22:31:05 +0300 Subject: [PATCH 25/35] Update v2.0.0 Readme and fix some merge bugs --- README.md | 84 ++++++ .../options/resource-usage-option/index.js | 53 ++-- index.d.ts | 23 ++ package.json | 6 +- .../multi-signature-account/account.js | 12 +- src/account/normal-account/account.js | 4 +- .../contract-function/contract-function.js | 1 - tests/cli-commands/deploy-tests.js | 2 +- tests/cli-commands/miscellaneous.js | 1 - tests/cli-commands/test-tests.js | 4 +- tests/testing-contracts/compiled/faucet.abi | 10 + tests/testing-contracts/compiled/faucet.wasm | Bin 12196 -> 12289 bytes tests/testing-contracts/faucet.cpp | 2 + tests/types/account.ts | 2 +- tests/types/provider.ts | 279 ++++++++++++++++++ tests/types/utils/index.ts | 38 +++ tsconfig.json | 27 ++ types/index.d.ts | 5 + types/miscellaneous/index.d.ts | 103 +++++++ types/table-reader/index.d.ts | 52 ++++ 20 files changed, 663 insertions(+), 45 deletions(-) create mode 100644 index.d.ts create mode 100644 tests/types/provider.ts create mode 100644 tests/types/utils/index.ts create mode 100644 tsconfig.json create mode 100644 types/index.d.ts create mode 100644 types/miscellaneous/index.d.ts create mode 100644 types/table-reader/index.d.ts diff --git a/README.md b/README.md index b7d644b..cfed40d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,90 @@ Thanks these wonderful people for helping improve EOSLime # Change log +## Version 2.0.0 change log +## [Typescript support && Codebase code coverage] +### Breaking changes +* Rename **Account.addAuthorityKey** to **Account.addOnBehalfKey** +* Rename **Account.executiveAuth** to **Account.authority** +* New way to access contract actions and tables + **Actions** + ``` + const tokenContract = await eoslime.Contract.at('contract name'); + // Before + tokenContract.issue(params, options) + // Now + tokenContract.actions.issue([params], options) + ``` + **Tables** + ``` + const tokenContract = await eoslime.Contract.at('contract name'); + // Before + tokenContract.balances() + // Now + tokenContract.tables.balances() + ``` +* Contract.on('deploy') + ``` + // Before + Contract.on('deploy', (tx, contract) => {})) + // Now + Contract.on('deploy', (contract, tx) => {})) + ``` +* Remove AuthorityAccount +* Deprecate **Account.createSubAuthority** + ``` + const account = await eoslime.Account.createRandom(); + + // ------------ [ Before ] ------------ + + // Add subAuthority and return an instance of AuthorityAccount + const subAuthorityAccount = await account.createSubAuthority('subauthority'); + + // Add what actions the new authority could access + await subAuthorityAccount.setAuthorityAbilities([ + { action: 'produce', contract: faucetContract.name } + ]); + + // ------------ [ Now ] ------------ + + // Add subAuthority and return tx receipt + const tx = await account.addAuthority('subauthority'); + + // Add what actions the new authority could access + await account.setAuthorityAbilities('subauthority', [ + { action: 'produce', contract: faucetContract.name } + ]); + + const subAuthorityAccount = eoslime.Account.load('name', 'key', 'subauthority'); + ``` + +### News +* Typescript support +* Refactor CLI commands +* Fix nodeos pre-loaded accounts to have different keys +* Unit tests for all CLI commands +* Return transaction receipts on every chain iteraction +* Use logger instead console.log +* Remove the check requiring an executor to be provided on contract instantiation. Without executor, one could fetch only the data from the contract tables +* contract.action.sign(params) + ``` + // Before + contract.action.sign(params) + + // Now + // Options are the same like the ones for contract.action(params, options) + contract.actions.action.sign([params], options) + ``` +* contract.action.getRawTransaction(params) + ``` + // Before + contract.action.getRawTransaction(params) + + // Now + // Options are the same like the ones for contract.action(params, options) + contract.actions.action.getRawTransaction([params], options) + ``` + ## Version 1.0.4 change log * **eoslime nodeos** diff --git a/cli-commands/commands/test/options/resource-usage-option/index.js b/cli-commands/commands/test/options/resource-usage-option/index.js index cece88c..e545701 100644 --- a/cli-commands/commands/test/options/resource-usage-option/index.js +++ b/cli-commands/commands/test/options/resource-usage-option/index.js @@ -81,36 +81,31 @@ const fillDeploymentsResources = function (eoslime, deploymentsResources) { const fillFunctionsResources = function (eoslime, contractsResources) { eoslime.Contract.on('init', (contract) => { - for (const functionName in contract) { - if (contract.hasOwnProperty(functionName)) { - const contractFunction = contract[functionName]; - - if (contractFunction.isTransactional) { - contractFunction.on('processed', (txReceipt, inputParams) => { - const usedResources = extractResourcesCostsFromReceipt(txReceipt); - if (!contractsResources[contract.name]) { - contractsResources[contract.name] = { - functions: {} - } - } - - if (!contractsResources[contract.name].functions[functionName]) { - contractsResources[contract.name].functions[functionName] = { - calls: 0, - ram: [], - cpu: [], - net: [] - } - } - - const functionResources = contractsResources[contract.name].functions[functionName]; - functionResources.ram = getMinMaxPair(functionResources.ram[0], functionResources.ram[1], usedResources.ramCost); - functionResources.cpu = getMinMaxPair(functionResources.cpu[0], functionResources.cpu[1], usedResources.cpuCost); - functionResources.net = getMinMaxPair(functionResources.net[0], functionResources.net[1], usedResources.netCost); - functionResources.calls += 1; - }); + for (const functionName in contract.actions) { + const contractFunction = contract.actions[functionName]; + contractFunction.on('processed', (txReceipt, inputParams) => { + const usedResources = extractResourcesCostsFromReceipt(txReceipt); + if (!contractsResources[contract.name]) { + contractsResources[contract.name] = { + functions: {} + } } - } + + if (!contractsResources[contract.name].functions[functionName]) { + contractsResources[contract.name].functions[functionName] = { + calls: 0, + ram: [], + cpu: [], + net: [] + } + } + + const functionResources = contractsResources[contract.name].functions[functionName]; + functionResources.ram = getMinMaxPair(functionResources.ram[0], functionResources.ram[1], usedResources.ramCost); + functionResources.cpu = getMinMaxPair(functionResources.cpu[0], functionResources.cpu[1], usedResources.cpuCost); + functionResources.net = getMinMaxPair(functionResources.net[0], functionResources.net[1], usedResources.netCost); + functionResources.calls += 1; + }); } }); } diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..cb77958 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,23 @@ +// Type definitions for EOSLime +// Project: https://github.com/LimeChain/eoslime +// Definitions by: Lyubomir Kiprov +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +import { utils } from './types/utils'; +import { ContractFactory } from './types/contract'; +import { Provider, NetworkDetails } from './types/provider'; +import { AccountFactory, MultiSignatureFactory } from './types/account'; + +interface EOSLime { + utils: utils; + Provider: Provider; + Account: AccountFactory; + Contract: ContractFactory; + MultiSigAccount: MultiSignatureFactory; +} + +export const utils: utils; +export const NETWORKS: Array; + +export function init (network?: string, config?: NetworkDetails): EOSLime; +export function init (config?: NetworkDetails): EOSLime; diff --git a/package.json b/package.json index c2d7376..3b2e569 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,11 @@ "version": "2.0.0", "description": "eoslime is an EOS development and deployment framework based on eosjs.js", "main": "index.js", + "types": "index.d.ts", "scripts": { "build": "tsc", - "test": "bash ./tests/testing-contracts/compile.sh && nyc --check-coverage mocha './tests/**/*.js'", - "test-dev": "bash ./tests/testing-contracts/compile.sh && mocha './tests/*.js'", - "test-types": "bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/*.ts", + "test": "tsc&& bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/*.ts && nyc --check-coverage mocha './tests/**/*.js'", + "test-dev": "tsc && bash ./tests/testing-contracts/compile.sh && mocha './tests/**/*.js' && mocha -r ts-node/register tests/**/*.ts", "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" }, "author": "Lyubomir Kiprov (Limechain)", diff --git a/src/account/multi-signature-account/account.js b/src/account/multi-signature-account/account.js index 0d26252..1e14040 100644 --- a/src/account/multi-signature-account/account.js +++ b/src/account/multi-signature-account/account.js @@ -3,27 +3,27 @@ const BaseAccount = require('../base-account'); class MultiSignatureAccount extends BaseAccount { - constructor(name, privateKey, provider, authority) { + constructor (name, privateKey, provider, authority) { super(name, privateKey, provider, authority) this.accounts = []; this.proposals = {}; } - loadKeys(privateKeys) { + loadKeys (privateKeys) { for (let i = 0; i < privateKeys.length; i++) { this.accounts.push(new BaseAccount(this.name, privateKeys[i], this.provider, this.authority.permission)); } } - loadAccounts(accounts) { + loadAccounts (accounts) { for (let i = 0; i < accounts.length; i++) { is(accounts[i]).instanceOf('BaseAccount'); this.accounts.push(accounts[i]); } } - async propose(contractAction, actionData) { + async propose (contractAction, actionData) { is(contractAction).instanceOf('ContractFunction'); const actionTx = await contractAction.sign(actionData, { from: this }); @@ -33,7 +33,7 @@ class MultiSignatureAccount extends BaseAccount { return proposalId; } - async approve(publicKey, proposalId) { + async approve (publicKey, proposalId) { const approver = this.accounts.find((account) => { return account.publicKey == publicKey }); requireExistingApprover(approver); requireExistingProposal(this.proposals, proposalId); @@ -47,7 +47,7 @@ class MultiSignatureAccount extends BaseAccount { proposalTx.signatures.push(approverSignedTx.transaction.signatures[0]); } - async processProposal(proposalId) { + async processProposal (proposalId) { requireExistingProposal(this.proposals, proposalId); await requireEnoughApprovals(this, this.proposals[proposalId]); diff --git a/src/account/normal-account/account.js b/src/account/normal-account/account.js index 9cf2529..45bc617 100644 --- a/src/account/normal-account/account.js +++ b/src/account/normal-account/account.js @@ -142,7 +142,7 @@ class Account extends BaseAccount { } const updateAuthority = async function (authorityName, parent, auth) { - await this.provider.eos.transaction(tr => { + const txReceipt = await this.provider.eos.transaction(tr => { tr.updateauth({ account: this.name, permission: authorityName, @@ -152,6 +152,8 @@ const updateAuthority = async function (authorityName, parent, auth) { }, { broadcast: true, sign: true, keyProvider: this.privateKey }); + return txReceipt; + } module.exports = Account; diff --git a/src/contract/contract-function/contract-function.js b/src/contract/contract-function/contract-function.js index b6b8171..c71e56f 100644 --- a/src/contract/contract-function/contract-function.js +++ b/src/contract/contract-function/contract-function.js @@ -14,7 +14,6 @@ class ContractFunction extends EventClass { this.contract = contract; this.functionName = functionName; this.functionFields = functionFields; - this.isTransactional = true; } async broadcast (params, options) { diff --git a/tests/cli-commands/deploy-tests.js b/tests/cli-commands/deploy-tests.js index e87b2db..134873a 100644 --- a/tests/cli-commands/deploy-tests.js +++ b/tests/cli-commands/deploy-tests.js @@ -192,7 +192,7 @@ describe('Deploy Command', function () { assert(deployer.constructor.name == 'Account'); assert(deployer.name == 'name'); assert(deployer.privateKey == '5KieRy975NgHk5XQfn8r6o3pcqJDF2vpeV9bDiuB5uF4xKCTwRF'); - assert(deployer.executiveAuthority.permission == 'active'); + assert(deployer.authority.permission == 'active'); }); }); }); diff --git a/tests/cli-commands/miscellaneous.js b/tests/cli-commands/miscellaneous.js index dbf4608..bf0e012 100644 --- a/tests/cli-commands/miscellaneous.js +++ b/tests/cli-commands/miscellaneous.js @@ -82,7 +82,6 @@ describe('Miscellaneous', function () { it('Should visualize the table', async () => { testLogger.hide(sinon); testLogger.on('log', (table) => { - console.log(table) assert(table.includes('sectionName')); assert(table.includes('row')); }); diff --git a/tests/cli-commands/test-tests.js b/tests/cli-commands/test-tests.js index 8bf1c66..0c82748 100644 --- a/tests/cli-commands/test-tests.js +++ b/tests/cli-commands/test-tests.js @@ -173,7 +173,7 @@ describe('Test Command', function () { assert(table[1][1] == contractAccount.name); assert(table[3][1] == contractAccount.name); - assert(table[4][2] == 'withdraw'); + assert(table[4][2] == 'test'); return resolve(true); }); @@ -181,7 +181,7 @@ describe('Test Command', function () { ResourceReportOption.process(true, { eoslime, testFramework }); const contract = await eoslime.Contract.deployOnAccount(FAUCET_WASM_PATH, FAUCET_ABI_PATH, contractAccount); - await contract.withdraw('testtesttest'); + await contract.actions.test(); testFramework.eventsHooks[0].callback(); }); diff --git a/tests/testing-contracts/compiled/faucet.abi b/tests/testing-contracts/compiled/faucet.abi index a4c8c59..02cd667 100644 --- a/tests/testing-contracts/compiled/faucet.abi +++ b/tests/testing-contracts/compiled/faucet.abi @@ -25,6 +25,11 @@ } ] }, + { + "name": "test", + "base": "", + "fields": [] + }, { "name": "withdraw", "base": "", @@ -64,6 +69,11 @@ "type": "produce", "ricardian_contract": "" }, + { + "name": "test", + "type": "test", + "ricardian_contract": "" + }, { "name": "withdraw", "type": "withdraw", diff --git a/tests/testing-contracts/compiled/faucet.wasm b/tests/testing-contracts/compiled/faucet.wasm index a6c4179e05e84faeac4dfaf174470e63e472cd82..5d5fd646c8ed11e09589c1f747981791791e1bf9 100755 GIT binary patch delta 224 zcmZ1y-pupnTFqu(F zj+04&ML|3 { + // Local + const localProvider = init().Provider; + assert(JSON.stringify(localProvider.network) == JSON.stringify(Networks.local)); + + // Jungle + const jungleProvider = init('jungle').Provider; + assert(JSON.stringify(jungleProvider.network) == JSON.stringify(Networks.jungle)); + + // Worbli + const worbliProvider = init('worbli').Provider; + assert(JSON.stringify(worbliProvider.network) == JSON.stringify(Networks.worbli)); + + // Main + const mainProvider = init('main').Provider; + assert(JSON.stringify(mainProvider.network) == JSON.stringify(Networks.main)); + + // Bos + const bosProvider = init('bos').Provider; + assert(JSON.stringify(bosProvider.network) == JSON.stringify(Networks.bos)); + + // Kylin + const kylinProvider = init('kylin').Provider; + assert(JSON.stringify(kylinProvider.network) == JSON.stringify(Networks.kylin)); + + // Custom + const customProvider = init({ url: Networks.custom.url, chainId: Networks.custom.chainId }).Provider; + assert(JSON.stringify(customProvider.network) == JSON.stringify(Networks.custom)); + }); + + it('Should instantiate with a correct Provider from provided connection', async () => { + + // Local + const localProvider = init('local', { url: Networks.custom.url }).Provider; + assert(JSON.stringify(localProvider.network.name) == JSON.stringify(Networks.local.name)); + assert(JSON.stringify(localProvider.network.chainId) == JSON.stringify(Networks.local.chainId)); + assert(JSON.stringify(localProvider.network.url) == JSON.stringify(Networks.custom.url)); + + // Jungle + const jungleProvider = init('jungle', { chainId: Networks.custom.chainId }).Provider; + assert(JSON.stringify(jungleProvider.network.name) == JSON.stringify(Networks.jungle.name)); + assert(JSON.stringify(jungleProvider.network.url) == JSON.stringify(Networks.jungle.url)); + assert(jungleProvider.network.chainId == Networks.custom.chainId); + + // Worbli + const worbliProvider = init('worbli', { url: Networks.custom.url }).Provider; + assert(JSON.stringify(worbliProvider.network.name) == JSON.stringify(Networks.worbli.name)); + assert(JSON.stringify(worbliProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(worbliProvider.network.chainId) == JSON.stringify(Networks.worbli.chainId)); + + // Main + const mainProvider = init('main', { url: Networks.custom.url }).Provider; + assert(JSON.stringify(mainProvider.network.name) == JSON.stringify(Networks.main.name)); + assert(JSON.stringify(mainProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(mainProvider.network.chainId) == JSON.stringify(Networks.main.chainId)); + + // Bos + const bosProvider = init('bos', { url: Networks.custom.url }).Provider; + assert(JSON.stringify(bosProvider.network.name) == JSON.stringify(Networks.bos.name)); + assert(JSON.stringify(bosProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(bosProvider.network.chainId) == JSON.stringify(Networks.bos.chainId)); + + // Kylin + const kylinProvider = init('kylin', { url: Networks.custom.url }).Provider; + assert(JSON.stringify(kylinProvider.network.name) == JSON.stringify(Networks.kylin.name)); + assert(JSON.stringify(kylinProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(kylinProvider.network.chainId) == JSON.stringify(Networks.kylin.chainId)); + }); + }); + + describe('Create Provider', function () { + it('Should be able to create a new Provider with default connection', async () => { + // Local + const localProvider = new Provider('local'); + assert(JSON.stringify(localProvider.network) == JSON.stringify(Networks.local)); + + // Jungle + const jungleProvider = new Provider('jungle'); + assert(JSON.stringify(jungleProvider.network) == JSON.stringify(Networks.jungle)); + + // Worbli + const worbliProvider = new Provider('worbli'); + assert(JSON.stringify(worbliProvider.network) == JSON.stringify(Networks.worbli)); + + // Main + const mainProvider = new Provider('main'); + assert(JSON.stringify(mainProvider.network) == JSON.stringify(Networks.main)); + + // Bos + const bosProvider = new Provider('bos'); + assert(JSON.stringify(bosProvider.network) == JSON.stringify(Networks.bos)); + + // Kylin + const kylinProvider = new Provider('kylin'); + assert(JSON.stringify(kylinProvider.network) == JSON.stringify(Networks.kylin)); + + // Custom + const customProvider = new Provider({ url: Networks.custom.url, chainId: Networks.custom.chainId }); + assert(JSON.stringify(customProvider.network) == JSON.stringify(Networks.custom)); + }); + + it('Should be able to create a new Provider from connection', async () => { + // Local + const localProvider = new Provider('local', { url: Networks.custom.url }); + assert(JSON.stringify(localProvider.network.name) == JSON.stringify(Networks.local.name)); + assert(JSON.stringify(localProvider.network.chainId) == JSON.stringify(Networks.local.chainId)); + assert(JSON.stringify(localProvider.network.url) == JSON.stringify(Networks.custom.url)); + + // Jungle + const jungleProvider = new Provider('jungle', { chainId: Networks.custom.chainId }); + assert(JSON.stringify(jungleProvider.network.name) == JSON.stringify(Networks.jungle.name)); + assert(JSON.stringify(jungleProvider.network.url) == JSON.stringify(Networks.jungle.url)); + assert(jungleProvider.network.chainId == Networks.custom.chainId); + + // Worbli + const worbliProvider = new Provider('worbli', { url: Networks.custom.url }); + assert(JSON.stringify(worbliProvider.network.name) == JSON.stringify(Networks.worbli.name)); + assert(JSON.stringify(worbliProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(worbliProvider.network.chainId) == JSON.stringify(Networks.worbli.chainId)); + + // Main + const mainProvider = new Provider('main', { url: Networks.custom.url }); + assert(JSON.stringify(mainProvider.network.name) == JSON.stringify(Networks.main.name)); + assert(JSON.stringify(mainProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(mainProvider.network.chainId) == JSON.stringify(Networks.main.chainId)); + + // Bos + const bosProvider = new Provider('bos', { url: Networks.custom.url }); + assert(JSON.stringify(bosProvider.network.name) == JSON.stringify(Networks.bos.name)); + assert(JSON.stringify(bosProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(bosProvider.network.chainId) == JSON.stringify(Networks.bos.chainId)); + + // Kylin + const kylinProvider = new Provider('kylin', { url: Networks.custom.url }); + assert(JSON.stringify(kylinProvider.network.name) == JSON.stringify(Networks.kylin.name)); + assert(JSON.stringify(kylinProvider.network.url) == JSON.stringify(Networks.custom.url)); + assert(JSON.stringify(kylinProvider.network.chainId) == JSON.stringify(Networks.kylin.chainId)); + }); + }); + + describe('Reset provider', function () { + it('Should be able to reset the provider', async () => { + const jungleProvider = new Provider('jungle'); + Provider.reset(jungleProvider); + + assert(JSON.stringify(Provider.network) == JSON.stringify(Networks.jungle)); + }); + }); + + describe('Retrieve table [Table Reader]', function () { + + describe("Select Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table'); + assert(typeof query.from == 'function'); + }); + }); + + describe("From Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table').from('from'); + assert(typeof query.scope == 'function'); + assert(typeof query.equal == 'function'); + assert(typeof query.range == 'function'); + assert(typeof query.limit == 'function'); + assert(typeof query.index == 'function'); + assert(typeof query.find == 'function'); + }); + }); + + describe("Scope Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table').from('from').scope('scope'); + assert(typeof query.equal == 'function'); + assert(typeof query.range == 'function'); + assert(typeof query.limit == 'function'); + assert(typeof query.index == 'function'); + assert(typeof query.find == 'function'); + }); + }); + + describe("Equal Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table').from('from').scope('scope').equal('equal'); + assert(typeof query.limit == 'function'); + assert(typeof query.index == 'function'); + assert(typeof query.find == 'function'); + }); + }); + + describe("Range Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table').from('from').scope('scope').range(0, 1); + assert(typeof query.limit == 'function'); + assert(typeof query.index == 'function'); + assert(typeof query.find == 'function'); + }); + }); + + describe("Limit Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table').from('from').scope('scope').limit(10); + assert(typeof query.equal == 'function'); + assert(typeof query.range == 'function'); + assert(typeof query.index == 'function'); + assert(typeof query.find == 'function'); + }); + }); + + describe("Index Query", function () { + it("Should have correct chain of functions", async () => { + const query = Provider.select('table').from('from').scope('scope').index(2); + assert(typeof query.equal == 'function'); + assert(typeof query.range == 'function'); + assert(typeof query.limit == 'function'); + assert(typeof query.find == 'function'); + }); + }); + }); + + describe('Retrieve contract ABI', function () { + it('Should retrieve contract ABI', async () => { + await Provider.getABI('eosio'); + }); + }); + + describe('Retrieve contract raw WASM', function () { + it('Should retrieve contract raw WASM', async () => { + await Provider.getRawWASM('eosio'); + }); + }); +}); diff --git a/tests/types/utils/index.ts b/tests/types/utils/index.ts new file mode 100644 index 0000000..3a6e6a2 --- /dev/null +++ b/tests/types/utils/index.ts @@ -0,0 +1,38 @@ +import assert from 'assert'; + +import { TransactionResult, RawTransaction, SignedTransaction } from '../../../types'; + +export function assertRawTransaction (rawTransaction: RawTransaction): void { + assert(rawTransaction.actions !== undefined); + assert(rawTransaction.delay_sec !== undefined); + assert(rawTransaction.expiration !== undefined); + assert(rawTransaction.ref_block_num !== undefined); + assert(rawTransaction.max_cpu_usage_ms !== undefined); + assert(rawTransaction.ref_block_prefix !== undefined); + assert(rawTransaction.max_net_usage_words !== undefined); + assert(rawTransaction.context_free_actions !== undefined); +} + +export function assertSignedAction (signedTx: SignedTransaction): void { + assert(signedTx.compression !== undefined); + assert(signedTx.signatures.length > 0); + assertRawTransaction(signedTx.transaction); +} + +export function assertTransactionResult (txResult: TransactionResult): void { + assert(txResult.broadcast !== undefined); + assert(txResult.transaction_id !== undefined); + assert(txResult.processed.id !== undefined); + assert(txResult.processed.block_num !== undefined); + assert(txResult.processed.block_time !== undefined); + assert(txResult.processed.producer_block_id !== undefined); + assert(txResult.processed.receipt !== undefined); + assert(txResult.processed.elapsed !== undefined); + assert(txResult.processed.net_usage !== undefined); + assert(txResult.processed.scheduled !== undefined); + assert(txResult.processed.action_traces !== undefined); + assert(txResult.processed.account_ram_delta !== undefined); + assert(txResult.processed.except !== undefined); + assert(txResult.processed.error_code !== undefined); + assertSignedAction(txResult.transaction); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3092dbc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "suppressImplicitAnyIndexErrors": true, + "target": "es6", + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + "baseUrl": ".", + "paths": { + "*": [ + "node_modules/*", + "types/*" + ] + }, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": [ + "./index.d.ts", + "./types/**/*" + ] +} \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..6d339da --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,5 @@ +export * from './account'; +export * from './contract'; +export * from './provider'; +export * from './miscellaneous'; +export * from './utils'; diff --git a/types/miscellaneous/index.d.ts b/types/miscellaneous/index.d.ts new file mode 100644 index 0000000..3ac9b0c --- /dev/null +++ b/types/miscellaneous/index.d.ts @@ -0,0 +1,103 @@ +interface TxActionTrace { + action_ordinal: number; + creator_action_ordinal: number; + closest_unnotified_ancestor_action_ordinal: number; + receipt: TxReceipt; + receiver: string; + act: TxAction; + context_free: boolean; + elapsed: number; + console: string; + trx_id: string; + block_num: number; + block_time: string; + producer_block_id: string; + account_ram_deltas: Array; + except: string; + error_code: string; + inline_traces: TxActionTrace +} + +interface TxReceipt { + status: string; + cpu_usage_us: number; + net_usage_words: number; +} + +interface TxAction { + account: string; + name: string; + authorization: [ + { + actor: string; + permission: string; + } + ]; + data: string; +} + +export interface RawTransaction { + expiration: string; + ref_block_num: number; + ref_block_prefix: number; + max_net_usage_words: number; + max_cpu_usage_ms: number; + delay_sec: number; + context_free_actions: Array; + actions: Array; + transaction_extensions: Array; +} + +export interface SignedTransaction { + compression: string; + transaction: RawTransaction; + signatures: Array; +} + +export interface BroadCastedTransaction { + transaction_id: string; + processed: + { + id: string; + block_num: number; + block_time: string; + producer_block_id: string; + receipt: TxReceipt; + elapsed: number; + net_usage: number; + scheduled: boolean; + action_traces: Array; + account_ram_delta: string; + except: string; + error_code: string; + } +} + +export interface TransactionResult extends BroadCastedTransaction { + broadcast: boolean; + transaction: SignedTransaction; +} + +interface TxAuthorityKey { + key: string; + weight: number; +} + +interface AuthAccount { + permission: { + actor: string; + permission: string + }; + weight: number; +} + +export interface AuthorityDetails { + perm_name: string; + parent: string; + required_auth: { + threshold: number; + keys: Array; + accounts: Array; + waits: Array; + } +} \ No newline at end of file diff --git a/types/table-reader/index.d.ts b/types/table-reader/index.d.ts new file mode 100644 index 0000000..7cb5f71 --- /dev/null +++ b/types/table-reader/index.d.ts @@ -0,0 +1,52 @@ +export class SelectQuery { + from (contractName: string): FromQuery; +} + +export class FromQuery { + equal (value: any): EqualQuery; + limit (limit: number): LimitQuery; + scope (accountName: string): ScopeQuery; + range (minValue: any, maxValue: any): RangeQuery; + index (index: number, keyType?: string): IndexQuery; + + find (): Promise>; +} + +export class ScopeQuery { + equal (value: any): EqualQuery; + limit (limit: number): LimitQuery; + range (minValue: any, maxValue: any): RangeQuery; + index (index: number, keyType?: string): IndexQuery; + + find (): Promise>; +} + +export class EqualQuery { + limit (limit: number): LimitQuery; + index (index: number, keyType: string): IndexQuery; + + find (): Promise>; +} + +export class RangeQuery { + limit (limit: number): LimitQuery; + index (index: number, keyType: string): IndexQuery; + + find (): Promise>; +} + +export class LimitQuery { + equal (value: any): EqualQuery; + range (minValue: any, maxValue: any): RangeQuery; + index (index: number, keyType: string): IndexQuery; + + find (): Promise>; +} + +export class IndexQuery { + equal (value: any): EqualQuery; + limit (limit: number): LimitQuery; + range (minValue: any, maxValue: any): RangeQuery; + + find (): Promise>; +} \ No newline at end of file From ca0cb212ba1a8bcfac456bb419105dfbb3f4408c Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Tue, 1 Sep 2020 14:18:53 +0300 Subject: [PATCH 26/35] Add Typescript support badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cfed40d..c08bb89 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![npm version](https://badge.fury.io/js/eoslime.svg)](https://badge.fury.io/js/eoslime.svg) [![codecov](https://codecov.io/gh/LimeChain/eoslime/branch/master/graph/badge.svg)](https://codecov.io/gh/LimeChain/eoslime) +[![support typescript](https://badgen.net/badge/Support/TypeScript/cyan)](https://badgen.net/badge/Support/TypeScript/cyan) eoslime.js ============ From ee9f232a809377e60bf21eedc488ded09b1c1bc3 Mon Sep 17 00:00:00 2001 From: prc Date: Tue, 1 Sep 2020 13:34:56 +0100 Subject: [PATCH 27/35] added jungle3 network provider and included it in provider factory --- src/network-providers/jungle3-provider.js | 15 +++++++++++++++ src/network-providers/provider-factory.js | 2 ++ 2 files changed, 17 insertions(+) create mode 100644 src/network-providers/jungle3-provider.js diff --git a/src/network-providers/jungle3-provider.js b/src/network-providers/jungle3-provider.js new file mode 100644 index 0000000..6bb0f31 --- /dev/null +++ b/src/network-providers/jungle3-provider.js @@ -0,0 +1,15 @@ +const BaseProvider = require('./base-provider'); + +const Jungle3NetworkConfig = { + name: 'jungle3', + url: 'https://jungle3.cryptolions.io', + chainId: '2a02a0053e5a8cf73a56ba0fda11e4d92e0238a4a2aa74fccf46d5a910746840' +} + +class Jungle3Provider extends BaseProvider { + constructor(networkConfig) { + super(Object.assign({}, Jungle3NetworkConfig, networkConfig)) + } +} + +module.exports = Jungle3Provider diff --git a/src/network-providers/provider-factory.js b/src/network-providers/provider-factory.js index 418095c..171d031 100644 --- a/src/network-providers/provider-factory.js +++ b/src/network-providers/provider-factory.js @@ -5,6 +5,7 @@ const MainProvider = require('./main-provider'); const KylinProvider = require('./kylin-provider'); const LocalProvider = require('./local-provider'); const JungleProvider = require('./jungle-provider'); +const Jungle3Provider = require('./jungle3-provider'); const WorbliProvider = require('./worbli-provider'); const CustomProvider = require('./custom-provider'); @@ -14,6 +15,7 @@ const PROVIDERS = { kylin: (networkConfig) => { return new KylinProvider(networkConfig) }, local: (networkConfig) => { return new LocalProvider(networkConfig) }, jungle: (networkConfig) => { return new JungleProvider(networkConfig) }, + jungle3: (networkConfig) => { return new Jungle3Provider(networkConfig) }, worbli: (networkConfig) => { return new WorbliProvider(networkConfig) }, custom: (networkConfig) => { return new CustomProvider(networkConfig) } } From 24ebcf9765dd4062c5703267d629b365ef850220 Mon Sep 17 00:00:00 2001 From: prc Date: Tue, 1 Sep 2020 13:46:52 +0100 Subject: [PATCH 28/35] updated kylin network provider url due to eoscanada shutdown --- src/network-providers/kylin-provider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network-providers/kylin-provider.js b/src/network-providers/kylin-provider.js index 9c99cda..7ef9497 100644 --- a/src/network-providers/kylin-provider.js +++ b/src/network-providers/kylin-provider.js @@ -3,7 +3,7 @@ const BaseProvider = require('./base-provider'); const KylinNetworkConfig = { name: 'kylin', - url: 'https://kylin.eoscanada.com', + url: 'https://api.kylin.alohaeos.com', chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191' } From f55a5605fa50be54f41ce204e7e82bc51aab9665 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 2 Sep 2020 09:01:15 +0300 Subject: [PATCH 29/35] A Pedro as a contributor and add his code to release v2.0.0 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c08bb89..8742591 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ Thanks these wonderful people for helping improve EOSLime
Artem

💡 +
Pedro Reis Colaço

+ 💻 + @@ -95,6 +98,8 @@ Thanks these wonderful people for helping improve EOSLime * Unit tests for all CLI commands * Return transaction receipts on every chain iteraction * Use logger instead console.log +* Update Kylin network endpoint +* Add Jungle3 support * Remove the check requiring an executor to be provided on contract instantiation. Without executor, one could fetch only the data from the contract tables * contract.action.sign(params) ``` From c7a706a45467e1fd714891c5db82d1293d1afabc Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 2 Sep 2020 09:04:34 +0300 Subject: [PATCH 30/35] Fix a bit readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8742591..809abc0 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Thanks these wonderful people for helping improve EOSLime ### Breaking changes * Rename **Account.addAuthorityKey** to **Account.addOnBehalfKey** * Rename **Account.executiveAuth** to **Account.authority** -* New way to access contract actions and tables +* New way to access contract actions and tables **Actions** ``` const tokenContract = await eoslime.Contract.at('contract name'); @@ -65,6 +65,7 @@ Thanks these wonderful people for helping improve EOSLime ``` * Remove AuthorityAccount * Deprecate **Account.createSubAuthority** +* Replace **createSubAuthority** with **addAuthority** ``` const account = await eoslime.Account.createRandom(); From 3675837fed7dc85b1f81b2be71ad222e4092889b Mon Sep 17 00:00:00 2001 From: prc Date: Wed, 2 Sep 2020 08:49:00 +0100 Subject: [PATCH 31/35] changed url in unit tests too --- tests/providers-tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/providers-tests.js b/tests/providers-tests.js index bdd70fc..8e30144 100644 --- a/tests/providers-tests.js +++ b/tests/providers-tests.js @@ -34,7 +34,7 @@ const Networks = { }, kylin: { name: 'kylin', - url: 'https://kylin.eoscanada.com', + url: 'https://api.kylin.alohaeos.com', chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191' }, custom: { From 23ea3734091d07dc033625cb5138228d0d969db6 Mon Sep 17 00:00:00 2001 From: prc Date: Wed, 2 Sep 2020 08:49:00 +0100 Subject: [PATCH 32/35] changed url in unit tests too --- tests/providers-tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/providers-tests.js b/tests/providers-tests.js index bdd70fc..8e30144 100644 --- a/tests/providers-tests.js +++ b/tests/providers-tests.js @@ -34,7 +34,7 @@ const Networks = { }, kylin: { name: 'kylin', - url: 'https://kylin.eoscanada.com', + url: 'https://api.kylin.alohaeos.com', chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191' }, custom: { From ffa33defaab01b7b80cd5f0831198885f6ce9895 Mon Sep 17 00:00:00 2001 From: prc Date: Tue, 1 Sep 2020 13:46:52 +0100 Subject: [PATCH 33/35] updated kylin network provider url due to eoscanada shutdown --- src/network-providers/kylin-provider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network-providers/kylin-provider.js b/src/network-providers/kylin-provider.js index 9c99cda..7ef9497 100644 --- a/src/network-providers/kylin-provider.js +++ b/src/network-providers/kylin-provider.js @@ -3,7 +3,7 @@ const BaseProvider = require('./base-provider'); const KylinNetworkConfig = { name: 'kylin', - url: 'https://kylin.eoscanada.com', + url: 'https://api.kylin.alohaeos.com', chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191' } From 955707d6c716f2067b3a310b39db5006be39e2dc Mon Sep 17 00:00:00 2001 From: prc Date: Wed, 2 Sep 2020 09:10:31 +0100 Subject: [PATCH 34/35] added jungle3 unit tests --- tests/providers-tests.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/providers-tests.js b/tests/providers-tests.js index 8e30144..a8d4b91 100644 --- a/tests/providers-tests.js +++ b/tests/providers-tests.js @@ -27,6 +27,11 @@ const Networks = { url: 'https://jungle2.cryptolions.io', chainId: 'e70aaab8997e1dfce58fbfac80cbbb8fecec7b99cf982a9444273cbc64c41473' }, + jungle3: { + name: 'jungle3', + url: 'https://jungle3.cryptolions.io', + chainId: '2a02a0053e5a8cf73a56ba0fda11e4d92e0238a4a2aa74fccf46d5a910746840' + }, main: { name: 'main', url: 'https://eos.greymass.com', @@ -66,6 +71,10 @@ describe('Providers', function () { const jungleProvider = eoslime.init('jungle').Provider; assert(JSON.stringify(jungleProvider.network) == JSON.stringify(Networks.jungle)); + // Jungle3 + const jungle3Provider = eoslime.init('jungle3').Provider; + assert(JSON.stringify(jungle3Provider.network) == JSON.stringify(Networks.jungle3)); + // Worbli const worbliProvider = eoslime.init('worbli').Provider; assert(JSON.stringify(worbliProvider.network) == JSON.stringify(Networks.worbli)); @@ -101,6 +110,12 @@ describe('Providers', function () { assert(JSON.stringify(jungleProvider.network.url) == JSON.stringify(Networks.jungle.url)); assert(jungleProvider.network.chainId == Networks.custom.chainId); + // Jungle3 + const jungle3Provider = eoslime.init('jungle3', { chainId: Networks.custom.chainId }).Provider; + assert(JSON.stringify(jungle3Provider.network.name) == JSON.stringify(Networks.jungle3.name)); + assert(JSON.stringify(jungle3Provider.network.url) == JSON.stringify(Networks.jungle3.url)); + assert(jungle3Provider.network.chainId == Networks.custom.chainId); + // Worbli const worbliProvider = eoslime.init('worbli', { url: Networks.custom.url }).Provider; assert(JSON.stringify(worbliProvider.network.name) == JSON.stringify(Networks.worbli.name)); @@ -147,6 +162,10 @@ describe('Providers', function () { const jungleProvider = new eoslimeInstance.Provider('jungle'); assert(JSON.stringify(jungleProvider.network) == JSON.stringify(Networks.jungle)); + // Jungle3 + const jungle3Provider = new eoslimeInstance.Provider('jungle3'); + assert(JSON.stringify(jungle3Provider.network) == JSON.stringify(Networks.jungle3)); + // Worbli const worbliProvider = new eoslimeInstance.Provider('worbli'); assert(JSON.stringify(worbliProvider.network) == JSON.stringify(Networks.worbli)); @@ -183,6 +202,12 @@ describe('Providers', function () { assert(JSON.stringify(jungleProvider.network.url) == JSON.stringify(Networks.jungle.url)); assert(jungleProvider.network.chainId == Networks.custom.chainId); + // Jungle3 + const jungle3Provider = new eoslimeInstance.Provider('jungle3', { chainId: Networks.custom.chainId }); + assert(JSON.stringify(jungle3Provider.network.name) == JSON.stringify(Networks.jungle3.name)); + assert(JSON.stringify(jungle3Provider.network.url) == JSON.stringify(Networks.jungle3.url)); + assert(jungle3Provider.network.chainId == Networks.custom.chainId); + // Worbli const worbliProvider = new eoslimeInstance.Provider('worbli', { url: Networks.custom.url }); assert(JSON.stringify(worbliProvider.network.name) == JSON.stringify(Networks.worbli.name)); From 740af63b0b792de31dcb6251d6ffd17c28766399 Mon Sep 17 00:00:00 2001 From: bakasura980 Date: Wed, 2 Sep 2020 11:46:11 +0300 Subject: [PATCH 35/35] Chenge mocha execution order of unit tests --- nyc.config.js | 3 ++- package.json | 14 +++++++------- tests/account-tests.js | 2 +- tests/types/provider.ts | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/nyc.config.js b/nyc.config.js index 98bbcd0..7feeb4d 100644 --- a/nyc.config.js +++ b/nyc.config.js @@ -3,6 +3,7 @@ module.exports = { "**/*.js" ], "exclude": [ - "tests/**/*.js" + "tests/**/*.js", + "cli-commands/commands/test/specific/test-frameworks/mocha/index.js" ] } \ No newline at end of file diff --git a/package.json b/package.json index 3b2e569..796ac67 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "types": "index.d.ts", "scripts": { "build": "tsc", - "test": "tsc&& bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/*.ts && nyc --check-coverage mocha './tests/**/*.js'", - "test-dev": "tsc && bash ./tests/testing-contracts/compile.sh && mocha './tests/**/*.js' && mocha -r ts-node/register tests/**/*.ts", + "test": "nyc --check-coverage mocha './tests/**/*.js' && tsc&& bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/*.ts", + "test-dev": "mocha './tests/**/*.js'&& tsc && bash ./tests/testing-contracts/compile.sh && mocha -r ts-node/register tests/**/*.ts", "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" }, "author": "Lyubomir Kiprov (Limechain)", @@ -23,15 +23,15 @@ "yargs": "12.0.5" }, "devDependencies": { - "@types/mocha": "^7.0.2", "@types/assert": "^1.5.1", + "@types/mocha": "^7.0.2", "assert": "^2.0.0", + "mocha": "^5.2.0", "nyc": "14.1.1", - "ts-node": "^8.10.2", - "typescript": "^3.9.5", - "mocha": "5.2.0", + "proxyquire": "^2.1.3", "sinon": "^9.0.2", - "proxyquire": "^2.1.3" + "ts-node": "^8.10.2", + "typescript": "^3.9.5" }, "keywords": [ "lime", diff --git a/tests/account-tests.js b/tests/account-tests.js index f9336b0..f9eb713 100644 --- a/tests/account-tests.js +++ b/tests/account-tests.js @@ -37,7 +37,7 @@ const Networks = { }, kylin: { name: 'kylin', - url: 'https://kylin.eoscanada.com', + url: 'https://api.kylin.alohaeos.com', chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191' }, custom: { diff --git a/tests/types/provider.ts b/tests/types/provider.ts index 0cf6030..1e7c3fc 100644 --- a/tests/types/provider.ts +++ b/tests/types/provider.ts @@ -34,7 +34,7 @@ describe('Provider', function () { }, kylin: { name: 'kylin', - url: 'https://kylin.eoscanada.com', + url: 'https://api.kylin.alohaeos.com', chainId: '5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191' }, custom: {