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", 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/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/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..e1c24ccc3 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); @@ -129,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) } @@ -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/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/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/connector-service.js b/src/services/connector-service.js index 2f1ceab98..6b8809e8e 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'); @@ -75,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); 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/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/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) 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 198d25d56..821991363 100644 --- a/test/src/services/agent-service.test.js +++ b/test/src/services/agent-service.test.js @@ -16,10 +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', () => { def('subject', () => AgentService); @@ -358,16 +363,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}`, () => { @@ -375,7 +380,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, @@ -1046,6 +1051,307 @@ 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'); + }) + }); + }); + // TODO + // describe('.putImageSnapshot()', () => { }); \ No newline at end of file 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 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