diff --git a/app/controllers/simplified-account/settings/email-notifications/email-notifications.controller.test.js b/app/controllers/simplified-account/settings/email-notifications/email-notifications.controller.test.js index e69de29bb..70b786d12 100644 --- a/app/controllers/simplified-account/settings/email-notifications/email-notifications.controller.test.js +++ b/app/controllers/simplified-account/settings/email-notifications/email-notifications.controller.test.js @@ -0,0 +1 @@ +// TODO diff --git a/app/controllers/simplified-account/settings/index.controller.test.js b/app/controllers/simplified-account/settings/index.controller.test.js index e69de29bb..e435762e7 100644 --- a/app/controllers/simplified-account/settings/index.controller.test.js +++ b/app/controllers/simplified-account/settings/index.controller.test.js @@ -0,0 +1,53 @@ +const { expect } = require('chai') +const { LIVE, NOT_STARTED } = require('../../../models/go-live-stage') +const proxyquire = require('proxyquire') +const sinon = require('sinon') +const paths = require('../../../paths') +const formatSimplifiedAccountPathsFor = require('../../../utils/simplified-account/format/format-simplified-account-paths-for') + +const LIVE_ACCOUNT_TYPE = 'live' +const TEST_ACCOUNT_TYPE = 'test' +const SERVICE_ID = 'service-id-123abc' + +let req, res, indexController + +const getController = () => { + return proxyquire('./index.controller', {}) +} + +describe('Controller: settings/index', () => { + describe('get', () => { + beforeEach(() => { + indexController = getController() + res = { + redirect: sinon.spy() + } + req = { + account: { + service_id: SERVICE_ID, + type: TEST_ACCOUNT_TYPE + }, + service: { + currentGoLiveStage: NOT_STARTED + } + } + }) + + it('should redirect to service name index for test account on service with no live account', () => { + indexController.get(req, res) + expect(res.redirect.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.index, SERVICE_ID, TEST_ACCOUNT_TYPE))).to.be.true // eslint-disable-line + }) + + it('should redirect to service name index for live account', () => { + req.account.type = 'live' + indexController.get(req, res) + expect(res.redirect.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.index, SERVICE_ID, LIVE_ACCOUNT_TYPE))).to.be.true // eslint-disable-line + }) + + it('should redirect to email notifications index for test account on service with live account', () => { + req.service.currentGoLiveStage = LIVE + indexController.get(req, res) + expect(res.redirect.calledWith(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.emailNotifications.index, SERVICE_ID, TEST_ACCOUNT_TYPE))).to.be.true // eslint-disable-line + }) + }) +}) diff --git a/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js b/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js index 5b6943ea9..bb24fac78 100644 --- a/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js +++ b/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js @@ -54,7 +54,7 @@ describe('Controller: settings/service-name', () => { before(() => setupTest('get')) it('should call the response method', () => { - expect(responseStub.called).to.equal(true) + expect(responseStub.called).to.be.true // eslint-disable-line }) it('should pass req, res and template path to the response method', () => { @@ -79,7 +79,7 @@ describe('Controller: settings/service-name', () => { before(() => setupTest('getEditServiceName', { query: queryParams })) it('should call the response method', () => { - expect(responseStub.called).to.equal(true) + expect(responseStub.called).to.be.true // eslint-disable-line }) it('should pass req, res and template path to the response method', () => { @@ -131,14 +131,14 @@ describe('Controller: settings/service-name', () => { before(() => { setupTest('postEditServiceName', { body: { - 'service-name-input': 'New Welsh Name', + 'service-name-input': 'Enw Cymraeg newydd', cy: 'true' } }) }) it('should update the service name', () => { - expect(updateServiceNameStub.calledWith(SERVICE_ID, EN_SERVICE_NAME, 'New Welsh Name')).to.be.true // eslint-disable-line + expect(updateServiceNameStub.calledWith(SERVICE_ID, EN_SERVICE_NAME, 'Enw Cymraeg newydd')).to.be.true // eslint-disable-line }) it('should redirect to the service name index page', () => { @@ -171,7 +171,7 @@ describe('Controller: settings/service-name', () => { it('should render the edit page with errors', () => { expect(responseStub.calledOnce).to.be.true // eslint-disable-line - const [ , , template, context] = responseStub.args[0] + const [, , template, context] = responseStub.args[0] expect(template).to.equal('simplified-account/settings/service-name/edit-service-name') expect(context.errors).to.deep.equal({ summary: ['Error summary'], diff --git a/app/middleware/simplified-account/simplified-account-opt-in.middleware.js b/app/middleware/simplified-account/simplified-account-opt-in.middleware.js index facd7eddf..38e7920c3 100644 --- a/app/middleware/simplified-account/simplified-account-opt-in.middleware.js +++ b/app/middleware/simplified-account/simplified-account-opt-in.middleware.js @@ -1,6 +1,6 @@ const { InvalidConfigurationError } = require('../../errors') -module.exports = function checkDegatewayParameters (req, res, next) { +module.exports = (req, res, next) => { const user = req.user if (user.isDegatewayed()) { return next() diff --git a/app/middleware/simplified-account/simplified-account-opt-in.middleware.test.js b/app/middleware/simplified-account/simplified-account-opt-in.middleware.test.js new file mode 100644 index 000000000..86409bea0 --- /dev/null +++ b/app/middleware/simplified-account/simplified-account-opt-in.middleware.test.js @@ -0,0 +1,44 @@ +const { expect } = require('chai') +const sinon = require('sinon') +const proxyquire = require('proxyquire') + +describe('Middleware: isOptedInToSimplifiedAccounts', () => { + let isOptedInToSimplifiedAccounts, req, res, next, invalidConfigurationError + + beforeEach(() => { + invalidConfigurationError = sinon.stub() + isOptedInToSimplifiedAccounts = proxyquire('./simplified-account-opt-in.middleware', { + '../../errors': { InvalidConfigurationError: invalidConfigurationError } + }) + + req = { + user: { + externalId: 'user-123', + isDegatewayed: sinon.stub() + } + } + res = {} + next = sinon.stub() + }) + + it('should call next() when user is opted in', () => { + req.user.isDegatewayed.returns(true) + + isOptedInToSimplifiedAccounts(req, res, next) + + expect(next.calledOnce).to.be.true // eslint-disable-line + expect(next.args[0]).to.be.empty // eslint-disable-line + }) + + it('should call next() with error when user is not opted in', () => { + req.user.isDegatewayed.returns(false) + + isOptedInToSimplifiedAccounts(req, res, next) + + expect(next.calledOnce).to.be.true // eslint-disable-line + expect(invalidConfigurationError.calledOnce).to.be.true // eslint-disable-line + expect(invalidConfigurationError.args[0][0]).to.equal( + 'User with id user-123 not opted in to account simplification or feature is disabled in this environment.' + ) + }) +}) diff --git a/app/middleware/simplified-account/simplified-account-strategy.middleware.js b/app/middleware/simplified-account/simplified-account-strategy.middleware.js index 8a3fcd93a..34d9e5d52 100644 --- a/app/middleware/simplified-account/simplified-account-strategy.middleware.js +++ b/app/middleware/simplified-account/simplified-account-strategy.middleware.js @@ -1,11 +1,11 @@ 'use strict' -const _ = require('lodash') const { keys } = require('@govuk-pay/pay-js-commons').logging const { addField } = require('../../services/clients/base/request-context') -const Connector = require('../../services/clients/connector.client.js').ConnectorClient const { getSwitchingCredentialIfExists } = require('../../utils/credentials') -const { SERVICE_EXTERNAL_ID, ACCOUNT_TYPE, ENVIRONMENT_ID } = require('../../paths').keys +const { SERVICE_EXTERNAL_ID, ACCOUNT_TYPE } = require('../../paths').keys +const _ = require('lodash') const logger = require('../../utils/logger')(__filename) +const Connector = require('../../services/clients/connector.client.js').ConnectorClient const connectorClient = new Connector(process.env.CONNECTOR_URL) function getService (user, serviceExternalId, gatewayAccountId) { @@ -76,9 +76,8 @@ module.exports = async function getSimplifiedAccount (req, res, next) { req.account = gatewayAccount addField(keys.GATEWAY_ACCOUNT_ID, gatewayAccount.gateway_account_id) addField(keys.GATEWAY_ACCOUNT_TYPE, gatewayAccount.type) - req.gateway_account = { - currentGatewayAccountExternalId: gatewayAccount.external_id - } + } else { + throw new Error('getGatewayAccountByServiceIdAndAccountType failed for provided parameters') } const service = getService(req.user, serviceExternalId, gatewayAccount.gateway_account_id) if (service) { diff --git a/app/middleware/simplified-account/simplified-account-strategy.middleware.test.js b/app/middleware/simplified-account/simplified-account-strategy.middleware.test.js new file mode 100644 index 000000000..57c78f2c9 --- /dev/null +++ b/app/middleware/simplified-account/simplified-account-strategy.middleware.test.js @@ -0,0 +1,211 @@ +'use strict' + +const { expect } = require('chai') +const path = require('path') +const proxyquire = require('proxyquire') +const sinon = require('sinon') +const User = require('../../models/User.class') +const userFixtures = require('../../../test/fixtures/user.fixtures') +const stripeAccountSetupFixture = require('../../../test/fixtures/stripe-account-setup.fixtures') + +const A_GATEWAY_EXTERNAL_ID = 'a-gateway-external-id' +const A_SERVICE_EXTERNAL_ID = 'a-service-external-id' + +let req, res, next + +const buildUser = (serviceExternalId, gatewayAccountIds) => { + return new User(userFixtures.validUserResponse({ + service_roles: [{ + service: { + external_id: serviceExternalId, + gateway_account_ids: gatewayAccountIds + } + }] + })) +} + +const setupSimplifiedAccountStrategyTest = function (options) { + const { + gatewayAccountID, + gatewayAccountExternalId, + paymentProvider, + serviceExternalId, + accountType, + errorCode + } = options + + req = { + params: { serviceExternalId, accountType }, + user: buildUser(serviceExternalId, [`${gatewayAccountID}`]) + } + next = sinon.spy() + + let connectorGetAccountMock + if (errorCode) { + connectorGetAccountMock = sinon.stub().rejects({ errorCode }) + } else { + connectorGetAccountMock = sinon.stub().resolves({ + gateway_account_id: gatewayAccountID, + external_id: gatewayAccountExternalId, + payment_provider: paymentProvider + }) + } + + const connectorMock = { + ConnectorClient: function () { + return { + getAccountByServiceIdAndAccountType: connectorGetAccountMock, + getStripeAccountSetup: paymentProvider === 'stripe' + ? sinon.stub().resolves(stripeAccountSetupFixture.buildGetStripeAccountSetupResponse()) + : undefined + } + } + } + + const simplifiedAccountStrategy = proxyquire( + path.join(__dirname, './simplified-account-strategy.middleware'), + { '../../services/clients/connector.client.js': connectorMock } + ) + + return { + simplifiedAccountStrategy, + connectorGetAccountMock + } +} + +describe('middleware: getSimplifiedAccount', () => { + it('should set gateway account and service on request object', async () => { + const { simplifiedAccountStrategy, connectorGetAccountMock } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider: 'worldpay', + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + + sinon.assert.calledOnce(next) + expect(connectorGetAccountMock.called).to.equal(true) + expect(connectorGetAccountMock.calledWith({ + serviceId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + })).to.equal(true) + + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + expect(req.service.externalId).to.equal(A_SERVICE_EXTERNAL_ID) + }) + it('should error if service external ID or gateway account type cannot be resolved from request parameters', async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider: 'worldpay', + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + req.params['serviceExternalId'] = undefined + req.params['accountType'] = undefined + + await simplifiedAccountStrategy(req, res, next) + + const expectedError = sinon.match.instanceOf(Error) + .and(sinon.match.has('message', 'Could not resolve service external ID or gateway account type from request params')) + sinon.assert.calledWith(next, expectedError) + }) + it('should error if gateway account lookup fails for account type', async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test', + errorCode: '404' + }) + await simplifiedAccountStrategy(req, res, next) + + const expectedError = sinon.match.instanceOf(Error) + .and(sinon.match.has('message', 'getGatewayAccountByServiceIdAndAccountType failed for provided parameters')) + sinon.assert.calledWith(next, expectedError) + }) + describe('extend gateway account data with disableToggle3ds field', () => { + ['worldpay', 'smartpay', 'epdq'].forEach(function (paymentProvider) { + it('should extend the account data with disableToggle3ds set to false if payment provider is ' + paymentProvider, async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider, + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + expect(req.account.disableToggle3ds).to.equal(false) + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + }) + }) + it('should extend the account data with disableToggle3ds set to true if payment provider is stripe', async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider: 'stripe', + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + expect(req.account.disableToggle3ds).to.equal(true) + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + }) + }) + describe('extend gateway account data with supports3ds field', () => { + ['worldpay', 'smartpay', 'epdq', 'stripe'].forEach(function (paymentProvider) { + it('should extend the account data with supports3ds set to true if payment provider is ' + paymentProvider, async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider, + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + expect(req.account.supports3ds).to.equal(true) + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + }) + }) + it('should extend the account data with supports3ds set to false if payment provider is sandbox', async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider: 'sandbox', + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + expect(req.account.supports3ds).to.equal(false) + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + }) + }) + describe('extend gateway account data stripe setup', () => { + ['worldpay', 'smartpay', 'epdq', 'sandbox'].forEach(function (paymentProvider) { + it('should not extend the account data with stripe setup if payment provider is ' + paymentProvider, async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider, + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + expect(req.account).to.not.have.property('connectorGatewayAccountStripeProgress') + }) + }) + it('should extend the account data with supports3ds set to false if payment provider is stripe', async () => { + const { simplifiedAccountStrategy } = setupSimplifiedAccountStrategyTest({ + gatewayAccountID: '1', + gatewayAccountExternalId: A_GATEWAY_EXTERNAL_ID, + paymentProvider: 'stripe', + serviceExternalId: A_SERVICE_EXTERNAL_ID, + accountType: 'test' + }) + await simplifiedAccountStrategy(req, res, next) + expect(req.account.external_id).to.equal(A_GATEWAY_EXTERNAL_ID) + expect(req.account).to.have.property('connectorGatewayAccountStripeProgress') + }) + }) +})