From 27d85d8aa018b558950c6f8412c0f338a1b4d578 Mon Sep 17 00:00:00 2001 From: Railag Date: Sat, 29 Dec 2018 17:58:27 +0300 Subject: [PATCH] test(core) unit tests for flow service (EWC-383) --- 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