From c0c53894b8aba89b89a17dbd9200eaa93165ad4f Mon Sep 17 00:00:00 2001 From: Railag Date: Wed, 12 Dec 2018 16:06:08 +0300 Subject: [PATCH 01/39] fix(bug) fixed system microservices (HAL, bluetooth) launch (EWC-413) (#424) --- src/sequelize/managers/microservice-manager.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sequelize/managers/microservice-manager.js b/src/sequelize/managers/microservice-manager.js index 8f60db033..2e6729cae 100644 --- a/src/sequelize/managers/microservice-manager.js +++ b/src/sequelize/managers/microservice-manager.js @@ -25,6 +25,7 @@ const User = models.User; const Routing = models.Routing; const Registry = models.Registry; const MicroserviceStatus = models.MicroserviceStatus; +const Op = require('sequelize').Op; const microserviceExcludedFields = [ 'configLastUpdated', @@ -135,18 +136,27 @@ class MicroserviceManager extends BaseManager { attributes: ['url'] } ], - attributes: ['picture'] + attributes: ['picture', 'category'] }, { model: Flow, as: 'flow', - required: true, + required: false, attributes: ['isActivated'] } ], where: { iofogUuid: iofogUuid, - '$flow.is_activated$': true + [Op.or]: + [ + { + '$flow.is_activated$': true + }, + { + '$catalogItem.category$': {[Op.eq]: 'SYSTEM'} + } + ] + } }, {transaction: transaction}) } From 45f07d62d49de8dbe62c9122bfb545ff676a77c1 Mon Sep 17 00:00:00 2001 From: epankou <35571073+epankou@users.noreply.github.com> Date: Thu, 13 Dec 2018 13:01:23 +0300 Subject: [PATCH 02/39] fix(bug) added registry email validation (EWC-418) --- src/schemas/registry.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/schemas/registry.js b/src/schemas/registry.js index de986a302..6acd7e6f8 100644 --- a/src/schemas/registry.js +++ b/src/schemas/registry.js @@ -19,7 +19,10 @@ const registryCreate = { "isPublic": {"type": "boolean"}, "username": {"type": "string", "minLength": 1}, "password": {"type": "string"}, - "email": {"type": "string"}, + "email": { + "type": "string", + "pattern": "^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$" + }, "requiresCert": {"type": "boolean"}, "certificate": {"type": "string"} }, @@ -45,7 +48,10 @@ const registryUpdate = { "isPublic": {"type": "boolean"}, "username": {"type": "string", "minLength": 1}, "password": {"type": "string"}, - "email": {"type": "string"}, + "email": { + "type": "string", + "pattern": "^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$" + }, "requiresCert": {"type": "boolean"}, "certificate": {"type": "string"} }, From c0c2b1986b92d2b93cb676e86ffc8a65431cc926 Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 13 Dec 2018 16:29:56 +0300 Subject: [PATCH 03/39] test(core) unit tests for iofog-controller (EWC-382) (#426) --- src/controllers/iofog-controller.js | 37 +- test/src/controllers/iofog-controller.test.js | 549 ++++++++++++++++++ .../microservices-controller.test.js | 10 +- 3 files changed, 574 insertions(+), 22 deletions(-) create mode 100644 test/src/controllers/iofog-controller.test.js diff --git a/src/controllers/iofog-controller.js b/src/controllers/iofog-controller.js index 72d27e9c0..52727dc01 100644 --- a/src/controllers/iofog-controller.js +++ b/src/controllers/iofog-controller.js @@ -30,16 +30,17 @@ async function updateFogEndPoint(req, user) { } async function deleteFogEndPoint(req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - const deleteFog = {}; - deleteFog.uuid = req.params.uuid; + const deleteFog = { + uuid: req.params.uuid + }; return await FogService.deleteFog(deleteFog, user, false) } async function getFogEndPoint(req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - const getFog = {}; - getFog.uuid = req.params.uuid; + const getFog = { + uuid: req.params.uuid + }; + return await FogService.getFogWithTransaction(getFog, user, false) } @@ -50,24 +51,27 @@ async function getFogListEndPoint(req, user) { } async function generateProvisionKeyEndPoint(req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - const fog = {}; - fog.uuid = req.params.uuid; + const fog = { + uuid: req.params.uuid + }; + return await FogService.generateProvisioningKey(fog, user, false) } async function setFogVersionCommandEndPoint(req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - const fogVersionCommand = {}; - fogVersionCommand.uuid = req.params.uuid; - fogVersionCommand.versionCommand = req.params.versionCommand; + const fogVersionCommand = { + uuid: req.params.uuid, + versionCommand: req.params.versionCommand + }; + return await FogService.setFogVersionCommand(fogVersionCommand, user, false) } async function setFogRebootCommandEndPoint(req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - const fog = {}; - fog.uuid = req.params.uuid; + const fog = { + uuid: req.params.uuid + }; + return await FogService.setFogRebootCommand(fog, user, false) } @@ -81,7 +85,6 @@ async function getHalHardwareInfoEndPoint(req, user) { return await FogService.getHalHardwareInfo(uuidObj, user, false); } - async function getHalUsbInfoEndPoint(req, user) { const uuidObj = { uuid: req.params.uuid diff --git a/test/src/controllers/iofog-controller.test.js b/test/src/controllers/iofog-controller.test.js new file mode 100644 index 000000000..9405c625d --- /dev/null +++ b/test/src/controllers/iofog-controller.test.js @@ -0,0 +1,549 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ioFogController = require('../../../src/controllers/iofog-controller'); +const ioFogService = require('../../../src/services/iofog-service'); +const qs = require('qs'); + + +describe('ioFog Controller', () => { + def('subject', () => ioFogController); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createFogEndPoint()', () => { + def('user', () => 'user!'); + + def('name', () => 'testName'); + def('location', () => 'testLocation'); + def('latitude', () => 15); + def('longitude', () => 16); + def('description', () => 'testDescription'); + def('dockerUrl', () => 'testDockerUrl'); + def('diskLimit', () => 25); + def('diskDirectory', () => 'testDiskDirectory'); + def('memoryLimit', () => 35); + def('cpuLimit', () => 45); + def('logLimit', () => 15); + def('logDirectory', () => 'testLogDirectory'); + def('logFileCount', () => 8); + def('statusFrequency', 6); + def('changeFrequency', 18); + def('deviceScanFrequency', 28); + def('bluetoothEnabled', () => false); + def('watchdogEnabled', () => true); + def('abstractedHardwareEnabled', () => false); + def('fogType', () => 0); + + def('req', () => ({ + body: { + name: $name, + location: $location, + latitude: $latitude, + longitude: $longitude, + description: $description, + dockerUrl: $dockerUrl, + diskLimit: $diskLimit, + diskDirectory: $diskDirectory, + memoryLimit: $memoryLimit, + cpuLimit: $cpuLimit, + logLimit: $logLimit, + logDirectory: $logDirectory, + logFileCount: $logFileCount, + statusFrequency: $statusFrequency, + changeFrequency: $changeFrequency, + deviceScanFrequency: $deviceScanFrequency, + bluetoothEnabled: $bluetoothEnabled, + watchdogEnabled: $watchdogEnabled, + abstractedHardwareEnabled: $abstractedHardwareEnabled, + fogType: $fogType + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.createFogEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'createFog').returns($response); + }); + + it('calls ioFogService.createFog with correct args', async () => { + await $subject; + expect(ioFogService.createFog).to.have.been.calledWith({ + name: $name, + location: $location, + latitude: $latitude, + longitude: $longitude, + description: $description, + dockerUrl: $dockerUrl, + diskLimit: $diskLimit, + diskDirectory: $diskDirectory, + memoryLimit: $memoryLimit, + cpuLimit: $cpuLimit, + logLimit: $logLimit, + logDirectory: $logDirectory, + logFileCount: $logFileCount, + statusFrequency: $statusFrequency, + changeFrequency: $changeFrequency, + deviceScanFrequency: $deviceScanFrequency, + bluetoothEnabled: $bluetoothEnabled, + watchdogEnabled: $watchdogEnabled, + abstractedHardwareEnabled: $abstractedHardwareEnabled, + fogType: $fogType + }, $user, false); + }); + + context('when ioFogService#createFog fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#createFog succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.updateFogEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('name', () => 'testName'); + def('location', () => 'testLocation'); + def('latitude', () => 15); + def('longitude', () => 16); + def('description', () => 'testDescription'); + def('dockerUrl', () => 'testDockerUrl'); + def('diskLimit', () => 25); + def('diskDirectory', () => 'testDiskDirectory'); + def('memoryLimit', () => 35); + def('cpuLimit', () => 45); + def('logLimit', () => 15); + def('logDirectory', () => 'testLogDirectory'); + def('logFileCount', () => 8); + def('statusFrequency', 6); + def('changeFrequency', 18); + def('deviceScanFrequency', 28); + def('bluetoothEnabled', () => false); + def('watchdogEnabled', () => true); + def('abstractedHardwareEnabled', () => false); + def('fogType', () => 0); + + def('req', () => ({ + params: { + uuid: $uuid + }, + body: { + name: $name, + location: $location, + latitude: $latitude, + longitude: $longitude, + description: $description, + dockerUrl: $dockerUrl, + diskLimit: $diskLimit, + diskDirectory: $diskDirectory, + memoryLimit: $memoryLimit, + cpuLimit: $cpuLimit, + logLimit: $logLimit, + logDirectory: $logDirectory, + logFileCount: $logFileCount, + statusFrequency: $statusFrequency, + changeFrequency: $changeFrequency, + deviceScanFrequency: $deviceScanFrequency, + bluetoothEnabled: $bluetoothEnabled, + watchdogEnabled: $watchdogEnabled, + abstractedHardwareEnabled: $abstractedHardwareEnabled, + fogType: $fogType + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateFogEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'updateFog').returns($response); + }); + + it('calls ioFogService.updateFog with correct args', async () => { + await $subject; + expect(ioFogService.updateFog).to.have.been.calledWith({ + uuid: $uuid, + name: $name, + location: $location, + latitude: $latitude, + longitude: $longitude, + description: $description, + dockerUrl: $dockerUrl, + diskLimit: $diskLimit, + diskDirectory: $diskDirectory, + memoryLimit: $memoryLimit, + cpuLimit: $cpuLimit, + logLimit: $logLimit, + logDirectory: $logDirectory, + logFileCount: $logFileCount, + statusFrequency: $statusFrequency, + changeFrequency: $changeFrequency, + deviceScanFrequency: $deviceScanFrequency, + bluetoothEnabled: $bluetoothEnabled, + watchdogEnabled: $watchdogEnabled, + abstractedHardwareEnabled: $abstractedHardwareEnabled, + fogType: $fogType + }, $user, false) + }); + + context('when ioFogService#updateFog fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#updateFog succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + + describe('.deleteFogEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'newTestUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.deleteFogEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'deleteFog').returns($response); + }); + + it('calls ioFogService.deleteFog with correct args', async () => { + await $subject; + expect(ioFogService.deleteFog).to.have.been.calledWith({uuid: $uuid}, $user, false); + }); + + context('when ioFogService#deleteFog fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#deleteFog succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getFogEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getFogEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'getFogWithTransaction').returns($response); + }); + + it('calls ioFogService.getFogWithTransaction with correct args', async () => { + await $subject; + expect(ioFogService.getFogWithTransaction).to.have.been.calledWith({uuid: $uuid}, $user, false); + }); + + context('when ioFogService#getFogWithTransaction fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#getFogWithTransaction succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getFogListEndPoint()', () => { + def('user', () => 'user!'); + def('filters', () => 'filtersQuery'); + + def('req', () => ({ + query: { + filters: $filters + } + })); + def('response', () => Promise.resolve()); + def('queryParseResponse', () => ({ + filters: $filters + })); + def('subject', () => $subject.getFogListEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(qs, 'parse').returns($queryParseResponse); + $sandbox.stub(ioFogService, 'getFogList').returns($response); + }); + + it('calls qs.parse with correct args', async () => { + await $subject; + expect(qs.parse).to.have.been.calledWith($queryParseResponse); + }); + + context('when qs.parse fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when qs.parse succeeds', () => { + it('calls ioFogService.getFogList with correct args', async () => { + await $subject; + expect(ioFogService.getFogList).to.have.been.calledWith($filters, $user, false); + }); + + context('when ioFogService.getFogList fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService.getFogList succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }); + + describe('.generateProvisionKeyEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.generateProvisioningKeyEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'generateProvisioningKey').returns($response); + }); + + it('calls ioFogService.generateProvisioningKey with correct args', async () => { + await $subject; + expect(ioFogService.generateProvisioningKey).to.have.been.calledWith({uuid: $uuid}, $user, false); + }); + + context('when ioFogService#generateProvisioningKey fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#generateProvisioningKey succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('setFogVersionCommandEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + def('versionCommand', () => 'version'); + + def('req', () => ({ + params: { + uuid: $uuid, + versionCommand: $versionCommand + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.setFogVersionCommandEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'setFogVersionCommand').returns($response); + }); + + it('calls ioFogService.setFogVersionCommand with correct args', async () => { + await $subject; + expect(ioFogService.setFogVersionCommand).to.have.been.calledWith({ + uuid: $uuid, + versionCommand: $versionCommand + }, $user, false); + }); + + context('when ioFogService#setFogVersionCommand fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#setFogVersionCommand succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('setFogRebootCommandEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.setFogRebootCommandEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'setFogRebootCommand').returns($response); + }); + + it('calls ioFogService.setFogRebootCommand with correct args', async () => { + await $subject; + expect(ioFogService.setFogRebootCommand).to.have.been.calledWith({uuid: $uuid}, $user, false); + }); + + context('when ioFogService#setFogRebootCommand fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#setFogRebootCommand succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getHalHardwareInfoEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getHalHardwareInfoEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'getHalHardwareInfo').returns($response); + }); + + it('calls ioFogService.getHalHardwareInfo with correct args', async () => { + await $subject; + expect(ioFogService.getHalHardwareInfo).to.have.been.calledWith({uuid: $uuid}, $user, false); + }); + + context('when ioFogService#getHalHardwareInfo fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#getHalHardwareInfo succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('getHalUsbInfoEndPoint.()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + def('response', () => Promise.resolve({info: undefined})); + def('subject', () => $subject.getHalUsbInfoEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(ioFogService, 'getHalUsbInfo').returns($response); + }); + + it('calls ioFogService.getHalUsbInfo with correct args', async () => { + await $subject; + expect(ioFogService.getHalUsbInfo).to.have.been.calledWith({uuid: $uuid}, $user, false); + }); + + context('when ioFogService#getHalUsbInfo fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogService#getHalUsbInfo succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.have.property('info'); + }) + }) + }); + +}); \ No newline at end of file diff --git a/test/src/controllers/microservices-controller.test.js b/test/src/controllers/microservices-controller.test.js index 405a3625c..3baa7d889 100644 --- a/test/src/controllers/microservices-controller.test.js +++ b/test/src/controllers/microservices-controller.test.js @@ -322,7 +322,7 @@ describe('Microservices Controller', () => { }) }); - describe('deleteMicroserviceRouteEndPoint.()', () => { + describe('deleteMicroserviceRouteEndPoint()', () => { def('user', () => 'user!'); def('uuid', () => 'testUuid'); def('receiverUuid', () => 'testReceiverUuid'); @@ -452,7 +452,7 @@ describe('Microservices Controller', () => { }) }); - describe('getMicroservicePortMappingListEndPoint.()', () => { + describe('getMicroservicePortMappingListEndPoint()', () => { def('user', () => 'user!'); def('uuid', () => 'testUuid'); @@ -490,7 +490,7 @@ describe('Microservices Controller', () => { }) }); - describe('createMicroserviceVolumeMappingEndPoint.()', () => { + describe('createMicroserviceVolumeMappingEndPoint()', () => { def('user', () => 'user!'); def('uuid', () => 'testUuid'); @@ -541,7 +541,7 @@ describe('Microservices Controller', () => { }) }); - describe('listMicroserviceVolumeMappingsEndPoint.()', () => { + describe('listMicroserviceVolumeMappingsEndPoint()', () => { def('user', () => 'user!'); def('uuid', () => 'testUuid'); @@ -579,7 +579,7 @@ describe('Microservices Controller', () => { }) }); - describe('deleteMicroserviceVolumeMappingEndPoint.()', () => { + describe('deleteMicroserviceVolumeMappingEndPoint()', () => { def('user', () => 'user!'); def('uuid', () => 'testUuid'); def('id', () => 35); From 6f488b50717f83cfe2624c7a266007819965fe47 Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 13 Dec 2018 17:29:02 +0300 Subject: [PATCH 04/39] test(core) unit tests for flow & diagnostics controllers, updated postman collection & refactored diagnostics (EWC-382) (#427) --- src/cli/diagnostics.js | 16 +- src/controllers/diagnostic-controller.js | 24 +- src/controllers/flow-controller.js | 28 +-- src/routes/diagnostics.js | 11 +- ...Controller Testing.postman_collection.json | 29 ++- .../diagnostics-controller.test.js | 238 ++++++++++++++++++ test/src/controllers/flow-controller.test.js | 228 +++++++++++++++++ 7 files changed, 520 insertions(+), 54 deletions(-) create mode 100644 test/src/controllers/diagnostics-controller.test.js create mode 100644 test/src/controllers/flow-controller.test.js diff --git a/src/cli/diagnostics.js b/src/cli/diagnostics.js index fd1fd6025..9506b8e1a 100644 --- a/src/cli/diagnostics.js +++ b/src/cli/diagnostics.js @@ -38,7 +38,7 @@ class Diagnostics extends BaseCLIHandler { group: [constants.CMD_STRACE_UPDATE] }, { - name: 'microservice-id', alias: 'i', type: String, description: 'Microservice ID', + name: 'microservice-uuid', alias: 'i', type: String, description: 'Microservice UUID', group: [constants.CMD_STRACE_UPDATE, constants.CMD_STRACE_INFO, constants.CMD_STRACE_FTP_POST, constants.CMD_IMAGE_SNAPSHOT_CREATE, constants.CMD_IMAGE_SNAPSHOT_GET] }, @@ -128,16 +128,16 @@ const _executeCase = async function (diagnosticCommand, commandName, f, isUserRe const _changeMicroserviceStraceState = async function (obj) { logger.info(JSON.stringify(obj)); - const enable = AppHelper.validateBooleanCliOptions(obj.enable, obj.disable); - await DiagnosticService.changeMicroserviceStraceState(obj.microserviceId, {enable: enable}, {}, true); - const msg = enable ? 'Microservice strace has been enabled' : 'Microservice strace has been disabled'; + 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); }; const _getMicroserviceStraceData = async function (obj) { logger.info(JSON.stringify(obj)); - const result = await DiagnosticService.getMicroserviceStraceData(obj.microserviceId, {format: obj.format}, {}, true); + const result = await DiagnosticService.getMicroserviceStraceData(obj.microserviceUuid, {format: obj.format}, {}, true); logger.info(JSON.stringify(result, null, 2)); logger.info('Microservice strace data has been retrieved successfully.'); }; @@ -145,21 +145,21 @@ const _getMicroserviceStraceData = async function (obj) { const _postMicroserviceStraceDataToFtp = async function (obj) { logger.info(JSON.stringify(obj)); - await DiagnosticService.postMicroserviceStraceDatatoFtp(obj.microserviceId, obj, {}, true); + await DiagnosticService.postMicroserviceStraceDatatoFtp(obj.microserviceUuid, obj, {}, true); logger.info('Strace data has been posted to ftp successfully.'); }; const _postMicroserviceImageSnapshotCreate = async function (obj) { logger.info(JSON.stringify(obj)); - await DiagnosticService.postMicroserviceImageSnapshotCreate(obj.microserviceId, {}, true); + await DiagnosticService.postMicroserviceImageSnapshotCreate(obj.microserviceUuid, {}, true); logger.info('Microservice image snapshot has been created successfully.'); }; const _getMicroserviceImageSnapshot = async function (obj) { logger.info(JSON.stringify(obj)); - const filePath = await DiagnosticService.getMicroserviceImageSnapshot(obj.microserviceId, {}, true); + const filePath = await DiagnosticService.getMicroserviceImageSnapshot(obj.microserviceUuid, {}, true); logger.info('Microservice images path = ' + filePath); }; diff --git a/src/controllers/diagnostic-controller.js b/src/controllers/diagnostic-controller.js index 699a6c382..7512f175e 100644 --- a/src/controllers/diagnostic-controller.js +++ b/src/controllers/diagnostic-controller.js @@ -17,32 +17,30 @@ const AuthDecorator = require('./../decorators/authorization-decorator'); const changeMicroserviceStraceStateEndPoint = async function (req, user) { logger.info("Parameters: " + JSON.stringify(req.body)); - logger.info("Microservice id: " + req.params.id); - return await DiagnosticService.changeMicroserviceStraceState(req.params.id, req.body, user, false); + logger.info("Microservice UUID: " + req.params.uuid); + return await DiagnosticService.changeMicroserviceStraceState(req.params.uuid, req.body, user, false); }; const getMicroserviceStraceDataEndPoint = async function (req, user) { logger.info("Parameters:" + JSON.stringify(req.query)); - logger.info("Microservice id: " + req.params.id); - return await DiagnosticService.getMicroserviceStraceData(req.params.id, req.query, user, false); + logger.info("Microservice UUID: " + req.params.uuid); + return await DiagnosticService.getMicroserviceStraceData(req.params.uuid, req.query, user, false); }; const postMicroserviceStraceDataToFtpEndPoint = async function (req, user) { logger.info("Parameters:" + JSON.stringify(req.body)); - logger.info("Microservice id: " + req.params.id); - return await DiagnosticService.postMicroserviceStraceDatatoFtp(req.params.id, req.body, user, false); + logger.info("Microservice UUID: " + req.params.uuid); + return await DiagnosticService.postMicroserviceStraceDatatoFtp(req.params.uuid, req.body, user, false); }; const createMicroserviceImageSnapshotEndPoint = async function (req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - logger.info("Microservice id: " + req.params.id); - return await DiagnosticService.postMicroserviceImageSnapshotCreate(req.params.id, user, false); + logger.info("Microservice UUID: " + req.params.uuid); + return await DiagnosticService.postMicroserviceImageSnapshotCreate(req.params.uuid, user, false); }; const getMicroserviceImageSnapshotEndPoint = async function (req, user) { - logger.info("Parameters:" + JSON.stringify(req.body)); - logger.info("Microservice id: " + req.params.id); - return await DiagnosticService.getMicroserviceImageSnapshot(req.params.id, user, false); + logger.info("Microservice UUID: " + req.params.uuid); + return await DiagnosticService.getMicroserviceImageSnapshot(req.params.uuid, user, false); }; module.exports = { @@ -50,5 +48,5 @@ module.exports = { getMicroserviceStraceDataEndPoint: AuthDecorator.checkAuthToken(getMicroserviceStraceDataEndPoint), postMicroserviceStraceDataToFtpEndPoint: AuthDecorator.checkAuthToken(postMicroserviceStraceDataToFtpEndPoint), createMicroserviceImageSnapshotEndPoint: AuthDecorator.checkAuthToken(createMicroserviceImageSnapshotEndPoint), - getMicroserviceImageSnapshotEndPoint: AuthDecorator.checkAuthToken(getMicroserviceImageSnapshotEndPoint), + getMicroserviceImageSnapshotEndPoint: AuthDecorator.checkAuthToken(getMicroserviceImageSnapshotEndPoint) }; diff --git a/src/controllers/flow-controller.js b/src/controllers/flow-controller.js index ecd9a432b..f66a0cd5e 100644 --- a/src/controllers/flow-controller.js +++ b/src/controllers/flow-controller.js @@ -15,7 +15,7 @@ const logger = require('../logger'); const AuthDecorator = require('./../decorators/authorization-decorator'); const FlowService = require('../services/flow-service'); -const _createFlowEndPoint = async function (req, user) { +const createFlowEndPoint = async function (req, user) { const flow = req.body; logger.info("Parameters:" + JSON.stringify(flow)); @@ -23,40 +23,40 @@ const _createFlowEndPoint = async function (req, user) { return await FlowService.createFlow(flow, user, false) }; -const _getFlowsByUserEndPoint = async function (req, user) { +const getFlowsByUserEndPoint = async function (req, user) { return await FlowService.getUserFlows(user, false) }; -const _getFlowEndPoint = async function (req, user) { +const getFlowEndPoint = async function (req, user) { const flowId = req.params.id; - logger.info("Flow id:" + JSON.stringify(flowId)) + logger.info("Flow id:" + JSON.stringify(flowId)); return await FlowService.getFlowWithTransaction(flowId, user, false) }; -const _updateFlowEndPoint = async function (req, user) { +const updateFlowEndPoint = async function (req, user) { const flow = req.body; const flowId = req.params.id; - logger.info("Parameters:" + JSON.stringify(flow)) - logger.info("Flow id:" + JSON.stringify(flowId)) + logger.info("Parameters:" + JSON.stringify(flow)); + logger.info("Flow id:" + JSON.stringify(flowId)); return await FlowService.updateFlow(flow, flowId, user, false) }; -const _deleteFlowEndPoint = async function (req, user) { +const deleteFlowEndPoint = async function (req, user) { const flowId = req.params.id; - logger.info("Flow id:" + JSON.stringify(flowId)) + logger.info("Flow id:" + JSON.stringify(flowId)); return await FlowService.deleteFlow(flowId, user, false) }; module.exports = { - createFlowEndPoint: AuthDecorator.checkAuthToken(_createFlowEndPoint), - getFlowsByUserEndPoint: AuthDecorator.checkAuthToken(_getFlowsByUserEndPoint), - getFlowEndPoint: AuthDecorator.checkAuthToken(_getFlowEndPoint), - updateFlowEndPoint: AuthDecorator.checkAuthToken(_updateFlowEndPoint), - deleteFlowEndPoint: AuthDecorator.checkAuthToken(_deleteFlowEndPoint) + createFlowEndPoint: AuthDecorator.checkAuthToken(createFlowEndPoint), + getFlowsByUserEndPoint: AuthDecorator.checkAuthToken(getFlowsByUserEndPoint), + getFlowEndPoint: AuthDecorator.checkAuthToken(getFlowEndPoint), + updateFlowEndPoint: AuthDecorator.checkAuthToken(updateFlowEndPoint), + deleteFlowEndPoint: AuthDecorator.checkAuthToken(deleteFlowEndPoint) }; \ No newline at end of file diff --git a/src/routes/diagnostics.js b/src/routes/diagnostics.js index 372828670..d9f512771 100644 --- a/src/routes/diagnostics.js +++ b/src/routes/diagnostics.js @@ -14,13 +14,12 @@ const constants = require('../helpers/constants'); const DiagnosticController = require('../controllers/diagnostic-controller'); const ResponseDecorator = require('../decorators/response-decorator'); const Errors = require('../helpers/errors'); -const ErrorMessages = require('../helpers/error-messages'); const fs = require('fs'); module.exports = [ { method: 'post', - path: '/api/v3/microservices/:id/image-snapshot', + path: '/api/v3/microservices/:uuid/image-snapshot', middleware: async (req, res) => { const successCode = constants.HTTP_CODE_CREATED; @@ -49,7 +48,7 @@ module.exports = [ }, { method: 'get', - path: '/api/v3/microservices/:id/image-snapshot', + path: '/api/v3/microservices/:uuid/image-snapshot', middleware: async (req, res) => { const successCode = constants.HTTP_CODE_SUCCESS; const errorCodes = [ @@ -85,7 +84,7 @@ module.exports = [ }, { method: 'patch', - path: '/api/v3/microservices/:id/strace', + path: '/api/v3/microservices/:uuid/strace', middleware: async (req, res) => { const successCode = constants.HTTP_CODE_NO_CONTENT; @@ -118,7 +117,7 @@ module.exports = [ }, { method: 'get', - path: '/api/v3/microservices/:id/strace', + path: '/api/v3/microservices/:uuid/strace', middleware: async (req, res) => { const successCode = constants.HTTP_CODE_SUCCESS; @@ -147,7 +146,7 @@ module.exports = [ }, { method: 'put', - path: '/api/v3/microservices/:id/strace', + path: '/api/v3/microservices/:uuid/strace', middleware: async (req, res) => { const successCode = constants.HTTP_CODE_NO_CONTENT; const errorCodes = [ diff --git a/test/Controller Testing.postman_collection.json b/test/Controller Testing.postman_collection.json index f18490ce9..0c1d186b1 100644 --- a/test/Controller Testing.postman_collection.json +++ b/test/Controller Testing.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "507ec948-6bd6-48ad-9272-ab609e6ce965", + "_postman_id": "f26c12d6-fdb5-4606-841b-de7979fe33f5", "name": "Controller Testing", "description": "iofog-controller collection", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" @@ -8,7 +8,6 @@ "item": [ { "name": "User", - "description": "User collection", "item": [ { "name": "Create user", @@ -508,6 +507,7 @@ "response": [] } ], + "description": "User collection", "event": [ { "listen": "prerequest", @@ -533,7 +533,6 @@ }, { "name": "General", - "description": "General collection", "item": [ { "name": "Status", @@ -653,6 +652,7 @@ "response": [] } ], + "description": "General collection", "event": [ { "listen": "prerequest", @@ -678,7 +678,6 @@ }, { "name": "Agent", - "description": "Agent collection", "item": [ { "name": "Create user", @@ -1635,7 +1634,7 @@ "", "var data = JSON.parse(responseBody);", "", - "tests[\"Response error message is valid\"] = data.name === \"ValidationError\" && data.message === \"Uploaded image snapshot file not found\"" + "tests[\"Response error message is valid\"] = data.name === \"ValidationError\" && data.message === \"Invalid content type\"" ], "type": "text/javascript" } @@ -1766,6 +1765,7 @@ "response": [] } ], + "description": "Agent collection", "event": [ { "listen": "prerequest", @@ -1791,7 +1791,6 @@ }, { "name": "Flow", - "description": "Flow collection", "item": [ { "name": "Create user", @@ -2175,6 +2174,7 @@ "response": [] } ], + "description": "Flow collection", "event": [ { "listen": "prerequest", @@ -2200,7 +2200,6 @@ }, { "name": "Catalog", - "description": "Catalog collection", "item": [ { "name": "Create user", @@ -2593,6 +2592,7 @@ "response": [] } ], + "description": "Catalog collection", "event": [ { "listen": "prerequest", @@ -2618,7 +2618,6 @@ }, { "name": "Tunnel", - "description": "Tunnel collection", "item": [ { "name": "Create user", @@ -2959,6 +2958,7 @@ "response": [] } ], + "description": "Tunnel collection", "event": [ { "listen": "prerequest", @@ -2984,7 +2984,6 @@ }, { "name": "Microservices", - "description": "Microservices collection", "item": [ { "name": "Create user", @@ -3759,7 +3758,10 @@ "value": "{{user-token}}" } ], - "body": {}, + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{host}}/api/v3/microservices/{{ms-id}}/volume-mapping", "host": [ @@ -4056,6 +4058,7 @@ "response": [] } ], + "description": "Microservices collection", "event": [ { "listen": "prerequest", @@ -4081,7 +4084,6 @@ }, { "name": "Diagnostics", - "description": "Diagnostics collection", "item": [ { "name": "Create user", @@ -4877,6 +4879,7 @@ "response": [] } ], + "description": "Diagnostics collection", "event": [ { "listen": "prerequest", @@ -4902,7 +4905,6 @@ }, { "name": "ioFog", - "description": "ioFog collection", "item": [ { "name": "Create user", @@ -5606,6 +5608,7 @@ "response": [] } ], + "description": "ioFog collection", "event": [ { "listen": "prerequest", @@ -5631,7 +5634,6 @@ }, { "name": "Registries", - "description": "Registries collection", "item": [ { "name": "Create user", @@ -5966,6 +5968,7 @@ "response": [] } ], + "description": "Registries collection", "event": [ { "listen": "prerequest", diff --git a/test/src/controllers/diagnostics-controller.test.js b/test/src/controllers/diagnostics-controller.test.js new file mode 100644 index 000000000..1403afd63 --- /dev/null +++ b/test/src/controllers/diagnostics-controller.test.js @@ -0,0 +1,238 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const DiagnosticController = require('../../../src/controllers/diagnostic-controller'); +const DiagnosticService = require('../../../src/services/diagnostic-service'); + +describe('Diagnostic Controller', () => { + def('subject', () => DiagnosticController); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.changeMicroserviceStraceStateEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('enable', () => true); + + def('req', () => ({ + params: { + uuid: $uuid + }, + body: { + enable: $enable + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.changeMicroserviceStraceStateEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(DiagnosticService, 'changeMicroserviceStraceState').returns($response); + }); + + it('calls DiagnosticService.changeMicroserviceStraceState with correct args', async () => { + await $subject; + expect(DiagnosticService.changeMicroserviceStraceState).to.have.been.calledWith($uuid, { + enable: $enable + }, $user, false); + }); + + context('when DiagnosticService#changeMicroserviceStraceState fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when DiagnosticService#changeMicroserviceStraceState succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getMicroserviceStraceDataEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + def('format', () => 'string'); + + def('req', () => ({ + params: { + uuid: $uuid + }, + query: { + format: $format + } + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getMicroserviceStraceDataEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(DiagnosticService, 'getMicroserviceStraceData').returns($response); + }); + + it('calls DiagnosticService.getMicroserviceStraceData with correct args', async () => { + await $subject; + expect(DiagnosticService.getMicroserviceStraceData).to.have.been.calledWith($uuid, { + format: $format + }, $user, false) + }); + + context('when DiagnosticService#getMicroserviceStraceData fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when DiagnosticService#getMicroserviceStraceData succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + + describe('.postMicroserviceStraceDataToFtpEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('ftpHost', () => 'testHost'); + def('ftpPort', () => 15); + def('ftpPass', () => 'ftpPass'); + def('ftpDestDir', () => 'ftpDestDirectory'); + + def('req', () => ({ + params: { + uuid: $uuid + }, + body: { + ftpHost: $ftpHost, + ftpPort: $ftpPort, + ftpPass: $ftpPass, + ftpDestDir: $ftpDestDir + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.postMicroserviceStraceDataToFtpEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(DiagnosticService, 'postMicroserviceStraceDatatoFtp').returns($response); + }); + + it('calls DiagnosticService.postMicroserviceStraceDatatoFtp with correct args', async () => { + await $subject; + expect(DiagnosticService.postMicroserviceStraceDatatoFtp).to.have.been.calledWith($uuid, { + ftpHost: $ftpHost, + ftpPort: $ftpPort, + ftpPass: $ftpPass, + ftpDestDir: $ftpDestDir + }, $user, false); + }); + + context('when DiagnosticService#postMicroserviceStraceDatatoFtp fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when DiagnosticService#postMicroserviceStraceDatatoFtp succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.createMicroserviceImageSnapshotEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.createMicroserviceImageSnapshotEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(DiagnosticService, 'postMicroserviceImageSnapshotCreate').returns($response); + }); + + it('calls DiagnosticService.postMicroserviceImageSnapshotCreate with correct args', async () => { + await $subject; + expect(DiagnosticService.postMicroserviceImageSnapshotCreate).to.have.been.calledWith($uuid, $user, false); + }); + + context('when DiagnosticService#postMicroserviceImageSnapshotCreate fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when DiagnosticService#postMicroserviceImageSnapshotCreate succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getMicroserviceImageSnapshotEndPoint()', () => { + def('user', () => 'user!'); + def('uuid', () => 'testUuid'); + + def('req', () => ({ + params: { + uuid: $uuid + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.getMicroserviceImageSnapshotEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(DiagnosticService, 'getMicroserviceImageSnapshot').returns($response); + }); + + it('calls DiagnosticService.getMicroserviceImageSnapshot with correct args', async () => { + await $subject; + expect(DiagnosticService.getMicroserviceImageSnapshot).to.have.been.calledWith($uuid, $user, false); + }); + + context('when DiagnosticService.getMicroserviceImageSnapshot fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when DiagnosticService.getMicroserviceImageSnapshot succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + +}); \ No newline at end of file diff --git a/test/src/controllers/flow-controller.test.js b/test/src/controllers/flow-controller.test.js new file mode 100644 index 000000000..ecb45b2a4 --- /dev/null +++ b/test/src/controllers/flow-controller.test.js @@ -0,0 +1,228 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const FlowController = require('../../../src/controllers/flow-controller'); +const FlowService = require('../../../src/services/flow-service'); + +describe('Flow Controller', () => { + def('subject', () => FlowController); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createFlowEndPoint()', () => { + def('user', () => 'user!'); + + def('name', () => 'testName'); + def('description', () => 'testDescription'); + def('isActivated', () => true); + + def('req', () => ({ + body: { + name: $name, + description: $description, + isActivated: $isActivated + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.createFlowEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(FlowService, 'createFlow').returns($response); + }); + + it('calls FlowService.createFlow with correct args', async () => { + await $subject; + expect(FlowService.createFlow).to.have.been.calledWith({ + name: $name, + description: $description, + isActivated: $isActivated + }, $user, false); + }); + + context('when FlowService#createFlow fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowService#createFlow succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getFlowsByUserEndPoint()', () => { + def('user', () => 'user!'); + + def('req', () => ({ + body: {} + })); + def('response', () => Promise.resolve()); + def('subject', () => $subject.getFlowsByUserEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(FlowService, 'getUserFlows').returns($response); + }); + + it('calls FlowService.getUserFlows with correct args', async () => { + await $subject; + expect(FlowService.getUserFlows).to.have.been.calledWith($user, false) + }); + + context('when FlowService#getUserFlows fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowService#getUserFlows succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + + describe('.getFlowEndPoint()', () => { + def('user', () => 'user!'); + def('id', () => 15); + + def('req', () => ({ + params: { + id: $id + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.getFlowEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(FlowService, 'getFlowWithTransaction').returns($response); + }); + + it('calls FlowService.getFlowWithTransaction with correct args', async () => { + await $subject; + expect(FlowService.getFlowWithTransaction).to.have.been.calledWith($id, $user, false); + }); + + context('when FlowService#getFlowWithTransaction fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowService#getFlowWithTransaction succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.updateFlowEndPoint()', () => { + def('user', () => 'user!'); + def('id', () => 15); + + def('name', () => 'updatedTestName'); + def('description', () => 'updatedTestDescription'); + def('isActivated', () => false); + + def('req', () => ({ + params: { + id: $id + }, + body: { + name: $name, + description: $description, + isActivated: $isActivated + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.updateFlowEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(FlowService, 'updateFlow').returns($response); + }); + + it('calls FlowService.updateFlow with correct args', async () => { + await $subject; + expect(FlowService.updateFlow).to.have.been.calledWith({ + name: $name, + description: $description, + isActivated: $isActivated + }, $id, $user, false); + }); + + context('when FlowService#updateFlow fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowService#updateFlow succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.deleteFlowEndPoint()', () => { + def('user', () => 'user!'); + def('id', () => 15); + + def('req', () => ({ + params: { + id: $id + } + })); + + def('response', () => Promise.resolve()); + def('subject', () => $subject.deleteFlowEndPoint($req, $user)); + + beforeEach(() => { + $sandbox.stub(FlowService, 'deleteFlow').returns($response); + }); + + it('calls FlowService.deleteFlow with correct args', async () => { + await $subject; + expect(FlowService.deleteFlow).to.have.been.calledWith($id, $user, false); + }); + + context('when FlowService.deleteFlow fails', () => { + const error = 'Error!'; + + def('response', () => Promise.reject(error)); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowService.deleteFlow succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + + }); + +}); \ No newline at end of file From a11cf4bbee5683559d54c8c49763cd614fe9b5a0 Mon Sep 17 00:00:00 2001 From: alexandershpak <35569337+alexandershpak@users.noreply.github.com> Date: Thu, 13 Dec 2018 18:19:12 +0300 Subject: [PATCH 05/39] bug(fix) CLI diagnostics: Incorrect error message is displayed when user try to get strace-info (#428) --- src/helpers/error-messages.js | 1 + src/services/diagnostic-service.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index c107044c0..6ab703477 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -23,6 +23,7 @@ module.exports = { INVALID_FOG_NODE_UUID: 'Invalid ioFog UUID {}', INVALID_USER_EMAIL: 'Invalid user email', INVALID_MICROSERVICE_UUID: "Invalid microservice UUID '{}'", + INVALID_MICROSERVICE_STRACE: "Strace data for this microservice not found", INVALID_VOLUME_MAPPING_UUID: "Invalid volume mapping id '{}'", ACTIVATION_CODE_NOT_FOUND: 'Activation code not found', INVALID_OLD_PASSWORD: 'Old password is incorrect', diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index d2aeeb6c0..741f122f4 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -56,6 +56,10 @@ const getMicroserviceStraceData = async function (id, data, user, isCLI, transac } const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: id}, transaction); + if (!straceData) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, id)) + } + const dir = config.get('Diagnostics:DiagnosticDir') || 'diagnostics'; const filePath = dir + '/' + id; From 8075b4dbf46daa13db76371b741f0018cf87ac3f Mon Sep 17 00:00:00 2001 From: Railag Date: Fri, 14 Dec 2018 16:42:00 +0300 Subject: [PATCH 06/39] test(core) unit tests for catalog, controller and agent controllers (EWC-382) (#430) --- src/decorators/authorization-decorator.js | 2 +- src/schemas/agent.js | 4 +- src/sequelize/managers/strace-manager.js | 2 +- src/services/agent-service.js | 4 +- test/src/controllers/agent-controller.test.js | 767 ++++++++++++++++++ .../controllers/catalog-controller.test.js | 320 ++++++++ .../controllers/controller-controller.test.js | 115 +++ 7 files changed, 1208 insertions(+), 6 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 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/schemas/agent.js b/src/schemas/agent.js index 3d3c19cc5..5dd990aa4 100644 --- a/src/schemas/agent.js +++ b/src/schemas/agent.js @@ -104,10 +104,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 }; 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..ab71dd9b9 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -315,9 +315,9 @@ 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/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 From 076f96b4a39e57c916206273bfc936e5d1d741e5 Mon Sep 17 00:00:00 2001 From: alexandershpak <35569337+alexandershpak@users.noreply.github.com> Date: Fri, 14 Dec 2018 17:36:26 +0300 Subject: [PATCH 07/39] bug(fix) CLI diagnostics ftp: Incorrect error message is displayed when user try to get strace-info (EWC-431) --- src/services/diagnostic-service.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 741f122f4..474db7797 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -80,6 +80,11 @@ const getMicroserviceStraceData = async function (id, data, user, isCLI, transac const postMicroserviceStraceDatatoFtp = async function (id, data, user, isCLI, transaction) { await Validator.validate(data, Validator.schemas.stracePostToFtp); const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: id}, transaction); + + if (!straceData) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, id)) + } + const dir = config.get('Diagnostics:DiagnosticDir'); const filePath = dir + '/' + id; From e1eeea5981fb942ab0426231619052b113dfb7b4 Mon Sep 17 00:00:00 2001 From: dbusel <10116634+dbusel@users.noreply.github.com> Date: Fri, 14 Dec 2018 21:48:54 +0300 Subject: [PATCH 08/39] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02a3da7d6..237c5b3f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.1.0", + "version": "1.0.30", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi", From 3933a0aeb3856bf9a775309a6571ea9a4068b33a Mon Sep 17 00:00:00 2001 From: dbusel <10116634+dbusel@users.noreply.github.com> Date: Mon, 17 Dec 2018 12:28:55 +0300 Subject: [PATCH 09/39] remove automated release --- .travis.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 From 146c10fd2ca707454750d1fab46ffabe91351f9d Mon Sep 17 00:00:00 2001 From: epankou <35571073+epankou@users.noreply.github.com> Date: Mon, 17 Dec 2018 15:11:48 +0300 Subject: [PATCH 10/39] bug(fix) strace feature bug (EWC-421) --- src/cli/diagnostics.js | 4 ++-- src/routes/agent.js | 2 +- src/sequelize/managers/iofog-manager.js | 4 ++-- src/services/agent-service.js | 14 ++++++++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) 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/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/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/services/agent-service.js b/src/services/agent-service.js index ab71dd9b9..c2463e712 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -300,7 +300,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,7 +308,17 @@ 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) { From 6d352f145fb93e1d0c8841cfcada7a9f43aa9e35 Mon Sep 17 00:00:00 2001 From: Railag Date: Mon, 17 Dec 2018 15:53:09 +0300 Subject: [PATCH 11/39] bug(fix) fixed deprecation warnings (EWC-422) (#438) --- src/controllers/user-controller.js | 17 +-- src/helpers/app-helper.js | 19 +++- src/services/connector-service.js | 4 +- src/services/user-service.js | 12 +- test/src/controllers/user-controller.test.js | 112 ++++++------------- test/src/helpers/app-helpers.test.js | 83 +++++++------- 6 files changed, 100 insertions(+), 147 deletions(-) diff --git a/src/controllers/user-controller.js b/src/controllers/user-controller.js index 43badf767..4eb75a30f 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); }; @@ -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/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/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/user-service.js b/src/services/user-service.js index 85e30c08e..affc2b40b 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -66,7 +66,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(); } @@ -174,14 +176,18 @@ const deleteUser = async function (user, isCLI, 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); }; diff --git a/test/src/controllers/user-controller.test.js b/test/src/controllers/user-controller.test.js index 815a45b0a..6fe296bd1 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 UserService#login fails', () => { + const error = 'Error!'; - 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!'; - - def('response', () => Promise.reject(error)); - - it(`fails with "${error}"`, () => { - return expect($subject).to.be.rejectedWith(error) - }) - }); + def('response', () => Promise.reject(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) + }) + }) + }); }); @@ -399,13 +383,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 +407,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 AppHelper#encryptText() for new password fails', () => { - it('fails', () => { - return expect($subject).to.eventually.equal(undefined); - }); - }); + context('when UserService#updateUserPassword fails', () => { + const error = 'Error!'; - 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) From c67e4fc7b70aae0459e33998286b63c23ad41d50 Mon Sep 17 00:00:00 2001 From: alexandershpak <35569337+alexandershpak@users.noreply.github.com> Date: Mon, 17 Dec 2018 17:17:59 +0300 Subject: [PATCH 12/39] bug(fix) CLI diagnostics ftp: invalid microservice uuid --- src/services/diagnostic-service.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 474db7797..6b2966104 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -79,6 +79,11 @@ const getMicroserviceStraceData = async function (id, data, user, isCLI, transac const postMicroserviceStraceDatatoFtp = async function (id, data, user, isCLI, transaction) { await Validator.validate(data, Validator.schemas.stracePostToFtp); + + const microservice = await MicroserviceManager.findOne(microserviceWhere, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, id)) + } const straceData = await StraceDiagnosticManager.findOne({microserviceUuid: id}, transaction); if (!straceData) { From 2156553045a6097187e36d7d3ce6e74016ea9441 Mon Sep 17 00:00:00 2001 From: alexandershpak <35569337+alexandershpak@users.noreply.github.com> Date: Mon, 17 Dec 2018 17:32:15 +0300 Subject: [PATCH 13/39] 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 --- src/services/diagnostic-service.js | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 6b2966104..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,21 +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 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, 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'); - const filePath = dir + '/' + id; + const filePath = dir + '/' + uuid; _createDirectoryIfNotExists(dir); _writeBufferToFile(filePath, straceData.buffer); From f5ecca1cebe7f304f668d7cdd10d163d64f5faee Mon Sep 17 00:00:00 2001 From: epankou <35571073+epankou@users.noreply.github.com> Date: Wed, 19 Dec 2018 13:42:43 +0300 Subject: [PATCH 14/39] bug(fix) iofog agent API: core network containers are returned when flow isn't active (EWC-424) --- src/sequelize/managers/microservice-manager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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} } ] From 9e808bf78a2fe107fdf66ecafd348ae4b3d989d4 Mon Sep 17 00:00:00 2001 From: Railag Date: Wed, 19 Dec 2018 14:54:38 +0300 Subject: [PATCH 15/39] test(core) unit tests progress for agent & access-token services (EWC-383) (#442) --- src/schemas/agent.js | 11 +- src/schemas/index.js | 2 - src/services/agent-service.js | 2 - .../src/services/access-token-service.test.js | 83 +++ test/src/services/agent-service.test.js | 619 ++++++++++++++++++ 5 files changed, 703 insertions(+), 14 deletions(-) create mode 100644 test/src/services/access-token-service.test.js create mode 100644 test/src/services/agent-service.test.js diff --git a/src/schemas/agent.js b/src/schemas/agent.js index 5dd990aa4..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", @@ -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/services/agent-service.js b/src/services/agent-service.js index c2463e712..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; 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 From a515d9bb1fa171423fd2e2322b4ab7c11fb3dbae Mon Sep 17 00:00:00 2001 From: MaksimChepelev Date: Wed, 19 Dec 2018 17:28:27 +0300 Subject: [PATCH 16/39] feat(npm-scripts): npm scripts improvements (EWC-358 EWC-430) 1. save configs during releases 2. migrate to new encryption algorithm --- scripts/postinstall.js | 190 +++++++++++++++++++++++++++++++--------- scripts/preuninstall.js | 29 ++++-- 2 files changed, 172 insertions(+), 47 deletions(-) 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`) } From e7cc6a240a70057eb5ebc31014d2fa98dee8e918 Mon Sep 17 00:00:00 2001 From: alexandershpak <35569337+alexandershpak@users.noreply.github.com> Date: Wed, 19 Dec 2018 17:54:51 +0300 Subject: [PATCH 17/39] 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) --- src/cli/user.js | 11 ++++++++--- src/controllers/user-controller.js | 2 +- src/helpers/error-messages.js | 1 + src/services/user-service.js | 20 ++++++++++++++++++-- test/src/controllers/user-controller.test.js | 6 ++++-- 5 files changed, 32 insertions(+), 8 deletions(-) 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 4eb75a30f..c86fa65ab 100644 --- a/src/controllers/user-controller.js +++ b/src/controllers/user-controller.js @@ -85,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) { 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/services/user-service.js b/src/services/user-service.js index affc2b40b..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'); @@ -169,7 +170,22 @@ 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); @@ -362,7 +378,7 @@ async function _getEmailData() { service: service } - } catch(errMsg) { + } catch (errMsg) { throw new Errors.EmailActivationSetupError(); } diff --git a/test/src/controllers/user-controller.test.js b/test/src/controllers/user-controller.test.js index 6fe296bd1..617ff8537 100644 --- a/test/src/controllers/user-controller.test.js +++ b/test/src/controllers/user-controller.test.js @@ -338,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)); @@ -349,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', () => { From 569eb849f1753f35702aea66c680afdf15ff7bff Mon Sep 17 00:00:00 2001 From: MaksimChepelev Date: Thu, 20 Dec 2018 13:03:57 +0300 Subject: [PATCH 18/39] feat(microservice): update change tracking on delete port mapping (#445) Closes EWC-441 --- src/services/microservices-service.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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) { From c94136056c317ea70797923248059284a1d93d18 Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 20 Dec 2018 13:57:21 +0300 Subject: [PATCH 19/39] version 1.0.31 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 237c5b3f1..638c28072 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.30", + "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", From 4acf22bd484f6aabb34d502662448f8bc1ea913b Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 20 Dec 2018 16:25:25 +0300 Subject: [PATCH 20/39] 1.0.31 Release (#448) * 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 From 30b9796fc4cc7919cc8019ee68f7ba7d81f87cc1 Mon Sep 17 00:00:00 2001 From: epankou <35571073+epankou@users.noreply.github.com> Date: Thu, 20 Dec 2018 16:55:16 +0300 Subject: [PATCH 21/39] Epankou/bug network containers not removed ewc 424 (#450) * EWC-418 added registry email validation * EWC-421 strace feature bug * EWC-421 strace feature bug fix * bug(fix) iofog agent API: core network containers are returned when flow isn't active (EWC-424) * bug(fix) microservice removal: port mapping removal (EWC-424) * bug(fix) microservice removal: switched to const (EWC-424) --- src/cli/diagnostics.js | 1 + src/services/microservices-service.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/cli/diagnostics.js b/src/cli/diagnostics.js index 7f156f5c2..1a0445f3d 100644 --- a/src/cli/diagnostics.js +++ b/src/cli/diagnostics.js @@ -139,6 +139,7 @@ const _getMicroserviceStraceData = async function (obj) { const result = await DiagnosticService.getMicroserviceStraceData(obj.microserviceUuid, {format: obj.format}, {}, true); logger.info('Strace data:'); + logger.info('============================='); logger.info(result.data); logger.info('Microservice strace data has been retrieved successfully.'); }; diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index f18866032..f7616e5fd 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -243,6 +243,8 @@ async function _deleteMicroservice(microserviceUuid, microserviceData, user, isC throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)); } + await _deletePortMappings(microservice, user, transaction); + if (microservice.microserviceStatus.status === MicroserviceStates.NOT_RUNNING) { await _deleteMicroserviceWithRoutes(microserviceUuid, transaction); } else { @@ -258,6 +260,20 @@ async function _deleteMicroservice(microserviceUuid, microserviceData, user, isC await _updateChangeTracking(false, microservice.iofogUuid, transaction) } +async function _deletePortMappings(microservice, user, transaction) { + const msPortMappings = await MicroservicePortManager.findAll({ + microserviceUuid: microservice.uuid + }, transaction); + + for (const msPorts of msPortMappings) { + if (msPorts.isPublic) { + await _deletePortMappingOverConnector(microservice, msPorts, user, transaction) + } else { + await _deleteSimplePortMapping(microservice, msPorts, user, transaction) + } + } +} + async function _deleteNotRunningMicroservices(transaction) { const microservices = await MicroserviceManager.findAllWithStatuses(transaction); microservices From f2d4d91f6eb87b809f40a398236e4e5adb15b658 Mon Sep 17 00:00:00 2001 From: Railag Date: Fri, 21 Dec 2018 16:53:52 +0300 Subject: [PATCH 22/39] test(core) unit tests for agent service progress (EWC-383) (#452) --- src/services/agent-service.js | 10 +- src/services/microservices-service.js | 2 +- test/src/services/agent-service.test.js | 432 ++++++++++++++++++++++++ 3 files changed, 438 insertions(+), 6 deletions(-) diff --git a/src/services/agent-service.js b/src/services/agent-service.js index 3b9f6b421..1fdcd258e 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -219,7 +219,7 @@ const getAgentMicroservices = async function (fog, transaction) { const image = images.find(image => image.fogTypeId === fogTypeId); const imageId = image ? image.containerImage : ''; - const routes = await MicroserviceService.getPhysicalConections(microservice, transaction); + const routes = await MicroserviceService.getPhysicalConnections(microservice, transaction); const responseMicroservice = { uuid: microservice.uuid, @@ -307,12 +307,12 @@ const getAgentStrace = async function (fog, transaction) { } const straceArr = []; - for (let msData of fogWithStrace.microservice) { + for (const msData of fogWithStrace.microservice) { straceArr.push({ microserviceUuid: msData.strace.microserviceUuid, straceRun: msData.strace.straceRun }) - }; + } return { straceValues: straceArr @@ -338,13 +338,13 @@ const getAgentChangeVersionCommand = async function (fog, transaction) { throw new Errors.NotFoundError(ErrorMessages.VERSION_COMMAND_NOT_FOUND); } - const provision = FogProvisionKeyManager.findOne({ + const provision = await FogProvisionKeyManager.findOne({ iofogUuid: fog.uuid }, transaction); return { versionCommand: versionCommand.versionCommand, - provisionKey: provision.key, + provisionKey: provision.provisionKey, expirationTime: provision.expirationTime } }; diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index f7616e5fd..2207b4d04 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -973,6 +973,6 @@ module.exports = { createVolumeMapping: TransactionDecorator.generateTransaction(_createVolumeMapping), deleteVolumeMapping: TransactionDecorator.generateTransaction(_deleteVolumeMapping), listVolumeMappings: TransactionDecorator.generateTransaction(_listVolumeMappings), - getPhysicalConections: getPhysicalConections, + getPhysicalConnections: getPhysicalConections, deleteNotRunningMicroservices: _deleteNotRunningMicroservices }; diff --git a/test/src/services/agent-service.test.js b/test/src/services/agent-service.test.js index a57a6e348..198d25d56 100644 --- a/test/src/services/agent-service.test.js +++ b/test/src/services/agent-service.test.js @@ -11,6 +11,15 @@ 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'); +const RegistryManager = require('../../../src/sequelize/managers/registry-manager'); +const TunnelManager = require('../../../src/sequelize/managers/tunnel-manager'); +const StraceManager = require('../../../src/sequelize/managers/strace-manager'); +const ioFogVersionCommandManager = require('../../../src/sequelize/managers/iofog-version-command-manager'); +const ioFogProvisionKeyManager = require('../../../src/sequelize/managers/iofog-provision-key-manager'); +const BaseManager = require('../../../src/sequelize/managers/base-manager'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; + describe('Agent Service', () => { def('subject', () => AgentService); @@ -616,4 +625,427 @@ describe('Agent Service', () => { }); + describe('.getAgentMicroservices()', () => { + const transaction = {}; + const error = 'Error!'; + + const routes = []; + + const microservice = { + uuid: 'testMicroserviceUuid', + imageId: '', + config: '{}', + rebuild: false, + rootHostAccess: false, + logSize: 15, + ports: 'testPorts', + volumeMappings: 'testVolumeMappings', + imageSnapshot: 'testImageSnapshot', + delete: false, + deleteWithCleanup: false, + catalogItem: { + images: [{ + fogTypeId: 1, + containerImage: 'testContainerImage' + } + ], + registry: { + url: 'testUrl' + } + }, + routes: routes + }; + + const microserviceResponse = { + microservices: [{ + uuid: 'testMicroserviceUuid', + imageId: 'testContainerImage', + config: '{}', + rebuild: false, + rootHostAccess: false, + logSize: 15, + portMappings: 'testPorts', + volumeMappings: 'testVolumeMappings', + imageSnapshot: 'testImageSnapshot', + delete: false, + deleteWithCleanup: false, + routes: routes, + registryUrl: 'testUrl' + }] + }; + + def('uuid', () => 'testUuid'); + def('fogTypeId', () => 1); + + def('fog', () => ({ + uuid: $uuid, + fogTypeId: $fogTypeId + })); + + def('token', () => 'testToken'); + + def('subject', () => $subject.getAgentMicroservices($fog, transaction)); + + def('findAllMicroservicesResponse', () => Promise.resolve([microservice])); + def('getPhysicalConnectionsResponse', () => Promise.resolve(routes)); + def('updateResponse', () => Promise.resolve(microserviceResponse)); + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findAllActiveFlowMicroservices').returns($findAllMicroservicesResponse); + $sandbox.stub(MicroserviceService, 'getPhysicalConnections').returns($getPhysicalConnectionsResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateResponse); + }); + + it('calls MicroserviceManager#findAllActiveFlowMicroservices() with correct args', async () => { + await $subject; + expect(MicroserviceManager.findAllActiveFlowMicroservices).to.have.been.calledWith($uuid, transaction); + }); + + context('when MicroserviceManager#findAllActiveFlowMicroservices() fails', () => { + def('findAllMicroservicesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findAllActiveFlowMicroservices() succeeds', () => { + it('calls MicroserviceService.getPhysicalConnections with correct args', async () => { + await $subject; + expect(MicroserviceService.getPhysicalConnections).to.have.been.calledWith(microservice, transaction); + }); + + context('when MicroserviceService#getPhysicalConnections fails', () => { + const error = 'Error!'; + + def('getPhysicalConnectionsResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when MicroserviceService#getPhysicalConnections succeeds', () => { + it('calls MicroserviceManager.update with correct args', async () => { + await $subject; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microservice.uuid + }, { + rebuild: false + }, transaction); + }); + + context('when MicroserviceManager#update fails', () => { + const error = 'Error!'; + + def('updateResponse', () => error); + + it(`fails with "${error}"`, () => { + return expect($subject).to.be.rejectedWith = (error) + }) + }); + + context('when MicroserviceManager#update succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.deep.equal(microserviceResponse); + }) + }) + }) + }); + }); + + describe('.getAgentMicroservice()', () => { + const transaction = {}; + const error = 'Error!'; + + const routes = []; + + const microservice = { + uuid: 'testMicroserviceUuid', + imageId: 'testContainerImage', + config: '{}', + rebuild: false, + rootHostAccess: false, + logSize: 15, + portMappings: 'testPorts', + volumeMappings: 'testVolumeMappings', + imageSnapshot: 'testImageSnapshot', + delete: false, + deleteWithCleanup: false, + routes: routes, + registryUrl: 'testUrl' + }; + + const microserviceResponse = { + microservice: microservice + }; + + def('uuid', () => 'testUuid'); + def('microserviceUuid', () => 'testMicroserviceUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('token', () => 'testToken'); + + def('subject', () => $subject.getAgentMicroservice($microserviceUuid, $fog, transaction)); + + def('findMicroserviceResponse', () => Promise.resolve(microservice)); + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOneWithDependencies').returns($findMicroserviceResponse); + }); + + it('calls MicroserviceManager#findOneWithDependencies() with correct args', async () => { + await $subject; + expect(MicroserviceManager.findOneWithDependencies).to.have.been.calledWith({ + uuid: $microserviceUuid, + iofogUuid: $uuid + }, {}, transaction); + }); + + context('when MicroserviceManager#findOneWithDependencies() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findOneWithDependencies() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.deep.equal(microserviceResponse); + }) + }); + }); + + describe('.getAgentRegistries()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + def('userId', () => 15); + + def('fog', () => ({ + uuid: $uuid, + userId: $userId + })); + + def('token', () => 'testToken'); + + def('subject', () => $subject.getAgentRegistries($fog, transaction)); + + def('getAgentRegistriesResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(RegistryManager, 'findAll').returns($getAgentRegistriesResponse); + }); + + it('calls RegistryManager#findAll() with correct args', async () => { + await $subject; + expect(RegistryManager.findAll).to.have.been.calledWith({ + [Op.or]: + [ + { + userId: $userId + }, + { + isPublic: true + } + ] + }, transaction); + }); + + context('when RegistryManager#findAll() fails', () => { + def('getAgentRegistriesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when RegistryManager#findAll() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.have.property('registries'); + }) + }); + }); + + describe('.getAgentTunnel()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('token', () => 'testToken'); + + def('subject', () => $subject.getAgentTunnel($fog, transaction)); + + def('getTunnelResponse', () => Promise.resolve({})); + + beforeEach(() => { + $sandbox.stub(TunnelManager, 'findOne').returns($getTunnelResponse); + }); + + it('calls TunnelManager#findOne() with correct args', async () => { + await $subject; + expect(TunnelManager.findOne).to.have.been.calledWith({ + iofogUuid: $uuid + }, transaction); + }); + + context('when TunnelManager#findOne() fails', () => { + def('getTunnelResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when TunnelManager#findOne() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.have.property('tunnel'); + }) + }); + }); + + describe('.getAgentStrace()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('microserviceUuid', () => 'testMicroserviceUuid'); + def('straceRun', () => 'testStraceRun'); + + def('strace', () => ({ + microserviceUuid: $microserviceUuid, + straceRun: $straceRun + })); + + def('getStracesData', () => ({ + microservice: [{ + strace: $strace + }] + })); + + def('straceResponse', () => ({ + straceValues: [$strace] + })); + + def('subject', () => $subject.getAgentStrace($fog, transaction)); + + def('getStracesResponse', () => Promise.resolve($getStracesData)); + + beforeEach(() => { + $sandbox.stub(ioFogManager, 'findFogStraces').returns($getStracesResponse); + }); + + it('calls ioFogManager#findFogStraces() with correct args', async () => { + await $subject; + expect(ioFogManager.findFogStraces).to.have.been.calledWith({ + uuid: $uuid + }, transaction); + }); + + context('when ioFogManager#findFogStraces() fails', () => { + def('getStracesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findFogStraces() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.deep.equal($straceResponse); + }) + }); + }); + + describe('.updateAgentStrace()', () => { + + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('microserviceUuid', () => 'testMicroserviceUuid'); + def('buffer', () => 'testBuffer'); + + def('strace', () => ({ + microserviceUuid: $microserviceUuid, + buffer: $buffer + })); + + def('straceData', () => ({ + straceData: [$strace] + })); + + def('straceResponse', () => ({ + straceValues: [$strace] + })); + + def('subject', () => $subject.updateAgentStrace($straceData, $fog, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('pushBufferResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(StraceManager, 'pushBufferByMicroserviceUuid').returns($pushBufferResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith($straceData, Validator.schemas.updateAgentStrace); + }); + + 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 StraceManager#pushBufferByMicroserviceUuid() with correct args', async () => { + await $subject; + expect(StraceManager.pushBufferByMicroserviceUuid).to.have.been.calledWith($microserviceUuid, $buffer, + transaction); + }); + + context('when StraceManager#pushBufferByMicroserviceUuid() fails', () => { + def('pushBufferResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when StraceManager#pushBufferByMicroserviceUuid() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + }); + }); + + + }); \ No newline at end of file From 8c0c3542d126d50fe0353bf920c1a349e4325086 Mon Sep 17 00:00:00 2001 From: MaksimChepelev Date: Fri, 21 Dec 2018 19:28:34 +0300 Subject: [PATCH 23/39] fix(cli): fix bug with generating access token for user in cli (#453) Closes EWC-443 --- src/services/user-service.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/user-service.js b/src/services/user-service.js index 9da7375d1..881670c15 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -68,6 +68,9 @@ const login = async function (credentials, isCLI, transaction) { } const pass = AppHelper.decryptText(user.password, user.email); + if (isCLI) { + credentials.password = AppHelper.decryptText(credentials.password, credentials.email); + } const validPassword = credentials.password === pass || credentials.password === user.tempPassword; if (!validPassword) { From 63186d14a4d1080295a633fe3286761b6a24f356 Mon Sep 17 00:00:00 2001 From: Railag Date: Sat, 22 Dec 2018 14:55:52 +0300 Subject: [PATCH 24/39] test(core) unit tests for agent-service (EWC-383) (#455) --- .../managers/iofog-provision-key-manager.js | 8 +- .../managers/iofog-version-command-manager.js | 8 +- src/services/agent-service.js | 8 +- test/src/services/agent-service.test.js | 306 +++++++++++++++++- 4 files changed, 318 insertions(+), 12 deletions(-) diff --git a/src/sequelize/managers/iofog-provision-key-manager.js b/src/sequelize/managers/iofog-provision-key-manager.js index cf6da1eee..8a78bcd0a 100644 --- a/src/sequelize/managers/iofog-provision-key-manager.js +++ b/src/sequelize/managers/iofog-provision-key-manager.js @@ -11,9 +11,9 @@ * */ -const BaseManager = require('../managers/base-manager') +const BaseManager = require('../managers/base-manager'); const models = require('./../models'); -const FogProvisionKey = models.FogProvisionKey +const FogProvisionKey = models.FogProvisionKey; class FogProvisionKeyManager extends BaseManager { getEntity() { @@ -21,5 +21,5 @@ class FogProvisionKeyManager extends BaseManager { } } -const instance = new FogProvisionKeyManager() -module.exports = instance \ No newline at end of file +const instance = new FogProvisionKeyManager(); +module.exports = instance; \ No newline at end of file diff --git a/src/sequelize/managers/iofog-version-command-manager.js b/src/sequelize/managers/iofog-version-command-manager.js index 71eed75a4..b628e033f 100644 --- a/src/sequelize/managers/iofog-version-command-manager.js +++ b/src/sequelize/managers/iofog-version-command-manager.js @@ -11,9 +11,9 @@ * */ -const BaseManager = require('../managers/base-manager') +const BaseManager = require('../managers/base-manager'); const models = require('./../models'); -const FogVersionCommand = models.FogVersionCommand +const FogVersionCommand = models.FogVersionCommand; class FogVersionCommandManager extends BaseManager { getEntity() { @@ -21,5 +21,5 @@ class FogVersionCommandManager extends BaseManager { } } -const instance = new FogVersionCommandManager() -module.exports = instance \ No newline at end of file +const instance = new FogVersionCommandManager(); +module.exports = instance; \ No newline at end of file diff --git a/src/services/agent-service.js b/src/services/agent-service.js index 1fdcd258e..01c53fcec 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -36,6 +36,8 @@ const formidable = require('formidable'); const Sequelize = require('sequelize'); const Op = Sequelize.Op; +const IncomingForm = formidable.IncomingForm; + const agentProvision = async function (provisionData, transaction) { await Validator.validate(provisionData, Validator.schemas.agentProvision); @@ -398,16 +400,16 @@ const putImageSnapshot = async function (req, fog, transaction) { throw new Errors.ValidationError(ErrorMessages.INVALID_CONTENT_TYPE); } - const form = new formidable.IncomingForm(opts); + const form = new IncomingForm(opts); form.uploadDir = path.join(appRoot, '../') + 'data'; if (!fs.existsSync(form.uploadDir)) { fs.mkdirSync(form.uploadDir); } - await saveSnapShot(req, form,fog, transaction); + await _saveSnapShot(req, form, fog, transaction); return {}; }; -const saveSnapShot = function (req, form, fog, transaction) { +const _saveSnapShot = function (req, form, fog, transaction) { return new Promise((resolve, reject) => { form.parse(req, async function (error, fields, files) { const file = files['upstream']; diff --git a/test/src/services/agent-service.test.js b/test/src/services/agent-service.test.js index 198d25d56..d13f6e81a 100644 --- a/test/src/services/agent-service.test.js +++ b/test/src/services/agent-service.test.js @@ -16,9 +16,15 @@ const TunnelManager = require('../../../src/sequelize/managers/tunnel-manager'); const StraceManager = require('../../../src/sequelize/managers/strace-manager'); const ioFogVersionCommandManager = require('../../../src/sequelize/managers/iofog-version-command-manager'); const ioFogProvisionKeyManager = require('../../../src/sequelize/managers/iofog-provision-key-manager'); -const BaseManager = require('../../../src/sequelize/managers/base-manager'); +const HWInfoManager = require('../../../src/sequelize/managers/hw-info-manager'); +const USBInfoManager = require('../../../src/sequelize/managers/usb-info-manager'); const Sequelize = require('sequelize'); const Op = Sequelize.Op; +const path = require('path'); +const formidable = ('./incoming_form'); +const IncomingForm = formidable.IncomingForm; + +global.appRoot = path.resolve(__dirname); describe('Agent Service', () => { @@ -1046,6 +1052,304 @@ describe('Agent Service', () => { }); }); + describe('.getAgentChangeVersionCommand()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('versionCommandLine', () => 'testVersionCommand'); + def('versionCommand', () => ({ + versionCommand: $versionCommandLine + })); + + def('provisionKey', () => 'testKey'); + def('expirationTime', () => 12535352525); + def('provision', () => ({ + provisionKey: $provisionKey, + expirationTime: $expirationTime + })); + + def('response', () => ({ + versionCommand: $versionCommandLine, + provisionKey: $provisionKey, + expirationTime: $expirationTime + })); + + def('subject', () => $subject.getAgentChangeVersionCommand($fog, transaction)); + + def('findCommandResponse', () => Promise.resolve($versionCommand)); + def('findProvisionResponse', () => Promise.resolve($provision)); + + beforeEach(() => { + $sandbox.stub(ioFogVersionCommandManager, 'findOne').returns($findCommandResponse); + $sandbox.stub(ioFogProvisionKeyManager, 'findOne').returns($findProvisionResponse); + }); + + it('calls ioFogVersionCommandManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogVersionCommandManager.findOne).to.have.been.calledWith({ + iofogUuid: $uuid + }, transaction); + }); + + context('when ioFogVersionCommandManager#findOne() fails', () => { + def('findCommandResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogVersionCommandManager#findOne() succeeds', () => { + it('calls ioFogProvisionKeyManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogProvisionKeyManager.findOne).to.have.been.calledWith({ + iofogUuid: $uuid + }, transaction); + + context('when ioFogProvisionKeyManager#findOne() fails', () => { + def('findProvisionResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.equal(undefined); + }) + }); + + context('when ioFogProvisionKeyManager#findOne() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.equal($response); + }) + }); + }); + }); + }); + + describe('.updateHalHardwareInfo()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('info', () => 'testInfo'); + def('hardwareData', () => ({ + info: $info + })); + + def('response', () => ({ + versionCommand: $versionCommandLine, + provisionKey: $provisionKey, + expirationTime: $expirationTime + })); + + def('subject', () => $subject.updateHalHardwareInfo($hardwareData, $fog, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('hwResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(HWInfoManager, 'updateOrCreate').returns($hwResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith($hardwareData, Validator.schemas.updateHardwareInfo); + }); + + 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 HWInfoManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(HWInfoManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: $uuid + }, $hardwareData, transaction); + + context('when HWInfoManager#updateOrCreate() fails', () => { + def('hwResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.equal(undefined); + }) + }); + + context('when HWInfoManager#updateOrCreate() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.equal(undefined); + }) + }); + }); + }); + }); + + describe('.updateHalUsbInfo()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + def('info', () => 'testInfo'); + def('usbData', () => ({ + info: $info + })); + + def('response', () => ({ + versionCommand: $versionCommandLine, + provisionKey: $provisionKey, + expirationTime: $expirationTime + })); + + def('subject', () => $subject.updateHalUsbInfo($usbData, $fog, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('usbResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(USBInfoManager, 'updateOrCreate').returns($usbResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith($usbData, Validator.schemas.updateUsbInfo); + }); + + 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 USBInfoManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(USBInfoManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: $uuid + }, $usbData, transaction); + + context('when USBInfoManager#updateOrCreate() fails', () => { + def('usbResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.equal(undefined); + }) + }); + + context('when USBInfoManager#updateOrCreate() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.equal(undefined); + }) + }); + }); + }); + }); + + describe('.deleteNode()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('subject', () => $subject.deleteNode($fog, transaction)); + + def('deleteResponse', () => Promise.resolve($getStracesData)); + + beforeEach(() => { + $sandbox.stub(ioFogManager, 'delete').returns($deleteResponse); + }); + + it('calls ioFogManager#delete() with correct args', async () => { + await $subject; + expect(ioFogManager.delete).to.have.been.calledWith({ + uuid: $uuid + }, transaction); + }); + + context('when ioFogManager#delete() fails', () => { + def('deleteResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#delete() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + }); + + describe('.getImageSnapshot()', () => { + const transaction = {}; + const error = 'Error!'; + + def('uuid', () => 'testUuid'); + + def('fog', () => ({ + uuid: $uuid + })); + + def('microserviceUuid', () => 'testMicroserviceUuid'); + + def('microserviceResponse', () => ({ + uuid: $microserviceUuid + })); + + def('subject', () => $subject.getImageSnapshot($fog, transaction)); + + def('findResponse', () => Promise.resolve($microserviceResponse)); + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOne').returns($findResponse); + }); + + it('calls MicroserviceManager#delete() with correct args', async () => { + await $subject; + expect(MicroserviceManager.findOne).to.have.been.calledWith({ + iofogUuid: $uuid, + imageSnapshot: 'get_image' + }, transaction); + }); + + context('when MicroserviceManager#delete() fails', () => { + def('findResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#delete() succeeds', () => { + it(`succeeds`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + }); }); \ No newline at end of file From a304b99ede109df6be554650023e72c26b148dc2 Mon Sep 17 00:00:00 2001 From: Railag Date: Sat, 22 Dec 2018 15:11:27 +0300 Subject: [PATCH 25/39] bug(fix) fixed deprecated warning about Buffer constructor (EWC-422) --- src/services/diagnostic-service.js | 8 ++++---- test/src/services/agent-service.test.js | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index fcd46e37b..9e48de668 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -68,7 +68,7 @@ const getMicroserviceStraceData = async function (uuid, data, user, isCLI, trans if (data.format === 'file') { _createDirectoryIfNotExists(dir); _writeBufferToFile(filePath, straceData.buffer); - result = _converFileToBase64(filePath); + result = _convertFileToBase64(filePath); _deleteFile(filePath); } @@ -230,9 +230,9 @@ const _writeBufferToFile = function (filePath, data) { }); }; -const _converFileToBase64 = function (filePath) { - const bitmap = fs.readFileSync(filePath); - return new Buffer(bitmap).toString('base64'); +const _convertFileToBase64 = function (filePath) { + const file = fs.readFileSync(filePath); + return new Buffer.from(file).toString('base64'); }; const _deleteFile = function (filePath) { diff --git a/test/src/services/agent-service.test.js b/test/src/services/agent-service.test.js index d13f6e81a..f60e5824b 100644 --- a/test/src/services/agent-service.test.js +++ b/test/src/services/agent-service.test.js @@ -1352,4 +1352,7 @@ describe('Agent Service', () => { }); }); + // TODO + // describe('.putImageSnapshot()', () => { + }); \ No newline at end of file From 5dad1a13b1da85560a60cd9ee008e6d498256182 Mon Sep 17 00:00:00 2001 From: Railag Date: Wed, 26 Dec 2018 16:27:25 +0300 Subject: [PATCH 26/39] test(core) unit tests for catalog service (EWC-383) (#458) --- src/services/catalog-service.js | 194 ++-- src/services/connector-service.js | 1 - src/services/email-activation-code-service.js | 3 - test/src/services/catalog-service.test.js | 906 ++++++++++++++++++ 4 files changed, 999 insertions(+), 105 deletions(-) create mode 100644 test/src/services/catalog-service.test.js diff --git a/src/services/catalog-service.js b/src/services/catalog-service.js index 1fc265605..18274a980 100644 --- a/src/services/catalog-service.js +++ b/src/services/catalog-service.js @@ -20,11 +20,11 @@ const CatalogItemImageManager = require('../sequelize/managers/catalog-item-imag const CatalogItemInputTypeManager = require('../sequelize/managers/catalog-item-input-type-manager'); const CatalogItemOutputTypeManager = require('../sequelize/managers/catalog-item-output-type-manager'); const Op = require('sequelize').Op; -const validator = require('../schemas/index'); +const Validator = require('../schemas/index'); const RegistryManager = require('../sequelize/managers/registry-manager'); const createCatalogItem = async function (data, user, transaction) { - await validator.validate(data, validator.schemas.catalogItemCreate); + await Validator.validate(data, Validator.schemas.catalogItemCreate); await _checkForDuplicateName(data.name, {userId: user.id}, transaction); await _checkForRestrictedPublisher(data.publisher); const catalogItem = await _createCatalogItem(data, user, transaction); @@ -38,7 +38,7 @@ const createCatalogItem = async function (data, user, transaction) { }; const updateCatalogItem = async function (id, data, user, isCLI, transaction) { - await validator.validate(data, validator.schemas.catalogItemUpdate); + await Validator.validate(data, Validator.schemas.catalogItemUpdate); const where = isCLI ? {id: id} @@ -50,76 +50,6 @@ const updateCatalogItem = async function (id, data, user, isCLI, transaction) { await _updateCatalogItemIOTypes(data, where, transaction); }; -const _updateCatalogItem = async function (data, where, transaction) { - let catalogItem = { - name: data.name, - description: data.description, - category: data.category, - configExample: data.configExample, - publisher: data.publisher, - diskRequired: data.diskRequired, - ramRequired: data.ramRequired, - picture: data.picture, - isPublic: data.isPublic, - registryId: data.registryId - }; - - catalogItem = AppHelper.deleteUndefinedFields(catalogItem); - if (!catalogItem || AppHelper.isEmpty(catalogItem)) { - return - } - const registry = await RegistryManager.findOne({id: data.registryId}, transaction); - if (!registry) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_REGISTRY_ID, data.registryId)); - } - - const item = await _checkIfItemExists(where, transaction); - await _checkForDuplicateName(data.name, item, transaction); - await CatalogItemManager.update(where, catalogItem, transaction); -}; - -const _updateCatalogItemImages = async function (data, transaction) { - if (data.images) { - for (let image of data.images) { - switch (image.fogTypeId) { - case 1: - await CatalogItemImageManager.updateOrCreate({ - catalogItemId: data.id, - fogTypeId: 1 - }, image, transaction); - break; - case 2: - await CatalogItemImageManager.updateOrCreate({ - catalogItemId: data.id, - fogTypeId: 2 - }, image, transaction); - break; - } - } - } -}; - -const _updateCatalogItemIOTypes = async function (data, where, transaction) { - if (data.inputType && data.inputType.length != 0) { - let inputType = { - catalogItemId: data.id, - infoType: data.inputType.infoType, - infoFormat: data.inputType.infoFormat - }; - inputType = AppHelper.deleteUndefinedFields(inputType); - await CatalogItemInputTypeManager.updateOrCreate({catalogItemId: data.id}, inputType, transaction); - } - if (data.outputType && data.outputType.length !== 0) { - let outputType = { - catalogItemId: data.id, - infoType: data.outputType.infoType, - infoFormat: data.outputType.infoFormat - }; - outputType = AppHelper.deleteUndefinedFields(outputType); - await CatalogItemOutputTypeManager.updateOrCreate({catalogItemId: data.id}, outputType, transaction); - } -}; - const listCatalogItems = async function (user, isCLI, transaction) { const where = isCLI ? {category: {[Op.ne]: 'SYSTEM'}} @@ -162,6 +92,37 @@ const deleteCatalogItem = async function (id, user, isCLI, transaction) { return affectedRows; }; +async function getNetworkCatalogItem(transaction) { + return await CatalogItemManager.findOne({ + name: 'Networking Tool', + category: 'SYSTEM', + publisher: 'Eclipse ioFog', + registry_id: 1, + user_id: null + }, transaction) +} + +async function getBluetoothCatalogItem(transaction) { + return await CatalogItemManager.findOne({ + name: 'RESTBlue', + category: 'SYSTEM', + publisher: 'Eclipse ioFog', + registry_id: 1, + user_id: null + }, transaction) +} + +async function getHalCatalogItem(transaction) { + return await CatalogItemManager.findOne({ + name: 'HAL', + category: 'SYSTEM', + publisher: 'Eclipse ioFog', + registry_id: 1, + user_id: null + }, transaction) +} + + const _checkForDuplicateName = async function (name, item, transaction) { if (name) { const where = item.id @@ -175,7 +136,7 @@ const _checkForDuplicateName = async function (name, item, transaction) { } }; -const _checkForRestrictedPublisher = async function(publisher) { +const _checkForRestrictedPublisher = async function (publisher) { if (publisher === 'Eclipse ioFog') { throw new Errors.ValidationError(ErrorMessages.RESTRICTED_PUBLISHER); } @@ -267,35 +228,66 @@ const _createCatalogItemOutputType = async function (data, catalogItem, transact return await CatalogItemOutputTypeManager.create(catalogItemOutputType, transaction); }; -async function getNetworkCatalogItem(transaction) { - return await CatalogItemManager.findOne({ - name: 'Networking Tool', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null - }, transaction) -} -async function getBluetoothCatalogItem(transaction) { - return await CatalogItemManager.findOne({ - name: 'RESTBlue', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null - }, transaction) -} +const _updateCatalogItem = async function (data, where, transaction) { + let catalogItem = { + name: data.name, + description: data.description, + category: data.category, + configExample: data.configExample, + publisher: data.publisher, + diskRequired: data.diskRequired, + ramRequired: data.ramRequired, + picture: data.picture, + isPublic: data.isPublic, + registryId: data.registryId + }; -async function getHalCatalogItem(transaction) { - return await CatalogItemManager.findOne({ - name: 'HAL', - category: 'SYSTEM', - publisher: 'Eclipse ioFog', - registry_id: 1, - user_id: null - }, transaction) -} + catalogItem = AppHelper.deleteUndefinedFields(catalogItem); + if (!catalogItem || AppHelper.isEmpty(catalogItem)) { + return + } + const registry = await RegistryManager.findOne({id: data.registryId}, transaction); + if (!registry) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_REGISTRY_ID, data.registryId)); + } + + const item = await _checkIfItemExists(where, transaction); + await _checkForDuplicateName(data.name, item, transaction); + await CatalogItemManager.update(where, catalogItem, transaction); +}; + +const _updateCatalogItemImages = async function (data, transaction) { + if (data.images) { + for (const image of data.images) { + await CatalogItemImageManager.updateOrCreate({ + catalogItemId: data.id, + fogTypeId: image.fogTypeId + }, image, transaction); + } + } +}; + +const _updateCatalogItemIOTypes = async function (data, where, transaction) { + if (data.inputType && data.inputType.length !== 0) { + let inputType = { + catalogItemId: data.id, + infoType: data.inputType.infoType, + infoFormat: data.inputType.infoFormat + }; + inputType = AppHelper.deleteUndefinedFields(inputType); + await CatalogItemInputTypeManager.updateOrCreate({catalogItemId: data.id}, inputType, transaction); + } + if (data.outputType && data.outputType.length !== 0) { + let outputType = { + catalogItemId: data.id, + infoType: data.outputType.infoType, + infoFormat: data.outputType.infoFormat + }; + outputType = AppHelper.deleteUndefinedFields(outputType); + await CatalogItemOutputTypeManager.updateOrCreate({catalogItemId: data.id}, outputType, transaction); + } +}; module.exports = { createCatalogItem: TransactionDecorator.generateTransaction(createCatalogItem), diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 2f1ceab98..787c97ab7 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -23,7 +23,6 @@ const constants = require('../helpers/constants'); const logger = require('../logger'); const qs = require('qs'); const Op = require('sequelize').Op; -const Sequelize = require('sequelize'); const fs = require('fs'); const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); diff --git a/src/services/email-activation-code-service.js b/src/services/email-activation-code-service.js index d842ff168..1e63963d8 100644 --- a/src/services/email-activation-code-service.js +++ b/src/services/email-activation-code-service.js @@ -11,9 +11,6 @@ * */ -const async = require('async'); -const logger = require('../logger'); - const EmailActivationCodeManager = require('../sequelize/managers/email-activation-code-manager'); const AppHelper = require('../helpers/app-helper'); const ErrorMessages = require('../helpers/error-messages'); diff --git a/test/src/services/catalog-service.test.js b/test/src/services/catalog-service.test.js new file mode 100644 index 000000000..12aa23567 --- /dev/null +++ b/test/src/services/catalog-service.test.js @@ -0,0 +1,906 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const CatalogItemManager = require('../../../src/sequelize/managers/catalog-item-manager'); +const CatalogService = require('../../../src/services/catalog-service'); +const Validator = require('../../../src/schemas'); +const CatalogItemImageManager = require('../../../src/sequelize/managers/catalog-item-image-manager'); +const CatalogItemInputTypeManager = require('../../../src/sequelize/managers/catalog-item-input-type-manager'); +const CatalogItemOutputTypeManager = require('../../../src/sequelize/managers/catalog-item-output-type-manager'); +const RegistryManager = require('../../../src/sequelize/managers/registry-manager'); +const AppHelper = require('../../../src/helpers/app-helper'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; + +describe('Catalog Service', () => { + def('subject', () => CatalogService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const data = { + "name": "testName", + "description": "string", + "category": "string", + "images": [ + { + "containerImage": "x86 docker image name", + "fogTypeId": 1 + }, + { + "containerImage": "ARM docker image name", + "fogTypeId": 2 + } + ], + "publisher": "string", + "diskRequired": 0, + "ramRequired": 0, + "picture": "string", + "isPublic": true, + "registryId": 1, + "inputType": { + "infoType": "string", + "infoFormat": "string" + }, + "outputType": { + "infoType": "string", + "infoFormat": "string" + }, + "configExample": "string" + }; + + let catalogItem = { + name: data.name, + description: data.description, + category: data.category, + configExample: data.configExample, + publisher: data.publisher, + diskRequired: data.diskRequired, + ramRequired: data.ramRequired, + picture: data.picture, + isPublic: data.isPublic, + registryId: data.registryId, + userId: user.id + }; + + const catalogItemImages = [ + { + fogTypeId: 1, + catalogItemId: catalogItem.id + }, + { + fogTypeId: 2, + catalogItemId: catalogItem.id + } + ]; + if (data.images) { + for (let image of data.images) { + switch (image.fogTypeId) { + case 1: + catalogItemImages[0].containerImage = image.containerImage; + break; + case 2: + catalogItemImages[1].containerImage = image.containerImage; + break; + } + } + } + + let catalogItemInputType = { + catalogItemId: catalogItem.id + }; + + if (data.inputType) { + catalogItemInputType.infoType = data.inputType.infoType; + catalogItemInputType.infoFormat = data.inputType.infoFormat; + } + + let catalogItemOutputType = { + catalogItemId: catalogItem.id + }; + + if (data.outputType) { + catalogItemOutputType.infoType = data.outputType.infoType; + catalogItemOutputType.infoFormat = data.outputType.infoFormat; + } + + def('subject', () => $subject.createCatalogItem(data, user, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('catalogItemFindResponse', () => Promise.resolve()); + def('deleteUndefinedFieldsResponse1', () => catalogItem); + def('deleteUndefinedFieldsResponse2', () => catalogItemInputType); + def('deleteUndefinedFieldsResponse3', () => catalogItemOutputType); + def('catalogItemCreateResponse', () => Promise.resolve(catalogItem)); + def('catalogItemImageCreateResponse', () => Promise.resolve()); + def('catalogItemInputTypeCreateResponse', () => Promise.resolve()); + def('catalogItemOutputTypeCreateResponse', () => Promise.resolve({})); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(CatalogItemManager, 'findOne').returns($catalogItemFindResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields') + .onFirstCall().returns($deleteUndefinedFieldsResponse1) + .onSecondCall().returns($deleteUndefinedFieldsResponse2) + .onThirdCall().returns($deleteUndefinedFieldsResponse3); + $sandbox.stub(CatalogItemManager, 'create').returns($catalogItemCreateResponse); + $sandbox.stub(CatalogItemImageManager, 'bulkCreate').returns($catalogItemImageCreateResponse); + $sandbox.stub(CatalogItemInputTypeManager, 'create').returns($catalogItemInputTypeCreateResponse); + $sandbox.stub(CatalogItemOutputTypeManager, 'create').returns($catalogItemOutputTypeCreateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.catalogItemCreate); + }); + + 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 CatalogItemManager#findOne() with correct args', async () => { + await $subject; + const where = catalogItem.id + ? {[Op.or]: [{userId: catalogItem.userId}, {userId: null}], name: data.name, id: {[Op.ne]: catalogItem.id}} + : {[Op.or]: [{userId: catalogItem.userId}, {userId: null}], name: data.name}; + expect(CatalogItemManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when CatalogItemManager#findOne() fails', () => { + def('catalogItemFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemManager#findOne() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(catalogItem); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse1', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls CatalogItemManager#create() with correct args', async () => { + await $subject; + expect(CatalogItemManager.create).to.have.been.calledWith(catalogItem, transaction); + }); + + context('when CatalogItemManager#create() fails', () => { + def('catalogItemCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemManager#create() succeeds', () => { + it('calls CatalogItemImageManager#bulkCreate() with correct args', async () => { + await $subject; + expect(CatalogItemImageManager.bulkCreate).to.have.been.calledWith(catalogItemImages, transaction); + }); + + context('when CatalogItemImageManager#bulkCreate() fails', () => { + def('catalogItemImageCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemImageManager#bulkCreate() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(catalogItemInputType); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls CatalogItemInputTypeManager#create() with correct args', async () => { + await $subject; + expect(CatalogItemInputTypeManager.create).to.have.been.calledWith(catalogItemInputType); + }); + + context('when CatalogItemInputTypeManager#create() fails', () => { + def('catalogItemInputTypeCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemInputTypeManager#create() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(catalogItemOutputType); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse3', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls CatalogItemOutputTypeManager#create() with correct args', async () => { + await $subject; + expect(CatalogItemOutputTypeManager.create).to.have.been.calledWith(catalogItemOutputType); + }); + + context('when CatalogItemOutputTypeManager#create() fails', () => { + def('catalogItemOutputTypeCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemOutputTypeManager#create() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.have.property('id') + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.updateCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const id = 25; + + const data = { + "name": "string", + "description": "string", + "category": "string", + "images": [ + { + "containerImage": "x86 docker image name", + "fogTypeId": 1 + }, + { + "containerImage": "ARM docker image name", + "fogTypeId": 2 + } + ], + "publisher": "string", + "diskRequired": 0, + "ramRequired": 0, + "picture": "string", + "isPublic": true, + "registryId": 1, + "inputType": { + "infoType": "string", + "infoFormat": "string" + }, + "outputType": { + "infoType": "string", + "infoFormat": "string" + }, + "configExample": "string" + }; + + const isCLI = false; + const where = isCLI + ? {id: id} + : {id: id, userId: user.id}; + + data.id = id; + + + let catalogItem = { + name: data.name, + description: data.description, + category: data.category, + configExample: data.configExample, + publisher: data.publisher, + diskRequired: data.diskRequired, + ramRequired: data.ramRequired, + picture: data.picture, + isPublic: data.isPublic, + registryId: data.registryId + }; + + const image1 = { + fogTypeId: 1, + catalogItemId: id + }; + const image2 = { + fogTypeId: 2, + catalogItemId: id + }; + const catalogItemImages = [ + image1, image2 + ]; + + if (data.images) { + for (let image of data.images) { + switch (image.fogTypeId) { + case 1: + catalogItemImages[0].containerImage = image.containerImage; + break; + case 2: + catalogItemImages[1].containerImage = image.containerImage; + break; + } + } + } + + const updatedImage1 = { + fogTypeId: 1, + containerImage: "x86 docker image name", + }; + + const updatedImage2 = { + fogTypeId: 2, + containerImage: "ARM docker image name", + }; + + let catalogItemInputType = { + catalogItemId: id + }; + + if (data.inputType) { + catalogItemInputType.infoType = data.inputType.infoType; + catalogItemInputType.infoFormat = data.inputType.infoFormat; + } + + let catalogItemOutputType = { + catalogItemId: id + }; + + if (data.outputType) { + catalogItemOutputType.infoType = data.outputType.infoType; + catalogItemOutputType.infoFormat = data.outputType.infoFormat; + } + + def('subject', () => $subject.updateCatalogItem(id, data, user, isCLI, transaction)); + + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse1', () => catalogItem); + def('deleteUndefinedFieldsResponse2', () => catalogItemInputType); + def('deleteUndefinedFieldsResponse3', () => catalogItemOutputType); + def('isEmptyResponse', () => false); + def('registryFindResponse', () => Promise.resolve({})); + def('catalogItemFindResponse1', () => Promise.resolve(catalogItem)); + def('catalogItemFindResponse2', () => Promise.resolve()); + def('catalogItemUpdateResponse', () => Promise.resolve()); + def('catalogItemImageUpdateOrCreateResponse', () => Promise.resolve()); + def('catalogItemInputTypeUpdateOrCreateResponse', () => Promise.resolve()); + def('catalogItemOutputTypeUpdateOrCreateResponse', () => Promise.resolve({})); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields') + .onFirstCall().returns($deleteUndefinedFieldsResponse1) + .onSecondCall().returns($deleteUndefinedFieldsResponse2) + .onThirdCall().returns($deleteUndefinedFieldsResponse3); + $sandbox.stub(AppHelper, 'isEmpty').returns($isEmptyResponse); + $sandbox.stub(RegistryManager, 'findOne').returns($registryFindResponse); + $sandbox.stub(CatalogItemManager, 'findOne') + .onCall(0).returns($catalogItemFindResponse1) + .onCall(1).returns($catalogItemFindResponse2) + .onCall(2).returns($catalogItemFindResponse1) + .onCall(3).returns($catalogItemFindResponse2); + $sandbox.stub(CatalogItemManager, 'update').returns($catalogItemUpdateResponse); + $sandbox.stub(CatalogItemImageManager, 'updateOrCreate').returns($catalogItemImageUpdateOrCreateResponse); // twice + $sandbox.stub(CatalogItemInputTypeManager, 'updateOrCreate').returns($catalogItemInputTypeUpdateOrCreateResponse); + $sandbox.stub(CatalogItemOutputTypeManager, 'updateOrCreate').returns($catalogItemOutputTypeUpdateOrCreateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.catalogItemUpdate); + }); + + 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(catalogItem); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse1', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls AppHelper#isEmpty() with correct args', async () => { + await $subject; + expect(AppHelper.isEmpty).to.have.been.calledWith(catalogItem); + }); + + context('when AppHelper#isEmpty() fails', () => { + def('isEmptyResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isEmpty() succeeds', () => { + it('calls RegistryManager#findOne() with correct args', async () => { + await $subject; + expect(RegistryManager.findOne).to.have.been.calledWith({ + id: data.registryId + }, transaction) + }); + + context('when RegistryManager#findOne() fails', () => { + def('registryFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when RegistryManager#findOne() succeeds', () => { + it('calls CatalogItemManager#findOne() with correct args', async () => { + await $subject; + const whereFind = catalogItem.id + ? { + [Op.or]: [{userId: catalogItem.userId}, {userId: null}], + name: data.name, + id: {[Op.ne]: catalogItem.id} + } + : {[Op.or]: [{userId: catalogItem.userId}, {userId: null}], name: data.name}; + expect(CatalogItemManager.findOne).to.have.been.calledWith(whereFind, transaction); + }); + + context('when CatalogItemManager#findOne() succeeds', () => { + it('calls CatalogItemManager#findOne() with correct args', async () => { + await $subject; + const whereFind = catalogItem.id + ? { + [Op.or]: [{userId: catalogItem.userId}, {userId: null}], + name: data.name, + id: {[Op.ne]: catalogItem.id} + } + : {[Op.or]: [{userId: catalogItem.userId}, {userId: null}], name: data.name}; + expect(CatalogItemManager.findOne).to.have.been.calledWith(whereFind, transaction); + }); + + context('when CatalogItemManager#findOne() succeeds', () => { + it('calls CatalogItemManager#update() with correct args', async () => { + await $subject; + expect(CatalogItemManager.update).to.have.been.calledWith(where, catalogItem, transaction); + }); + + context('when CatalogItemManager#update() fails', () => { + def('catalogItemUpdateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemManager#update() succeeds', () => { + it('calls CatalogItemImageManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(CatalogItemImageManager.updateOrCreate).to.have.been.calledWith({ + catalogItemId: data.id, + fogTypeId: image1.fogTypeId + }, updatedImage1, transaction); + }); + + context('when CatalogItemImageManager#updateOrCreate() fails', () => { + def('catalogItemImageUpdateOrCreateResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when CatalogItemImageManager#updateOrCreate() succeeds', () => { + it('calls CatalogItemImageManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(CatalogItemImageManager.updateOrCreate).to.have.been.calledWith({ + catalogItemId: id, + fogTypeId: image2.fogTypeId + }, updatedImage2, transaction); + }); + + context('when CatalogItemImageManager#updateOrCreate() fails', () => { + def('catalogItemImageUpdateOrCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemImageManager#updateOrCreate() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(catalogItemInputType); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls CatalogItemInputTypeManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(CatalogItemInputTypeManager.updateOrCreate).to.have.been.calledWith({ + catalogItemId: data.id + }, catalogItemInputType, transaction); + }); + + context('when CatalogItemInputTypeManager#updateOrCreate() fails', () => { + def('catalogItemInputTypeUpdateOrCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemInputTypeManager#updateOrCreate() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(catalogItemOutputType); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse3', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls CatalogItemOutputTypeManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(CatalogItemOutputTypeManager.updateOrCreate).to.have.been.calledWith({ + catalogItemId: data.id + }, catalogItemOutputType, transaction); + }); + + context('when CatalogItemOutputTypeManager#updateOrCreate() fails', () => { + def('catalogItemOutputTypeUpdateOrCreateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when CatalogItemOutputTypeManager#updateOrCreate() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.listCatalogItems()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const isCLI = false; + + const where = isCLI + ? {category: {[Op.ne]: 'SYSTEM'}} + : {[Op.or]: [{userId: user.id}, {userId: null}], category: {[Op.ne]: 'SYSTEM'}}; + + const attributes = isCLI + ? {} + : {exclude: ["userId"]}; + + def('subject', () => $subject.listCatalogItems(user, isCLI, transaction)); + + def('catalogItemsFindResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(CatalogItemManager, 'findAllWithDependencies').returns($catalogItemFindResponse); + }); + + it('calls CatalogItemManager#findAllWithDependencies() with correct args', async () => { + await $subject; + expect(CatalogItemManager.findAllWithDependencies).to.have.been.calledWith(where, attributes, transaction); + }); + + context('when CatalogItemManager#findAllWithDependencies() fails', () => { + def('catalogItemsFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.eventually.have.property('catalogItems'); + }) + }); + + context('when CatalogItemManager#findAllWithDependencies() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.have.property('catalogItems') + }) + }) + + }); + + describe('.getCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const isCLI = false; + + const id = 5; + + const where = isCLI + ? {id: id, category: {[Op.ne]: 'SYSTEM'}} + : {[Op.or]: [{userId: user.id}, {userId: null}], id: id, category: {[Op.ne]: 'SYSTEM'}}; + + const attributes = isCLI + ? {} + : {exclude: ["userId"]}; + + def('subject', () => $subject.getCatalogItem(id, user, isCLI, transaction)); + + def('catalogItemFindResponse', () => Promise.resolve({})); + + beforeEach(() => { + $sandbox.stub(CatalogItemManager, 'findOneWithDependencies').returns($catalogItemFindResponse); + }); + + it('calls CatalogItemManager#findOneWithDependencies() with correct args', async () => { + await $subject; + expect(CatalogItemManager.findOneWithDependencies).to.have.been.calledWith(where, attributes, transaction); + }); + + context('when CatalogItemManager#findOneWithDependencies() fails', () => { + def('catalogItemFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogItemManager#findOneWithDependencies() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.deep.equal({}) + }) + }) + + }); + + describe('.deleteCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const isCLI = false; + + const id = 5; + + const where = isCLI + ? {id: id} + : {userId: user.id, id: id}; + + def('subject', () => $subject.deleteCatalogItem(id, user, isCLI, transaction)); + + def('response', () => 1); + def('catalogItemDeleteResponse', () => Promise.resolve($response)); + + beforeEach(() => { + $sandbox.stub(CatalogItemManager, 'delete').returns($catalogItemDeleteResponse); + }); + + it('calls CatalogItemManager#delete() with correct args', async () => { + await $subject; + expect(CatalogItemManager.delete).to.have.been.calledWith(where, transaction); + }); + + context('when CatalogItemManager#delete() fails', () => { + def('catalogItemDeleteResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogItemManager#delete() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.deep.equal($response) + }) + }) + + }); + + describe('.getNetworkCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getNetworkCatalogItem(transaction)); + + def('response', () => 1); + def('catalogItemFindResponse', () => Promise.resolve($response)); + + beforeEach(() => { + $sandbox.stub(CatalogItemManager, 'findOne').returns($catalogItemFindResponse); + }); + + it('calls CatalogItemManager#findOne() with correct args', async () => { + await $subject; + expect(CatalogItemManager.findOne).to.have.been.calledWith({ + name: 'Networking Tool', + category: 'SYSTEM', + publisher: 'Eclipse ioFog', + registry_id: 1, + user_id: null + }, transaction); + }); + + context('when CatalogItemManager#findOne() fails', () => { + def('catalogItemFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogItemManager#findOne() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.deep.equal($response) + }) + }) + + }); + + describe('.getBluetoothCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getBluetoothCatalogItem(transaction)); + + def('response', () => 1); + def('catalogItemFindResponse', () => Promise.resolve($response)); + + beforeEach(() => { + $sandbox.stub(CatalogItemManager, 'findOne').returns($catalogItemFindResponse); + }); + + it('calls CatalogItemManager#findOne() with correct args', async () => { + await $subject; + expect(CatalogItemManager.findOne).to.have.been.calledWith({ + name: 'RESTBlue', + category: 'SYSTEM', + publisher: 'Eclipse ioFog', + registry_id: 1, + user_id: null + }, transaction); + }); + + context('when CatalogItemManager#findOne() fails', () => { + def('catalogItemFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogItemManager#findOne() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.deep.equal($response) + }) + }) + + }); + + describe('.getHalCatalogItem()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getHalCatalogItem(transaction)); + + def('response', () => 1); + def('catalogItemFindResponse', () => Promise.resolve($response)); + + beforeEach(() => { + $sandbox.stub(CatalogItemManager, 'findOne').returns($catalogItemFindResponse); + }); + + it('calls CatalogItemManager#findOne() with correct args', async () => { + await $subject; + expect(CatalogItemManager.findOne).to.have.been.calledWith({ + name: 'HAL', + category: 'SYSTEM', + publisher: 'Eclipse ioFog', + registry_id: 1, + user_id: null + }, transaction); + }); + + context('when CatalogItemManager#findOne() fails', () => { + def('catalogItemFindResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogItemManager#findOne() succeeds', () => { + it('succeeds', () => { + return expect($subject).to.eventually.deep.equal($response) + }) + }) + + }); + +}); \ No newline at end of file From 9ab82180ebcea8ebc3e808870db251de46843f39 Mon Sep 17 00:00:00 2001 From: MaksimChepelev Date: Wed, 26 Dec 2018 16:29:13 +0300 Subject: [PATCH 27/39] Maksimchepelev/bugs (#457) * fix(cli): fix validation on delete connector Closes EWC-447 * fix(cli): fix data types and descriptions in help '... settings JSON file' -> 'Path to ... settings JSON file' flowId and catalogId type 'string' -> 'number' in help Closes EWC-445 EWC-446 --- src/cli/catalog.js | 4 ++-- src/cli/flow.js | 4 ++-- src/cli/iofog.js | 2 +- src/cli/microservice.js | 2 +- src/services/connector-service.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cli/catalog.js b/src/cli/catalog.js index f17e0e54f..7d83d7e76 100644 --- a/src/cli/catalog.js +++ b/src/cli/catalog.js @@ -57,11 +57,11 @@ class Catalog extends BaseCLIHandler { group: [constants.CMD] }, { - name: 'file', alias: 'f', type: String, description: 'Catalog item settings JSON file', + name: 'file', alias: 'f', type: String, description: 'Path to catalog item settings JSON file', group: [constants.CMD_ADD, constants.CMD_UPDATE] }, { - name: 'item-id', alias: 'i', type: String, description: 'Catalog item ID', + name: 'item-id', alias: 'i', type: Number, description: 'Catalog item ID', group: [constants.CMD_UPDATE, constants.CMD_REMOVE, constants.CMD_INFO] }, { diff --git a/src/cli/flow.js b/src/cli/flow.js index a5d422ca6..5c7a718c4 100644 --- a/src/cli/flow.js +++ b/src/cli/flow.js @@ -37,11 +37,11 @@ class Flow extends BaseCLIHandler { }, { name: 'file', alias: 'f', type: String, - description: 'Application flow settings JSON file', + description: 'Path to application flow settings JSON file', group: [constants.CMD_ADD, constants.CMD_UPDATE] }, { - name: 'flow-id', alias: 'i', type: String, + name: 'flow-id', alias: 'i', type: Number, description: 'Application flow ID', group: [constants.CMD_UPDATE, constants.CMD_REMOVE, constants.CMD_INFO] }, diff --git a/src/cli/iofog.js b/src/cli/iofog.js index a00e79ec0..8749dbb83 100644 --- a/src/cli/iofog.js +++ b/src/cli/iofog.js @@ -53,7 +53,7 @@ class IOFog extends BaseCLIHandler { group: [constants.CMD]}, { name: 'file', alias: 'f', type: String, - description: 'ioFog settings JSON file', + description: 'Path to ioFog settings JSON file', group: [constants.CMD_ADD, constants.CMD_UPDATE] }, { diff --git a/src/cli/microservice.js b/src/cli/microservice.js index d94a33e59..687c0fe52 100644 --- a/src/cli/microservice.js +++ b/src/cli/microservice.js @@ -78,7 +78,7 @@ class Microservice extends BaseCLIHandler { group: [constants.CMD] }, { - name: 'file', alias: 'f', type: String, description: 'Microservice settings JSON file', + name: 'file', alias: 'f', type: String, description: 'Path to microservice settings JSON file', group: [constants.CMD_ADD, constants.CMD_UPDATE] }, { diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 787c97ab7..6b8809e8e 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -74,7 +74,7 @@ async function _deleteConnector(connectorData, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)) } const ports = await ConnectorPortManager.findAll({connectorId: connector.id}, transaction); - if (ports) { + if (ports && ports.length > 0) { throw new Errors.ValidationError(ErrorMessages.CONNECTOR_IS_IN_USE) } await ConnectorManager.delete(queryConnectorData, transaction); From 8274ead630bd2b1c8eb64c8c2780f623af1b2c5e Mon Sep 17 00:00:00 2001 From: MaksimChepelev Date: Thu, 27 Dec 2018 12:46:23 +0300 Subject: [PATCH 28/39] Maksimchepelev/bugs (#459) * fix(iofog): add validation for version command compare version command with isReadyToUpgrade and isReadyToRollback fields Closes EWC-448 * fix(code): fix logger is not defined error in app-helper Closes EWC-397 --- src/helpers/app-helper.js | 1 + src/helpers/error-messages.js | 3 ++- src/services/iofog-service.js | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/helpers/app-helper.js b/src/helpers/app-helper.js index 5f9d2cde7..f6730b452 100644 --- a/src/helpers/app-helper.js +++ b/src/helpers/app-helper.js @@ -14,6 +14,7 @@ const crypto = require('crypto'); const Errors = require('./errors'); +const logger = require('../logger'); const fs = require('fs'); const Config = require('../config'); const path = require('path'); diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index 0625d2952..0ad086a46 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -72,5 +72,6 @@ module.exports = { INVALID_INTERNAL_PORT: 'Internal parsing error. Please provide valid internal port.', INVALID_ROUTE: 'Route parsing error. Please provide valid route.' }, - CONNECTOR_IS_IN_USE: 'You can\'t delete connector, because it is used for routing now.' + CONNECTOR_IS_IN_USE: 'You can\'t delete connector, because it is used for routing now.', + INVALID_VERSION_COMMAND: 'Can\'t {} version now.' }; diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index 46e595751..0776cee92 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -280,6 +280,11 @@ async function _setFogVersionCommand(fogVersionData, user, isCli, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FOG_NODE_UUID, fogData.uuid)) } + if ((!fog.isReadyToRollback && fogVersionData.versionCommand === 'rollback') + || (!fog.isReadyToUpgrade && fogVersionData.versionCommand === 'upgrade')) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VERSION_COMMAND, fogVersionData.versionCommand)) + } + await _generateProvisioningKey({uuid: fogVersionData.uuid}, user, isCli, transaction); await FogVersionCommandManager.updateOrCreate({iofogUuid: fogVersionData.uuid}, newVersionCommand, transaction); await ChangeTrackingService.update(fogVersionData.uuid, ChangeTrackingService.events.version, transaction) From d51abc570a49babdc0e76094c7bb065b8447629c Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 27 Dec 2018 13:04:08 +0300 Subject: [PATCH 29/39] test(core) unit tests for change-tracking service (EWC-383) (#460) --- src/services/agent-service.js | 2 +- src/services/change-tracking-service.js | 18 +- src/services/user-service.js | 3 - test/src/services/agent-service.test.js | 10 +- .../services/change-tracking-service.test.js | 159 ++++++++++++++++++ 5 files changed, 174 insertions(+), 18 deletions(-) create mode 100644 test/src/services/change-tracking-service.test.js diff --git a/src/services/agent-service.js b/src/services/agent-service.js index 01c53fcec..e1c24ccc3 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -131,7 +131,7 @@ const updateAgentConfig = async function (updateData, fog, transaction) { const getAgentConfigChanges = async function (fog, transaction) { - const changeTracking = await ChangeTrackingService.getByFogId(fog.uuid, transaction); + const changeTracking = await ChangeTrackingService.getByIoFogUuid(fog.uuid, transaction); if (!changeTracking) { throw new Errors.NotFoundError(ErrorMessages.INVALID_NODE_ID) } diff --git a/src/services/change-tracking-service.js b/src/services/change-tracking-service.js index bed1669e9..572043da6 100644 --- a/src/services/change-tracking-service.js +++ b/src/services/change-tracking-service.js @@ -71,20 +71,20 @@ const events = Object.freeze({ } }); -async function update(fogId, data, transaction) { - await ChangeTrackingManager.update({iofogUuid: fogId}, data, transaction); +async function create(ioFogUuid, transaction) { + await ChangeTrackingManager.create({iofogUuid: ioFogUuid}, transaction); } -async function updateIfChanged(fogId, data, transaction) { - await ChangeTrackingManager.updateIfChanged({iofogUuid: fogId}, data, transaction); +async function update(ioFogUuid, data, transaction) { + await ChangeTrackingManager.update({iofogUuid: ioFogUuid}, data, transaction); } -async function create(fogId, transaction) { - await ChangeTrackingManager.create({iofogUuid: fogId}, transaction); +async function updateIfChanged(ioFogUuid, data, transaction) { + await ChangeTrackingManager.updateIfChanged({iofogUuid: ioFogUuid}, data, transaction); } -async function getByFogId(fogId, transaction) { - return await ChangeTrackingManager.findOne({iofogUuid: fogId}, transaction); +async function getByIoFogUuid(ioFogUuid, transaction) { + return await ChangeTrackingManager.findOne({iofogUuid: ioFogUuid}, transaction); } module.exports = { @@ -92,5 +92,5 @@ module.exports = { create: create, update: update, updateIfChanged: updateIfChanged, - getByFogId: getByFogId + getByIoFogUuid: getByIoFogUuid }; \ No newline at end of file diff --git a/src/services/user-service.js b/src/services/user-service.js index 881670c15..53224dbeb 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -27,9 +27,6 @@ const EmailActivationCodeService = require('./email-activation-code-service'); const AccessTokenService = require('./access-token-service'); -const logger = require('../logger'); -const constants = require('../helpers/constants'); - const TransactionDecorator = require('../decorators/transaction-decorator'); const Validator = require('../schemas'); diff --git a/test/src/services/agent-service.test.js b/test/src/services/agent-service.test.js index f60e5824b..3af91e846 100644 --- a/test/src/services/agent-service.test.js +++ b/test/src/services/agent-service.test.js @@ -364,16 +364,16 @@ describe('Agent Service', () => { def('updateIfChangedResponse', () => Promise.resolve()); beforeEach(() => { - $sandbox.stub(ChangeTrackingService, 'getByFogId').returns($getByFogIdResponse); + $sandbox.stub(ChangeTrackingService, 'getByIoFogUuid').returns($getByFogIdResponse); $sandbox.stub(ChangeTrackingService, 'updateIfChanged').returns($updateIfChangedResponse); }); - it('calls ChangeTrackingService#getByFogId() with correct args', async () => { + it('calls ChangeTrackingService#getByIoFogUuid() with correct args', async () => { await $subject; - expect(ChangeTrackingService.getByFogId).to.have.been.calledWith($uuid, transaction); + expect(ChangeTrackingService.getByIoFogUuid).to.have.been.calledWith($uuid, transaction); }); - context('when ChangeTrackingService#getByFogId() fails', () => { + context('when ChangeTrackingService#getByIoFogUuid() fails', () => { def('getByFogIdResponse', () => Promise.reject(error)); it(`fails with ${error}`, () => { @@ -381,7 +381,7 @@ describe('Agent Service', () => { }) }); - context('when ChangeTrackingService#getByFogId() succeeds', () => { + context('when ChangeTrackingService#getByIoFogUuid() succeeds', () => { it('calls ChangeTrackingService.updateIfChanged with correct args', async () => { await $subject; expect(ChangeTrackingService.updateIfChanged).to.have.been.calledWith($uuid, diff --git a/test/src/services/change-tracking-service.test.js b/test/src/services/change-tracking-service.test.js new file mode 100644 index 000000000..d61cadb0c --- /dev/null +++ b/test/src/services/change-tracking-service.test.js @@ -0,0 +1,159 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ChangeTrackingManager = require('../../../src/sequelize/managers/change-tracking-manager'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); + +describe('ChangeTracking Service', () => { + def('subject', () => ChangeTrackingService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.create()', () => { + const transaction = {}; + const error = 'Error!'; + + const ioFogUuid = 'testUuid'; + + def('subject', () => $subject.create(ioFogUuid, transaction)); + def('createResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(ChangeTrackingManager, 'create').returns($createResponse); + }); + + it('calls ChangeTrackingManager#create() with correct args', async () => { + await $subject; + expect(ChangeTrackingManager.create).to.have.been.calledWith({ + iofogUuid: ioFogUuid + }, transaction); + }); + + context('when ChangeTrackingManager#create() fails', () => { + def('createResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.update()', () => { + const transaction = {}; + const error = 'Error!'; + + const ioFogUuid = 'testUuid'; + + def('subject', () => $subject.update(ioFogUuid, data, transaction)); + def('updateResponse', () => Promise.resolve()); + + + const data = ChangeTrackingService.events.clean; + + beforeEach(() => { + $sandbox.stub(ChangeTrackingManager, 'update').returns($updateResponse); + }); + + it('calls ChangeTrackingManager#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingManager.update).to.have.been.calledWith({ + iofogUuid: ioFogUuid + }, data, transaction); + }); + + context('when ChangeTrackingManager#update() fails', () => { + def('updateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.updateIfChanged()', () => { + const transaction = {}; + const error = 'Error!'; + + const ioFogUuid = 'testUuid'; + + const data = ChangeTrackingService.events.clean; + + def('subject', () => $subject.updateIfChanged(ioFogUuid, data, transaction)); + def('updateResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(ChangeTrackingManager, 'updateIfChanged').returns($updateResponse); + }); + + it('calls ChangeTrackingManager#updateIfChanged() with correct args', async () => { + await $subject; + expect(ChangeTrackingManager.updateIfChanged).to.have.been.calledWith({ + iofogUuid: ioFogUuid + }, data, transaction); + }); + + context('when ChangeTrackingManager#updateIfChanged() fails', () => { + def('updateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingManager#updateIfChanged() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + describe('.getByIoFogUuid()', () => { + const transaction = {}; + const error = 'Error!'; + + const ioFogUuid = 'testUuid'; + + def('subject', () => $subject.getByIoFogUuid(ioFogUuid, transaction)); + def('updateResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(ChangeTrackingManager, 'findOne').returns($updateResponse); + }); + + it('calls ChangeTrackingManager#findOne() with correct args', async () => { + await $subject; + expect(ChangeTrackingManager.findOne).to.have.been.calledWith({ + iofogUuid: ioFogUuid + }, transaction); + }); + + context('when ChangeTrackingManager#findOne() fails', () => { + def('updateResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingManager#findOne() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + +}); \ No newline at end of file From 8ce03d2f8f6a73f504ea525736e8051923a4b598 Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 27 Dec 2018 13:19:11 +0300 Subject: [PATCH 30/39] version 1.0.32 (#461) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 638c28072..88220c30e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.31", + "version": "1.0.32", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi", From c236f1cbf44cace690b4b751275c3c780ee0d6cc Mon Sep 17 00:00:00 2001 From: MaksimChepelev Date: Thu, 27 Dec 2018 17:30:19 +0300 Subject: [PATCH 31/39] fix(iofog): add validation for version command (#463) * fix(iofog): add validation for version command compare version command with isReadyToUpgrade and isReadyToRollback fields Closes EWC-448 * fix(cli): fix data types and descriptions in help '... settings JSON file' -> 'Path to ... settings JSON file' flowId and catalogId type 'string' -> 'number' in help Closes EWC-445 EWC-446 --- src/helpers/error-messages.js | 3 ++- src/services/iofog-service.js | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index 0ad086a46..37e68e8d7 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -73,5 +73,6 @@ module.exports = { INVALID_ROUTE: 'Route parsing error. Please provide valid route.' }, CONNECTOR_IS_IN_USE: 'You can\'t delete connector, because it is used for routing now.', - INVALID_VERSION_COMMAND: 'Can\'t {} version now.' + INVALID_VERSION_COMMAND_UPGRADE: 'Can\'t upgrade version now. Latest is already installed', + INVALID_VERSION_COMMAND_ROLLBACK: 'Can\'t rollback version now. There are no backups on agent' }; diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index 0776cee92..b665416be 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -280,9 +280,11 @@ async function _setFogVersionCommand(fogVersionData, user, isCli, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FOG_NODE_UUID, fogData.uuid)) } - if ((!fog.isReadyToRollback && fogVersionData.versionCommand === 'rollback') - || (!fog.isReadyToUpgrade && fogVersionData.versionCommand === 'upgrade')) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VERSION_COMMAND, fogVersionData.versionCommand)) + if (!fog.isReadyToRollback && fogVersionData.versionCommand === 'rollback') { + throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_ROLLBACK) + } + if (!fog.isReadyToUpgrade && fogVersionData.versionCommand === 'upgrade') { + throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_UPGRADE) } await _generateProvisioningKey({uuid: fogVersionData.uuid}, user, isCli, transaction); From 68b12090ba35cd4735eae8949b027b54b6b484b4 Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 27 Dec 2018 17:53:43 +0300 Subject: [PATCH 32/39] test(core) unit tests for connector & controller services (EWC-383) (#464) --- src/services/connector-service.js | 121 ++--- src/services/controller-service.js | 22 +- src/services/microservices-service.js | 8 +- test/src/services/connector-service.test.js | 467 +++++++++++++++++++ test/src/services/controller-service.test.js | 101 ++++ 5 files changed, 647 insertions(+), 72 deletions(-) create mode 100644 test/src/services/connector-service.test.js create mode 100644 test/src/services/controller-service.test.js diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 6b8809e8e..dcb022376 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -26,14 +26,20 @@ const Op = require('sequelize').Op; const fs = require('fs'); const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); -async function _createConnector(connectorData, transaction) { +async function createConnector(connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorCreate); - validateConnectorData(connectorData); + _validateConnectorData(connectorData); const connector = await ConnectorManager.findOne({ [Op.or]: [ - {name: connectorData.name}, - {publicIp: connectorData.publicIp}, - {domain: connectorData.domain} + { + name: connectorData.name + }, + { + publicIp: connectorData.publicIp + }, + { + domain: connectorData.domain + } ] }, transaction); if (connector) { @@ -42,29 +48,16 @@ async function _createConnector(connectorData, transaction) { return await ConnectorManager.create(connectorData, transaction) } -async function _updateConnector(connectorData, transaction) { - await Validator.validate(connectorData, Validator.schemas.connectorUpdate) - validateConnectorData(connectorData); +async function updateConnector(connectorData, transaction) { + await Validator.validate(connectorData, Validator.schemas.connectorUpdate); + _validateConnectorData(connectorData); const queryConnectorData = { publicIp: connectorData.publicIp }; await ConnectorManager.update(queryConnectorData, connectorData, transaction) } -function validateConnectorData(connectorData) { - if (connectorData.domain) { - const validDomain = AppHelper.isValidDomain(connectorData.domain) || AppHelper.isValidPublicIP(connectorData.domain); - if (!validDomain) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_DOMAIN, connectorData.domain)); - } - } - - if (!AppHelper.isValidPublicIP(connectorData.publicIp)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)); - } -} - -async function _deleteConnector(connectorData, transaction) { +async function deleteConnector(connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorDelete); const queryConnectorData = { publicIp: connectorData.publicIp @@ -80,7 +73,7 @@ async function _deleteConnector(connectorData, transaction) { await ConnectorManager.delete(queryConnectorData, transaction); } -async function _getConnectorList(transaction) { +async function getConnectorList(transaction) { return await ConnectorManager.findAll({}, transaction) } @@ -92,17 +85,17 @@ async function openPortOnRandomConnector(isPublicAccess, transaction) { for (let i = 0; i < maxAttempts; i++) { try { connector = await _getRandomConnector(transaction); - ports = await openPortsOnConnector(connector, isPublicAccess, transaction); + ports = await _openPortsOnConnector(connector, isPublicAccess); if (ports) { isConnectorPortOpen = true; break; } } catch (e) { - logger.warn(`Failed to open ports on comsat. Attempts ${i + 1}/${maxAttempts}`) + logger.warn(`Failed to open ports on Connector. Attempts ${i + 1}/${maxAttempts}`) } } if (!isConnectorPortOpen) { - throw new Error('Not able to open port on remote CONNECTOR. Gave up after 5 attempts.') + throw new Error('Not able to open port on remote Connector. Gave up after 5 attempts.') } ports.connectorId = connector.id; return {ports: ports, connector: connector} @@ -143,22 +136,18 @@ async function _makeRequest(connector, options, data) { }) } -async function openPortsOnConnector(connector, isPublicAccess, transaction) { - let data = isPublicAccess - ? await qs.stringify({ - mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' - }) - : await qs.stringify({ - mapping: '{"type":"private","maxconnectionsport1":1, "maxconnectionsport2":1, ' + - '"heartbeatabsencethresholdport1":200000, "heartbeatabsencethresholdport2":200000}' - }); +async function closePortOnConnector(connector, ports) { + let data = qs.stringify({ + mappingid: ports.mappingId + }); + console.log(data); let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; let options = { host: connector.domain, port: port, - path: '/api/v2/mapping/add', + path: '/api/v2/mapping/remove', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -170,32 +159,39 @@ async function openPortsOnConnector(connector, isPublicAccess, transaction) { options.ca = new Buffer.from(ca); } - const ports = await _makeRequest(connector, options, data); - return ports + + await _makeRequest(connector, options, data) } -async function _getRandomConnector(transaction) { - const connectors = await _getConnectorList(transaction); - if (connectors && connectors.length > 0) { - const randomNumber = Math.round((Math.random() * (connectors.length - 1))); - return connectors[randomNumber] - } else { - throw new Error('no connectors defined') +function _validateConnectorData(connectorData) { + if (connectorData.domain) { + const validDomain = AppHelper.isValidDomain(connectorData.domain) || AppHelper.isValidPublicIP(connectorData.domain); + if (!validDomain) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_DOMAIN, connectorData.domain)); + } + } + + if (!AppHelper.isValidPublicIP(connectorData.publicIp)) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)); } } -async function closePortOnConnector(connector, ports, transaction) { - let data = qs.stringify({ - mappingid: ports.mappingId - }); - console.log(data); +async function _openPortsOnConnector(connector, isPublicAccess) { + let data = isPublicAccess + ? await qs.stringify({ + mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' + }) + : await qs.stringify({ + mapping: '{"type":"private","maxconnectionsport1":1, "maxconnectionsport2":1, ' + + '"heartbeatabsencethresholdport1":200000, "heartbeatabsencethresholdport2":200000}' + }); let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; let options = { host: connector.domain, port: port, - path: '/api/v2/mapping/remove', + path: '/api/v2/mapping/add', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -207,15 +203,26 @@ async function closePortOnConnector(connector, ports, transaction) { options.ca = new Buffer.from(ca); } + const ports = await _makeRequest(connector, options, data); + return ports +} + +async function _getRandomConnector(transaction) { + const connectors = await ConnectorManager.findAll({}, transaction); - await _makeRequest(connector, options, data) + if (connectors && connectors.length > 0) { + const randomNumber = Math.round((Math.random() * (connectors.length - 1))); + return connectors[randomNumber] + } else { + throw new Error('no connectors defined') + } } module.exports = { - createConnector: TransactionDecorator.generateTransaction(_createConnector), - updateConnector: TransactionDecorator.generateTransaction(_updateConnector), - deleteConnector: TransactionDecorator.generateTransaction(_deleteConnector), - getConnectorList: TransactionDecorator.generateTransaction(_getConnectorList), + createConnector: TransactionDecorator.generateTransaction(createConnector), + updateConnector: TransactionDecorator.generateTransaction(updateConnector), + deleteConnector: TransactionDecorator.generateTransaction(deleteConnector), + getConnectorList: TransactionDecorator.generateTransaction(getConnectorList), openPortOnRandomConnector: openPortOnRandomConnector, closePortOnConnector: closePortOnConnector -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/services/controller-service.js b/src/services/controller-service.js index 906c1cc35..d42aea13d 100644 --- a/src/services/controller-service.js +++ b/src/services/controller-service.js @@ -11,26 +11,26 @@ * */ -const FogTypesManager = require('../sequelize/managers/iofog-type-manager'); +const ioFogTypesManager = require('../sequelize/managers/iofog-type-manager'); const Config = require('../config'); const TransactionDecorator = require('../decorators/transaction-decorator'); const packageJson = require('../../package'); const getFogTypes = async function (isCLI, transaction) { - const fogTypes = await FogTypesManager.findAll({}, transaction); - let res = []; + const ioFogTypes = await ioFogTypesManager.findAll({}, transaction); + let response = []; - for (fogType of fogTypes) { - res.push({ - id: fogType.id, - name: fogType.name, - image: fogType.image, - description: fogType.description + for (ioFogType of ioFogTypes) { + response.push({ + id: ioFogType.id, + name: ioFogType.name, + image: ioFogType.image, + description: ioFogType.description }) } return { - fogTypes: res + fogTypes: response } }; @@ -59,7 +59,7 @@ const statusController = async function (isCLI) { }; const getVersion = async function (isCLI) { - return `Iofog-Controller version: ${packageJson.version}`; + return `ioFog-Controller version: ${packageJson.version}`; }; module.exports = { diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index 2207b4d04..1a2f9b909 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -380,7 +380,7 @@ async function _createSimpleRoute(sourceMicroservice, destMicroservice, transact } async function _createRouteOverConnector(sourceMicroservice, destMicroservice, user, transaction) { - //open comsat + //open connector const justOpenedConnectorsPorts = await ConnectorService.openPortOnRandomConnector(false, transaction) const ports = justOpenedConnectorsPorts.ports @@ -542,7 +542,7 @@ async function _deleteRouteOverConnector(route, transaction) { const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction) try { - await ConnectorService.closePortOnConnector(connector, ports, transaction); + await ConnectorService.closePortOnConnector(connector, ports); } catch (e) { logger.warn(`Can't close ports pair ${ports.mappingId} on connector ${connector.publicIp}. Delete manually if necessary`); } @@ -611,7 +611,7 @@ async function _createSimplePortMapping(microservice, portMappingData, user, tra } async function _createPortMappingOverConnector(microservice, portMappingData, user, transaction) { - //open comsat + //open connector const justOpenedConnectorsPorts = await ConnectorService.openPortOnRandomConnector(true, transaction) const ports = justOpenedConnectorsPorts.ports @@ -741,7 +741,7 @@ async function _deletePortMappingOverConnector(microservice, msPorts, user, tran const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction); try { - await ConnectorService.closePortOnConnector(connector, ports, transaction); + await ConnectorService.closePortOnConnector(connector, ports); } catch (e) { logger.warn(`Can't close ports pair ${ports.mappingId} on connector ${connector.publicIp}. Delete manually if necessary`); } diff --git a/test/src/services/connector-service.test.js b/test/src/services/connector-service.test.js new file mode 100644 index 000000000..5467cc39f --- /dev/null +++ b/test/src/services/connector-service.test.js @@ -0,0 +1,467 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ConnectorManager = require('../../../src/sequelize/managers/connector-manager'); +const ConnectorService = require('../../../src/services/connector-service'); +const Validator = require('../../../src/schemas'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ConnectorPortManager = require('../../../src/sequelize/managers/connector-port-manager'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const qs = require('qs'); + +describe('Connector Service', () => { + def('subject', () => ConnectorService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createConnector()', () => { + const transaction = {}; + const error = 'Error!'; + + const connectorData = { + name: 'testName', + domain: 'testDomain', + publicIp: 'testPublicIp', + cert: 'testCert', + isSelfSignedCert: false, + devMode: true + }; + + def('subject', () => $subject.createConnector(connectorData, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('isValidDomainResponse', () => false); + def('isValidPublicIpResponse', () => true); + def('isValidPublicIpResponse2', () => true); + def('findConnectorResponse', () => Promise.resolve()); + def('createConnectorResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'isValidDomain').returns($isValidDomainResponse); + $sandbox.stub(AppHelper, 'isValidPublicIP') + .onFirstCall().returns($isValidPublicIpResponse) + .onSecondCall().returns($isValidPublicIpResponse2); + $sandbox.stub(ConnectorManager, 'findOne').returns($findConnectorResponse); + $sandbox.stub(ConnectorManager, 'create').returns($createConnectorResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(connectorData, Validator.schemas.connectorCreate); + }); + + 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#isValidDomain() with correct args', async () => { + await $subject; + expect(AppHelper.isValidDomain).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidDomain() fails', () => { + def('isValidDomainResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidDomain() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.publicIp); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls ConnectorManager#findOne() with correct args', async () => { + await $subject; + expect(ConnectorManager.findOne).to.have.been.calledWith({ + [Op.or]: [ + { + name: connectorData.name + }, + { + publicIp: connectorData.publicIp + }, + { + domain: connectorData.domain + } + ] + }, transaction); + }); + + context('when ConnectorManager#findOne() fails', () => { + def('findConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#findOne() succeeds', () => { + it('calls ConnectorManager#create() with correct args', async () => { + await $subject; + expect(ConnectorManager.create).to.have.been.calledWith(connectorData, transaction); + }); + + context('when ConnectorManager#create() fails', () => { + def('createConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.updateConnector()', () => { + const transaction = {}; + const error = 'Error!'; + + const connectorData = { + name: 'testName', + domain: 'testDomain', + publicIp: 'testPublicIp', + cert: 'testCert', + isSelfSignedCert: false, + devMode: true + }; + + def('subject', () => $subject.updateConnector(connectorData, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('isValidDomainResponse', () => false); + def('isValidPublicIpResponse', () => true); + def('isValidPublicIpResponse2', () => true); + def('updateConnectorResponse', () => Promise.resolve()); + + const queryConnectorData = { + publicIp: connectorData.publicIp + }; + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'isValidDomain').returns($isValidDomainResponse); + $sandbox.stub(AppHelper, 'isValidPublicIP') + .onFirstCall().returns($isValidPublicIpResponse) + .onSecondCall().returns($isValidPublicIpResponse2); + $sandbox.stub(ConnectorManager, 'update').returns($updateConnectorResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(connectorData, Validator.schemas.connectorUpdate); + }); + + 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#isValidDomain() with correct args', async () => { + await $subject; + expect(AppHelper.isValidDomain).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidDomain() fails', () => { + def('isValidDomainResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidDomain() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.publicIp); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls ConnectorManager#update() with correct args', async () => { + await $subject; + expect(ConnectorManager.update).to.have.been.calledWith(queryConnectorData, connectorData, transaction); + }); + + context('when ConnectorManager#update() fails', () => { + def('updateConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }); + + describe('.deleteConnector()', () => { + const transaction = {}; + const error = 'Error!'; + + const connectorData = { + name: 'testName', + domain: 'testDomain', + publicIp: 'testPublicIp', + cert: 'testCert', + isSelfSignedCert: false, + devMode: true + }; + + const connector = { + id: 15 + }; + + def('subject', () => $subject.deleteConnector(connectorData, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findConnectorResponse', () => Promise.resolve(connector)); + def('findConnectorPortsResponse', () => Promise.resolve()); + def('deleteConnectorResponse', () => Promise.resolve()); + + const queryConnectorData = { + publicIp: connectorData.publicIp + }; + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ConnectorManager, 'findOne').returns($findConnectorResponse); + $sandbox.stub(ConnectorPortManager, 'findAll').returns($findConnectorPortsResponse); + $sandbox.stub(ConnectorManager, 'delete').returns($deleteConnectorResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(connectorData, Validator.schemas.connectorDelete); + }); + + 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 ConnectorManager#findOne() with correct args', async () => { + await $subject; + expect(ConnectorManager.findOne).to.have.been.calledWith(queryConnectorData, transaction); + }); + + context('when ConnectorManager#findOne() fails', () => { + def('findConnectorResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when ConnectorManager#findOne() succeeds', () => { + it('calls ConnectorPortManager#findAll() with correct args', async () => { + await $subject; + expect(ConnectorPortManager.findAll).to.have.been.calledWith({ + connectorId: connector.id + }, transaction); + }); + + context('when ConnectorPortManager#findAll() fails', () => { + def('findConnectorPortsResponse', () => { + }); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when ConnectorPortManager#findAll() succeeds', () => { + it('calls ConnectorManager#delete() with correct args', async () => { + await $subject; + expect(ConnectorManager.delete).to.have.been.calledWith(queryConnectorData, transaction); + }); + + context('when ConnectorManager#delete() fails', () => { + def('deleteConnectorResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when ConnectorManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }); + + describe('.getConnectorList()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getConnectorList(transaction)); + def('findConnectorsResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(ConnectorManager, 'findAll').returns($findConnectorsResponse); + }); + + it('calls ConnectorManager#findAll() with correct args', async () => { + await $subject; + expect(ConnectorManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when ConnectorManager#findAll() fails', () => { + def('findConnectorsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); + + + // TODO finish with qs.stringify mock EWC-452 + // describe('.openPortOnRandomConnector()', () => { + // const transaction = {}; + // const error = 'Error!'; + // + // const isPublicAccess = false; + // + // const connectors = [ + // { + // id: 15 + // }, + // { + // id: 16 + // } + // ]; + // + // def('subject', () => $subject.openPortOnRandomConnector(isPublicAccess, transaction)); + // def('findConnectorsResponse', () => Promise.resolve(connectors)); + // def('stringifyResponse', () => Promise.resolve()); + // + // beforeEach(() => { + // $sandbox.stub(ConnectorManager, 'findAll').returns($findConnectorsResponse); + // $sandbox.stub(qs, 'stringify').returns($stringifyResponse); + // }); + // + // it('calls ConnectorManager#findAll() with correct args', async () => { + // await $subject; + // expect(ConnectorManager.findAll).to.have.been.calledWith({}, transaction); + // }); + // + // context('when ConnectorManager#findAll() fails', () => { + // def('findConnectorsResponse', () => Promise.reject(error)); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.rejectedWith(error) + // }) + // }); + // + // context('when ConnectorManager#findAll() succeeds', () => { + // it('calls qs#stringify() with correct args', async () => { + // await $subject; + // expect(qs.stringify).to.have.been.calledWith({ + // mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' + // }); + // }); + // + // context('when qs#stringify() fails', () => { + // def('stringifyResponse', () => error); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.eventually.equal(undefined) + // }) + // }); + // + // context('when qs#stringify() succeeds', () => { + // it('fulfills the promise', () => { + // return expect($subject).to.eventually.equal(undefined) + // }) + // }) + // }) + // }); + + +}); \ No newline at end of file diff --git a/test/src/services/controller-service.test.js b/test/src/services/controller-service.test.js new file mode 100644 index 000000000..98650722c --- /dev/null +++ b/test/src/services/controller-service.test.js @@ -0,0 +1,101 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ControllerService = require('../../../src/services/controller-service'); +const ioFogTypesManager = require('../../../src/sequelize/managers/iofog-type-manager'); +const Config = require('../../../src/config'); + +describe('Controller Service', () => { + def('subject', () => ControllerService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + const isCLI = false; + + describe('.getFogTypes()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getFogTypes(isCLI, transaction)); + def('findResponse', () => Promise.resolve([{ + id: 15, + name: 'testName', + image: 'testImage', + description: 'testDescription' + }])); + + beforeEach(() => { + $sandbox.stub(ioFogTypesManager, 'findAll').returns($findResponse); + }); + + it('calls ioFogTypesManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogTypesManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when ioFogTypesManager#findAll() fails', () => { + def('findResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogTypesManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('fogTypes') + }) + }) + }); + + describe('.emailActivation()', () => { + const error = 'Error!'; + + def('subject', () => $subject.emailActivation(isCLI)); + def('getResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Config, 'get').returns($getResponse); + }); + + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith('Email:ActivationEnabled'); + }); + + context('when Config#get() fails', () => { + def('getResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Config#get() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('isEmailActivationEnabled') + }) + }) + }); + + + describe('.statusController()', () => { + const error = 'Error!'; + + def('subject', () => $subject.statusController(isCLI)); + def('getResponse', () => Promise.resolve()); + + beforeEach(() => { + // TODO daemon.status + //$sandbox.stub(daemon, 'status').returns($getResponse); + }); + + // it('Returns valid status', () => { + // return expect($subject).to.have.property('status') && + // expect($subject).to.have.property('timestamp') + // }) + }); + + +}); \ No newline at end of file From 415801f57b60d31efb4010da15c94e6124f8f484 Mon Sep 17 00:00:00 2001 From: Railag Date: Fri, 28 Dec 2018 15:27:32 +0300 Subject: [PATCH 33/39] test(core) unit tests for diagnostic service (EWC-383) (#465) --- src/services/diagnostic-service.js | 7 +- test/src/services/diagnostic-service.test.js | 502 +++++++++++++++++++ 2 files changed, 505 insertions(+), 4 deletions(-) create mode 100644 test/src/services/diagnostic-service.test.js diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 9e48de668..2f3349eeb 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -20,11 +20,10 @@ const MicroserviceService = require('../services/microservices-service'); const StraceDiagnosticManager = require('../sequelize/managers/strace-diagnostics-manager'); const ChangeTrackingService = require('./change-tracking-service'); const MicroserviceManager = require('../sequelize/managers/microservice-manager'); -const config = require('../config'); +const Config = require('../config'); const fs = require('fs'); const logger = require('../logger'); const ftpClient = require('ftp'); -const path = require('path'); const mime = require('mime'); @@ -60,7 +59,7 @@ const getMicroserviceStraceData = async function (uuid, data, user, isCLI, trans throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } - const dir = config.get('Diagnostics:DiagnosticDir') || 'diagnostics'; + const dir = Config.get('Diagnostics:DiagnosticDir') || 'diagnostics'; const filePath = dir + '/' + uuid; let result = straceData.buffer; @@ -93,7 +92,7 @@ const postMicroserviceStraceDatatoFtp = async function (uuid, data, user, isCLI, throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } - const dir = config.get('Diagnostics:DiagnosticDir'); + const dir = Config.get('Diagnostics:DiagnosticDir'); const filePath = dir + '/' + uuid; _createDirectoryIfNotExists(dir); diff --git a/test/src/services/diagnostic-service.test.js b/test/src/services/diagnostic-service.test.js new file mode 100644 index 000000000..d18b303cf --- /dev/null +++ b/test/src/services/diagnostic-service.test.js @@ -0,0 +1,502 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const StraceDiagnosticManager = require('../../../src/sequelize/managers/strace-diagnostics-manager'); +const DiagnosticService = require('../../../src/services/diagnostic-service'); +const MicroserviceService = require('../../../src/services/microservices-service'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const Validator = require('../../../src/schemas'); +const MicroserviceManager = require('../../../src/sequelize/managers/microservice-manager'); +const Config = require('../../../src/config'); + +describe('DiagnosticService Service', () => { + def('subject', () => DiagnosticService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = true; + + afterEach(() => $sandbox.restore()); + + describe('.changeMicroserviceStraceState()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = 'user!'; + + const uuid = 'testUuid'; + + const data = { + enable: true + }; + + const straceObj = { + straceRun: data.enable, + microserviceUuid: uuid + }; + + const microservice = { + iofogUuid: 'testIoFogUuid' + }; + + def('subject', () => $subject.changeMicroserviceStraceState(uuid, data, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('getMicroserviceResponse', () => Promise.resolve(microservice)); + def('updateOrCreateDiagnosticResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(MicroserviceService, 'getMicroservice').returns($getMicroserviceResponse); + $sandbox.stub(StraceDiagnosticManager, 'updateOrCreate').returns($updateOrCreateDiagnosticResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.straceStateUpdate); + }); + + 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 MicroserviceService#getMicroservice() with correct args', async () => { + await $subject; + expect(MicroserviceService.getMicroservice).to.have.been.calledWith(uuid, user, isCLI, transaction); + }); + + context('when MicroserviceService#getMicroservice() fails', () => { + def('getMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceService#getMicroservice() succeeds', () => { + it('calls StraceDiagnosticManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(StraceDiagnosticManager.updateOrCreate).to.have.been.calledWith({ + microserviceUuid: uuid + }, straceObj, transaction); + }); + + context('when StraceDiagnosticManager#updateOrCreate() fails', () => { + def('updateOrCreateDiagnosticResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when StraceDiagnosticManager#updateOrCreate() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microservice.iofogUuid, + ChangeTrackingService.events.diagnostics, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }); + + describe('.getMicroserviceStraceData()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const data = { + format: 'string' + }; + + const microservice = { + iofogUuid: 'testIoFogUuid' + }; + + def('subject', () => $subject.getMicroserviceStraceData(uuid, data, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('getMicroserviceResponse', () => Promise.resolve(microservice)); + def('findStraceResponse', () => Promise.resolve({})); + def('configGetResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(MicroserviceManager, 'findOne').returns($getMicroserviceResponse); + $sandbox.stub(StraceDiagnosticManager, 'findOne').returns($findStraceResponse); + $sandbox.stub(Config, 'get').returns($configGetResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.straceGetData); + }); + + 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 MicroserviceManager#findOne() with correct args', async () => { + await $subject; + const microserviceWhere = isCLI + ? {uuid: uuid} + : {uuid: uuid, userId: user.id}; + expect(MicroserviceManager.findOne).to.have.been.calledWith(microserviceWhere, transaction); + }); + + + context('when MicroserviceManager#findOne() fails', () => { + def('getMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findOne() succeeds', () => { + it('calls StraceDiagnosticManager#findOne() with correct args', async () => { + await $subject; + expect(StraceDiagnosticManager.findOne).to.have.been.calledWith({ + microserviceUuid: uuid + }, transaction); + }); + + context('when StraceDiagnosticManager#findOne() fails', () => { + def('findStraceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when StraceDiagnosticManager#findOne() succeeds', () => { + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith('Diagnostics:DiagnosticDir'); + }); + + context('when Config#get() fails', () => { + def('configGetResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.eventually.have.property('data') + }) + }); + + context('when Config#get() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('data') + }) + }) + }) + }) + }) + }); + + // TODO handle FTP stuff + // describe('.postMicroserviceStraceDatatoFtp()', () => { + // const transaction = {}; + // const error = 'Error!'; + // + // const user = { + // id: 15 + // }; + // + // const uuid = 'testUuid'; + // + // const data = { + // ftpHost: 'testHost', + // ftpPort: 5555, + // ftpUser: 'testUser', + // ftpPass: 'testPass', + // ftpDestDir: 'testDir' + // }; + // + // const microservice = { + // iofogUuid: 'testIoFogUuid' + // }; + // + // def('subject', () => $subject.postMicroserviceStraceDatatoFtp(uuid, data, user, isCLI, transaction)); + // def('validatorResponse', () => Promise.resolve(true)); + // def('getMicroserviceResponse', () => Promise.resolve(microservice)); + // def('findStraceResponse', () => Promise.resolve({})); + // def('configGetResponse', () => Promise.resolve()); + // + // + // beforeEach(() => { + // $sandbox.stub(Validator, 'validate').returns($validatorResponse); + // $sandbox.stub(MicroserviceManager, 'findOne').returns($getMicroserviceResponse); + // $sandbox.stub(StraceDiagnosticManager, 'findOne').returns($findStraceResponse); + // $sandbox.stub(Config, 'get').returns($configGetResponse); + // }); + // + // it('calls Validator#validate() with correct args', async () => { + // await $subject; + // expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.stracePostToFtp); + // }); + // + // 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 MicroserviceManager#findOne() with correct args', async () => { + // await $subject; + // const microserviceWhere = isCLI + // ? {uuid: uuid} + // : {uuid: uuid, userId: user.id}; + // expect(MicroserviceManager.findOne).to.have.been.calledWith(microserviceWhere, transaction); + // }); + // + // + // context('when MicroserviceManager#findOne() fails', () => { + // def('getMicroserviceResponse', () => Promise.reject(error)); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.rejectedWith(error) + // }) + // }); + // + // context('when MicroserviceManager#findOne() succeeds', () => { + // it('calls StraceDiagnosticManager#findOne() with correct args', async () => { + // await $subject; + // expect(StraceDiagnosticManager.findOne).to.have.been.calledWith({ + // microserviceUuid: uuid + // }, transaction); + // }); + // + // context('when StraceDiagnosticManager#findOne() fails', () => { + // def('findStraceResponse', () => Promise.reject(error)); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.rejectedWith(error) + // }) + // }); + // + // context('when StraceDiagnosticManager#findOne() succeeds', () => { + // it('calls Config#get() with correct args', async () => { + // await $subject; + // expect(Config.get).to.have.been.calledWith('Diagnostics:DiagnosticDir'); + // }); + // + // context('when Config#get() fails', () => { + // def('configGetResponse', () => error); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.eventually.have.property('data') + // }) + // }); + // + // context('when Config#get() succeeds', () => { + // it('fulfills the promise', () => { + // return expect($subject).to.eventually.have.property('data') + // }) + // }) + // }) + // }) + // }) + // }); + + describe('.postMicroserviceImageSnapshotCreate()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = 'user!'; + + const microserviceUuid = 'testUuid'; + + const microserviceToUpdate = { + imageSnapshot: 'get_image' + }; + + const microservice = { + iofogUuid: 'testIoFogUuid', + uuid: 'testMicroserviceUuid' + }; + + + def('subject', () => $subject.postMicroserviceImageSnapshotCreate(microserviceUuid, user, isCLI, transaction)); + def('findMicroserviceResponse', () => Promise.resolve(microservice)); + def('updateMicroserviceResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOneWithDependencies').returns($findMicroserviceResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls MicroserviceManager#findOneWithDependencies() with correct args', async () => { + await $subject; + const where = isCLI ? + { + uuid: microserviceUuid + } + : + { + uuid: microserviceUuid, + userId: user.id + }; + expect(MicroserviceManager.findOneWithDependencies).to.have.been.calledWith(where, {}, transaction); + }); + + context('when MicroserviceManager#findOneWithDependencies() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findOneWithDependencies() succeeds', () => { + it('calls MicroserviceManager#update() with correct args', async () => { + await $subject; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microservice.uuid + }, microserviceToUpdate, transaction); + }); + + context('when MicroserviceManager#update() fails', () => { + def('updateMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microservice.iofogUuid, + ChangeTrackingService.events.imageSnapshot, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }); + + describe('.getMicroserviceImageSnapshot()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = 'user!'; + + const microserviceUuid = 'testUuid'; + + const microserviceToUpdate = { + imageSnapshot: '' + }; + + const microservice = { + iofogUuid: 'testIoFogUuid', + uuid: 'testMicroserviceUuid', + imageSnapshot: 'testImagePath' + }; + + + def('subject', () => $subject.getMicroserviceImageSnapshot(microserviceUuid, user, isCLI, transaction)); + def('findMicroserviceResponse', () => Promise.resolve(microservice)); + def('updateMicroserviceResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOneWithDependencies').returns($findMicroserviceResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); + }); + + it('calls MicroserviceManager#findOneWithDependencies() with correct args', async () => { + await $subject; + const where = isCLI ? + { + uuid: microserviceUuid + } + : + { + uuid: microserviceUuid, + userId: user.id + }; + expect(MicroserviceManager.findOneWithDependencies).to.have.been.calledWith(where, {}, transaction); + }); + + context('when MicroserviceManager#findOneWithDependencies() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findOneWithDependencies() succeeds', () => { + it('calls MicroserviceManager#update() with correct args', async () => { + await $subject; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microservice.uuid + }, microserviceToUpdate, transaction); + }); + + context('when MicroserviceManager#update() fails', () => { + def('updateMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(microservice.imageSnapshot) + }) + }) + }) + }); + + +}); \ No newline at end of file From 10ab04950ba486b3e4260ba9f2c8d028c70f965c Mon Sep 17 00:00:00 2001 From: epankou <35571073+epankou@users.noreply.github.com> Date: Sat, 29 Dec 2018 13:12:24 +0300 Subject: [PATCH 34/39] Epankou/bug route removal ewc 374 (#466) * EWC-418 added registry email validation * EWC-421 strace feature bug * EWC-421 strace feature bug fix * bug(fix) iofog agent API: core network containers are returned when flow isn't active (EWC-424) * bug(fix) microservice removal: port mapping removal (EWC-424) * bug(fix) microservice removal: switched to const (EWC-424) * bug(fix) routes removal: added routes update on connector update (EWC-374) * bug(fix) routes removal: added routes update on connector update (EWC-374) * bug(fix) tests (EWC-374) * bug(fix) create connectorPortService (EWC-374) * bug(fix) tests (EWC-374) * bug(fix) refactoring (EWC-374) --- src/config/development.json | 3 +- src/helpers/app-helper.js | 1 - .../microservice-public-mode-manager.js | 18 ++- src/sequelize/managers/routing-manager.js | 16 ++ src/services/connector-port-service.js | 153 ++++++++++++++++++ src/services/connector-service.js | 144 +---------------- src/services/microservices-service.js | 70 ++++++-- .../services/connector-port-service.test.js | 61 +++++++ test/src/services/connector-service.test.js | 125 +++++++------- 9 files changed, 372 insertions(+), 219 deletions(-) create mode 100644 src/services/connector-port-service.js create mode 100644 test/src/services/connector-port-service.test.js diff --git a/src/config/development.json b/src/config/development.json index 9217782be..5dffbac92 100644 --- a/src/config/development.json +++ b/src/config/development.json @@ -29,5 +29,4 @@ "Diagnostics": { "DiagnosticDir": "diagnostic" } -} - +} \ No newline at end of file diff --git a/src/helpers/app-helper.js b/src/helpers/app-helper.js index f6730b452..93ecd24ad 100644 --- a/src/helpers/app-helper.js +++ b/src/helpers/app-helper.js @@ -279,7 +279,6 @@ function isEmpty(obj) { return true; } - module.exports = { encryptText, decryptText, diff --git a/src/sequelize/managers/microservice-public-mode-manager.js b/src/sequelize/managers/microservice-public-mode-manager.js index 5d0561b69..e25e26968 100644 --- a/src/sequelize/managers/microservice-public-mode-manager.js +++ b/src/sequelize/managers/microservice-public-mode-manager.js @@ -13,12 +13,28 @@ const BaseManager = require('../managers/base-manager') const models = require('./../models'); -const MicroservicePublicMode = models.MicroservicePublicMode +const MicroservicePublicMode = models.MicroservicePublicMode; +const ConnectorPort = models.ConnectorPort; class MicroservicePublicModeManager extends BaseManager { getEntity() { return MicroservicePublicMode } + + findAllMicroservicePublicModesByConnectorId(connectorId, transaction) { + return MicroservicePublicMode.findAll({ + include: [ + { + model: ConnectorPort, + as: 'connectorPort', + required: true + } + ], + where: { + '$connectorPort.connector_id$': connectorId + } + }, {transaction: transaction}) + } } const instance = new MicroservicePublicModeManager() diff --git a/src/sequelize/managers/routing-manager.js b/src/sequelize/managers/routing-manager.js index 0ba42fd1e..df135f232 100644 --- a/src/sequelize/managers/routing-manager.js +++ b/src/sequelize/managers/routing-manager.js @@ -14,11 +14,27 @@ const BaseManager = require('./base-manager'); const models = require('./../models'); const Routing = models.Routing; +const ConnectorPort = models.ConnectorPort; class RoutingManager extends BaseManager { getEntity() { return Routing; } + + findAllRoutesByConnectorId(connectorId, transaction) { + return Routing.findAll({ + include: [ + { + model: ConnectorPort, + as: 'connectorPort', + required: true + } + ], + where: { + '$connectorPort.connector_id$': connectorId + } + }, {transaction: transaction}) + } } const instance = new RoutingManager(); diff --git a/src/services/connector-port-service.js b/src/services/connector-port-service.js new file mode 100644 index 000000000..7d8bb89ee --- /dev/null +++ b/src/services/connector-port-service.js @@ -0,0 +1,153 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2018 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const ConnectorManager = require('../sequelize/managers/connector-manager'); +const https = require('https'); +const http = require('http'); +const constants = require('../helpers/constants'); +const logger = require('../logger'); +const qs = require('qs'); +const fs = require('fs'); + +async function openPortOnRandomConnector(isPublicAccess, transaction) { + let isConnectorPortOpen = false; + let ports = null; + let connector = null; + const maxAttempts = 5; + for (let i = 0; i < maxAttempts; i++) { + try { + connector = await _getRandomConnector(transaction); + ports = await _openPortsOnConnector(connector, isPublicAccess); + if (ports) { + isConnectorPortOpen = true; + break; + } + } catch (e) { + logger.warn(`Failed to open ports on Connector. Attempts ${i + 1}/${maxAttempts}`) + } + } + if (!isConnectorPortOpen) { + throw new Error('Not able to open port on remote Connector. Gave up after 5 attempts.') + } + ports.connectorId = connector.id; + return {ports: ports, connector: connector} +} + +async function _openPortsOnConnector(connector, isPublicAccess) { + let data = isPublicAccess + ? await qs.stringify({ + mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' + }) + : await qs.stringify({ + mapping: '{"type":"private","maxconnectionsport1":1, "maxconnectionsport2":1, ' + + '"heartbeatabsencethresholdport1":200000, "heartbeatabsencethresholdport2":200000}' + }); + + let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; + + let options = { + host: connector.domain, + port: port, + path: '/api/v2/mapping/add', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(data) + } + }; + if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { + const ca = fs.readFileSync(connector.cert); + options.ca = new Buffer.from(ca); + } + + const ports = await _makeRequest(connector, options, data); + return ports +} + +async function _getRandomConnector(transaction) { + const connectors = await ConnectorManager.findAll({}, transaction); + + if (connectors && connectors.length > 0) { + const randomNumber = Math.round((Math.random() * (connectors.length - 1))); + return connectors[randomNumber] + } else { + throw new Error('no connectors defined') + } +} + +async function closePortOnConnector(connector, ports) { + let data = qs.stringify({ + mappingid: ports.mappingId + }); + console.log(data); + + let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; + + let options = { + host: connector.domain, + port: port, + path: '/api/v2/mapping/remove', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(data) + } + }; + if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { + const ca = fs.readFileSync(connector.cert); + options.ca = new Buffer.from(ca); + } + + + await _makeRequest(connector, options, data) +} + +async function _makeRequest(connector, options, data) { + return new Promise((resolve, reject) => { + let httpreq = (connector.devMode ? http : https).request(options, function (response) { + console.log(response.statusCode); + let output = ''; + response.setEncoding('utf8'); + + response.on('data', function (chunk) { + output += chunk; + }); + + response.on('end', function () { + let responseObj = JSON.parse(output); + console.log(responseObj); + if (responseObj.errormessage) { + return reject(new Error(responseObj.errormessage)); + } else { + return resolve(responseObj); + } + }); + }); + + httpreq.on('error', function (err) { + console.log(err); + if (err instanceof Error) + return reject(new Error(err.message)); + else + return reject(new Error(JSON.stringify(err))); + }); + + httpreq.write(data); + httpreq.end(); + }) +} + +module.exports = { + openPortOnRandomConnector: openPortOnRandomConnector, + closePortOnConnector: closePortOnConnector +}; diff --git a/src/services/connector-service.js b/src/services/connector-service.js index dcb022376..78fc7a8e8 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -17,14 +17,9 @@ const ConnectorManager = require('../sequelize/managers/connector-manager'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); const AppHelper = require('../helpers/app-helper'); -const https = require('https'); -const http = require('http'); -const constants = require('../helpers/constants'); -const logger = require('../logger'); -const qs = require('qs'); const Op = require('sequelize').Op; -const fs = require('fs'); const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); +const MicroserviceService = require('../services/microservices-service'); async function createConnector(connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorCreate); @@ -54,7 +49,10 @@ async function updateConnector(connectorData, transaction) { const queryConnectorData = { publicIp: connectorData.publicIp }; - await ConnectorManager.update(queryConnectorData, connectorData, transaction) + await ConnectorManager.update(queryConnectorData, connectorData, transaction); + const connector = await ConnectorManager.findOne({name: connectorData.name}, transaction); + await MicroserviceService.updateRouteOverConnector(connector, transaction); + await MicroserviceService.updatePortMappingOverConnector(connector, transaction); } async function deleteConnector(connectorData, transaction) { @@ -77,92 +75,6 @@ async function getConnectorList(transaction) { return await ConnectorManager.findAll({}, transaction) } -async function openPortOnRandomConnector(isPublicAccess, transaction) { - let isConnectorPortOpen = false; - let ports = null; - let connector = null; - const maxAttempts = 5; - for (let i = 0; i < maxAttempts; i++) { - try { - connector = await _getRandomConnector(transaction); - ports = await _openPortsOnConnector(connector, isPublicAccess); - if (ports) { - isConnectorPortOpen = true; - break; - } - } catch (e) { - logger.warn(`Failed to open ports on Connector. Attempts ${i + 1}/${maxAttempts}`) - } - } - if (!isConnectorPortOpen) { - throw new Error('Not able to open port on remote Connector. Gave up after 5 attempts.') - } - ports.connectorId = connector.id; - return {ports: ports, connector: connector} -} - -async function _makeRequest(connector, options, data) { - return new Promise((resolve, reject) => { - let httpreq = (connector.devMode ? http : https).request(options, function (response) { - console.log(response.statusCode); - let output = ''; - response.setEncoding('utf8'); - - response.on('data', function (chunk) { - output += chunk; - }); - - response.on('end', function () { - let responseObj = JSON.parse(output); - console.log(responseObj); - if (responseObj.errormessage) { - return reject(new Error(responseObj.errormessage)); - } else { - return resolve(responseObj); - } - }); - }); - - httpreq.on('error', function (err) { - console.log(err); - if (err instanceof Error) - return reject(new Error(err.message)); - else - return reject(new Error(JSON.stringify(err))); - }); - - httpreq.write(data); - httpreq.end(); - }) -} - -async function closePortOnConnector(connector, ports) { - let data = qs.stringify({ - mappingid: ports.mappingId - }); - console.log(data); - - let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; - - let options = { - host: connector.domain, - port: port, - path: '/api/v2/mapping/remove', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(data) - } - }; - if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { - const ca = fs.readFileSync(connector.cert); - options.ca = new Buffer.from(ca); - } - - - await _makeRequest(connector, options, data) -} - function _validateConnectorData(connectorData) { if (connectorData.domain) { const validDomain = AppHelper.isValidDomain(connectorData.domain) || AppHelper.isValidPublicIP(connectorData.domain); @@ -176,53 +88,9 @@ function _validateConnectorData(connectorData) { } } -async function _openPortsOnConnector(connector, isPublicAccess) { - let data = isPublicAccess - ? await qs.stringify({ - mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' - }) - : await qs.stringify({ - mapping: '{"type":"private","maxconnectionsport1":1, "maxconnectionsport2":1, ' + - '"heartbeatabsencethresholdport1":200000, "heartbeatabsencethresholdport2":200000}' - }); - - let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; - - let options = { - host: connector.domain, - port: port, - path: '/api/v2/mapping/add', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(data) - } - }; - if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { - const ca = fs.readFileSync(connector.cert); - options.ca = new Buffer.from(ca); - } - - const ports = await _makeRequest(connector, options, data); - return ports -} - -async function _getRandomConnector(transaction) { - const connectors = await ConnectorManager.findAll({}, transaction); - - if (connectors && connectors.length > 0) { - const randomNumber = Math.round((Math.random() * (connectors.length - 1))); - return connectors[randomNumber] - } else { - throw new Error('no connectors defined') - } -} - module.exports = { createConnector: TransactionDecorator.generateTransaction(createConnector), updateConnector: TransactionDecorator.generateTransaction(updateConnector), deleteConnector: TransactionDecorator.generateTransaction(deleteConnector), - getConnectorList: TransactionDecorator.generateTransaction(getConnectorList), - openPortOnRandomConnector: openPortOnRandomConnector, - closePortOnConnector: closePortOnConnector + getConnectorList: TransactionDecorator.generateTransaction(getConnectorList) }; \ No newline at end of file diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index 1a2f9b909..0ed9a1e97 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -22,17 +22,18 @@ const ConnectorManager = require('../sequelize/managers/connector-manager'); const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); const MicroservicePublicModeManager = require('../sequelize/managers/microservice-public-mode-manager'); const ChangeTrackingService = require('./change-tracking-service'); +const ConnectorPortService = require('./connector-port-service'); const IoFogService = require('../services/iofog-service'); const AppHelper = require('../helpers/app-helper'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); const Validation = require('../schemas/index'); -const ConnectorService = require('../services/connector-service'); const FlowService = require('../services/flow-service'); const CatalogService = require('../services/catalog-service'); const RoutingManager = require('../sequelize/managers/routing-manager'); const Op = require('sequelize').Op; const fs = require('fs'); +const _ = require('underscore'); async function _listMicroservices(flowId, user, isCLI, transaction) { if (!isCLI) { @@ -379,12 +380,53 @@ async function _createSimpleRoute(sourceMicroservice, destMicroservice, transact await _switchOnUpdateFlagsForMicroservicesInRoute(sourceMicroservice, destMicroservice, transaction) } +async function updateRouteOverConnector(connector, transaction) { + const routes = await RoutingManager.findAllRoutesByConnectorId(connector.id, transaction); + const networkMicroserviceUuids = _.flatten(_.map( + routes, route => [route.sourceNetworkMicroserviceUuid, route.destNetworkMicroserviceUuid] + )); + await _updateNetworkMicroserviceConfigs(networkMicroserviceUuids, connector, transaction); +} + +async function _updateNetworkMicroserviceConfigs(networkMicroserviceUuids, connector, transaction) { + const microservices = await MicroserviceManager.findAll({uuid: networkMicroserviceUuids}, transaction); + + let cert; + if (!connector.devMode && connector.cert) { + cert = AppHelper.trimCertificate(fs.readFileSync(connector.cert, "utf-8")) + } + + for (const microservice of microservices) { + const msConfig = JSON.parse(microservice.config); + msConfig.host = connector.domain; + msConfig.cert = cert; + msConfig.devmode = connector.devMode; + const newConfig = { + config: JSON.stringify(msConfig), + rebuild: true + }; + await MicroserviceManager.update({ + uuid: microservice.uuid + }, newConfig, transaction); + } + + const onlyUnique = (value, index, self) => self.indexOf(value) === index; + const iofogUuids = microservices + .map(obj => obj.iofogUuid) + .filter(onlyUnique) + .filter(val => val !== null); + + for (const iofogUuid of iofogUuids) { + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction); + } +} + async function _createRouteOverConnector(sourceMicroservice, destMicroservice, user, transaction) { //open connector - const justOpenedConnectorsPorts = await ConnectorService.openPortOnRandomConnector(false, transaction) + const justOpenedConnectorsPorts = await ConnectorPortService.openPortOnRandomConnector(false, transaction) - const ports = justOpenedConnectorsPorts.ports - const connector = justOpenedConnectorsPorts.connector + const ports = justOpenedConnectorsPorts.ports; + const connector = justOpenedConnectorsPorts.connector; const createConnectorPortData = { port1: ports.port1, @@ -398,9 +440,9 @@ async function _createRouteOverConnector(sourceMicroservice, destMicroservice, u connectorId: ports.connectorId, mappingId: ports.id }; - const connectorPort = await ConnectorPortManager.create(createConnectorPortData, transaction) + const connectorPort = await ConnectorPortManager.create(createConnectorPortData, transaction); - const networkCatalogItem = await CatalogService.getNetworkCatalogItem(transaction) + const networkCatalogItem = await CatalogService.getNetworkCatalogItem(transaction); let cert; if (!connector.devMode && connector.cert) { @@ -542,7 +584,7 @@ async function _deleteRouteOverConnector(route, transaction) { const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction) try { - await ConnectorService.closePortOnConnector(connector, ports); + await ConnectorPortService.closePortOnConnector(connector, ports); } catch (e) { logger.warn(`Can't close ports pair ${ports.mappingId} on connector ${connector.publicIp}. Delete manually if necessary`); } @@ -610,9 +652,15 @@ async function _createSimplePortMapping(microservice, portMappingData, user, tra await _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, false, transaction) } +async function updatePortMappingOverConnector(connector, transaction) { + const microservicePublicModes = await MicroservicePublicModeManager.findAllMicroservicePublicModesByConnectorId(connector.id, transaction); + const networkMicroserviceUuids = microservicePublicModes.map(obj => obj.networkMicroserviceUuid); + await _updateNetworkMicroserviceConfigs(networkMicroserviceUuids, connector, transaction); +} + async function _createPortMappingOverConnector(microservice, portMappingData, user, transaction) { //open connector - const justOpenedConnectorsPorts = await ConnectorService.openPortOnRandomConnector(true, transaction) + const justOpenedConnectorsPorts = await ConnectorPortService.openPortOnRandomConnector(true, transaction) const ports = justOpenedConnectorsPorts.ports const connector = justOpenedConnectorsPorts.connector @@ -741,7 +789,7 @@ async function _deletePortMappingOverConnector(microservice, msPorts, user, tran const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction); try { - await ConnectorService.closePortOnConnector(connector, ports); + await ConnectorPortService.closePortOnConnector(connector, ports); } catch (e) { logger.warn(`Can't close ports pair ${ports.mappingId} on connector ${connector.publicIp}. Delete manually if necessary`); } @@ -974,5 +1022,7 @@ module.exports = { deleteVolumeMapping: TransactionDecorator.generateTransaction(_deleteVolumeMapping), listVolumeMappings: TransactionDecorator.generateTransaction(_listVolumeMappings), getPhysicalConnections: getPhysicalConections, - deleteNotRunningMicroservices: _deleteNotRunningMicroservices + deleteNotRunningMicroservices: _deleteNotRunningMicroservices, + updateRouteOverConnector: updateRouteOverConnector, + updatePortMappingOverConnector: updatePortMappingOverConnector }; diff --git a/test/src/services/connector-port-service.test.js b/test/src/services/connector-port-service.test.js new file mode 100644 index 000000000..c2cb72e22 --- /dev/null +++ b/test/src/services/connector-port-service.test.js @@ -0,0 +1,61 @@ +// TODO finish with qs.stringify mock EWC-452 +// describe('.openPortOnRandomConnector()', () => { +// const transaction = {}; +// const error = 'Error!'; +// +// const isPublicAccess = false; +// +// const connectors = [ +// { +// id: 15 +// }, +// { +// id: 16 +// } +// ]; +// +// def('subject', () => $subject.openPortOnRandomConnector(isPublicAccess, transaction)); +// def('findConnectorsResponse', () => Promise.resolve(connectors)); +// def('stringifyResponse', () => Promise.resolve()); +// +// beforeEach(() => { +// $sandbox.stub(ConnectorManager, 'findAll').returns($findConnectorsResponse); +// $sandbox.stub(qs, 'stringify').returns($stringifyResponse); +// }); +// +// it('calls ConnectorManager#findAll() with correct args', async () => { +// await $subject; +// expect(ConnectorManager.findAll).to.have.been.calledWith({}, transaction); +// }); +// +// context('when ConnectorManager#findAll() fails', () => { +// def('findConnectorsResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error) +// }) +// }); +// +// context('when ConnectorManager#findAll() succeeds', () => { +// it('calls qs#stringify() with correct args', async () => { +// await $subject; +// expect(qs.stringify).to.have.been.calledWith({ +// mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' +// }); +// }); +// +// context('when qs#stringify() fails', () => { +// def('stringifyResponse', () => error); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.eventually.equal(undefined) +// }) +// }); +// +// context('when qs#stringify() succeeds', () => { +// it('fulfills the promise', () => { +// return expect($subject).to.eventually.equal(undefined) +// }) +// }) +// }) +// }); \ No newline at end of file diff --git a/test/src/services/connector-service.test.js b/test/src/services/connector-service.test.js index 5467cc39f..2abf8b4fe 100644 --- a/test/src/services/connector-service.test.js +++ b/test/src/services/connector-service.test.js @@ -2,6 +2,7 @@ const {expect} = require('chai'); const sinon = require('sinon'); const ConnectorManager = require('../../../src/sequelize/managers/connector-manager'); +const MicroserviceService = require('../../../src/services/microservices-service'); const ConnectorService = require('../../../src/services/connector-service'); const Validator = require('../../../src/schemas'); const AppHelper = require('../../../src/helpers/app-helper'); @@ -167,12 +168,17 @@ describe('Connector Service', () => { devMode: true }; + const connector = {}; + def('subject', () => $subject.updateConnector(connectorData, transaction)); def('validatorResponse', () => Promise.resolve(true)); def('isValidDomainResponse', () => false); def('isValidPublicIpResponse', () => true); def('isValidPublicIpResponse2', () => true); def('updateConnectorResponse', () => Promise.resolve()); + def('findOneConnectorResponse', () => Promise.resolve(connector)); + def('updateRouteOverConnectorResponse', () => Promise.resolve()); + def('updatePortMappingOverConnectorResponse', () => Promise.resolve()); const queryConnectorData = { publicIp: connectorData.publicIp @@ -185,6 +191,9 @@ describe('Connector Service', () => { .onFirstCall().returns($isValidPublicIpResponse) .onSecondCall().returns($isValidPublicIpResponse2); $sandbox.stub(ConnectorManager, 'update').returns($updateConnectorResponse); + $sandbox.stub(ConnectorManager, 'findOne').returns($findOneConnectorResponse); + $sandbox.stub(MicroserviceService, 'updateRouteOverConnector').returns($updateRouteOverConnectorResponse); + $sandbox.stub(MicroserviceService, 'updatePortMappingOverConnector').returns($updatePortMappingOverConnectorResponse); }); it('calls Validator#validate() with correct args', async () => { @@ -257,8 +266,55 @@ describe('Connector Service', () => { }); context('when ConnectorManager#update() succeeds', () => { - it('fulfills the promise', () => { - return expect($subject).to.eventually.equal(undefined) + it('calls ConnectorManager#findOne() with correct args', async () => { + await $subject; + expect(ConnectorManager.findOne).to.have.been.calledWith({ + name: connectorData.name + }, transaction); + }); + + context('when ConnectorManager#findOne() fails', () => { + def('findOneConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#findOne() succeeds', () => { + it('calls MicroserviceService#updateRouteOverConnector() with correct args', async () => { + await $subject; + expect(MicroserviceService.updateRouteOverConnector).to.have.been.calledWith(connector, transaction); + }); + + context('when MicroserviceService#updateRouteOverConnector() fails', () => { + def('updateRouteOverConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceService#updateRouteOverConnector() succeeds', () => { + it('calls MicroserviceService#updatePortMappingOverConnector() with correct args', async () => { + await $subject; + expect(MicroserviceService.updatePortMappingOverConnector).to.have.been.calledWith(connector, transaction); + }); + + context('when MicroserviceService#updatePortMappingOverConnector() fails', () => { + def('updatePortMappingOverConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceService#updatePortMappingOverConnector() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) }) }) }) @@ -399,69 +455,4 @@ describe('Connector Service', () => { }) }) }); - - - // TODO finish with qs.stringify mock EWC-452 - // describe('.openPortOnRandomConnector()', () => { - // const transaction = {}; - // const error = 'Error!'; - // - // const isPublicAccess = false; - // - // const connectors = [ - // { - // id: 15 - // }, - // { - // id: 16 - // } - // ]; - // - // def('subject', () => $subject.openPortOnRandomConnector(isPublicAccess, transaction)); - // def('findConnectorsResponse', () => Promise.resolve(connectors)); - // def('stringifyResponse', () => Promise.resolve()); - // - // beforeEach(() => { - // $sandbox.stub(ConnectorManager, 'findAll').returns($findConnectorsResponse); - // $sandbox.stub(qs, 'stringify').returns($stringifyResponse); - // }); - // - // it('calls ConnectorManager#findAll() with correct args', async () => { - // await $subject; - // expect(ConnectorManager.findAll).to.have.been.calledWith({}, transaction); - // }); - // - // context('when ConnectorManager#findAll() fails', () => { - // def('findConnectorsResponse', () => Promise.reject(error)); - // - // it(`fails with ${error}`, () => { - // return expect($subject).to.be.rejectedWith(error) - // }) - // }); - // - // context('when ConnectorManager#findAll() succeeds', () => { - // it('calls qs#stringify() with correct args', async () => { - // await $subject; - // expect(qs.stringify).to.have.been.calledWith({ - // mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' - // }); - // }); - // - // context('when qs#stringify() fails', () => { - // def('stringifyResponse', () => error); - // - // it(`fails with ${error}`, () => { - // return expect($subject).to.eventually.equal(undefined) - // }) - // }); - // - // context('when qs#stringify() succeeds', () => { - // it('fulfills the promise', () => { - // return expect($subject).to.eventually.equal(undefined) - // }) - // }) - // }) - // }); - - }); \ No newline at end of file From 613f73dd43e59eeccd0430629d63f0b37809ec07 Mon Sep 17 00:00:00 2001 From: Railag Date: Sat, 29 Dec 2018 13:30:40 +0300 Subject: [PATCH 35/39] test(core) unit tests for email activation service (EWC-383) (#468) --- src/services/email-activation-code-service.js | 13 +- .../email-activation-code-service.test.js | 179 ++++++++++++++++++ 2 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 test/src/services/email-activation-code-service.test.js diff --git a/src/services/email-activation-code-service.js b/src/services/email-activation-code-service.js index 1e63963d8..a79709c91 100644 --- a/src/services/email-activation-code-service.js +++ b/src/services/email-activation-code-service.js @@ -14,7 +14,6 @@ const EmailActivationCodeManager = require('../sequelize/managers/email-activation-code-manager'); const AppHelper = require('../helpers/app-helper'); const ErrorMessages = require('../helpers/error-messages'); -const TransactionDecorator = require('../decorators/transaction-decorator'); const generateActivationCode = async function (transaction) { while (true) { @@ -52,20 +51,12 @@ const verifyActivationCode = async function (activationCode, transaction) { const deleteActivationCode = async function (activationCode, transaction) { return await EmailActivationCodeManager.delete({ activationCode: activationCode - }, transaction) + }, transaction); }; -const findActivationCodeByUserId = async function (userId, transaction) { - return await EmailActivationCodeManager.findOne({ - userId: userId - }, transaction) -}; - - module.exports = { generateActivationCode: generateActivationCode, saveActivationCode: saveActivationCode, verifyActivationCode: verifyActivationCode, - deleteActivationCode: deleteActivationCode, - findActivationCodeByUserId: TransactionDecorator.generateTransaction(findActivationCodeByUserId) + deleteActivationCode: deleteActivationCode }; \ No newline at end of file diff --git a/test/src/services/email-activation-code-service.test.js b/test/src/services/email-activation-code-service.test.js new file mode 100644 index 000000000..862007ae5 --- /dev/null +++ b/test/src/services/email-activation-code-service.test.js @@ -0,0 +1,179 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const EmailActivationCodeManager = require('../../../src/sequelize/managers/email-activation-code-manager'); +const EmailActivationCodeService = require('../../../src/services/email-activation-code-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ErrorMessages = require('../../../src/helpers/error-messages'); + + +describe('EmailActivationCode Service', () => { + def('subject', () => EmailActivationCodeService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.generateActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const response = { + activationCode: "abcdefgwdwdwdwdwd", + expirationTime: new Date().getTime() + ((60 * 60 * 24 * 3) * 1000) + }; + + def('subject', () => $subject.generateActivationCode(transaction)); + def('generateStringResponse', () => response.activationCode); + def('findActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateStringResponse); + $sandbox.stub(EmailActivationCodeManager, 'getByActivationCode').returns($findActivationCodeResponse); + }); + + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + expect(AppHelper.generateRandomString).to.have.been.calledWith(16); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('activationCode') + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls EmailActivationCodeManager#getByActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.getByActivationCode).to.have.been.calledWith(response.activationCode, + transaction); + }); + + context('when EmailActivationCodeManager#getByActivationCode() fails', () => { + def('findActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when EmailActivationCodeManager#getByActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('activationCode') && + expect($subject).to.eventually.have.property('expirationTime') + }) + }) + }) + }); + + describe('.saveActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const userId = 15; + + const activationCodeData = { + activationCode: "abcdefgwdwdwdwdwd", + expirationTime: new Date().getTime() + ((60 * 60 * 24 * 3) * 1000) + }; + + def('subject', () => $subject.saveActivationCode(userId, activationCodeData, transaction)); + def('createActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(EmailActivationCodeManager, 'createActivationCode').returns($createActivationCodeResponse); + }); + + it('calls EmailActivationCodeManager#createActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.createActivationCode).to.have.been.calledWith(userId, + activationCodeData.activationCode, activationCodeData.expirationTime, transaction); + }); + + context('when EmailActivationCodeManager#createActivationCode() fails', () => { + def('createActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(ErrorMessages.UNABLE_TO_CREATE_ACTIVATION_CODE); + }) + }); + + context('when EmailActivationCodeManager#createActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(undefined) + }) + }) + }); + + describe('.verifyActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const activationCode = "abcdefgwdwdwdwdwd"; + + def('subject', () => $subject.verifyActivationCode(activationCode, transaction)); + def('verifyActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(EmailActivationCodeManager, 'verifyActivationCode').returns($verifyActivationCodeResponse); + }); + + it('calls EmailActivationCodeManager#verifyActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.verifyActivationCode).to.have.been.calledWith(activationCode, transaction); + }); + + context('when EmailActivationCodeManager#verifyActivationCode() fails', () => { + def('verifyActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(ErrorMessages.UNABLE_TO_GET_ACTIVATION_CODE); + }) + }); + + context('when EmailActivationCodeManager#verifyActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(undefined) + }) + }) + }); + + describe('.deleteActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const activationCode = "abcdefgwdwdwdwdwd"; + + def('subject', () => $subject.deleteActivationCode(activationCode, transaction)); + def('deleteActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(EmailActivationCodeManager, 'delete').returns($deleteActivationCodeResponse); + }); + + it('calls EmailActivationCodeManager#delete() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.delete).to.have.been.calledWith({ + activationCode: activationCode + }, transaction); + }); + + context('when EmailActivationCodeManager#delete() fails', () => { + def('deleteActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when EmailActivationCodeManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(undefined) + }) + }) + }); + + +}); \ No newline at end of file From 54b5e1de57a9c829dddfb52e47b49660ca8e7c7f Mon Sep 17 00:00:00 2001 From: Railag Date: Sat, 29 Dec 2018 18:05:03 +0300 Subject: [PATCH 36/39] test(core) unit tests for flow service (EWC-383) (#469) --- src/services/flow-service.js | 99 ++--- test/src/services/flow-service.test.js | 531 +++++++++++++++++++++++++ 2 files changed, 582 insertions(+), 48 deletions(-) create mode 100644 test/src/services/flow-service.test.js diff --git a/src/services/flow-service.js b/src/services/flow-service.js index 55c446850..3ef591730 100644 --- a/src/services/flow-service.js +++ b/src/services/flow-service.js @@ -16,13 +16,15 @@ const FlowManager = require('../sequelize/managers/flow-manager'); const AppHelper = require('../helpers/app-helper'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); -const Validation = require('../schemas'); +const Validator = require('../schemas'); const ChangeTrackingService = require('./change-tracking-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; -const _createFlow = async function (flowData, user, isCLI, transaction) { - await Validation.validate(flowData, Validation.schemas.flowCreate); +const createFlow = async function (flowData, user, isCLI, transaction) { + await Validator.validate(flowData, Validator.schemas.flowCreate); - await _checkForDuplicateName(flowData.name, {}, user.id, transaction); + await _checkForDuplicateName(flowData.name, null, user.id, transaction); const flowToCreate = { name: flowData.name, @@ -40,7 +42,7 @@ const _createFlow = async function (flowData, user, isCLI, transaction) { } }; -const _deleteFlow = async function (flowId, user, isCLI, transaction) { +const deleteFlow = async function (flowId, user, isCLI, transaction) { const whereObj = { id: flowId, userId: user.id @@ -52,25 +54,10 @@ const _deleteFlow = async function (flowId, user, isCLI, transaction) { await FlowManager.delete(where, transaction); }; -async function _updateChangeTrackingsByFlowId(flowId, transaction) { - const flowWithMicroservices = await FlowManager.findFlowMicroservices({id: flowId}, transaction); - if (!flowWithMicroservices) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)); - } - const onlyUnique = (value, index, self) => self.indexOf(value) === index; - const iofogUuids = flowWithMicroservices.microservices - .map(obj => obj.iofogUuid) - .filter(onlyUnique) - .filter(val => val !== null); - for (let iofogUuid of iofogUuids) { - await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceFull, transaction); - } -} - -const _updateFlow = async function (flowData, flowId, user, isCLI, transaction) { - await Validation.validate(flowData, Validation.schemas.flowUpdate); +const updateFlow = async function (flowData, flowId, user, isCLI, transaction) { + await Validator.validate(flowData, Validator.schemas.flowUpdate); - const oldFlow = await _getFlow(flowId, user, isCLI, transaction); + const oldFlow = await getFlow(flowId, user, isCLI, transaction); if (!oldFlow) { throw new Errors.NotFoundError(ErrorMessages.INVALID_FLOW_ID) } @@ -97,20 +84,7 @@ const _updateFlow = async function (flowData, flowId, user, isCLI, transaction) } }; -const _getFlow = async function (flowId, user, isCLI, transaction) { - const where = isCLI - ? {id: flowId} - : {id: flowId, userId: user.id}; - - const flow = await FlowManager.findOneExcludeFields(where, transaction); - - if (!flow) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)) - } - return flow -}; - -const _getUserFlows = async function (user, isCLI, transaction) { +const getUserFlows = async function (user, isCLI, transaction) { const flow = { userId: user.id }; @@ -121,17 +95,31 @@ const _getUserFlows = async function (user, isCLI, transaction) { } }; -const _getAllFlows = async function (isCLI, transaction) { +const getAllFlows = async function (isCLI, transaction) { const flows = await FlowManager.findAll({}, transaction); return { flows: flows } }; -const _checkForDuplicateName = async function (name, item, userId, transaction) { +const getFlow = async function (flowId, user, isCLI, transaction) { + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + + const flow = await FlowManager.findOneExcludeFields(where, transaction); + + if (!flow) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)) + } + return flow +}; + + +const _checkForDuplicateName = async function (name, flowId, userId, transaction) { if (name) { - const where = item.id - ? {name: name, id: {[Op.ne]: item.id, userId: userId}} + const where = flowId + ? {name: name, id: {[Op.ne]: flowId, userId: userId}} : {name: name, userId: userId}; const result = await FlowManager.findOne(where, transaction); @@ -141,12 +129,27 @@ const _checkForDuplicateName = async function (name, item, userId, transaction) } }; +async function _updateChangeTrackingsByFlowId(flowId, transaction) { + const flowWithMicroservices = await FlowManager.findFlowMicroservices({id: flowId}, transaction); + if (!flowWithMicroservices) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)); + } + const onlyUnique = (value, index, self) => self.indexOf(value) === index; + const iofogUuids = flowWithMicroservices.microservices + .map(obj => obj.iofogUuid) + .filter(onlyUnique) + .filter(val => val !== null); + for (const iofogUuid of iofogUuids) { + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceFull, transaction); + } +} + module.exports = { - createFlow: TransactionDecorator.generateTransaction(_createFlow), - deleteFlow: TransactionDecorator.generateTransaction(_deleteFlow), - updateFlow: TransactionDecorator.generateTransaction(_updateFlow), - getFlowWithTransaction: TransactionDecorator.generateTransaction(_getFlow), - getUserFlows: TransactionDecorator.generateTransaction(_getUserFlows), - getAllFlows: TransactionDecorator.generateTransaction(_getAllFlows), - getFlow: _getFlow + createFlow: TransactionDecorator.generateTransaction(createFlow), + deleteFlow: TransactionDecorator.generateTransaction(deleteFlow), + updateFlow: TransactionDecorator.generateTransaction(updateFlow), + getUserFlows: TransactionDecorator.generateTransaction(getUserFlows), + getAllFlows: TransactionDecorator.generateTransaction(getAllFlows), + getFlowWithTransaction: TransactionDecorator.generateTransaction(getFlow), + getFlow: getFlow }; diff --git a/test/src/services/flow-service.test.js b/test/src/services/flow-service.test.js new file mode 100644 index 000000000..d0045af4b --- /dev/null +++ b/test/src/services/flow-service.test.js @@ -0,0 +1,531 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const FlowManager = require('../../../src/sequelize/managers/flow-manager'); +const FlowService = require('../../../src/services/flow-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const Validator = require('../../../src/schemas'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const ErrorMessages = require('../../../src/helpers/error-messages'); + +describe('Flow Service', () => { + def('subject', () => FlowService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.createFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = null; + + const flowData = { + name: 'testName', + description: 'testDescription', + isActivated: false + }; + + const flowToCreate = { + name: flowData.name, + description: flowData.description, + isActivated: flowData.isActivated, + userId: user.id + }; + + const response = { + id: 25 + }; + + def('subject', () => $subject.createFlow(flowData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findFlowResponse', () => Promise.resolve()); + def('deleteUndefinedFieldsResponse', () => flowToCreate); + def('createFlowResponse', () => Promise.resolve(response)); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(FlowManager, 'findOne').returns($findFlowResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(FlowManager, 'create').returns($createFlowResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(flowData, Validator.schemas.flowCreate); + }); + + 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 FlowManager#findOne() with correct args', async () => { + await $subject; + const where = flowId + ? {name: flowData.name, id: {[Op.ne]: flowId, userId: user.id}} + : {name: flowData.name, userId: user.id}; + + expect(FlowManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOne() fails', () => { + def('findFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowManager#findOne() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(flowToCreate); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls FlowManager#create() with correct args', async () => { + await $subject; + + expect(FlowManager.create).to.have.been.calledWith(flowToCreate); + }); + + context('when FlowManager#create() fails', () => { + def('createFlowResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when FlowManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('id') + }) + }) + }) + }) + }) + }); + + describe('.deleteFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = 75; + + const whereObj = { + id: flowId, + userId: user.id + }; + + const flowWithMicroservices = { + microservices: [ + { + iofogUuid: 15 + } + ] + }; + + def('subject', () => $subject.deleteFlow(flowId, user, isCLI, transaction)); + def('deleteUndefinedFieldsResponse', () => whereObj); + def('findFlowMicroservicesResponse', () => Promise.resolve(flowWithMicroservices)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + def('deleteFlowResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(FlowManager, 'findFlowMicroservices').returns($findFlowMicroservicesResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + $sandbox.stub(FlowManager, 'delete').returns($deleteFlowResponse); + }); + + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(whereObj); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls FlowManager#findFlowMicroservices() with correct args', async () => { + await $subject; + + expect(FlowManager.findFlowMicroservices).to.have.been.calledWith({ + id: flowId + }, transaction); + }); + + context('when FlowManager#findFlowMicroservices() fails', () => { + def('findFlowMicroservicesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findFlowMicroservices() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(flowWithMicroservices.microservices[0].iofogUuid, + ChangeTrackingService.events.microserviceFull, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('calls FlowManager#delete() with correct args', async () => { + await $subject; + + expect(FlowManager.delete).to.have.been.calledWith(whereObj, transaction); + }); + + context('when FlowManager#delete() fails', () => { + def('deleteFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }); + + + describe('.updateFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = 75; + + const oldFlowData = { + name: 'testName', + description: 'testDescription', + isActivated: true + }; + + const flowData = { + name: 'testName', + description: 'testDescription', + isActivated: false + }; + + const flowWithMicroservices = { + microservices: [ + { + iofogUuid: 15 + } + ] + }; + + def('subject', () => $subject.updateFlow(flowData, flowId, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findExcludedFlowResponse', () => Promise.resolve(oldFlowData)); + def('findFlowResponse', () => Promise.resolve()); + def('deleteUndefinedFieldsResponse', () => flowData); + def('updateFlowResponse', () => Promise.resolve()); + def('findFlowMicroservicesResponse', () => Promise.resolve(flowWithMicroservices)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(FlowManager, 'findOneExcludeFields').returns($findExcludedFlowResponse); + $sandbox.stub(FlowManager, 'findOne').returns($findFlowResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(FlowManager, 'update').returns($updateFlowResponse); + $sandbox.stub(FlowManager, 'findFlowMicroservices').returns($findFlowMicroservicesResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(flowData, Validator.schemas.flowUpdate); + }); + + 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 FlowManager#findOneExcludeFields() with correct args', async () => { + await $subject; + + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + expect(FlowManager.findOneExcludeFields).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOneExcludeFields() fails', () => { + def('findExcludedFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findOneExcludeFields() succeeds', () => { + it('calls FlowManager#findOne() with correct args', async () => { + await $subject; + + const where = flowId + ? {name: flowData.name, id: {[Op.ne]: flowId, userId: user.id}} + : {name: flowData.name, userId: user.id}; + expect(FlowManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOne() fails', () => { + def('findFlowResponse', () => Promise.reject(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, + flowData.name))); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, + flowData.name)); + }) + }); + + context('when FlowManager#findOne() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(flowData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls FlowManager#update() with correct args', async () => { + await $subject; + + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + expect(FlowManager.update).to.have.been.calledWith(where, flowData, transaction); + }); + + context('when FlowManager#update() fails', () => { + def('updateFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#update() succeeds', () => { + it('calls FlowManager#findFlowMicroservices() with correct args', async () => { + await $subject; + + expect(FlowManager.findFlowMicroservices).to.have.been.calledWith({ + id: flowId + }, transaction); + }); + + context('when FlowManager#findFlowMicroservices() fails', () => { + def('findFlowMicroservicesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findFlowMicroservices() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(flowWithMicroservices.microservices[0].iofogUuid, + ChangeTrackingService.events.microserviceFull, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.getUserFlows()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flow = { + userId: user.id + }; + + def('subject', () => $subject.getUserFlows(user, isCLI, transaction)); + def('findExcludedFlowResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(FlowManager, 'findAllExcludeFields').returns($findExcludedFlowResponse); + }); + + it('calls FlowManager#findAllExcludeFields() with correct args', async () => { + await $subject; + expect(FlowManager.findAllExcludeFields).to.have.been.calledWith(flow, transaction); + }); + + context('when FlowManager#findAllExcludeFields() fails', () => { + def('findExcludedFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findAllExcludeFields() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('flows'); + }) + }) + }); + + + describe('.getAllFlows()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getAllFlows(isCLI, transaction)); + def('findAllFlowsResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(FlowManager, 'findAll').returns($findAllFlowsResponse); + }); + + it('calls FlowManager#findAll() with correct args', async () => { + await $subject; + expect(FlowManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when FlowManager#findAll() fails', () => { + def('findAllFlowsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('flows'); + }) + }) + }); + + describe('.getFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const flowId = 75; + + const user = { + id: 15 + }; + + def('subject', () => $subject.getFlow(flowId, user, isCLI, transaction)); + def('findFlowResponse', () => Promise.resolve({})); + + beforeEach(() => { + $sandbox.stub(FlowManager, 'findOneExcludeFields').returns($findFlowResponse); + }); + + it('calls FlowManager#findOneExcludeFields() with correct args', async () => { + await $subject; + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + expect(FlowManager.findOneExcludeFields).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOneExcludeFields() fails', () => { + def('findFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findOneExcludeFields() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal({}); + }) + }) + }); + +}); \ No newline at end of file From 0864cc9526dbfe07696691e23648356e39e00822 Mon Sep 17 00:00:00 2001 From: Railag Date: Wed, 2 Jan 2019 15:58:41 +0300 Subject: [PATCH 37/39] test(core) unit tests for iofog service (EWC-383) (#470) --- src/cli/iofog.js | 2 +- src/services/iofog-service.js | 195 +-- test/src/services/iofog-service.test.js | 1708 +++++++++++++++++++++++ 3 files changed, 1810 insertions(+), 95 deletions(-) create mode 100644 test/src/services/iofog-service.test.js diff --git a/src/cli/iofog.js b/src/cli/iofog.js index 8749dbb83..7f43f9faa 100644 --- a/src/cli/iofog.js +++ b/src/cli/iofog.js @@ -314,7 +314,7 @@ async function _deleteFog(obj, user) { } async function _getFogList(obj, user) { - const emptyFilters = [] + const emptyFilters = []; const list = await FogService.getFogList(emptyFilters, user, true); logger.info(JSON.stringify(list, null, 2)); logger.info('Fog list has been gotten successfully.'); diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index b665416be..ef5002a4e 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -25,7 +25,7 @@ const USBInfoManager = require('../sequelize/managers/usb-info-manager'); const CatalogService = require('../services/catalog-service'); const MicroserviceManager = require('../sequelize/managers/microservice-manager'); -async function _createFog(fogData, user, isCli, transaction) { +async function createFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogCreate); let createFogData = { @@ -71,15 +71,15 @@ async function _createFog(fogData, user, isCli, transaction) { await _createBluetoothMicroserviceForFog(fog, null, user, transaction); } - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) + await ChangeTrackingService.update(createFogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction); return res } -async function _updateFog(fogData, user, isCli, transaction) { +async function updateFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogUpdate); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -118,21 +118,21 @@ async function _updateFog(fogData, user, isCli, transaction) { let msChanged = false; - if (oldFog.bluetoothEnabled === true && fogData.bluetoothEnabled === false) { - await _deleteBluetoothMicroserviceByFog(fogData, transaction); + if (oldFog.abstractedHardwareEnabled === true && fogData.abstractedHardwareEnabled === false) { + await _deleteHalMicroserviceByFog(fogData, transaction); msChanged = true; } - if (oldFog.bluetoothEnabled === false && fogData.bluetoothEnabled === true) { - await _createBluetoothMicroserviceForFog(fogData, oldFog, user, transaction); + if (oldFog.abstractedHardwareEnabled === false && fogData.abstractedHardwareEnabled === true) { + await _createHalMicroserviceForFog(fogData, oldFog, user, transaction); msChanged = true; } - if (oldFog.abstractedHardwareEnabled === true && fogData.abstractedHardwareEnabled === false) { - await _deleteHalMicroseviceByFog(fogData, transaction); + if (oldFog.bluetoothEnabled === true && fogData.bluetoothEnabled === false) { + await _deleteBluetoothMicroserviceByFog(fogData, transaction); msChanged = true; } - if (oldFog.abstractedHardwareEnabled === false && fogData.abstractedHardwareEnabled === true) { - await _createHalMicroserviceForFog(fogData, oldFog, user, transaction); + if (oldFog.bluetoothEnabled === false && fogData.bluetoothEnabled === true) { + await _createBluetoothMicroserviceForFog(fogData, oldFog, user, transaction); msChanged = true; } @@ -141,10 +141,10 @@ async function _updateFog(fogData, user, isCli, transaction) { } } -async function _deleteFog(fogData, user, isCli, transaction) { +async function deleteFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogDelete); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -156,10 +156,10 @@ async function _deleteFog(fogData, user, isCli, transaction) { await _processDeleteCommand(fog, transaction) } -async function _getFog(fogData, user, isCli, transaction) { +async function getFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogGet); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -167,81 +167,84 @@ async function _getFog(fogData, user, isCli, transaction) { if (!fog) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FOG_NODE_UUID, fogData.uuid)) } - await _updateFogsConnectionStatus(fog, transaction); + + const updatedFog = await _updateFogsConnectionStatus(fog, transaction); return { - uuid: fog.uuid, - name: fog.name, - location: fog.location, - gpsMode: fog.gpsMode, - latitude: fog.latitude, - longitude: fog.longitude, - description: fog.description, - lastActive: fog.lastActive, - daemonStatus: fog.daemonStatus, - daemonOperatingDuration: fog.daemonOperatingDuration, - daemonLastStart: fog.daemonLastStart, - memoryUsage: fog.memoryUsage, - diskUsage: fog.diskUsage, - cpuUsage: fog.cpuUsage, - memoryViolation: fog.memoryViolation, - diskViolation: fog.diskViolation, - cpuViolation: fog.cpuViolation, - catalogItemStatus: fog.catalogItemStatus, - repositoryCount: fog.repositoryCount, - repositoryStatus: fog.repositoryStatus, - systemTime: fog.systemTime, - lastStatusTime: fog.lastStatusTime, - ipAddress: fog.ipAddress, - processedMessages: fog.processedMessages, - catalogItemMessageCounts: fog.catalogItemMessageCounts, - messageSpeed: fog.messageSpeed, - lastCommandTime: fog.lastCommandTime, - networkInterface: fog.networkInterface, - dockerUrl: fog.dockerUrl, - diskLimit: fog.diskLimit, - diskDirectory: fog.diskDirectory, - memoryLimit: fog.memoryLimit, - cpuLimit: fog.cpuLimit, - logLimit: fog.logLimit, - logDirectory: fog.logDirectory, - bluetoothEnabled: fog.bluetoothEnabled, - abstractedHardwareEnabled: fog.abstractedHardwareEnabled, - logFileCount: fog.logFileCount, - version: fog.version, - isReadyToUpgrade: fog.isReadyToUpgrade, - isReadyToRollback: fog.isReadyToRollback, - statusFrequency: fog.statusFrequency, - changeFrequency: fog.changeFrequency, - deviceScanFrequency: fog.deviceScanFrequency, - tunnel: fog.tunnel, - watchdogEnabled: fog.watchdogEnabled, - fogTypeId: fog.fogTypeId, - userId: fog.userId + uuid: updatedFog.uuid, + name: updatedFog.name, + location: updatedFog.location, + gpsMode: updatedFog.gpsMode, + latitude: updatedFog.latitude, + longitude: updatedFog.longitude, + description: updatedFog.description, + lastActive: updatedFog.lastActive, + daemonStatus: updatedFog.daemonStatus, + daemonOperatingDuration: updatedFog.daemonOperatingDuration, + daemonLastStart: updatedFog.daemonLastStart, + memoryUsage: updatedFog.memoryUsage, + diskUsage: updatedFog.diskUsage, + cpuUsage: updatedFog.cpuUsage, + memoryViolation: updatedFog.memoryViolation, + diskViolation: updatedFog.diskViolation, + cpuViolation: updatedFog.cpuViolation, + catalogItemStatus: updatedFog.catalogItemStatus, + repositoryCount: updatedFog.repositoryCount, + repositoryStatus: updatedFog.repositoryStatus, + systemTime: updatedFog.systemTime, + lastStatusTime: updatedFog.lastStatusTime, + ipAddress: updatedFog.ipAddress, + processedMessages: updatedFog.processedMessages, + catalogItemMessageCounts: updatedFog.catalogItemMessageCounts, + messageSpeed: updatedFog.messageSpeed, + lastCommandTime: updatedFog.lastCommandTime, + networkInterface: updatedFog.networkInterface, + dockerUrl: updatedFog.dockerUrl, + diskLimit: updatedFog.diskLimit, + diskDirectory: updatedFog.diskDirectory, + memoryLimit: updatedFog.memoryLimit, + cpuLimit: updatedFog.cpuLimit, + logLimit: updatedFog.logLimit, + logDirectory: updatedFog.logDirectory, + bluetoothEnabled: updatedFog.bluetoothEnabled, + abstractedHardwareEnabled: updatedFog.abstractedHardwareEnabled, + logFileCount: updatedFog.logFileCount, + version: updatedFog.version, + isReadyToUpgrade: updatedFog.isReadyToUpgrade, + isReadyToRollback: updatedFog.isReadyToRollback, + statusFrequency: updatedFog.statusFrequency, + changeFrequency: updatedFog.changeFrequency, + deviceScanFrequency: updatedFog.deviceScanFrequency, + tunnel: updatedFog.tunnel, + watchdogEnabled: updatedFog.watchdogEnabled, + fogTypeId: updatedFog.fogTypeId, + userId: updatedFog.userId }; } -async function _getFogList(filters, user, isCli, transaction) { +async function getFogList(filters, user, isCLI, transaction) { await Validator.validate(filters, Validator.schemas.iofogFilters); - const queryFogData = isCli + const queryFogData = isCLI ? {} : {userId: user.id}; let fogs = await FogManager.findAll(queryFogData, transaction); fogs = _filterFogs(fogs, filters); + const response = []; for (const fog of fogs) { - await _updateFogsConnectionStatus(fog, transaction) + const updatedFog = await _updateFogsConnectionStatus(fog, transaction); + response.push(updatedFog); } return { - fogs: fogs + fogs: response } } -async function _generateProvisioningKey(fogData, user, isCli, transaction) { +async function generateProvisioningKey(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogGenerateProvision); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -263,10 +266,10 @@ async function _generateProvisioningKey(fogData, user, isCli, transaction) { } } -async function _setFogVersionCommand(fogVersionData, user, isCli, transaction) { +async function setFogVersionCommand(fogVersionData, user, isCLI, transaction) { await Validator.validate(fogVersionData, Validator.schemas.iofogSetVersionCommand); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogVersionData.uuid} : {uuid: fogVersionData.uuid, userId: user.id}; @@ -287,15 +290,15 @@ async function _setFogVersionCommand(fogVersionData, user, isCli, transaction) { throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_UPGRADE) } - await _generateProvisioningKey({uuid: fogVersionData.uuid}, user, isCli, transaction); + await generateProvisioningKey({uuid: fogVersionData.uuid}, user, isCLI, transaction); await FogVersionCommandManager.updateOrCreate({iofogUuid: fogVersionData.uuid}, newVersionCommand, transaction); await ChangeTrackingService.update(fogVersionData.uuid, ChangeTrackingService.events.version, transaction) } -async function _setFogRebootCommand(fogData, user, isCli, transaction) { +async function setFogRebootCommand(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogReboot); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -307,7 +310,7 @@ async function _setFogRebootCommand(fogData, user, isCli, transaction) { await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.reboot, transaction) } -async function _getHalHardwareInfo(uuidObj, user, isCLI, transaction) { +async function getHalHardwareInfo(uuidObj, user, isCLI, transaction) { await Validator.validate(uuidObj, Validator.schemas.halGet); const fog = await FogManager.findOne({ @@ -322,7 +325,7 @@ async function _getHalHardwareInfo(uuidObj, user, isCLI, transaction) { }, transaction); } -async function _getHalUsbInfo(uuidObj, user, isCLI, transaction) { +async function getHalUsbInfo(uuidObj, user, isCLI, transaction) { await Validator.validate(uuidObj, Validator.schemas.halGet); const fog = await FogManager.findOne({ @@ -367,12 +370,16 @@ async function _updateFogsConnectionStatus(fog, transaction) { const minInMs = 60000; const intervalInMs = fog.statusFrequency > minInMs ? fog.statusFrequency * 2 : minInMs; if (fog.daemonStatus !== 'UNKNOWN' && Date.now() - fog.lastStatusTime > intervalInMs) { - fog.daemonStatus = 'UNKNOWN'; - fog.ipAddress = '0.0.0.0'; const queryFogData = {uuid: fog.uuid}; - const toUpdate = {daemonStatus: fog.daemonStatus, ipAddress: fog.ipAddress}; - await FogManager.update(queryFogData, toUpdate, transaction) + const toUpdate = {daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0'}; + await FogManager.update(queryFogData, toUpdate, transaction); + const updatedFog = Object.assign({}, fog); + updatedFog.daemonStatus = 'UNKNOWN'; + updatedFog.ipAddress = '0.0.0.0'; + return updatedFog; } + + return fog; } async function _processDeleteCommand(fog, transaction) { @@ -401,7 +408,7 @@ async function _createHalMicroserviceForFog(fogData, oldFog, user, transaction) await MicroserviceManager.create(halMicroserviceData, transaction); } -async function _deleteHalMicroseviceByFog(fogData, transaction) { +async function _deleteHalMicroserviceByFog(fogData, transaction) { const halItem = await CatalogService.getHalCatalogItem(transaction); const deleteHalMicroserviceData = { iofogUuid: fogData.uuid, @@ -440,15 +447,15 @@ async function _deleteBluetoothMicroserviceByFog(fogData, transaction) { } module.exports = { - createFog: TransactionDecorator.generateTransaction(_createFog), - updateFog: TransactionDecorator.generateTransaction(_updateFog), - deleteFog: TransactionDecorator.generateTransaction(_deleteFog), - getFogWithTransaction: TransactionDecorator.generateTransaction(_getFog), - getFogList: TransactionDecorator.generateTransaction(_getFogList), - generateProvisioningKey: TransactionDecorator.generateTransaction(_generateProvisioningKey), - setFogVersionCommand: TransactionDecorator.generateTransaction(_setFogVersionCommand), - setFogRebootCommand: TransactionDecorator.generateTransaction(_setFogRebootCommand), - getHalHardwareInfo: TransactionDecorator.generateTransaction(_getHalHardwareInfo), - getHalUsbInfo: TransactionDecorator.generateTransaction(_getHalUsbInfo), - getFog: _getFog + createFog: TransactionDecorator.generateTransaction(createFog), + updateFog: TransactionDecorator.generateTransaction(updateFog), + deleteFog: TransactionDecorator.generateTransaction(deleteFog), + getFogWithTransaction: TransactionDecorator.generateTransaction(getFog), + getFogList: TransactionDecorator.generateTransaction(getFogList), + generateProvisioningKey: TransactionDecorator.generateTransaction(generateProvisioningKey), + setFogVersionCommand: TransactionDecorator.generateTransaction(setFogVersionCommand), + setFogRebootCommand: TransactionDecorator.generateTransaction(setFogRebootCommand), + getHalHardwareInfo: TransactionDecorator.generateTransaction(getHalHardwareInfo), + getHalUsbInfo: TransactionDecorator.generateTransaction(getHalUsbInfo), + getFog: getFog }; \ No newline at end of file diff --git a/test/src/services/iofog-service.test.js b/test/src/services/iofog-service.test.js new file mode 100644 index 000000000..485053a26 --- /dev/null +++ b/test/src/services/iofog-service.test.js @@ -0,0 +1,1708 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ioFogManager = require('../../../src/sequelize/managers/iofog-manager'); +const ioFogService = require('../../../src/services/iofog-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const Validator = require('../../../src/schemas'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const CatalogService = require('../../../src/services/catalog-service'); +const MicroserviceManager = require('../../../src/sequelize/managers/microservice-manager'); +const ioFogProvisionKeyManager = require('../../../src/sequelize/managers/iofog-provision-key-manager'); +const ioFogVersionCommandManager = require('../../../src/sequelize/managers/iofog-version-command-manager'); +const HWInfoManager = require('../../../src/sequelize/managers/hw-info-manager'); +const USBInfoManager = require('../../../src/sequelize/managers/usb-info-manager'); + +describe('ioFog Service', () => { + def('subject', () => ioFogService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.createFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + const uuid2 = 'testUuid2'; + const uuid3 = 'testUuid3'; + + const fogData = { + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + deviceScanFrequency: 26, + bluetoothEnabled: true, + watchdogEnabled: false, + abstractedHardwareEnabled: true, + fogType: 1 + }; + + const createFogData = { + uuid: uuid, + name: fogData.name, + location: fogData.location, + latitude: fogData.latitude, + longitude: fogData.longitude, + gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, + description: fogData.description, + dockerUrl: fogData.dockerUrl, + diskLimit: fogData.diskLimit, + diskDirectory: fogData.diskDirectory, + memoryLimit: fogData.memoryLimit, + cpuLimit: fogData.cpuLimit, + logLimit: fogData.logLimit, + logDirectory: fogData.logDirectory, + logFileCount: fogData.logFileCount, + statusFrequency: fogData.statusFrequency, + changeFrequency: fogData.changeFrequency, + deviceScanFrequency: fogData.deviceScanFrequency, + bluetoothEnabled: fogData.bluetoothEnabled, + watchdogEnabled: fogData.watchdogEnabled, + abstractedHardwareEnabled: fogData.abstractedHardwareEnabled, + fogTypeId: fogData.fogType, + userId: user.id + }; + + const halItem = { + id: 10 + }; + + const oldFog = null; + const halMicroserviceData = { + uuid: uuid2, + name: `Hal for Fog ${createFogData.uuid}`, + config: '{}', + catalogItemId: halItem.id, + iofogUuid: createFogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + + const bluetoothItem = { + id: 10 + }; + const bluetoothMicroserviceData = { + uuid: uuid3, + name: `Bluetooth for Fog ${createFogData.uuid}`, + config: '{}', + catalogItemId: bluetoothItem.id, + iofogUuid: createFogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + + const response = { + uuid: uuid + }; + + def('subject', () => $subject.createFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('generateRandomStringResponse', () => uuid); + def('generateRandomStringResponse2', () => uuid2); + def('generateRandomStringResponse3', () => uuid3); + def('deleteUndefinedFieldsResponse', () => createFogData); + def('createIoFogResponse', () => Promise.resolve(response)); + def('createChangeTrackingResponse', () => Promise.resolve()); + def('getHalCatalogItemResponse', () => Promise.resolve(halItem)); + def('createMicroserviceResponse', () => Promise.resolve()); + def('createMicroserviceResponse2', () => Promise.resolve()); + def('getBluetoothCatalogItemResponse', () => Promise.resolve(bluetoothItem)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'generateRandomString') + .onFirstCall().returns($generateRandomStringResponse) + .onSecondCall().returns($generateRandomStringResponse2) + .onThirdCall().returns($generateRandomStringResponse3); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(ioFogManager, 'create').returns($createIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'create').returns($createChangeTrackingResponse); + $sandbox.stub(CatalogService, 'getHalCatalogItem').returns($getHalCatalogItemResponse); + $sandbox.stub(MicroserviceManager, 'create') + .onFirstCall().returns($createMicroserviceResponse) + .onSecondCall().returns($createMicroserviceResponse2); + $sandbox.stub(CatalogService, 'getBluetoothCatalogItem').returns($getBluetoothCatalogItemResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogCreate); + }); + + 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#generateRandomString() with correct args', async () => { + await $subject; + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(createFogData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls ioFogManager#create() with correct args', async () => { + await $subject; + + expect(ioFogManager.create).to.have.been.calledWith(createFogData); + }); + + context('when ioFogManager#create() fails', () => { + def('createIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#create() succeeds', () => { + it('calls ChangeTrackingService#create() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.create).to.have.been.calledWith(uuid, transaction); + }); + + context('when ChangeTrackingService#create() fails', () => { + def('createIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#create() succeeds', () => { + it('calls CatalogService#getHalCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getHalCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getHalCatalogItem() fails', () => { + def('getHalCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getHalCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(halMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls CatalogService#getBluetoothCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getBluetoothCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getBluetoothCatalogItem() fails', () => { + def('getBluetoothCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getBluetoothCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse3', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(bluetoothMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(createFogData.uuid, + ChangeTrackingService.events.microserviceCommon, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('uuid') + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.updateFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + const uuid2 = 'testUuid2'; + const uuid3 = 'testUuid3'; + + const fogData = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + deviceScanFrequency: 26, + bluetoothEnabled: true, + watchdogEnabled: false, + abstractedHardwareEnabled: true, + fogType: 1 + }; + + const oldFog = { + uuid: uuid2, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + let updateFogData = { + name: fogData.name, + location: fogData.location, + latitude: fogData.latitude, + longitude: fogData.longitude, + gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, + description: fogData.description, + dockerUrl: fogData.dockerUrl, + diskLimit: fogData.diskLimit, + diskDirectory: fogData.diskDirectory, + memoryLimit: fogData.memoryLimit, + cpuLimit: fogData.cpuLimit, + logLimit: fogData.logLimit, + logDirectory: fogData.logDirectory, + logFileCount: fogData.logFileCount, + statusFrequency: fogData.statusFrequency, + changeFrequency: fogData.changeFrequency, + deviceScanFrequency: fogData.deviceScanFrequency, + bluetoothEnabled: fogData.bluetoothEnabled, + watchdogEnabled: fogData.watchdogEnabled, + abstractedHardwareEnabled: fogData.abstractedHardwareEnabled, + fogTypeId: fogData.fogType, + }; + + const halItem = { + id: 10 + }; + const halMicroserviceData = { + uuid: uuid2, + name: `Hal for Fog ${fogData.uuid}`, + config: '{}', + catalogItemId: halItem.id, + iofogUuid: fogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + + const bluetoothItem = { + id: 10 + }; + const bluetoothMicroserviceData = { + uuid: uuid3, + name: `Bluetooth for Fog ${fogData.uuid}`, + config: '{}', + catalogItemId: bluetoothItem.id, + iofogUuid: fogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + def('subject', () => $subject.updateFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => updateFogData); + def('findIoFogResponse', () => Promise.resolve(oldFog)); + def('updateIoFogResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse2', () => Promise.resolve()); + def('getHalCatalogItemResponse', () => Promise.resolve(halItem)); + def('generateRandomStringResponse', () => uuid2); + def('generateRandomStringResponse2', () => uuid3); + def('createMicroserviceResponse', () => Promise.resolve()); + def('createMicroserviceResponse2', () => Promise.resolve()); + def('getBluetoothCatalogItemResponse', () => Promise.resolve(bluetoothItem)); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'update') + .onFirstCall().returns($updateChangeTrackingResponse) + .onSecondCall().returns($updateChangeTrackingResponse2); + $sandbox.stub(CatalogService, 'getHalCatalogItem').returns($getHalCatalogItemResponse); + $sandbox.stub(AppHelper, 'generateRandomString') + .onFirstCall().returns($generateRandomStringResponse) + .onSecondCall().returns($generateRandomStringResponse2); + $sandbox.stub(MicroserviceManager, 'create') + .onFirstCall().returns($createMicroserviceResponse) + .onSecondCall().returns($createMicroserviceResponse2); + $sandbox.stub(CatalogService, 'getBluetoothCatalogItem').returns($getBluetoothCatalogItemResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogUpdate); + }); + + 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(updateFogData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + + expect(ioFogManager.update).to.have.been.calledWith(queryFogData, + updateFogData, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(uuid, + ChangeTrackingService.events.config, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('calls CatalogService#getHalCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getHalCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getHalCatalogItem() fails', () => { + def('getHalCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getHalCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(halMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls CatalogService#getBluetoothCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getBluetoothCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getBluetoothCatalogItem() fails', () => { + def('getBluetoothCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getBluetoothCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(bluetoothMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(fogData.uuid, + ChangeTrackingService.events.microserviceCommon, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.deleteFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + + const fogData = { + uuid: uuid + }; + + const fog = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + daemonStatus: 'RUNNING', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + lastStatusTime: 1555, + ipAddress: 'testIpAddress', + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + const toUpdate = { + daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0' + }; + + def('subject', () => $subject.deleteFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve(fog)); + def('updateIoFogResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogDelete); + }); + + 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 ioFogManager#findOne() with correct args', async () => { + await $subject; + + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + const query = { + uuid: uuid + }; + expect(ioFogManager.update).to.have.been.calledWith(query, + toUpdate, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(uuid, ChangeTrackingService.events.deleteNode, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }); + + describe('.getFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + + const fogData = { + uuid: uuid + }; + + const fog = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + daemonStatus: 'RUNNING', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + lastStatusTime: 1555, + ipAddress: 'testIpAddress', + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + const toUpdate = { + daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0' + }; + + const fogResponse = { + uuid: fog.uuid, + name: fog.name, + location: fog.location, + gpsMode: fog.gpsMode, + latitude: fog.latitude, + longitude: fog.longitude, + description: fog.description, + lastActive: fog.lastActive, + daemonStatus: 'UNKNOWN', + daemonOperatingDuration: fog.daemonOperatingDuration, + daemonLastStart: fog.daemonLastStart, + memoryUsage: fog.memoryUsage, + diskUsage: fog.diskUsage, + cpuUsage: fog.cpuUsage, + memoryViolation: fog.memoryViolation, + diskViolation: fog.diskViolation, + cpuViolation: fog.cpuViolation, + catalogItemStatus: fog.catalogItemStatus, + repositoryCount: fog.repositoryCount, + repositoryStatus: fog.repositoryStatus, + systemTime: fog.systemTime, + lastStatusTime: fog.lastStatusTime, + ipAddress: '0.0.0.0', + processedMessages: fog.processedMessages, + catalogItemMessageCounts: fog.catalogItemMessageCounts, + messageSpeed: fog.messageSpeed, + lastCommandTime: fog.lastCommandTime, + networkInterface: fog.networkInterface, + dockerUrl: fog.dockerUrl, + diskLimit: fog.diskLimit, + diskDirectory: fog.diskDirectory, + memoryLimit: fog.memoryLimit, + cpuLimit: fog.cpuLimit, + logLimit: fog.logLimit, + logDirectory: fog.logDirectory, + bluetoothEnabled: fog.bluetoothEnabled, + abstractedHardwareEnabled: fog.abstractedHardwareEnabled, + logFileCount: fog.logFileCount, + version: fog.version, + isReadyToUpgrade: fog.isReadyToUpgrade, + isReadyToRollback: fog.isReadyToRollback, + statusFrequency: fog.statusFrequency, + changeFrequency: fog.changeFrequency, + deviceScanFrequency: fog.deviceScanFrequency, + tunnel: fog.tunnel, + watchdogEnabled: fog.watchdogEnabled, + fogTypeId: fog.fogTypeId, + userId: fog.userId + }; + + def('subject', () => $subject.getFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve(fog)); + def('updateIoFogResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogGet); + }); + + 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 ioFogManager#findOne() with correct args', async () => { + await $subject; + + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + const query = { + uuid: uuid + }; + expect(ioFogManager.update).to.have.been.calledWith(query, + toUpdate, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(fogResponse); + }) + }) + }) + }) + }); + + describe('.getFogList()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + + const fog = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + daemonStatus: 'RUNNING', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + lastStatusTime: 1555, + ipAddress: 'testIpAddress', + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const fogs = [fog]; + + const queryFogData = isCLI + ? {} + : {userId: user.id}; + + const toUpdate = { + daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0' + }; + + const filters = []; + + def('subject', () => $subject.getFogList(filters, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findAllIoFogResponse', () => Promise.resolve(fogs)); + def('updateIoFogResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findAll').returns($findAllIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(filters, Validator.schemas.iofogFilters); + }); + + 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 ioFogManager#findAll() with correct args', async () => { + await $subject; + + expect(ioFogManager.findAll).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findAllIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + const query = { + uuid: uuid + }; + expect(ioFogManager.update).to.have.been.calledWith(query, + toUpdate, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('fogs'); + }) + }) + }) + }) + }); + + describe('.generateProvisioningKey()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const fogData = { + uuid: uuid + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + const provisionKey = 'tttttttt'; + const expirationTime = date + (20 * 60 * 1000); + + const newProvision = { + iofogUuid: fogData.uuid, + provisionKey: provisionKey, + expirationTime: expirationTime, + }; + + def('subject', () => $subject.generateProvisioningKey(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('generateRandomStringResponse', () => provisionKey); + def('findIoFogResponse', () => Promise.resolve({})); + def('updateOrCreateProvisionKeyResponse', () => Promise.resolve(newProvision)); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateRandomStringResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogProvisionKeyManager, 'updateOrCreate').returns($updateOrCreateProvisionKeyResponse); + + $sandbox.stub(Date.prototype, 'getTime').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogGenerateProvision); + }); + + 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#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(8); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('key'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogProvisionKeyManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(ioFogProvisionKeyManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: fogData.uuid + }, newProvision, transaction); + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal({ + key: provisionKey, + expirationTime: expirationTime + }) + }) + }) + }) + }) + }) + }); + + describe('.setFogVersionCommand()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const fogVersionData = { + uuid: uuid, + versionCommand: 'upgrade' + }; + + const queryFogData = isCLI + ? {uuid: fogVersionData.uuid} + : {uuid: fogVersionData.uuid, userId: user.id}; + + const ioFog = { + uuid: uuid, + isReadyToUpgrade: true, + isReadyToRollback: true + }; + + const newVersionCommand = { + iofogUuid: fogVersionData.uuid, + versionCommand: fogVersionData.versionCommand + }; + + const provisionKey = 'tttttttt'; + const expirationTime = date + (20 * 60 * 1000); + + const newProvision = { + iofogUuid: uuid, + provisionKey: provisionKey, + expirationTime: expirationTime, + }; + + def('subject', () => $subject.setFogVersionCommand(fogVersionData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve(ioFog)); + def('generateRandomStringResponse', () => provisionKey); + def('updateOrCreateProvisionKeyResponse', () => Promise.resolve(newProvision)); + def('findIoFogVersionCommandResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateRandomStringResponse); + $sandbox.stub(ioFogProvisionKeyManager, 'updateOrCreate').returns($updateOrCreateProvisionKeyResponse); + $sandbox.stub(ioFogVersionCommandManager, 'updateOrCreate').returns($findIoFogVersionCommandResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + + $sandbox.stub(Date.prototype, 'getTime').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogVersionData, Validator.schemas.iofogSetVersionCommand); + }); + + 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 ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith({ + uuid: fogVersionData.uuid + }, Validator.schemas.iofogGenerateProvision); + }); + + 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#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(8); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogProvisionKeyManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(ioFogProvisionKeyManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: uuid + }, newProvision, transaction); + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() succeeds', () => { + it('calls ioFogVersionCommandManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(ioFogVersionCommandManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: fogVersionData.uuid + }, newVersionCommand, transaction); + }); + + context('when ioFogVersionCommandManager#updateOrCreate() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogVersionCommandManager#updateOrCreate() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(fogVersionData.uuid, + ChangeTrackingService.events.version, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.setFogRebootCommand()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const fogData = { + uuid: uuid + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + def('subject', () => $subject.setFogRebootCommand(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve({})); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogReboot); + }); + + 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 ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(fogData.uuid, + ChangeTrackingService.events.reboot, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }); + + describe('.getHalHardwareInfo()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const uuidObj = { + uuid: uuid + }; + + def('subject', () => $subject.getHalHardwareInfo(uuidObj, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve({})); + def('findHalHardwareResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(HWInfoManager, 'findOne').returns($findHalHardwareResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(uuidObj, Validator.schemas.halGet); + }); + + 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 ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith({ + uuid: uuidObj.uuid + }, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls HWInfoManager#findOne() with correct args', async () => { + await $subject; + expect(HWInfoManager.findOne).to.have.been.calledWith({ + iofogUuid: uuidObj.uuid + }, transaction); + }); + + context('when HWInfoManager#findOne() fails', () => { + def('findHalHardwareResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when HWInfoManager#findOne() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }); + + describe('.getHalUsbInfo()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const uuidObj = { + uuid: uuid + }; + + def('subject', () => $subject.getHalUsbInfo(uuidObj, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve({})); + def('findHalUsbResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(USBInfoManager, 'findOne').returns($findHalUsbResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(uuidObj, Validator.schemas.halGet); + }); + + 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 ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith({ + uuid: uuidObj.uuid + }, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls USBInfoManager#findOne() with correct args', async () => { + await $subject; + expect(USBInfoManager.findOne).to.have.been.calledWith({ + iofogUuid: uuidObj.uuid + }, transaction); + }); + + context('when USBInfoManager#findOne() fails', () => { + def('findHalUsbResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when USBInfoManager#findOne() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }); + + +}); \ No newline at end of file From adc97b3cbf5bc8f04bc855dc700ac38875083624 Mon Sep 17 00:00:00 2001 From: epankou <35571073+epankou@users.noreply.github.com> Date: Thu, 3 Jan 2019 13:35:59 +0300 Subject: [PATCH 38/39] Epankou/bug hal usb cli ewc 449 (#471) * EWC-418 added registry email validation * EWC-421 strace feature bug * EWC-421 strace feature bug fix * bug(fix) iofog agent API: core network containers are returned when flow isn't active (EWC-424) * bug(fix) microservice removal: port mapping removal (EWC-424) * bug(fix) microservice removal: switched to const (EWC-424) * bug(fix) routes removal: added routes update on connector update (EWC-374) * bug(fix) routes removal: added routes update on connector update (EWC-374) * bug(fix) tests (EWC-374) * bug(fix) create connectorPortService (EWC-374) * bug(fix) tests (EWC-374) * bug(fix) refactoring (EWC-374) * bug(fix) usb hal info: fixed cli usb function (EWC-449) --- src/cli/iofog.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cli/iofog.js b/src/cli/iofog.js index 7f43f9faa..5a7606587 100644 --- a/src/cli/iofog.js +++ b/src/cli/iofog.js @@ -359,8 +359,11 @@ async function _getHalHardwareInfo(obj) { logger.info("Parameters" + JSON.stringify(uuidObj)); - const info = await FogService.getHalHardwareInfo(uuidObj, {}, true); - logger.info(JSON.stringify(info, null, 2)); + const data = await FogService.getHalHardwareInfo(uuidObj, {}, true); + if (data.info) { + data.info = JSON.parse(data.info); + } + logger.info(JSON.stringify(data, null, 2)); logger.info('Hardware info has been retrieved successfully.') } @@ -371,8 +374,11 @@ async function _getHalUsbInfo(obj) { logger.info("Parameters" + JSON.stringify(uuidObj)); - const info = await FogService.getHalHardwareInfo(uuidObj, {}, true); - logger.info(JSON.stringify(info, null, 2)); + const data = await FogService.getHalUsbInfo(uuidObj, {}, true); + if (data.info) { + data.info = JSON.parse(data.info); + } + logger.info(JSON.stringify(data, null, 2)); logger.info('Usb info has been retrieved successfully.') } From 0c56262ce10ced072f92589bcd5c56b029595e6c Mon Sep 17 00:00:00 2001 From: Railag Date: Thu, 3 Jan 2019 13:41:57 +0300 Subject: [PATCH 39/39] version 1.0.33 (#472) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 88220c30e..2995967f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.32", + "version": "1.0.33", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi",