From a0e7f39c691141565cd41a7b540db840457a1681 Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 20 Dec 2018 14:51:27 +0300 Subject: [PATCH] 1.0.31 Release * fix(bug) fixed system microservices (HAL, bluetooth) launch (EWC-413) (#424) * fix(bug) added registry email validation (EWC-418) * test(core) unit tests for iofog-controller (EWC-382) (#426) * test(core) unit tests for flow & diagnostics controllers, updated postman collection & refactored diagnostics (EWC-382) (#427) * bug(fix) CLI diagnostics: Incorrect error message is displayed when user try to get strace-info (#428) * test(core) unit tests for catalog, controller and agent controllers (EWC-382) (#430) * bug(fix) CLI diagnostics ftp: Incorrect error message is displayed when user try to get strace-info (EWC-431) * Release (#434) * release merge (#433) * Release 1.0.28 * EWC-379 migrations between versions (#410) * Maksimchepelev/bugs (#411) * EWC-385 cli helps' outputs * EWC-395 cli `microservice port-mapping-remove` error message fix * EWC-389 cli `diagnostics strace-update` correct parsing of boolean * EWC-390 cli `diagnostics strace-...` correct validation error messages * EWC-392 cli `diagnostics strace-info` microservice validation * EWC-394 sequelize (deprecated)find -> findOne * EWC-388 validation of flowId in delete flow service * EWC-393 validation of microserviceUuid in image snapshot services * Maksimchepelev/bugs (#412) * fix(help): Fix rsa-key description in cli tunnel help need to update description for parameter -k, --rsa-key in help for tunnel from 'Tunnel RSA key' to 'Path to tunnel RSA key' Closes EWC-396 * fix(tunnel): if port range not provided in config use default values default range: 2000-10000 Closes EWC-397 * fix(tests): rename logLimit -> logSize (#416) create microservice request in Postman Collection diagnostics block Closes EWC-401 * feat(npm-scripts): allow to use only one image on catalog item creation (#415) Closes EWC-399 * fix(transactions): fix transaction validation if last method's arg is undefined (#414) Closes EWC-400 * feat(npm-scripts): init db automatically after installation (#413) Closes EWC-368 * Maksimchepelev/bugs (#417) * fix(code): delete routes on microservice deletion Closes EWC-362 * update version 1.0.27 (#418) * Merge versions (#420) * release 1.0.27 version (#419) * feat(core): add migration between versions (EWC-379) * feat(core): add cli help output (EWC-385) * fix(bug): fix cli `microservice port-mapping-remove` error message (EWC-395) * fix(bug): fix cli `diagnostics strace-update` parsing of boolean (EWC-389) * fix(bug): fix cli `diagnostics strace-...` validation error messages (EWC-390) * feat(core): add cli `diagnostics strace-info` microservice validation (EWC-392) * refactor(core): replace sequelize (deprecated)find -> findOne (EWC-394) * refactor(core): edit validation of flowId in delete flow service (EWC-388) * feat(core): add validation of microserviceUuid in image snapshot services (EWC-393) * fix(help): Fix rsa-key description in cli tunnel help need to update description for parameter -k, --rsa-key in help for tunnel from 'Tunnel RSA key' to 'Path to tunnel RSA key' Closes EWC-396 * fix(tunnel): if port range not provided in config use default values default range: 2000-10000 Closes EWC-397 * fix(tests): rename logLimit -> logSize (#416) create microservice request in Postman Collection diagnostics block Closes EWC-401 * feat(npm-scripts): allow to use only one image on catalog item creation (#415) Closes EWC-399 * fix(transactions): fix transaction validation if last method's arg is undefined (#414) Closes EWC-400 * feat(npm-scripts): init db automatically after installation (#413) Closes EWC-368 * fix(code): delete routes on microservice deletion Closes EWC-362 * Update CHANGELOG.md * Release of version 1.0.27 * test(core) unit tests for user-controller (EWC-382) * docs(core) Remove swagger.yml, remove CLI docs in README, add PULL_REQUEST_TEMPLATE.md (#422) * remove swagger.yml - its content moved to https://github.com/ioFog/iofog.org/blob/master/content/docs/1.0.0/controllers/rest-api.md * Add PULL_REQUEST_TEMPLATE.md * remove docs from README, instead use https://iofog.org/ * Maksimchepelev/bugs (#421) * fix(cli): user can't update deviceScanFrequency via cli deviceScanFrequency -> deviceFrequency in build cli data object Closes EWC-350 * fix(catalog): forbid to create system microservices Closes EWC-409 * fix(cli): forbid to delete connector when it used Closes EWC-364 * fix(connector): add certificates on close port request Closes EWC-411 EWC-406 * fix(cli): fix messages on incorrect requests Closes EWC-403 * fix(catalog): add validation in catalog service registryId validation empty update object validation Closes EWC-417 * changed connector error message variable * test(core) unit tests for microservices & registry controllers (EWC-382) (#423) * add build stage for release branch * Pre-release 1.0.28 (#429) * fix(bug) fixed system microservices (HAL, bluetooth) launch (EWC-413) (#424) * fix(bug) added registry email validation (EWC-418) * test(core) unit tests for iofog-controller (EWC-382) (#426) * test(core) unit tests for flow & diagnostics controllers, updated postman collection & refactored diagnostics (EWC-382) (#427) * bug(fix) CLI diagnostics: Incorrect error message is displayed when user try to get strace-info (#428) * Release 1.0.28 [skip ci] * Release 1.1.0 [skip ci] * Update package.json * Update CHANGELOG.md * Release (#435) * release merge (#433) * Release 1.0.28 * EWC-379 migrations between versions (#410) * Maksimchepelev/bugs (#411) * EWC-385 cli helps' outputs * EWC-395 cli `microservice port-mapping-remove` error message fix * EWC-389 cli `diagnostics strace-update` correct parsing of boolean * EWC-390 cli `diagnostics strace-...` correct validation error messages * EWC-392 cli `diagnostics strace-info` microservice validation * EWC-394 sequelize (deprecated)find -> findOne * EWC-388 validation of flowId in delete flow service * EWC-393 validation of microserviceUuid in image snapshot services * Maksimchepelev/bugs (#412) * fix(help): Fix rsa-key description in cli tunnel help need to update description for parameter -k, --rsa-key in help for tunnel from 'Tunnel RSA key' to 'Path to tunnel RSA key' Closes EWC-396 * fix(tunnel): if port range not provided in config use default values default range: 2000-10000 Closes EWC-397 * fix(tests): rename logLimit -> logSize (#416) create microservice request in Postman Collection diagnostics block Closes EWC-401 * feat(npm-scripts): allow to use only one image on catalog item creation (#415) Closes EWC-399 * fix(transactions): fix transaction validation if last method's arg is undefined (#414) Closes EWC-400 * feat(npm-scripts): init db automatically after installation (#413) Closes EWC-368 * Maksimchepelev/bugs (#417) * fix(code): delete routes on microservice deletion Closes EWC-362 * update version 1.0.27 (#418) * Merge versions (#420) * release 1.0.27 version (#419) * feat(core): add migration between versions (EWC-379) * feat(core): add cli help output (EWC-385) * fix(bug): fix cli `microservice port-mapping-remove` error message (EWC-395) * fix(bug): fix cli `diagnostics strace-update` parsing of boolean (EWC-389) * fix(bug): fix cli `diagnostics strace-...` validation error messages (EWC-390) * feat(core): add cli `diagnostics strace-info` microservice validation (EWC-392) * refactor(core): replace sequelize (deprecated)find -> findOne (EWC-394) * refactor(core): edit validation of flowId in delete flow service (EWC-388) * feat(core): add validation of microserviceUuid in image snapshot services (EWC-393) * fix(help): Fix rsa-key description in cli tunnel help need to update description for parameter -k, --rsa-key in help for tunnel from 'Tunnel RSA key' to 'Path to tunnel RSA key' Closes EWC-396 * fix(tunnel): if port range not provided in config use default values default range: 2000-10000 Closes EWC-397 * fix(tests): rename logLimit -> logSize (#416) create microservice request in Postman Collection diagnostics block Closes EWC-401 * feat(npm-scripts): allow to use only one image on catalog item creation (#415) Closes EWC-399 * fix(transactions): fix transaction validation if last method's arg is undefined (#414) Closes EWC-400 * feat(npm-scripts): init db automatically after installation (#413) Closes EWC-368 * fix(code): delete routes on microservice deletion Closes EWC-362 * Update CHANGELOG.md * Release of version 1.0.27 * test(core) unit tests for user-controller (EWC-382) * docs(core) Remove swagger.yml, remove CLI docs in README, add PULL_REQUEST_TEMPLATE.md (#422) * remove swagger.yml - its content moved to https://github.com/ioFog/iofog.org/blob/master/content/docs/1.0.0/controllers/rest-api.md * Add PULL_REQUEST_TEMPLATE.md * remove docs from README, instead use https://iofog.org/ * Maksimchepelev/bugs (#421) * fix(cli): user can't update deviceScanFrequency via cli deviceScanFrequency -> deviceFrequency in build cli data object Closes EWC-350 * fix(catalog): forbid to create system microservices Closes EWC-409 * fix(cli): forbid to delete connector when it used Closes EWC-364 * fix(connector): add certificates on close port request Closes EWC-411 EWC-406 * fix(cli): fix messages on incorrect requests Closes EWC-403 * fix(catalog): add validation in catalog service registryId validation empty update object validation Closes EWC-417 * changed connector error message variable * test(core) unit tests for microservices & registry controllers (EWC-382) (#423) * add build stage for release branch * Pre-release 1.0.28 (#429) * fix(bug) fixed system microservices (HAL, bluetooth) launch (EWC-413) (#424) * fix(bug) added registry email validation (EWC-418) * test(core) unit tests for iofog-controller (EWC-382) (#426) * test(core) unit tests for flow & diagnostics controllers, updated postman collection & refactored diagnostics (EWC-382) (#427) * bug(fix) CLI diagnostics: Incorrect error message is displayed when user try to get strace-info (#428) * Release 1.0.28 [skip ci] * Release 1.1.0 [skip ci] * Update package.json * Update CHANGELOG.md * Update package.json * Merge (#436) * fix(bug) fixed system microservices (HAL, bluetooth) launch (EWC-413) (#424) * fix(bug) added registry email validation (EWC-418) * test(core) unit tests for iofog-controller (EWC-382) (#426) * test(core) unit tests for flow & diagnostics controllers, updated postman collection & refactored diagnostics (EWC-382) (#427) * bug(fix) CLI diagnostics: Incorrect error message is displayed when user try to get strace-info (#428) * test(core) unit tests for catalog, controller and agent controllers (EWC-382) (#430) * bug(fix) CLI diagnostics ftp: Incorrect error message is displayed when user try to get strace-info (EWC-431) * Release 1.1.0 [skip ci] * Update package.json * Release 1.1.0 [skip ci] * Update package.json * remove automated release * bug(fix) strace feature bug (EWC-421) * bug(fix) fixed deprecation warnings (EWC-422) (#438) * bug(fix) CLI diagnostics ftp: invalid microservice uuid * bug(fix) CLI diagnostics ftp: invalid microservice id (update) (#440) * bug(fix) CLI diagnostics ftp: invalid microservice id (update) * bug(fix): update id to uuid * bug(fix) iofog agent API: core network containers are returned when flow isn't active (EWC-424) * test(core) unit tests progress for agent & access-token services (EWC-383) (#442) * feat(npm-scripts): npm scripts improvements (EWC-358 EWC-430) 1. save configs during releases 2. migrate to new encryption algorithm * bug(fix): microservices are shown after deleting user, new error message * bug(fix): microservices are shown after deleting user, new error message (up test) * bug(fix): microservices are shown after deleting user, new error message (up test2) * feat(microservice): update change tracking on delete port mapping (#445) Closes EWC-441 * version 1.0.31 --- .travis.yml | 20 +- package.json | 2 +- scripts/postinstall.js | 190 ++++- scripts/preuninstall.js | 29 +- src/cli/diagnostics.js | 4 +- src/cli/user.js | 11 +- src/controllers/user-controller.js | 19 +- src/decorators/authorization-decorator.js | 2 +- src/helpers/app-helper.js | 19 +- src/helpers/error-messages.js | 1 + src/routes/agent.js | 2 +- src/schemas/agent.js | 15 +- src/schemas/index.js | 2 - src/sequelize/managers/iofog-manager.js | 4 +- .../managers/microservice-manager.js | 3 +- src/sequelize/managers/strace-manager.js | 2 +- src/services/agent-service.js | 20 +- src/services/connector-service.js | 4 +- src/services/diagnostic-service.js | 41 +- src/services/microservices-service.js | 7 +- src/services/user-service.js | 32 +- test/src/controllers/agent-controller.test.js | 767 ++++++++++++++++++ .../controllers/catalog-controller.test.js | 320 ++++++++ .../controllers/controller-controller.test.js | 115 +++ test/src/controllers/user-controller.test.js | 118 +-- test/src/helpers/app-helpers.test.js | 83 +- .../src/services/access-token-service.test.js | 83 ++ test/src/services/agent-service.test.js | 619 ++++++++++++++ 28 files changed, 2276 insertions(+), 258 deletions(-) create mode 100644 test/src/controllers/agent-controller.test.js create mode 100644 test/src/controllers/catalog-controller.test.js create mode 100644 test/src/controllers/controller-controller.test.js create mode 100644 test/src/services/access-token-service.test.js create mode 100644 test/src/services/agent-service.test.js diff --git a/.travis.yml b/.travis.yml index 9799fe7e8..7621a18e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,16 +37,16 @@ jobs: script: - sshpass -p $PROD_MACHINE_PASSWORD ssh -o StrictHostKeyChecking=no $PROD_MACHINE_USERNAME@$PROD_MACHINE_IP "cd /Controller; NODE_ENV=production node src/main.js stop; git pull; npm i; NODE_ENV=production node src/main.js start" - stage: release - before_install: - - git clone "https://github.com/$TRAVIS_REPO_SLUG.git" "$TRAVIS_REPO_SLUG"; - - cd "$TRAVIS_REPO_SLUG"; - - git checkout -qf "$TRAVIS_COMMIT"; - - git checkout master - before_deploy: - - git config --global user.name "${GH_USERNAME}"; - - git config credential.helper "store --file=.git/credentials"; - - echo "https://${GH_TOKEN}:@github.com" > .git/credentials; - - npm run automatic-release + #before_install: + #- git clone "https://github.com/$TRAVIS_REPO_SLUG.git" "$TRAVIS_REPO_SLUG"; + #- cd "$TRAVIS_REPO_SLUG"; + #- git checkout -qf "$TRAVIS_COMMIT"; + #- git checkout master + #before_deploy: + #- git config --global user.name "${GH_USERNAME}"; + #- git config credential.helper "store --file=.git/credentials"; + #- echo "https://${GH_TOKEN}:@github.com" > .git/credentials; + #- npm run automatic-release deploy: skip_cleanup: true provider: npm diff --git a/package.json b/package.json index 5d062410a..638c28072 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.29", + "version": "1.0.31", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi", diff --git a/scripts/postinstall.js b/scripts/postinstall.js index a5bf6e22e..263e111d1 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -17,57 +17,49 @@ const fs = require('fs'); const semver = require('semver'); const currentVersion = require('../package').version; -const rootDir = `${__dirname}/../`; +const rootDir = `${__dirname}/..`; let installationVariablesFileName = 'iofogcontroller_install_variables'; -let installationVariablesFile; -let tempDir; - -if (os.type() === 'Linux') { - tempDir = '/tmp/'; -} else if (os.type() === 'Darwin') { - tempDir = '/tmp/'; -} else if (os.type() === 'Windows_NT') { - tempDir = `${process.env.APPDATA}/`; -} else { - throw new Error("Unsupported OS found: " + os.type()); -} - -installationVariablesFile = tempDir + installationVariablesFileName; +let tempDir = getTempDirLocation(); +const installationVariablesFile = tempDir + '/' + installationVariablesFileName; -const devDbBackup = `${tempDir}dev_database.sqlite`; +//restore all files +const devDbBackup = `${tempDir}/dev_database.sqlite`; const devDb = `${rootDir}/src/sequelize/dev_database.sqlite`; -if (fs.existsSync(devDbBackup)) { - fs.renameSync(devDbBackup, devDb); -} +moveFileIfExists(devDbBackup, devDb); -const prodDbBackup = `${tempDir}prod_database.sqlite`; +const prodDbBackup = `${tempDir}/prod_database.sqlite`; const prodDb = `${rootDir}/src/sequelize/prod_database.sqlite`; -if (fs.existsSync(prodDbBackup)) { - fs.renameSync(prodDbBackup, prodDb); -} +moveFileIfExists(prodDbBackup, prodDb); + +const defConfigBackup = `${tempDir}/default_iofog_backup.json`; +const defConfig = `${rootDir}/src/config/default.json`; +moveFileIfExists(defConfigBackup, defConfig); +const prodConfigBackup = `${tempDir}/production_iofog_backup.json`; +const prodConfig = `${rootDir}/src/config/production.json`; +moveFileIfExists(prodConfigBackup, prodConfig); + +const devConfigBackup = `${tempDir}/development_iofog_backup.json`; +const devConfig = `${rootDir}/src/config/development.json`; +moveFileIfExists(devConfigBackup, devConfig); + +//process migrations try { - const instalationVarsStr = fs.readFileSync(installationVariablesFile); - const instalationVars = JSON.parse(instalationVarsStr); - const prevVersion = instalationVars.prevVer; + const installationVarsStr = fs.readFileSync(installationVariablesFile); + const installationVars = JSON.parse(installationVarsStr); + const prevVersion = installationVars.prevVer; console.log(`previous version - ${prevVersion}`); console.log(`new version - ${currentVersion}`); if (semver.satisfies(prevVersion, '<=1.0.0')) { - console.log('upgrading from version <=1.0.0 :'); - console.log(' inserting seeds meta info in db'); - const options = { - env: { - "PATH": process.env.PATH - }, - stdio: [process.stdin, process.stdout, process.stderr] - }; - - execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928110125-insert-registry.js');"`, options); - execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928111532-insert-catalog-item.js');"`, options); - execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928112152-insert-iofog-type.js');"`, options); - execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928121334-insert-catalog-item-image.js');"`, options); + console.log('upgrading from version <= 1.0.0 :'); + insertSeeds(); + } + + if (semver.satisfies(prevVersion, '<=1.0.30')) { + console.log('upgrading from version <= 1.0.30 :'); + updateEncryptionMethod(); } fs.unlinkSync(installationVariablesFile); @@ -84,4 +76,122 @@ const options = { stdio: [process.stdin, process.stdout, process.stderr] }; -execSync('node ./src/main.js init', options); \ No newline at end of file +execSync('node ./src/main.js init', options); + +//other functions definitions + +function getTempDirLocation() { + let tempDir; + if (os.type() === 'Linux') { + tempDir = '/tmp'; + } else if (os.type() === 'Darwin') { + tempDir = '/tmp'; + } else if (os.type() === 'Windows_NT') { + tempDir = `${process.env.APPDATA}`; + } else { + throw new Error("Unsupported OS found: " + os.type()); + } + return tempDir; +} + +function moveFileIfExists(from, to) { + if (fs.existsSync(from)) { + fs.renameSync(from, to); + } +} + +function insertSeeds() { + console.log(' inserting seeds meta info in db'); + const options = { + env: { + "PATH": process.env.PATH + }, + stdio: [process.stdin, process.stdout, process.stderr] + }; + + execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928110125-insert-registry.js');"`, options); + execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928111532-insert-catalog-item.js');"`, options); + execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928112152-insert-iofog-type.js');"`, options); + execSync(`sqlite3 ${prodDb} "insert into SequelizeMeta (name) values ('20180928121334-insert-catalog-item-image.js');"`, options); +} + +function updateEncryptionMethodForUsersPassword(decryptionFunc) { + const options = { + env: { + "PATH": process.env.PATH + } + }; + + const usersOutput = execSync(`sqlite3 ${prodDb} "select id, email, password from Users;"`, options).toString(); + const usersLines = usersOutput.match(/[^\r\n]+/g); + for (let line of usersLines) { + let id, email, oldEncryptedPassword; + try { + const vals = line.split('|'); + id = vals[0]; + email = vals[1]; + oldEncryptedPassword = vals[2]; + + const decryptedPassword = decryptionFunc(oldEncryptedPassword, email); + + const AppHelper = require('../src/helpers/app-helper'); + const newEncryptedPassword = AppHelper.encryptText(decryptedPassword, email); + + const options = { + env: { + "PATH": process.env.PATH + }, + stdio: [process.stdin, process.stdout, process.stderr] + }; + execSync(`sqlite3 ${prodDb} "update Users set password='${newEncryptedPassword}' where id=${id};"`, options); + } catch (e) { + console.log('db problem'); + } + } +} + +function updateEncryptionMethodForEmailService(configFile, decryptionFunc) { + console.log(configFile); + if (!configFile) { + return + } + const configObj = JSON.parse(fs.readFileSync(configFile, 'utf8')); + console.log(configObj); + if (!configObj || !configObj.Email || !configObj.Email.Address || !configObj.Email.Password) { + return + } + + const email = configObj.Email.Address; + const oldEncryptedPassword = configObj.Email.Password; + + const decryptedPassword = decryptionFunc(oldEncryptedPassword, email); + + const AppHelper = require('../src/helpers/app-helper'); + configObj.Email.Password = AppHelper.encryptText(decryptedPassword, email); + + console.log(configObj); + try { + fs.writeFileSync(configFile, JSON.stringify(configObj, null, 2)); + } catch (e) { + console.log(e) + } +} + +function updateEncryptionMethod() { + console.log(' updating encryption method for old users'); + + function decryptTextVer30(text, salt) { + const crypto = require('crypto'); + const ALGORITHM = 'aes-256-ctr'; + + const decipher = crypto.createDecipher(ALGORITHM, salt); + let dec = decipher.update(text, 'hex', 'utf8'); + dec += decipher.final('utf8'); + return dec + } + + updateEncryptionMethodForUsersPassword(decryptTextVer30); + updateEncryptionMethodForEmailService(defConfig, decryptTextVer30); + updateEncryptionMethodForEmailService(devConfig, decryptTextVer30); + updateEncryptionMethodForEmailService(prodConfig, decryptTextVer30); +} \ No newline at end of file diff --git a/scripts/preuninstall.js b/scripts/preuninstall.js index da1fbcea5..1c8ed5106 100644 --- a/scripts/preuninstall.js +++ b/scripts/preuninstall.js @@ -16,22 +16,22 @@ const execSync = require('child_process').execSync; const fs = require('fs'); const version = require('../package').version; -const rootDir = `${__dirname}/../`; +const rootDir = `${__dirname}/..`; let installationVariablesFileName = 'iofogcontroller_install_variables'; let installationVariablesFile; let tempDir; if (os.type() === 'Linux') { - tempDir = '/tmp/'; + tempDir = '/tmp'; } else if (os.type() === 'Darwin') { - tempDir = '/tmp/'; + tempDir = '/tmp'; } else if (os.type() === 'Windows_NT') { - tempDir = `${process.env.APPDATA}/`; + tempDir = `${process.env.APPDATA}`; } else { throw new Error("Unsupported OS found: " + os.type()); } -installationVariablesFile = tempDir + installationVariablesFileName; +installationVariablesFile = tempDir + '/' + installationVariablesFileName; const instalationVars = { prevVer: version @@ -41,11 +41,26 @@ fs.writeFileSync(installationVariablesFile, JSON.stringify(instalationVars)); const devDb = `${rootDir}/src/sequelize/dev_database.sqlite`; if (fs.existsSync(devDb)) { - fs.renameSync(devDb, `${tempDir}dev_database.sqlite`) + fs.renameSync(devDb, `${tempDir}/dev_database.sqlite`) } const prodDb = `${rootDir}/src/sequelize/prod_database.sqlite`; if (fs.existsSync(prodDb)) { - fs.renameSync(prodDb, `${tempDir}prod_database.sqlite`) + fs.renameSync(prodDb, `${tempDir}/prod_database.sqlite`) +} + +const defConfig = `${rootDir}/src/config/default.json`; +if (fs.existsSync(defConfig)) { + fs.renameSync(defConfig, `${tempDir}/default_iofog_backup.json`) +} + +const devConfig = `${rootDir}/src/config/development.json`; +if (fs.existsSync(devConfig)) { + fs.renameSync(devConfig, `${tempDir}/development_iofog_backup.json`) +} + +const prodConfig = `${rootDir}/src/config/production.json`; +if (fs.existsSync(prodConfig)) { + fs.renameSync(prodConfig, `${tempDir}/production_iofog_backup.json`) } diff --git a/src/cli/diagnostics.js b/src/cli/diagnostics.js index dd5e096d0..7f156f5c2 100644 --- a/src/cli/diagnostics.js +++ b/src/cli/diagnostics.js @@ -131,7 +131,6 @@ const _changeMicroserviceStraceState = async function (obj) { const isEnable = AppHelper.validateBooleanCliOptions(obj.enable, obj.disable); await DiagnosticService.changeMicroserviceStraceState(obj.microserviceUuid, {enable: isEnable}, {}, true); const msg = isEnable ? 'Microservice strace has been enabled' : 'Microservice strace has been disabled'; - logger.info(msg); }; @@ -139,7 +138,8 @@ const _getMicroserviceStraceData = async function (obj) { logger.info(JSON.stringify(obj)); const result = await DiagnosticService.getMicroserviceStraceData(obj.microserviceUuid, {format: obj.format}, {}, true); - logger.info(JSON.stringify(result, null, 2)); + logger.info('Strace data:'); + logger.info(result.data); logger.info('Microservice strace data has been retrieved successfully.'); }; diff --git a/src/cli/user.js b/src/cli/user.js index 71f3f6d70..bf61143e3 100644 --- a/src/cli/user.js +++ b/src/cli/user.js @@ -51,6 +51,11 @@ class User extends BaseCLIHandler { name: 'password', alias: 'p', type: String, description: 'User\'s password', group: [constants.CMD_ADD, constants.CMD_UPDATE], + }, + { + name: 'force', alias: 'F', type: Boolean, + description: 'User\'s force delete', + group: [constants.CMD_REMOVE], } ]; this.commands = { @@ -136,10 +141,10 @@ const _updateUserDetails = async function (userDetails, user) { logger.info('User updated successfully.'); }; -const _deleteUser = async function (emailObj, user) { - logger.info(JSON.stringify(emailObj)); +const _deleteUser = async function (obj, user) { + logger.info(JSON.stringify(obj)); - await UserService.deleteUser(user, true); + await UserService.deleteUser(obj.force, user, true); logger.info('User removed successfully.'); }; diff --git a/src/controllers/user-controller.js b/src/controllers/user-controller.js index 43badf767..c86fa65ab 100644 --- a/src/controllers/user-controller.js +++ b/src/controllers/user-controller.js @@ -42,14 +42,11 @@ const userLoginEndPoint = async function (req) { await Validator.validate(user, Validator.schemas.login); - const encryptedPassword = AppHelper.encryptText(user.password, user.email); const credentials = { email: user.email, - password: encryptedPassword + password: user.password }; - logger.info("Parameters:" + JSON.stringify(credentials)); - return await UserService.login(credentials, false); }; @@ -88,7 +85,7 @@ const updateUserProfileEndPoint = async function (req, user) { }; const deleteUserProfileEndPoint = async function (req, user) { - return await UserService.deleteUser(user, false); + return await UserService.deleteUser(req.body.force, user, false); }; const updateUserPasswordEndPoint = async function (req, user) { @@ -96,17 +93,7 @@ const updateUserPasswordEndPoint = async function (req, user) { await Validator.validate(passwordUpdates, Validator.schemas.updatePassword); - const encryptedOldPassword = AppHelper.encryptText(passwordUpdates.oldPassword, user.email); - const encryptedNewPassword = AppHelper.encryptText(passwordUpdates.newPassword, user.email); - - const encryptedPasswordUpdates = { - oldPassword: encryptedOldPassword, - newPassword: encryptedNewPassword - }; - - logger.info("Parameters:" + JSON.stringify(encryptedPasswordUpdates)); - - return await UserService.updateUserPassword(encryptedPasswordUpdates, user, false); + return await UserService.updateUserPassword(passwordUpdates, user, false); }; const resetUserPasswordEndPoint = async function (req) { diff --git a/src/decorators/authorization-decorator.js b/src/decorators/authorization-decorator.js index a3f78aee4..9702d2235 100644 --- a/src/decorators/authorization-decorator.js +++ b/src/decorators/authorization-decorator.js @@ -86,4 +86,4 @@ function checkFogToken(f) { module.exports = { checkAuthToken: checkAuthToken, checkFogToken: checkFogToken -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/helpers/app-helper.js b/src/helpers/app-helper.js index ff7056322..5f9d2cde7 100644 --- a/src/helpers/app-helper.js +++ b/src/helpers/app-helper.js @@ -21,19 +21,30 @@ const portscanner = require('portscanner'); const format = require('string-format'); const ALGORITHM = 'aes-256-ctr'; +const IV_LENGTH = 16; + const Transaction = require('sequelize/lib/transaction'); function encryptText(text, salt) { - const cipher = crypto.createCipher(ALGORITHM, salt); + const iv = crypto.randomBytes(IV_LENGTH); + const processedSalt = crypto.createHash('md5').update(salt).digest("hex"); + + const cipher = crypto.createCipheriv(ALGORITHM, processedSalt, iv); let crypted = cipher.update(text, 'utf8', 'hex'); crypted += cipher.final('hex'); - return crypted + return iv.toString('hex') + ':' + crypted.toString('hex'); } function decryptText(text, salt) { - const decipher = crypto.createDecipher(ALGORITHM, salt); - let dec = decipher.update(text, 'hex', 'utf8'); + const processedSalt = crypto.createHash('md5').update(salt).digest("hex"); + + const textParts = text.split(':'); + const iv = new Buffer(textParts.shift(), 'hex'); + let encryptedText = new Buffer(textParts.join(':'), 'hex'); + + const decipher = crypto.createDecipheriv(ALGORITHM, processedSalt, iv); + let dec = decipher.update(encryptedText, 'hex', 'utf8'); dec += decipher.final('utf8'); return dec } diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index 6ab703477..0625d2952 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -27,6 +27,7 @@ module.exports = { INVALID_VOLUME_MAPPING_UUID: "Invalid volume mapping id '{}'", ACTIVATION_CODE_NOT_FOUND: 'Activation code not found', INVALID_OLD_PASSWORD: 'Old password is incorrect', + NEEDED_FORCE_DELETE_USER: "There are running iofog-agents, stop them before removal or pass 'force' parameter", ACCOUNT_NOT_FOUND: 'Account not found', USER_NOT_UPDATED: 'User not updated', EMAIL_NOT_ACTIVATED: 'Email is not activated. Please activate your account first.', diff --git a/src/routes/agent.js b/src/routes/agent.js index f2610e1e1..2ab7f255d 100644 --- a/src/routes/agent.js +++ b/src/routes/agent.js @@ -278,7 +278,7 @@ module.exports = [ method: 'put', path: '/api/v3/agent/strace', middleware: async (req, res) => { - const successCode = constants.HTTP_CODE_SUCCESS; + const successCode = constants.HTTP_CODE_NO_CONTENT; const errorCodes = [ { code: constants.HTTP_CODE_NOT_FOUND, diff --git a/src/schemas/agent.js b/src/schemas/agent.js index 3d3c19cc5..e1bfbb44a 100644 --- a/src/schemas/agent.js +++ b/src/schemas/agent.js @@ -46,15 +46,6 @@ const updateAgentConfig = { "additionalProperties": false }; -const agentConfigChanges = { - "id": "/agentConfigChanges", - "type": "object", - "properties": { - "timestamp": {"type": "integer"} - }, - "additionalProperties": false -}; - const updateAgentStatus = { "id": "/updateAgentStatus", "type": "object", @@ -104,10 +95,10 @@ const straceData = { "id": "/straceData", "type": "object", "properties": { - "microserviceId": {"type": "string"}, + "microserviceUuid": {"type": "string"}, "buffer": {"type": "string"} }, - "required": ["microserviceId", "buffer"], + "required": ["microserviceUuid", "buffer"], "additionalProperties": false }; @@ -148,7 +139,7 @@ const updateUsbInfo = { }; module.exports = { - mainSchemas: [agentProvision, updateAgentConfig, agentConfigChanges, updateAgentStatus, updateAgentStrace, + mainSchemas: [agentProvision, updateAgentConfig, updateAgentStatus, updateAgentStrace, updateHardwareInfo, updateUsbInfo], innerSchemas: [straceData, microserviceStatus] }; \ No newline at end of file diff --git a/src/schemas/index.js b/src/schemas/index.js index 158302d73..7f524724b 100644 --- a/src/schemas/index.js +++ b/src/schemas/index.js @@ -19,8 +19,6 @@ const basename = path.basename(__filename); const Errors = require('../helpers/errors'); -const Logger = require('../logger'); - const v = new Validator(); const schemas = {}; diff --git a/src/sequelize/managers/iofog-manager.js b/src/sequelize/managers/iofog-manager.js index 4289ef7b5..0ac555f4e 100644 --- a/src/sequelize/managers/iofog-manager.js +++ b/src/sequelize/managers/iofog-manager.js @@ -54,11 +54,11 @@ class FogManager extends BaseManager { { model: Microservice, as: 'microservice', - required: false, + required: true, include: [{ model: Strace, as: 'strace', - required: false + required: true }] }], where: where diff --git a/src/sequelize/managers/microservice-manager.js b/src/sequelize/managers/microservice-manager.js index 2e6729cae..ca103f664 100644 --- a/src/sequelize/managers/microservice-manager.js +++ b/src/sequelize/managers/microservice-manager.js @@ -153,7 +153,8 @@ class MicroserviceManager extends BaseManager { '$flow.is_activated$': true }, { - '$catalogItem.category$': {[Op.eq]: 'SYSTEM'} + '$catalogItem.category$': {[Op.eq]: 'SYSTEM'}, + '$catalogItem.id$': {[Op.ne]: 1} } ] diff --git a/src/sequelize/managers/strace-manager.js b/src/sequelize/managers/strace-manager.js index f910e196b..8d0f50422 100644 --- a/src/sequelize/managers/strace-manager.js +++ b/src/sequelize/managers/strace-manager.js @@ -26,7 +26,7 @@ class StraceManager extends BaseManager { return Strace } - async pushBufferByMicroserviceId(uuid, pushingData, transaction) { + async pushBufferByMicroserviceUuid(uuid, pushingData, transaction) { const strace = await this.findOne({ microserviceUuid: uuid }, transaction); diff --git a/src/services/agent-service.js b/src/services/agent-service.js index 8160eb82e..3b9f6b421 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -14,7 +14,6 @@ const TransactionDecorator = require('../decorators/transaction-decorator'); const FogProvisionKeyManager = require('../sequelize/managers/iofog-provision-key-manager'); -const FogTypeManager = require('../sequelize/managers/iofog-type-manager'); const FogManager = require('../sequelize/managers/iofog-manager'); const FogAccessTokenService = require('../services/iofog-access-token-service'); const ChangeTrackingService = require('./change-tracking-service'); @@ -34,7 +33,6 @@ const MicroserviceService = require('../services/microservices-service'); const path = require('path'); const fs = require('fs'); const formidable = require('formidable'); -const logger = require('../logger'); const Sequelize = require('sequelize'); const Op = Sequelize.Op; @@ -300,7 +298,7 @@ const getAgentTunnel = async function (fog, transaction) { }; const getAgentStrace = async function (fog, transaction) { - const fogWithStrace = FogManager.findFogStraces({ + const fogWithStrace = await FogManager.findFogStraces({ uuid: fog.uuid }, transaction); @@ -308,16 +306,26 @@ const getAgentStrace = async function (fog, transaction) { throw new Errors.NotFoundError(ErrorMessages.STRACE_NOT_FOUND); } - return fogWithStrace.strace; + const straceArr = []; + for (let msData of fogWithStrace.microservice) { + straceArr.push({ + microserviceUuid: msData.strace.microserviceUuid, + straceRun: msData.strace.straceRun + }) + }; + + return { + straceValues: straceArr + } }; const updateAgentStrace = async function (straceData, fog, transaction) { await Validator.validate(straceData, Validator.schemas.updateAgentStrace); for (const strace of straceData.straceData) { - const microserviceId = strace.microserviceId; + const microserviceUuid = strace.microserviceUuid; const buffer = strace.buffer; - await StraceManager.pushBufferByMicroserviceId(microserviceId, buffer, transaction) + await StraceManager.pushBufferByMicroserviceUuid(microserviceUuid, buffer, transaction) } }; diff --git a/src/services/connector-service.js b/src/services/connector-service.js index dd10c8a04..2f1ceab98 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -168,7 +168,7 @@ async function openPortsOnConnector(connector, isPublicAccess, transaction) { }; if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { const ca = fs.readFileSync(connector.cert); - options.ca = new Buffer(ca); + options.ca = new Buffer.from(ca); } const ports = await _makeRequest(connector, options, data); @@ -205,7 +205,7 @@ async function closePortOnConnector(connector, ports, transaction) { }; if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { const ca = fs.readFileSync(connector.cert); - options.ca = new Buffer(ca); + options.ca = new Buffer.from(ca); } diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 741f122f4..fcd46e37b 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -28,40 +28,40 @@ const path = require('path'); const mime = require('mime'); -const changeMicroserviceStraceState = async function (id, data, user, isCLI, transaction) { +const changeMicroserviceStraceState = async function (uuid, data, user, isCLI, transaction) { await Validator.validate(data, Validator.schemas.straceStateUpdate); - const microservice = await MicroserviceService.getMicroservice(id, user, isCLI, transaction); + const microservice = await MicroserviceService.getMicroservice(uuid, user, isCLI, transaction); if (microservice.iofogUuid === null) { throw new Errors.ValidationError(ErrorMessages.STRACE_WITHOUT_FOG); } const straceObj = { straceRun: data.enable, - microserviceUuid: id + microserviceUuid: uuid }; - await StraceDiagnosticManager.updateOrCreate({microserviceUuid: id}, straceObj, transaction); + await StraceDiagnosticManager.updateOrCreate({microserviceUuid: uuid}, straceObj, transaction); await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.diagnostics, transaction) }; -const getMicroserviceStraceData = async function (id, data, user, isCLI, transaction) { +const getMicroserviceStraceData = async function (uuid, data, user, isCLI, transaction) { await Validator.validate(data, Validator.schemas.straceGetData); const microserviceWhere = isCLI - ? {uuid: id} - : {uuid: id, userId: user.id}; + ? {uuid: uuid} + : {uuid: uuid, userId: user.id}; const microservice = await MicroserviceManager.findOne(microserviceWhere, transaction); if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, id)) + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, uuid)) } - const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: id}, transaction); + const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: uuid}, transaction); if (!straceData) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, id)) + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } const dir = config.get('Diagnostics:DiagnosticDir') || 'diagnostics'; - const filePath = dir + '/' + id; + const filePath = dir + '/' + uuid; let result = straceData.buffer; @@ -77,11 +77,24 @@ const getMicroserviceStraceData = async function (id, data, user, isCLI, transac }; }; -const postMicroserviceStraceDatatoFtp = async function (id, data, user, isCLI, transaction) { +const postMicroserviceStraceDatatoFtp = async function (uuid, data, user, isCLI, transaction) { await Validator.validate(data, Validator.schemas.stracePostToFtp); - const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: id}, transaction); + + const microserviceWhere = isCLI + ? {uuid: uuid} + : {uuid: uuid, userId: user.id}; + const microservice = await MicroserviceManager.findOne(microserviceWhere, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, uuid)) + } + const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: uuid}, transaction); + + if (!straceData) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) + } + const dir = config.get('Diagnostics:DiagnosticDir'); - const filePath = dir + '/' + id; + const filePath = dir + '/' + uuid; _createDirectoryIfNotExists(dir); _writeBufferToFile(filePath, straceData.buffer); diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index f0e0b7dd1..f18866032 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -709,12 +709,13 @@ async function _deletePortMapping(microserviceUuid, internalPort, user, isCLI, t } async function _deleteSimplePortMapping(microservice, msPorts, user, transaction) { - await MicroservicePortManager.delete({id: msPorts.id}, transaction) + await MicroservicePortManager.delete({id: msPorts.id}, transaction); const updateRebuildMs = { rebuild: true - } - await MicroserviceManager.update({uuid: microservice.uuid}, updateRebuildMs, transaction) + }; + await MicroserviceManager.update({uuid: microservice.uuid}, updateRebuildMs, transaction); + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction); } async function _deletePortMappingOverConnector(microservice, msPorts, user, transaction) { diff --git a/src/services/user-service.js b/src/services/user-service.js index 85e30c08e..9da7375d1 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -18,6 +18,7 @@ const AppHelper = require('../helpers/app-helper'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); const Config = require('../config'); +const ioFogManager = require('../sequelize/managers/iofog-manager'); const emailActivationTemplate = require('../views/email-activation-temp'); const emailRecoveryTemplate = require('../views/email-temp'); @@ -66,7 +67,9 @@ const login = async function (credentials, isCLI, transaction) { throw new Errors.InvalidCredentialsError(); } - const validPassword = credentials.password === user.password || credentials.password === user.tempPassword; + const pass = AppHelper.decryptText(user.password, user.email); + + const validPassword = credentials.password === pass || credentials.password === user.tempPassword; if (!validPassword) { throw new Errors.InvalidCredentialsError(); } @@ -167,21 +170,40 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { } }; -const deleteUser = async function (user, isCLI, transaction) { +const deleteUser = async function (force, user, isCLI, transaction) { + + if (!force) { + const ioFogArray = await ioFogManager.findAll({ + userId: user.id + }, transaction); + + if (!!ioFogArray) { + for (const ioFog of ioFogArray) { + if (ioFog.daemonStatus === 'RUNNING') { + throw new Errors.ValidationError(ErrorMessages.NEEDED_FORCE_DELETE_USER); + } + } + } + } + await UserManager.delete({ id: user.id }, transaction); }; const updateUserPassword = async function (passwordUpdates, user, isCLI, transaction) { - if (user.password !== passwordUpdates.oldPassword && user.tempPassword !== passwordUpdates.oldPassword) { + const pass = AppHelper.decryptText(user.password, user.email); + + if (pass !== passwordUpdates.oldPassword && user.tempPassword !== passwordUpdates.oldPassword) { throw new Errors.ValidationError(ErrorMessages.INVALID_OLD_PASSWORD); } const emailData = await _getEmailData(); const transporter = await _userEmailSender(emailData); - await UserManager.updatePassword(user.id, passwordUpdates.newPassword, transaction); + const newPass = AppHelper.encryptText(passwordUpdates.newPassword, user.email); + + await UserManager.updatePassword(user.id, newPass, transaction); await _notifyUserAboutPasswordChange(user, emailData, transporter); }; @@ -356,7 +378,7 @@ async function _getEmailData() { service: service } - } catch(errMsg) { + } catch (errMsg) { throw new Errors.EmailActivationSetupError(); } diff --git a/test/src/controllers/agent-controller.test.js b/test/src/controllers/agent-controller.test.js new file mode 100644 index 000000000..4524ba76b --- /dev/null +++ b/test/src/controllers/agent-controller.test.js @@ -0,0 +1,767 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const AgentController = require('../../../src/controllers/agent-controller'); +const AgentService = require('../../../src/services/agent-service'); + +describe('Agent Controller', () => { + def('subject', () => AgentController); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.agentProvisionEndPoint()', () => { + def('type', () => 1); + def('key', () => 'testKey'); + + def('req', () => ({ + body: { + type: $type, + key: $key + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.agentProvisionEndPoint($req)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'agentProvision').returns($response); + }); + + it('calls AgentService.agentProvision with correct args', async () => { + await $subject; + expect(AgentService.agentProvision).to.have.been.calledWith({ + type: $type, + key: $key + }); + }); + + context('when AgentService#agentProvision fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#agentProvision succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getAgentConfigEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentConfigEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentConfig').returns($response); + }); + + it('calls AgentService.getAgentConfig with correct args', async () => { + await $subject; + expect(AgentService.getAgentConfig).to.have.been.calledWith($fog) + }); + + context('when AgentService#getAgentConfig fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentConfig succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + + describe('.updateAgentConfigEndPoint()', () => { + def('fog', () => 'fog!'); + + def('networkInterface', () => 'testNetworkInterface'); + def('dockerUrl', () => 'testDockerUrl'); + def('diskLimit', 15); + def('diskDirectory', () => 'testDiskDirectory'); + def('memoryLimit', () => 25); + def('cpuLimit', () => 35); + def('logLimit', () => 45); + def('logDirectory', () => 'testLogDirectory'); + def('logFileCount', () => 5); + def('statusFrequency', () => 60); + def('changeFrequency', () => 30); + def('deviceScanFrequency', () => 40); + def('watchdogEnabled', () => true); + def('latitude', () => 30); + def('longitude', () => 40); + def('gpsMode', () => 'testGpsMode'); + + def('req', () => ({ + body: { + networkInterface: $networkInterface, + dockerUrl: $dockerUrl, + diskLimit: $diskLimit, + diskDirectory: $diskDirectory, + memoryLimit: $memoryLimit, + cpuLimit: $cpuLimit, + logLimit: $logLimit, + logDirectory: $logDirectory, + logFileCount: $logFileCount, + statusFrequency: $statusFrequency, + changeFrequency: $changeFrequency, + devicesScanFrequency: $deviceScanFrequency, + watchdogEnabled: $watchdogEnabled, + latitude: $latitude, + longitude: $longitude, + gpsMode: $gpsMode + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateAgentConfigEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'updateAgentConfig').returns($response); + }); + + it('calls AgentService.updateAgentConfig with correct args', async () => { + await $subject; + expect(AgentService.updateAgentConfig).to.have.been.calledWith({ + networkInterface: $networkInterface, + dockerUrl: $dockerUrl, + diskLimit: $diskLimit, + diskDirectory: $diskDirectory, + memoryLimit: $memoryLimit, + cpuLimit: $cpuLimit, + logLimit: $logLimit, + logDirectory: $logDirectory, + logFileCount: $logFileCount, + statusFrequency: $statusFrequency, + changeFrequency: $changeFrequency, + devicesScanFrequency: $deviceScanFrequency, + watchdogEnabled: $watchdogEnabled, + latitude: $latitude, + longitude: $longitude, + gpsMode: $gpsMode + }, $fog); + }); + + context('when AgentService#updateAgentConfig fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#updateAgentConfig succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getAgentConfigChangesEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentConfigChangesEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentConfigChanges').returns($response); + }); + + it('calls AgentService.getAgentConfigChanges with correct args', async () => { + await $subject; + expect(AgentService.getAgentConfigChanges).to.have.been.calledWith($fog); + }); + + context('when AgentService#getAgentConfigChanges fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentConfigChanges succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.updateAgentStatusEndPoint()', () => { + def('fog', () => 'fog!'); + + def('daemonStatus', () => 'testDaemonStatus'); + def('daemonOperatingDuration', () => 'testDaemonOperatingDuration'); + def('daemonLastStart', () => 5); + def('memoryUsage', () => 25); + def('diskUsage', () => 35); + def('cpuUsage', () => 45); + def('memoryViolation', () => true); + def('diskViolation', () => true); + def('cpuViolation', () => true); + def('microserviceStatus', () => 'RUNNING'); + def('repositoryCount', () => 5); + def('repositoryStatus', () => 'testRepositoryStatus'); + def('systemTime', () => 1555555); + def('lastStatusTime', () => 15555555); + def('ipAddress', () => 'testIpAddress'); + def('processedMessages', () => 155); + def('microserviceMessageCounts', () => 1555); + def('messageSpeed', () => 5); + def('lastCommandTime', () => 155555555); + def('tunnelStatus', () => 'testTunnelStatus'); + def('version', () => '1.5.6'); + def('isReadyToUpgrade', () => true); + def('isReadyToRollback', () => true); + + def('req', () => ({ + body: { + daemonStatus: $daemonStatus, + daemonOperatingDuration: $daemonOperatingDuration, + daemonLastStart: $daemonLastStart, + memoryUsage: $memoryUsage, + diskUsage: $diskUsage, + cpuUsage: $cpuUsage, + memoryViolation: $memoryViolation, + diskViolation: $diskViolation, + cpuViolation: $cpuViolation, + microserviceStatus: $microserviceStatus, + repositoryCount: $repositoryCount, + repositoryStatus: $repositoryStatus, + systemTime: $systemTime, + lastStatusTime: $lastStatusTime, + ipAddress: $ipAddress, + processedMessages: $processedMessages, + microserviceMessageCounts: $microserviceMessageCounts, + messageSpeed: $messageSpeed, + lastCommandTime: $lastCommandTime, + tunnelStatus: $tunnelStatus, + version: $version, + IsReadyToUpgrade: $isReadyToUpgrade, + isReadyToRollback: $isReadyToRollback + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateAgentStatusEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'updateAgentStatus').returns($response); + }); + + it('calls AgentService.updateAgentStatus with correct args', async () => { + await $subject; + expect(AgentService.updateAgentStatus).to.have.been.calledWith({ + daemonStatus: $daemonStatus, + daemonOperatingDuration: $daemonOperatingDuration, + daemonLastStart: $daemonLastStart, + memoryUsage: $memoryUsage, + diskUsage: $diskUsage, + cpuUsage: $cpuUsage, + memoryViolation: $memoryViolation, + diskViolation: $diskViolation, + cpuViolation: $cpuViolation, + microserviceStatus: $microserviceStatus, + repositoryCount: $repositoryCount, + repositoryStatus: $repositoryStatus, + systemTime: $systemTime, + lastStatusTime: $lastStatusTime, + ipAddress: $ipAddress, + processedMessages: $processedMessages, + microserviceMessageCounts: $microserviceMessageCounts, + messageSpeed: $messageSpeed, + lastCommandTime: $lastCommandTime, + tunnelStatus: $tunnelStatus, + version: $version, + IsReadyToUpgrade: $isReadyToUpgrade, + isReadyToRollback: $isReadyToRollback + }, $fog); + }); + + context('when AgentService#updateAgentStatus fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#updateAgentStatus succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getAgentMicroservicesEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentMicroservicesEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentMicroservices').returns($response); + }); + + it('calls AgentService.getAgentMicroservices with correct args', async () => { + await $subject; + expect(AgentService.getAgentMicroservices).to.have.been.calledWith($fog); + }); + + context('when AgentService#getAgentMicroservices fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentMicroservices succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getAgentMicroserviceEndPoint()', () => { + def('fog', () => 'fog!'); + def('microserviceUuid', () => 'testUuid'); + + def('req', () => ({ + params: { + microserviceUuid: $microserviceUuid, + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentMicroserviceEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentMicroservice').returns($response); + }); + + it('calls AgentService.getAgentMicroservice with correct args', async () => { + await $subject; + expect(AgentService.getAgentMicroservice).to.have.been.calledWith($microserviceUuid, $fog); + }); + + context('when AgentService#getAgentMicroservice fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentMicroservice succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getAgentRegistriesEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentRegistriesEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentRegistries').returns($response); + }); + + it('calls AgentService.getAgentRegistries with correct args', async () => { + await $subject; + expect(AgentService.getAgentRegistries).to.have.been.calledWith($fog); + }); + + context('when AgentService#getAgentRegistries fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentRegistries succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getAgentTunnelEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentTunnelEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentTunnel').returns($response); + }); + + it('calls AgentService.getAgentTunnel with correct args', async () => { + await $subject; + expect(AgentService.getAgentTunnel).to.have.been.calledWith($fog); + }); + + context('when AgentService#getAgentTunnel fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentTunnel succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getAgentStraceEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentStraceEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentStrace').returns($response); + }); + + it('calls AgentService.getAgentStrace with correct args', async () => { + await $subject; + expect(AgentService.getAgentStrace).to.have.been.calledWith($fog); + }); + + context('when AgentService#getAgentStrace fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentStrace succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('updateAgentStraceEndPoint()', () => { + def('fog', () => 'fog!'); + def('microserviceUuid', () => 'microserviceUuid'); + def('buffer', () => 'testBuffer'); + + def('straceData', [{ + microserviceUuid: $microserviceUuid, + buffer: $buffer + }]); + + def('req', () => ({ + body: { + straceData: $straceData + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateAgentStraceEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'updateAgentStrace').returns($response); + }); + + it('calls AgentService.updateAgentStrace with correct args', async () => { + await $subject; + expect(AgentService.updateAgentStrace).to.have.been.calledWith({ + straceData: $straceData + }, $fog); + }); + + context('when AgentService#updateAgentStrace fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#updateAgentStrace succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getAgentChangeVersionCommandEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getAgentChangeVersionCommandEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getAgentChangeVersionCommand').returns($response); + }); + + it('calls AgentService.getAgentChangeVersionCommand with correct args', async () => { + await $subject; + expect(AgentService.getAgentChangeVersionCommand).to.have.been.calledWith($fog); + }); + + context('when AgentService#getAgentChangeVersionCommand fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getAgentChangeVersionCommand succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('updateHalHardwareInfoEndPoint()', () => { + def('fog', () => 'fog!'); + + def('info', () => 'testInfo'); + + def('req', () => ({ + body: { + info: $info + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateHalHardwareInfoEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'updateHalHardwareInfo').returns($response); + }); + + it('calls AgentService.updateHalHardwareInfo with correct args', async () => { + await $subject; + expect(AgentService.updateHalHardwareInfo).to.have.been.calledWith({ + info: $info + }, $fog); + }); + + context('when AgentService#updateHalHardwareInfo fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#updateHalHardwareInfo succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('updateHalUsbInfoEndPoint()', () => { + def('fog', () => 'fog!'); + + def('info', () => 'testInfo'); + + def('req', () => ({ + body: { + info: $info + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateHalUsbInfoEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'updateHalUsbInfo').returns($response); + }); + + it('calls AgentService.updateHalUsbInfo with correct args', async () => { + await $subject; + expect(AgentService.updateHalUsbInfo).to.have.been.calledWith({ + info: $info + }, $fog); + }); + + context('when AgentService#updateHalUsbInfo fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#updateHalUsbInfo succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('deleteNodeEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.deleteNodeEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'deleteNode').returns($response); + }); + + it('calls AgentService.deleteNode with correct args', async () => { + await $subject; + expect(AgentService.deleteNode).to.have.been.calledWith($fog); + }); + + context('when AgentService#deleteNode fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#deleteNode succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getImageSnapshotEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getImageSnapshotEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'getImageSnapshot').returns($response); + }); + + it('calls AgentService.getImageSnapshot with correct args', async () => { + await $subject; + expect(AgentService.getImageSnapshot).to.have.been.calledWith($fog); + }); + + context('when AgentService#getImageSnapshot fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#getImageSnapshot succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('putImageSnapshotEndPoint()', () => { + def('fog', () => 'fog!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.putImageSnapshotEndPoint($req, $fog)); + + beforeEach(() => { + $sandbox.stub(AgentService, 'putImageSnapshot').returns($response); + }); + + it('calls AgentService.putImageSnapshot with correct args', async () => { + await $subject; + expect(AgentService.putImageSnapshot).to.have.been.calledWith($req, $fog); + }); + + context('when AgentService#putImageSnapshot fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AgentService#putImageSnapshot succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + +}); \ No newline at end of file diff --git a/test/src/controllers/catalog-controller.test.js b/test/src/controllers/catalog-controller.test.js new file mode 100644 index 000000000..18c611d04 --- /dev/null +++ b/test/src/controllers/catalog-controller.test.js @@ -0,0 +1,320 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const CatalogController = require('../../../src/controllers/catalog-controller'); +const CatalogService = require('../../../src/services/catalog-service'); + +describe('Catalog Controller', () => { + def('subject', () => CatalogController); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createCatalogItemEndPoint()', () => { + def('user', () => 'user!'); + + def('name', () => 'testName'); + def('description', () => 'testDescription'); + def('category', () => 'testCategory'); + def('containerImage', () => 'testContainerImage'); + def('fogTypeId', () => 'testFogTypeId'); + def('images', () => [{ + containerImage: $containerImage, + fogTypeId: $fogTypeId + }]); + def('publisher', () => 'testPublisher'); + def('diskRequired', () => 15); + def('ramRequired', () => 25); + def('picture', () => 'testPicture'); + def('isPublic', () => false); + def('registryId', () => 5); + def('inputInfoType', () => 'testInfoType'); + def('inputInfoFormat', () => 'testInfoFormat'); + def('inputType', () => ({ + infoType: $inputInfoType, + infoFormat: $inputInfoFormat + })); + def('outputInfoType', () => 'testInfoType'); + def('outputInfoFormat', () => 'testInfoFormat'); + def('outputType', () => ({ + infoType: $outputInfoType, + infoFormat: $outputInfoFormat + })); + def('configExample', () => '{}'); + + def('req', () => ({ + body: { + name: $name, + description: $description, + category: $category, + images: $images, + publisher: $publisher, + diskRequired: $diskRequired, + ramRequired: $ramRequired, + picture: $picture, + isPublic: $isPublic, + registryId: $registryId, + inputType: $inputType, + outputType: $outputType, + configExample: $configExample + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.createCatalogItemEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(CatalogService, 'createCatalogItem').returns($response); + }); + + it('calls CatalogService.createCatalogItem with correct args', async () => { + await $subject; + expect(CatalogService.createCatalogItem).to.have.been.calledWith({ + name: $name, + description: $description, + category: $category, + images: $images, + publisher: $publisher, + diskRequired: $diskRequired, + ramRequired: $ramRequired, + picture: $picture, + isPublic: $isPublic, + registryId: $registryId, + inputType: $inputType, + outputType: $outputType, + configExample: $configExample + }, $user); + }); + + context('when CatalogService#createCatalogItem fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogService#createCatalogItem succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.listCatalogItemsEndPoint()', () => { + def('user', () => 'user!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.listCatalogItemsEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(CatalogService, 'listCatalogItems').returns($response); + }); + + it('calls CatalogService.listCatalogItems with correct args', async () => { + await $subject; + expect(CatalogService.listCatalogItems).to.have.been.calledWith($user, false) + }); + + context('when CatalogService#listCatalogItems fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogService#listCatalogItems succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + + + + describe('.listCatalogItemEndPoint()', () => { + def('user', () => 'user!'); + def('id', () => 15); + + def('req', () => ({ + params: { + id: $id + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.listCatalogItemEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(CatalogService, 'getCatalogItem').returns($response); + }); + + it('calls CatalogService.getCatalogItem with correct args', async () => { + await $subject; + expect(CatalogService.getCatalogItem).to.have.been.calledWith($id, $user, false); + }); + + context('when CatalogService#getCatalogItem fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogService#getCatalogItem succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.deleteCatalogItemEndPoint()', () => { + def('user', () => 'user!'); + def('id', () => 15); + + def('req', () => ({ + params: { + id: $id + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.deleteCatalogItemEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(CatalogService, 'deleteCatalogItem').returns($response); + }); + + it('calls CatalogService.deleteCatalogItem with correct args', async () => { + await $subject; + expect(CatalogService.deleteCatalogItem).to.have.been.calledWith($id, $user, false); + }); + + context('when CatalogService#deleteCatalogItem fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogService#deleteCatalogItem succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.updateCatalogItemEndPoint()', () => { + def('user', () => 'user!'); + def('id', () => 15); + + def('name', () => 'testName'); + def('description', () => 'testDescription'); + def('category', () => 'testCategory'); + def('containerImage', () => 'testContainerImage'); + def('fogTypeId', () => 'testFogTypeId'); + def('images', () => [{ + containerImage: $containerImage, + fogTypeId: $fogTypeId + }]); + def('publisher', () => 'testPublisher'); + def('diskRequired', () => 15); + def('ramRequired', () => 25); + def('picture', () => 'testPicture'); + def('isPublic', () => false); + def('registryId', () => 5); + def('inputInfoType', () => 'testInfoType'); + def('inputInfoFormat', () => 'testInfoFormat'); + def('inputType', () => ({ + infoType: $inputInfoType, + infoFormat: $inputInfoFormat + })); + def('outputInfoType', () => 'testInfoType'); + def('outputInfoFormat', () => 'testInfoFormat'); + def('outputType', () => ({ + infoType: $outputInfoType, + infoFormat: $outputInfoFormat + })); + def('configExample', () => '{}'); + + def('req', () => ({ + params: { + id: $id + }, + body: { + name: $name, + description: $description, + category: $category, + images: $images, + publisher: $publisher, + diskRequired: $diskRequired, + ramRequired: $ramRequired, + picture: $picture, + isPublic: $isPublic, + registryId: $registryId, + inputType: $inputType, + outputType: $outputType, + configExample: $configExample + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateCatalogItemEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(CatalogService, 'updateCatalogItem').returns($response); + }); + + it('calls CatalogService.updateCatalogItem with correct args', async () => { + await $subject; + expect(CatalogService.updateCatalogItem).to.have.been.calledWith($id, { + name: $name, + description: $description, + category: $category, + images: $images, + publisher: $publisher, + diskRequired: $diskRequired, + ramRequired: $ramRequired, + picture: $picture, + isPublic: $isPublic, + registryId: $registryId, + inputType: $inputType, + outputType: $outputType, + configExample: $configExample + }, $user, false); + }); + + context('when CatalogService.updateCatalogItem fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogService.updateCatalogItem succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + +}); \ No newline at end of file diff --git a/test/src/controllers/controller-controller.test.js b/test/src/controllers/controller-controller.test.js new file mode 100644 index 000000000..fe5b70471 --- /dev/null +++ b/test/src/controllers/controller-controller.test.js @@ -0,0 +1,115 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const Controller = require('../../../src/controllers/controller'); +const ControllerService = require('../../../src/services/controller-service'); + +describe('Controller', () => { + def('subject', () => Controller); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.statusControllerEndPoint()', () => { + def('req', () => ({ + body: {} + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.statusControllerEndPoint($req)); + + beforeEach(() => { + $sandbox.stub(ControllerService, 'statusController').returns($response); + }); + + it('calls ControllerService.statusController with correct args', async () => { + await $subject; + expect(ControllerService.statusController).to.have.been.calledWith(false); + }); + + context('when ControllerService#statusController fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ControllerService#statusController succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.emailActivationEndPoint()', () => { + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.emailActivationEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ControllerService, 'emailActivation').returns($response); + }); + + it('calls ControllerService.emailActivation with correct args', async () => { + await $subject; + expect(ControllerService.emailActivation).to.have.been.calledWith(false) + }); + + context('when ControllerService#emailActivation fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ControllerService#emailActivation succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + + describe('.fogTypesEndPoint()', () => { + def('req', () => ({ + body: {} + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.fogTypesEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ControllerService, 'getFogTypes').returns($response); + }); + + it('calls ControllerService.getFogTypes with correct args', async () => { + await $subject; + expect(ControllerService.getFogTypes).to.have.been.calledWith(false); + }); + + context('when ControllerService#getFogTypes fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ControllerService#getFogTypes succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + +}); \ No newline at end of file diff --git a/test/src/controllers/user-controller.test.js b/test/src/controllers/user-controller.test.js index 815a45b0a..617ff8537 100644 --- a/test/src/controllers/user-controller.test.js +++ b/test/src/controllers/user-controller.test.js @@ -113,14 +113,11 @@ describe('User Controller', () => { } })); def('response', () => Promise.resolve()); - def('encryptedPassword', () => 'encryptedPassword'); def('validatorResponse', () => Promise.resolve(true)); - def('encryptTextResponse', () => $encryptedPassword); def('subject', () => $subject.userLoginEndPoint($req)); beforeEach(() => { $sandbox.stub(Validator, 'validate').returns($validatorResponse); - $sandbox.stub(AppHelper, 'encryptText').returns($encryptTextResponse); $sandbox.stub(UserService, 'login').returns($response); }); @@ -141,43 +138,30 @@ describe('User Controller', () => { }); context('when Validator#validate() succeeds', () => { - it('calls AppHelper#encryptText() with correct args', async () => { + it('calls UserService.login with correct args', async () => { await $subject; - expect(AppHelper.encryptText).to.have.been.calledWith($password, $email); + expect(UserService.login).to.have.been.calledWith({ + email: $email, + password: $password + }, false) }); - context('when AppHelper#encryptText() fails', () => { - it('fails', () => { - return expect($subject).to.eventually.equal(undefined); - }); - }); - - context('when AppHelper#encryptText() succeeds', () => { - it('calls UserService.login with correct args', async () => { - await $subject; - expect(UserService.login).to.have.been.calledWith({ - email: $email, - password: $encryptedPassword - }, false) - }); - - context('when UserService#login fails', () => { - const error = 'Error!'; + context('when UserService#login fails', () => { + const error = 'Error!'; - def('response', () => Promise.reject(error)); + def('response', () => Promise.reject(error)); - it(`fails with "${error}"`, () => { - return expect($subject).to.be.rejectedWith(error) - }) - }); - - context('when UserService#login succeeds', () => { - it(`succeeds`, () => { - return expect($subject).to.eventually.equal(undefined) - }) + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) }) }); + context('when UserService#login succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); }); @@ -354,7 +338,9 @@ describe('User Controller', () => { def('user', () => 'user!'); def('req', () => ({ - body: {} + body: { + force: true + } })); def('response', () => Promise.resolve()); def('subject', () => $subject.deleteUserProfileEndPoint($req, $user)); @@ -365,7 +351,7 @@ describe('User Controller', () => { it('calls UserService.deleteUser with correct args', async () => { await $subject; - expect(UserService.deleteUser).to.have.been.calledWith($user, false); + expect(UserService.deleteUser).to.have.been.calledWith(true, $user, false); }); context('when UserService#deleteUser fails', () => { @@ -399,13 +385,10 @@ describe('User Controller', () => { })); def('response', () => Promise.resolve()); def('validatorResponse', () => Promise.resolve(true)); - def('encryptedPassword', () => 'encryptedPassword'); - def('encryptTextResponse', () => $encryptedPassword); def('subject', () => $subject.updateUserPasswordEndPoint($req, $user)); beforeEach(() => { $sandbox.stub(Validator, 'validate').returns($validatorResponse); - $sandbox.stub(AppHelper, 'encryptText').returns($encryptTextResponse); $sandbox.stub(UserService, 'updateUserPassword').returns($response); }); @@ -426,56 +409,29 @@ describe('User Controller', () => { }); context('when Validator#validate() succeeds', () => { - it('calls AppHelper#encryptText() for old password with correct args', async () => { + it('calls UserService.updateUserPassword with correct args', async () => { await $subject; - expect(AppHelper.encryptText).to.have.been.calledWith($oldPassword, $user.email); - }); - - context('when AppHelper#encryptText() for old password fails', () => { - it('fails', () => { - return expect($subject).to.eventually.equal(undefined); - }); + expect(UserService.updateUserPassword).to.have.been.calledWith({ + oldPassword: $oldPassword, + newPassword: $newPassword + }, $user, false); }); - context('when AppHelper#encryptText() for old password succeeds', () => { - it('calls AppHelper#encryptText() for new password with correct args', async () => { - await $subject; - expect(AppHelper.encryptText).to.have.been.calledWith($newPassword, $user.email); - }); + context('when UserService#updateUserPassword fails', () => { + const error = 'Error!'; - context('when AppHelper#encryptText() for new password fails', () => { - it('fails', () => { - return expect($subject).to.eventually.equal(undefined); - }); - }); - - context('when AppHelper#encryptText() for new password succeeds', () => { - it('calls UserService.updateUserPassword with correct args', async () => { - await $subject; - expect(UserService.updateUserPassword).to.have.been.calledWith({ - oldPassword: $encryptedPassword, - newPassword: $encryptedPassword - }, $user, false); - }); - - context('when UserService#updateUserPassword fails', () => { - const error = 'Error!'; - - def('response', () => Promise.reject(error)); - - it(`fails with "${error}"`, () => { - return expect($subject).to.be.rejectedWith(error) - }) - }); - - context('when UserService#updateUserPassword succeeds', () => { - it(`succeeds`, () => { - return expect($subject).to.eventually.equal(undefined) - }) - }) - }); + def('response', () => Promise.reject(error)); + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) }); + + context('when UserService#updateUserPassword succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) }); }); diff --git a/test/src/helpers/app-helpers.test.js b/test/src/helpers/app-helpers.test.js index 69935d8d2..f832ef517 100644 --- a/test/src/helpers/app-helpers.test.js +++ b/test/src/helpers/app-helpers.test.js @@ -1,93 +1,88 @@ -const crypto = require('crypto') -const { expect } = require('chai') -const fs = require('fs') -const path = require('path') -const portscanner = require('portscanner') -const sinon = require('sinon') +const crypto = require('crypto'); +const { expect } = require('chai'); +const fs = require('fs'); +const path = require('path'); +const portscanner = require('portscanner'); +const sinon = require('sinon'); -const AppHelpers = require('../../../src/helpers/app-helper') -const Config = require('../../../src/config') +const AppHelpers = require('../../../src/helpers/app-helper'); +const Config = require('../../../src/config'); describe('App Helpers', () => { - const text = 'some-text' - const salt = 'kosher-salt' - const encrypted = '17f4faa5c532708c8f' - - def('subject', () => AppHelpers) - def('sandbox', () => sinon.createSandbox()) + const text = 'some-text'; + const salt = 'kosher-salt'; + const encrypted = '18f4faa5c532708c8f'; + const processedSalt = 'c2cd22c1a8133704f09fc8a218088b1b'; + const encryptedPasswordLine = '17f4faa5c532708c8f:18f4faa5c532708c8f'; + + def('subject', () => AppHelpers); + def('sandbox', () => sinon.createSandbox()); def('cipher', () => ({ update: $sandbox.stub().returns(''), final: $sandbox.stub().returns(encrypted) - })) + })); def('decipher', () => ({ update: $sandbox.stub().returns(''), final: $sandbox.stub().returns(text) - })) + })); - afterEach(() => $sandbox.restore()) + afterEach(() => $sandbox.restore()); describe('.encryptText()', () => { - def('subject', () => $subject.encryptText(text, salt)) + def('subject', () => $subject.encryptText(text, salt)); + def('iv', () => '17f4faa5c532708c8f'); beforeEach(() => { - $sandbox.stub(crypto, 'createCipher').returns($cipher) - }) + $sandbox.stub(crypto, 'randomBytes').returns($iv); + $sandbox.stub(crypto, 'createCipheriv').returns($cipher); + }); - it('calls crypto#createCipher() with correct args', () => { + it('calls crypto#createCipheriv() with correct args', () => { $subject - expect(crypto.createCipher).to.have.been.calledWith('aes-256-ctr', salt) - }) + expect(crypto.createCipheriv).to.have.been.calledWith('aes-256-ctr', processedSalt, $iv) + }); it('calls crypto.cipher#update() with correct args', () => { $subject expect($cipher.update).to.have.been.calledWith(text, 'utf8', 'hex') - }) + }); it('calls crypto.cipher#final() with correct args', () => { $subject expect($cipher.final).to.have.been.calledWith('hex') - }) + }); it('returns the encrypted text', () => { - expect($subject).to.equal(encrypted) + expect($subject).to.equal(encryptedPasswordLine) }) - }) + }); describe('.decryptText()', () => { - def('subject', () => $subject.decryptText(encrypted, salt)) + def('iv', () => '17f4faa5c532708c8f'); + def('subject', () => $subject.decryptText(encryptedPasswordLine, salt)); beforeEach(() => { - $sandbox.stub(crypto, 'createDecipher').returns($decipher) - }) - - it('calls crypto#createDecipher() with correct args', () => { - $subject - expect(crypto.createDecipher).to.have.been.calledWith('aes-256-ctr', salt) - }) - - it('calls crypto.decipher#update() with correct args', () => { - $subject - expect($decipher.update).to.have.been.calledWith(encrypted, 'hex', 'utf8') - }) + $sandbox.stub(crypto, 'createDecipheriv').returns($decipher); + }); it('calls crypto.decipher#final() with correct args', () => { $subject expect($decipher.final).to.have.been.calledWith('utf8') - }) + }); it('returns the decrypted text', () => { expect($subject).to.equal(text) }) - }) + }); describe('.generateRandomString()', () => { - def('size', () => 12) + def('size', () => 12); context('when size is greater than zero', () => { it('returns a random string with length of size', () => { expect(AppHelpers.generateRandomString($size)).to.have.lengthOf($size) }) - }) + }); context('when size is zero', () => { def('size', () => 0) diff --git a/test/src/services/access-token-service.test.js b/test/src/services/access-token-service.test.js new file mode 100644 index 000000000..ba058e0af --- /dev/null +++ b/test/src/services/access-token-service.test.js @@ -0,0 +1,83 @@ +const { expect } = require('chai'); +const sinon = require('sinon'); + +const AccessTokenManager = require('../../../src/sequelize/managers/access-token-manager'); +const AccessTokenService = require('../../../src/services/access-token-service'); + +describe('AccessToken Service', () => { + def('subject', () => AccessTokenService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createAccessToken()', () => { + const accessToken = "accessToken"; + const transaction = {}; + const error = 'Error!'; + + def('accessTokenObj', () => 'accessTokenResponse'); + + def('subject', () => $subject.createAccessToken(accessToken, transaction)); + def('accessTokenResponse', () => Promise.resolve($accessTokenObj)); + + beforeEach(() => { + $sandbox.stub(AccessTokenManager, 'create').returns($accessTokenResponse); + }); + + it('calls AccessTokenManager#create() with correct args', async () => { + await $subject; + expect(AccessTokenManager.create).to.have.been.calledWith(accessToken, transaction); + }); + + context('when AccessTokenManager#create() fails', () => { + def('accessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AccessTokenManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal($accessTokenObj) + }) + }) + }); + + describe('.removeAccessTokenByUserId()', () => { + const userId = 15; + const transaction = {}; + const error = 'Error!'; + + def('removeTokenObj', () => 'removeToken'); + + def('subject', () => $subject.removeAccessTokenByUserId(userId, transaction)); + def('removeTokenResponse', () => Promise.resolve($removeTokenObj)); + + beforeEach(() => { + $sandbox.stub(AccessTokenManager, 'delete').returns($removeTokenResponse); + }); + + it('calls AccessTokenManager#delete() with correct args', async () => { + await $subject; + expect(AccessTokenManager.delete).to.have.been.calledWith({ + userId: userId + }, transaction); + }); + + context('when AccessTokenManager#delete() fails', () => { + def('removeTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when AccessTokenManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal($removeTokenObj) + }) + }) + }) + +}); \ No newline at end of file diff --git a/test/src/services/agent-service.test.js b/test/src/services/agent-service.test.js new file mode 100644 index 000000000..a57a6e348 --- /dev/null +++ b/test/src/services/agent-service.test.js @@ -0,0 +1,619 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const AgentService = require('../../../src/services/agent-service'); +const Validator = require('../../../src/schemas'); +const FogProvisionKeyManager = require('../../../src/sequelize/managers/iofog-provision-key-manager'); +const MicroserviceManager = require('../../../src/sequelize/managers/microservice-manager'); +const ioFogManager = require('../../../src/sequelize/managers/iofog-manager'); +const FogAccessTokenService = require('../../../src/services/iofog-access-token-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const MicroserviceStatusManager = require('../../../src/sequelize/managers/microservice-status-manager'); +const MicroserviceService = require('../../../src/services/microservices-service'); + +describe('Agent Service', () => { + def('subject', () => AgentService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.agentProvision()', () => { + const provisionData = { + type: 1, + key: 'dpodkqwdpj' + }; + + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + def('token', () => 'testToken'); + + def('provisionResponse', () => 'provisionResponse'); + + def('subject', () => $subject.agentProvision(provisionData, transaction)); + def('accessTokenResponse', () => Promise.resolve($accessTokenObj)); + + def('validatorResponse', () => Promise.resolve(true)); + def('fogProvisionKeyManagerResponse', () => Promise.resolve({ + uuid: $uuid + })); + def('microserviceManagerResponse', () => Promise.resolve()); + def('iofogManagerResponse', () => Promise.resolve({ + uuid: $uuid + })); + def('fogAccessTokenServiceGenerateResponse', () => Promise.resolve({ + token: $token + })); + def('fogAccessTokenServiceUpdateResponse', () => Promise.resolve()); + def('iofogManagerUpdateResponse', () => Promise.resolve()); + def('fogProvisionKeyManagerDeleteResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(FogProvisionKeyManager, 'findOne').returns($fogProvisionKeyManagerResponse); + $sandbox.stub(MicroserviceManager, 'findAllWithDependencies').returns($microserviceManagerResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($iofogManagerResponse); + $sandbox.stub(FogAccessTokenService, 'generateAccessToken').returns($fogAccessTokenServiceGenerateResponse); + $sandbox.stub(FogAccessTokenService, 'updateAccessToken').returns($fogAccessTokenServiceUpdateResponse); + $sandbox.stub(ioFogManager, 'update').returns($iofogManagerUpdateResponse); + $sandbox.stub(FogProvisionKeyManager, 'delete').returns($fogProvisionKeyManagerDeleteResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(provisionData, Validator.schemas.agentProvision); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls FogProvisionKeyManager.findOne with correct args', async () => { + await $subject; + expect(FogProvisionKeyManager.findOne).to.have.been.calledWith({ + provisionKey: provisionData.key + }, transaction); + }); + + context('when FogProvisionKeyManager#findOne fails', () => { + const error = 'Error!'; + + def('fogProvisionKeyManagerResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogManager#findOne succeeds', () => { + it('calls ioFogManager.findOne with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith({ + uuid: $fogProvisionKeyManagerResponse.uuid + }, transaction); + }); + + context('when ioFogManager#findOne fails', () => { + const error = 'Error!'; + + def('iofogManagerResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogManager#findOne succeeds', () => { + it('calls MicroserviceManager.findAllWithDependencies with correct args', async () => { + await $subject; + expect(MicroserviceManager.findAllWithDependencies).to.have.been.calledWith({ + iofogUuid: $uuid + }, {}, transaction); + }); + + context('when MicroserviceManager#findAllWithDependencies fails', () => { + const error = 'Error!'; + + def('microserviceManagerResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findAllWithDependencies succeeds', () => { + it('calls FogAccessTokenService.generateAccessToken with correct args', async () => { + await $subject; + expect(FogAccessTokenService.generateAccessToken).to.have.been.calledWith(transaction); + }); + + context('when FogAccessTokenService#generateAccessToken fails', () => { + const error = 'Error!'; + + def('fogAccessTokenServiceGenerateResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FogAccessTokenService#generateAccessToken succeeds', () => { + it('calls FogAccessTokenService.updateAccessToken with correct args', async () => { + await $subject; + expect(FogAccessTokenService.updateAccessToken).to.have.been.calledWith($uuid, { + token: $token + }, transaction); + }); + + context('when FogAccessTokenService#updateAccessToken fails', () => { + const error = 'Error!'; + + def('fogAccessTokenServiceUpdateResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FogAccessTokenService#updateAccessToken succeeds', () => { + it('calls ioFogManager.update with correct args', async () => { + await $subject; + expect(ioFogManager.update).to.have.been.calledWith({ + uuid: $uuid + }, { + fogTypeId: provisionData.type + }, transaction); + }); + + context('when ioFogManager#update fails', () => { + const error = 'Error!'; + + def('iofogManagerUpdateResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogManager#update succeeds', () => { + it('calls FogProvisionKeyManager.delete with correct args', async () => { + await $subject; + expect(FogProvisionKeyManager.delete).to.have.been.calledWith({ + provisionKey: provisionData.key + }, transaction); + }); + + context('when FogProvisionKeyManager#delete fails', () => { + const error = 'Error!'; + + def('fogProvisionKeyManagerDeleteResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FogProvisionKeyManager#delete succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.have.property('uuid') && + expect($subject).to.eventually.have.property('token'); + }) + }) + }) + }) + }) + }) + }) + }) + }); + }); + + describe('.updateAgentConfig()', () => { + const agentConfig = { + networkInterface: "testNetworkInterface", + dockerUrl: "testDockerUrl", + diskLimit: 5, + diskDirectory: "testDiskDirectory", + memoryLimit: 15, + cpuLimit: 25, + logLimit: 35, + logDirectory: 'testLogDirectory', + logFileCount: 15, + statusFrequency: 40, + changeFrequency: 45, + deviceScanFrequency: 50, + watchdogEnabled: false, + latitude: 35, + longitude: 36, + gpsMode: 'testGpsMode' + }; + + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('token', () => 'testToken'); + + def('updateAgentResponse', () => 'updateAgentResponse'); + + def('subject', () => $subject.updateAgentConfig(agentConfig, $fog, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => agentConfig); + def('iofogManagerUpdateResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(ioFogManager, 'update').returns($iofogManagerUpdateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(agentConfig, Validator.schemas.updateAgentConfig); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper.deleteUndefinedFields with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(agentConfig); + }); + + context('when AppHelper#deleteUndefinedFields fails', () => { + const error = 'Error!'; + + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when AppHelper#deleteUndefinedFields succeeds', () => { + it('calls ioFogManager.update with correct args', async () => { + await $subject; + expect(ioFogManager.update).to.have.been.calledWith({ + uuid: $uuid + }, agentConfig, transaction); + }); + + context('when ioFogManager#update fails', () => { + const error = 'Error!'; + + def('iofogManagerUpdateResponse', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogManager#update succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + + }) + }); + }); + + describe('.getAgentConfigChanges()', () => { + const configChanges = { + config: undefined, + version: undefined, + reboot: undefined, + deleteNode: undefined, + microserviceList: undefined, + microserviceConfig: undefined, + routing: undefined, + registries: undefined, + tunnel: undefined, + diagnostics: undefined, + isImageSnapshot: undefined + }; + + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('token', () => 'testToken'); + + def('subject', () => $subject.getAgentConfigChanges($fog, transaction)); + + def('getByFogIdResponse', () => 'getByFogIdResponse'); + def('updateIfChangedResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(ChangeTrackingService, 'getByFogId').returns($getByFogIdResponse); + $sandbox.stub(ChangeTrackingService, 'updateIfChanged').returns($updateIfChangedResponse); + }); + + it('calls ChangeTrackingService#getByFogId() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.getByFogId).to.have.been.calledWith($uuid, transaction); + }); + + context('when ChangeTrackingService#getByFogId() fails', () => { + def('getByFogIdResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#getByFogId() succeeds', () => { + it('calls ChangeTrackingService.updateIfChanged with correct args', async () => { + await $subject; + expect(ChangeTrackingService.updateIfChanged).to.have.been.calledWith($uuid, + ChangeTrackingService.events.clean, transaction); + }); + + context('when ChangeTrackingService#updateIfChanged fails', () => { + const error = 'Error!'; + + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when ChangeTrackingService#updateIfChanged succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.deep.equal(configChanges); + }) + }) + }); + }); + + describe('.updateAgentStatus()', () => { + const microservicesStatus = "[{\"containerId\":\"testContainerId\", \"status\":\"RUNNING\"" + + ",\"startTime\":5325543453454,\"operatingDuration\":534535435435,\"cpuUsage\":35,\"memoryUsage\":45}]"; + + const microserviceStatus = { + "containerId": "testContainerId", + "status": "RUNNING", + "startTime": 5325543453454, + "operatingDuration": 534535435435, + "cpuUsage": 35, + "memoryUsage": 45 + }; + + const microserviceStatusArray = [microserviceStatus]; + + const fogStatus = { + daemonStatus: 'RUNNING', + daemonOperatingDuration: 25, + daemonLastStart: 15325235253, + memoryUsage: 15, + diskUsage: 16, + cpuUsage: 17, + memoryViolation: false, + diskViolation: false, + cpuViolation: false, + repositoryCount: 5, + repositoryStatus: 'testStatus', + systemTime: 15325235253, + lastStatusTime: 15325235253, + ipAddress: 'testIpAddress', + processedMessages: 155, + microserviceMessageCounts: 'testMessageCounts', + messageSpeed: 255, + lastCommandTime: 15325235253, + tunnelStatus: 'testTunnelStatus', + version: '1.0.0', + isReadyToUpgrade: false, + isReadyToRollback: false, + microserviceStatus: microservicesStatus + }; + + const agentStatus = { + daemonStatus: 'RUNNING', + daemonOperatingDuration: 25, + daemonLastStart: 15325235253, + memoryUsage: 15, + diskUsage: 16, + cpuUsage: 17, + memoryViolation: false, + diskViolation: false, + cpuViolation: false, + repositoryCount: 5, + repositoryStatus: 'testStatus', + systemTime: 15325235253, + lastStatusTime: 15325235253, + ipAddress: 'testIpAddress', + processedMessages: 155, + microserviceMessageCounts: 'testMessageCounts', + messageSpeed: 255, + lastCommandTime: 15325235253, + tunnelStatus: 'testTunnelStatus', + version: '1.0.0', + isReadyToUpgrade: false, + isReadyToRollback: false + }; + + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('token', () => 'testToken'); + + def('subject', () => $subject.updateAgentStatus(fogStatus, $fog, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => agentStatus); + def('deleteUndefinedFieldsResponse2', () => microserviceStatus); + def('updateResponse', () => Promise.resolve()); + def('jsonParseResponse', () => microserviceStatusArray); + def('updateMicroserviceStatusesResponse', () => Promise.resolve()); + def('deleteNotRunningResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields') + .onFirstCall().returns($deleteUndefinedFieldsResponse) + .onSecondCall().returns($deleteUndefinedFieldsResponse2); + $sandbox.stub(ioFogManager, 'update').returns($updateResponse); + $sandbox.stub(JSON, 'parse').returns($jsonParseResponse); + $sandbox.stub(MicroserviceStatusManager, 'update').returns($updateMicroserviceStatusesResponse); + $sandbox.stub(MicroserviceService, 'deleteNotRunningMicroservices').returns($deleteNotRunningResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogStatus, Validator.schemas.updateAgentStatus); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper.deleteUndefinedFields with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(agentStatus); + }); + + context('when AppHelper#deleteUndefinedFields fails', () => { + const error = 'Error!'; + + def('$deleteUndefinedFieldsResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when AppHelper#deleteUndefinedFields succeeds', () => { + it('calls ioFogManager.update with correct args', async () => { + await $subject; + expect(ioFogManager.update).to.have.been.calledWith({ + uuid: $uuid + }, agentStatus, transaction); + }); + + context('when ioFogManager#update fails', () => { + const error = 'Error!'; + + def('updateResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when ioFogManager#update succeeds', () => { + it('calls JSON.parse with correct args', async () => { + await $subject; + expect(JSON.parse).to.have.been.calledWith(fogStatus.microserviceStatus); + }); + + context('when JSON#parse fails', () => { + const error = 'Error!'; + + def('jsonParseResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when JSON#parse succeeds', () => { + it('calls AppHelper.deleteUndefinedFields with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(microserviceStatus); + }); + + context('when AppHelper#deleteUndefinedFields fails', () => { + const error = 'Error!'; + + def('$deleteUndefinedFieldsResponse2', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when AppHelper#deleteUndefinedFields succeeds', () => { + it('calls MicroserviceStatusManager.update with correct args', async () => { + await $subject; + expect(MicroserviceStatusManager.update).to.have.been.calledWith({ + microserviceUuid: microserviceStatus.id + }, microserviceStatus, transaction); + }); + + context('when MicroserviceStatusManager#update fails', () => { + const error = 'Error!'; + + def('updateMicroserviceStatusesResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when MicroserviceStatusManager#update succeeds', () => { + it('calls MicroserviceService.deleteNotRunningMicroservices with correct args', async () => { + await $subject; + expect(MicroserviceService.deleteNotRunningMicroservices).to.have.been.calledWith(transaction); + }); + + context('when MicroserviceService#deleteNotRunningMicroservices fails', () => { + const error = 'Error!'; + + def('deleteNotRunningResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when MicroserviceService#deleteNotRunningMicroservices succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }) + }); + }); + + +}); \ No newline at end of file