diff --git a/src/helpers/app-helper.js b/src/helpers/app-helper.js index 93ecd24ad..6dacf9799 100644 --- a/src/helpers/app-helper.js +++ b/src/helpers/app-helper.js @@ -78,13 +78,7 @@ const findAvailablePort = async function (hostname) { } let portBounds = portRange.split("-").map(i => parseInt(i)); return await portscanner.findAPortNotInUse(portBounds[0], portBounds[1], hostname); -} - -/** - * @desc generates a random String of the size specified by the input param - * @param Integer - size - * @return String - returns random string - */ +}; function isFileExists(filePath) { if (path.extname(filePath).indexOf(".") >= 0) { @@ -109,7 +103,7 @@ function isValidDomain(domain) { } const isValidPublicIP = function (publicIP) { - let re = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; + const re = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; return re.test(publicIP); }; @@ -142,7 +136,7 @@ function deleteUndefinedFields(obj) { } else if (obj[fld] instanceof Object) { obj[fld] = deleteUndefinedFields(obj[fld]) } - }) + }); return obj } @@ -191,11 +185,11 @@ function trimCertificate(cert) { function validateParameters(command, commandDefinitions, args) { // 1st argument = command args.shift(); - + const possibleAliasesList = _getPossibleAliasesList(command, commandDefinitions); const possibleArgsList = _getPossibleArgsList(command, commandDefinitions); - for (let arg of args) { + for (const arg of args) { // arg is [argument, alias, value] if (arg.startsWith("--")) { // argument @@ -272,8 +266,8 @@ function isTest() { } function isEmpty(obj) { - for(let key in obj) { - if(obj.hasOwnProperty(key)) + for (const key in obj) { + if (obj.hasOwnProperty(key)) return false; } return true; diff --git a/src/services/user-service.js b/src/services/user-service.js index 53224dbeb..5a88ae9c2 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -19,28 +19,18 @@ 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'); const emailResetTemplate = require('../views/reset-password-temp'); const EmailActivationCodeService = require('./email-activation-code-service'); - const AccessTokenService = require('./access-token-service'); - const TransactionDecorator = require('../decorators/transaction-decorator'); - const Validator = require('../schemas'); -const createUser = async function (user, transaction) { - return await UserManager.create(user, transaction) -}; - const signUp = async function (user, isCLI, transaction) { - let isEmailActivationEnabled = Config.get("Email:ActivationEnabled"); if (isEmailActivationEnabled) { - const newUser = await _handleCreateUser(user, isEmailActivationEnabled, transaction); const activationCodeData = await EmailActivationCodeService.generateActivationCode(transaction); @@ -56,7 +46,6 @@ const signUp = async function (user, isCLI, transaction) { }; const login = async function (credentials, isCLI, transaction) { - const user = await UserManager.findOne({ email: credentials.email }, transaction); @@ -138,7 +127,7 @@ const logout = async function (user, isCLI, transaction) { return await AccessTokenService.removeAccessTokenByUserId(user.id, transaction) }; -const updateDetails = async function (user, profileData, isCLI, transaction) { +const updateUserDetails = async function (user, profileData, isCLI, transaction) { if (isCLI) { await Validator.validate(profileData, Validator.schemas.updateUserProfileCLI); } else { @@ -147,7 +136,7 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { const password = (profileData.password) ? AppHelper.encryptText(profileData.password, user.email) : undefined; - const updateObject = isCLI ? + let updateObject = isCLI ? { firstName: profileData.firstName, lastName: profileData.lastName, @@ -159,7 +148,7 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { lastName: profileData.lastName }; - AppHelper.deleteUndefinedFields(updateObject); + updateObject = AppHelper.deleteUndefinedFields(updateObject); await UserManager.updateDetails(user, updateObject, transaction); @@ -171,7 +160,6 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { }; const deleteUser = async function (force, user, isCLI, transaction) { - if (!force) { const ioFogArray = await ioFogManager.findAll({ userId: user.id @@ -321,7 +309,7 @@ async function _handleCreateUser(user, isEmailActivationEnabled, transaction) { async function _createNewUser(user, isEmailActivationEnabled, transaction) { user.emailActivated = !isEmailActivationEnabled; - return await createUser(user, transaction) + return await UserManager.create(user, transaction); } async function _notifyUserAboutActivationCode(email, url, emailSenderData, activationCodeData, transporter) { @@ -390,7 +378,7 @@ module.exports = { resendActivation: TransactionDecorator.generateTransaction(resendActivation), activateUser: TransactionDecorator.generateTransaction(activateUser), logout: TransactionDecorator.generateTransaction(logout), - updateUserDetails: TransactionDecorator.generateTransaction(updateDetails), + updateUserDetails: TransactionDecorator.generateTransaction(updateUserDetails), deleteUser: TransactionDecorator.generateTransaction(deleteUser), updateUserPassword: TransactionDecorator.generateTransaction(updateUserPassword), resetUserPassword: TransactionDecorator.generateTransaction(resetUserPassword), diff --git a/test/src/services/user-service.test.js b/test/src/services/user-service.test.js new file mode 100644 index 000000000..093213ed9 --- /dev/null +++ b/test/src/services/user-service.test.js @@ -0,0 +1,694 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const UserManager = require('../../../src/sequelize/managers/user-manager'); +const UserService = require('../../../src/services/user-service'); +const Config = require('../../../src/config'); +const AccessTokenService = require('../../../src/services/access-token-service'); +const Validator = require('../../../src/schemas'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ioFogManager = require('../../../src/sequelize/managers/iofog-manager'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const EmailActivationCodeService = require('../../../src/services/email-activation-code-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const ErrorMessages = require('../../../src/helpers/error-messages'); +const nodemailer = require('nodemailer'); + +describe('User Service', () => { + def('subject', () => UserService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.signUp()', () => { + const transaction = {}; + const error = 'Error!'; + + const newUser = { + id: 16, + firstName: 'testFirstName', + lastName: 'testLastName', + email: 'testEmail', + emailActivated: true + }; + + const response = { + userId: 16, + firstName: newUser.firstName, + lastName: newUser.lastName, + email: newUser.email, + emailActivated: newUser.emailActivated + }; + + def('subject', () => $subject.signUp(newUser, isCLI, transaction)); + def('configGetResponse', () => false); + def('findUserResponse', () => Promise.resolve()); + def('createUserResponse', () => Promise.resolve(newUser)); + + beforeEach(() => { + $sandbox.stub(Config, 'get').returns($configGetResponse); + $sandbox.stub(UserManager, 'findOne').returns($findUserResponse); + $sandbox.stub(UserManager, 'create').returns($createUserResponse); + }); + + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith("Email:ActivationEnabled"); + }); + + context('when Config#get() succeeds', () => { + it('calls UserManager#findOne() with correct args', async () => { + await $subject; + expect(UserManager.findOne).to.have.been.calledWith({ + email: newUser.email + }, transaction); + }); + + context('when UserManager#findOne() fails', () => { + def('findUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findOne() succeeds', () => { + it('calls UserManager#create() with correct args', async () => { + await $subject; + expect(UserManager.create).to.have.been.calledWith(newUser, transaction); + }); + + context('when UserManager#create() fails', () => { + def('createUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(response); + }) + }) + }) + }) + }); + + describe('.login()', () => { + const transaction = {}; + const error = 'Error!'; + + const credentials = { + email: 'testEmail', + password: 'testPassword' + }; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const testAccessToken = 'testAccessToken'; + + const configGet2 = 155; + const date = 1555; + const tokenExpireTime = date + (configGet2 * 1000); + + const createToken = { + token: testAccessToken, + expirationTime: tokenExpireTime, + userId: user.id + }; + + + def('subject', () => $subject.login(credentials, isCLI, transaction)); + def('findUserResponse', () => Promise.resolve(user)); + def('decryptTextResponse', () => credentials.password); + def('getConfigResponse', () => false); + def('getConfigResponse2', () => configGet2); + def('generateAccessTokenResponse', () => 'testAccessToken'); + def('findByAccessTokenResponse', () => false); + def('createAccessTokenResponse', () => Promise.resolve({ + token: 'token' + })); + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(UserManager, 'findOne').returns($findUserResponse); + $sandbox.stub(AppHelper, 'decryptText').returns($decryptTextResponse); + $sandbox.stub(Config, 'get') + .onFirstCall().returns($getConfigResponse) + .onSecondCall().returns($getConfigResponse2); + $sandbox.stub(AppHelper, 'generateAccessToken').returns($generateAccessTokenResponse); + $sandbox.stub(UserManager, 'findByAccessToken').returns($findByAccessTokenResponse); + $sandbox.stub(AccessTokenService, 'createAccessToken').returns($createAccessTokenResponse); + $sandbox.stub(Date.prototype, 'getTime').returns($dateResponse); + }); + + it('calls UserManager#findOne() with correct args', async () => { + await $subject; + expect(UserManager.findOne).to.have.been.calledWith({ + email: credentials.email + }, transaction); + }); + + context('when UserManager#findOne() fails', () => { + def('findUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findOne() succeeds', () => { + it('calls AppHelper#decryptText() with correct args', async () => { + await $subject; + expect(AppHelper.decryptText).to.have.been.calledWith(user.password, user.email); + }); + + context('when AppHelper#decryptText() fails', () => { + const err = 'Invalid credentials'; + def('decryptTextResponse', () => Promise.reject(err)); + + it(`fails with ${err}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when AppHelper#decryptText() succeeds', () => { + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith("Email:ActivationEnabled"); + }); + + context('when Config#get() fails', () => { + const err = 'Email is not activated. Please activate your account first.'; + def('getConfigResponse', () => Promise.reject(err)); + + it(`fails with ${err}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when Config#get() succeeds', () => { + it('calls AppHelper#generateAccessToken() with correct args', async () => { + await $subject; + expect(AppHelper.generateAccessToken).to.have.been.calledWith(); + }); + + context('when AppHelper#generateAccessToken() fails', () => { + def('generateAccessTokenResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('accessToken'); + }) + }); + + context('when AppHelper#generateAccessToken() succeeds', () => { + it('calls UserManager#findByAccessToken() with correct args', async () => { + await $subject; + expect(UserManager.findByAccessToken).to.have.been.calledWith(testAccessToken, transaction); + }); + + context('when UserManager#findByAccessToken() fails', () => { + def('findByAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findByAccessToken() succeeds', () => { + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith('Settings:UserTokenExpirationIntervalSeconds'); + }); + + context('when Config#get() fails', () => { + def('getConfigResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('accessToken'); + }) + }); + + context('when Config#get() succeeds', () => { + it('calls AccessTokenService#createAccessToken() with correct args', async () => { + await $subject; + expect(AccessTokenService.createAccessToken).to.have.been.calledWith(createToken, transaction); + }); + + context('when AccessTokenService#createAccessToken() fails', () => { + def('createAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when AccessTokenService#createAccessToken() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('accessToken'); + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.resendActivation()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const configGet2 = 155; + + const emailObj = { + email: 'testEmail' + }; + + def('subject', () => $subject.resendActivation(emailObj, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findUserResponse', () => Promise.resolve(user)); + def('generateActivationCodeResponse', () => Promise.resolve()); + def('saveActivationCodeResponse', () => Promise.resolve()); + def('getConfigResponse', () => false); + def('getConfigResponse2', () => configGet2); + def('getConfigResponse3', () => configGet2); + def('getConfigResponse4', () => configGet2); + def('decryptTextResponse', () => 'testPassword'); + def('createTransportResponse', () => Promise.resolve()); + def('sendMailResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(UserManager, 'findOne').returns($findUserResponse); + $sandbox.stub(EmailActivationCodeService, 'generateActivationCode').returns($generateActivationCodeResponse); + $sandbox.stub(EmailActivationCodeService, 'saveActivationCode').returns($saveActivationCodeResponse); + $sandbox.stub(Config, 'get') + .onCall(0).returns($getConfigResponse) + .onCall(1).returns($getConfigResponse2) + .onCall(2).returns($getConfigResponse3) + .onCall(3).returns($getConfigResponse4); + $sandbox.stub(AppHelper, 'decryptText').returns($decryptTextResponse); + // TODO with rewire + // $sandbox.stub(nodemailer, 'createTransport').returns($createTransportResponse); + // $sandbox.stub(transporter, 'sendmail').returns($sendMailResponse); + }); + + // TODO finish + + }); + + + describe('.activateUser()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const updatedObj = { + emailActivated: true + }; + + const codeData = { + activationCode: 'testActivationCode' + }; + + def('subject', () => $subject.activateUser(codeData, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('verifyActivationCodeResponse', () => Promise.resolve({ + userId: user.id + })); + def('updateUserResponse', () => Promise.resolve()); + def('deleteActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(EmailActivationCodeService, 'verifyActivationCode').returns($verifyActivationCodeResponse); + $sandbox.stub(UserManager, 'update').returns($updateUserResponse); + $sandbox.stub(EmailActivationCodeService, 'deleteActivationCode').returns($deleteActivationCodeResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(codeData, Validator.schemas.activateUser); + }); + + 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 EmailActivationCodeService#verifyActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeService.verifyActivationCode).to.have.been.calledWith(codeData.activationCode, transaction); + }); + + context('when EmailActivationCodeService#verifyActivationCode() fails', () => { + def('verifyActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when EmailActivationCodeService#verifyActivationCode() succeeds', () => { + it('calls UserManager#update() with correct args', async () => { + await $subject; + expect(UserManager.update).to.have.been.calledWith({ + id: user.id + }, updatedObj, transaction); + }); + + context('when UserManager#update() fails', () => { + const err = 'User not updated'; + def('updateUserResponse', () => Promise.reject(err)); + + it(`fails with ${err}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when UserManager#update() succeeds', () => { + it('calls EmailActivationCodeService#deleteActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeService.deleteActivationCode).to.have.been.calledWith(codeData.activationCode, + transaction); + }); + + context('when EmailActivationCodeService#deleteActivationCode() fails', () => { + def('deleteActivationCodeResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when EmailActivationCodeService#deleteActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }); + + describe('.logout()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + def('subject', () => $subject.logout(user, isCLI, transaction)); + def('removeAccessTokenResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AccessTokenService, 'removeAccessTokenByUserId').returns($removeAccessTokenResponse); + }); + + it('calls AccessTokenService#removeAccessTokenByUserId() with correct args', async () => { + await $subject; + expect(AccessTokenService.removeAccessTokenByUserId).to.have.been.calledWith(user.id, transaction); + }); + + context('when AccessTokenService#removeAccessTokenByUserId() fails', () => { + def('removeAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when AccessTokenService#removeAccessTokenByUserId() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }); + + describe('.updateUserDetails()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const profileData = { + firstName: 'testFirstName', + lastName: 'testLastName' + }; + + def('subject', () => $subject.updateUserDetails(user, profileData, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => profileData); + def('updateDetailsResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(UserManager, 'updateDetails').returns($updateDetailsResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(profileData, Validator.schemas.updateUserProfile); + }); + + 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(profileData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('firstName'); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls UserManager#updateDetails() with correct args', async () => { + await $subject; + expect(UserManager.updateDetails).to.have.been.calledWith(user, profileData, transaction); + }); + + context('when UserManager#updateDetails() fails', () => { + def('updateUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('firstName'); + }) + }); + + context('when UserManager#updateDetails() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('firstName'); + }) + }) + }) + }) + }); + + describe('.deleteUser()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const profileData = { + firstName: 'testFirstName', + lastName: 'testLastName' + }; + + const force = false; + + def('subject', () => $subject.deleteUser(force, user, isCLI, transaction)); + def('findAllResponse', () => Promise.resolve([{}])); + def('deleteUserResponse', () => profileData); + + beforeEach(() => { + $sandbox.stub(ioFogManager, 'findAll').returns($findAllResponse); + $sandbox.stub(UserManager, 'delete').returns($deleteUserResponse); + }); + + it('calls ioFogManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogManager.findAll).to.have.been.calledWith({ + userId: user.id + }, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findAllResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls UserManager#delete() with correct args', async () => { + await $subject; + expect(UserManager.delete).to.have.been.calledWith({ + id: user.id + }, transaction); + }); + + context('when UserManager#delete() fails', () => { + def('deleteUserResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when UserManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }); + + // TODO updateUserPassword, resetUserPassword with rewire + + describe('.list()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const response = [{ + id: user.id + }]; + + def('subject', () => $subject.list(isCLI, transaction)); + def('findAllResponse', () => Promise.resolve(response)); + + beforeEach(() => { + $sandbox.stub(UserManager, 'findAll').returns($findAllResponse); + }); + + it('calls UserManager#findAll() with correct args', async () => { + await $subject; + expect(UserManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when UserManager#findAll() fails', () => { + def('findAllResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(response); + }) + }) + }); + + describe('.suspendUser()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const updatedObj = { + emailActivated: false + }; + + def('subject', () => $subject.suspendUser(user, isCLI, transaction)); + def('removeAccessTokenResponse', () => Promise.resolve()); + def('updateUserResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AccessTokenService, 'removeAccessTokenByUserId').returns($removeAccessTokenResponse); + $sandbox.stub(UserManager, 'update').returns($updateUserResponse); + }); + + it('calls AccessTokenService#removeAccessTokenByUserId() with correct args', async () => { + await $subject; + expect(AccessTokenService.removeAccessTokenByUserId).to.have.been.calledWith(user.id, transaction); + }); + + context('when AccessTokenService#removeAccessTokenByUserId() fails', () => { + def('removeAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when AccessTokenService#removeAccessTokenByUserId() succeeds', () => { + it('calls UserManager#update() with correct args', async () => { + await $subject; + expect(UserManager.update).to.have.been.calledWith({ + id: user.id + }, updatedObj, transaction); + }); + + context('when UserManager#update() fails', () => { + def('removeAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }); + +}); \ No newline at end of file