diff --git a/package-lock.json b/package-lock.json index dc41c3c8b..991ee422f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@companieshouse/api-sdk-node": "^2.0.209", + "@companieshouse/api-sdk-node": "^2.0.234", "@companieshouse/node-session-handler": "^5.0.1", "@companieshouse/structured-logging-node": "^2.0.1", "@companieshouse/web-security-node": "^4.1.1", @@ -592,9 +592,9 @@ } }, "node_modules/@companieshouse/api-sdk-node": { - "version": "2.0.209", - "resolved": "https://registry.npmjs.org/@companieshouse/api-sdk-node/-/api-sdk-node-2.0.209.tgz", - "integrity": "sha512-EX8gQiPYLQ24l+4VrNaHenrUiJzEWIyLIz9aXPqx0jw5uixAcf+Lo3DfF3dbvq7lehKkpep3yu340emDm64V9A==", + "version": "2.0.234", + "resolved": "https://registry.npmjs.org/@companieshouse/api-sdk-node/-/api-sdk-node-2.0.234.tgz", + "integrity": "sha512-+TohiJJr2q7pl8LacyWxVHq+S3FooJYdh9hr+oTr9HtoQ5aBmXhdIAUKRv+gYc2IpWjc32KHXrHE98pNihsoJA==", "dependencies": { "axios": "^1.7.4", "camelcase-keys": "~6.2.2", diff --git a/package.json b/package.json index 655d85174..4e194e01c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "license": "MIT", "homepage": "https://github.com/companieshouse/overseas-entities-web#readme", "dependencies": { - "@companieshouse/api-sdk-node": "^2.0.209", + "@companieshouse/api-sdk-node": "^2.0.234", "@companieshouse/node-session-handler": "^5.0.1", "@companieshouse/structured-logging-node": "^2.0.1", "@companieshouse/web-security-node": "^4.1.1", diff --git a/src/config/index.ts b/src/config/index.ts index 09d2c9406..4487a8df0 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -172,6 +172,7 @@ export const REVIEW_OWNER_INDEX_PARAM = "?index="; export const UPDATE_REVIEW_OWNERS_PARAMS = REVIEW_OWNER_INDEX_PARAM + ID; export const ROUTE_PARAM_TRANSACTION_ID = "transactionId"; export const ROUTE_PARAM_SUBMISSION_ID = "submissionId"; +export const ROUTE_PARAM_OVERSEAS_ENTITY_ID = "overseasEntityId"; export const LANDING_PAGE_QUERY_PARAM = "start"; export const JOURNEY_QUERY_PARAM = "journey"; export const PREVIOUS_PAGE_QUERY_PARAM = "previousPage"; @@ -327,7 +328,7 @@ export const TRANSACTION = "transaction"; export const OVERSEAS_ENTITY = "overseas-entity"; const TRANSACTION_PATH = TRANSACTION + "/:transactionId/"; -const OVERSEAS_ENTITY_PATH = OVERSEAS_ENTITY + "/:overseaEntityId/"; +const OVERSEAS_ENTITY_PATH = OVERSEAS_ENTITY + "/:overseasEntityId/"; export const PAYMENT_WITH_TRANSACTION_URL = REGISTER_AN_OVERSEAS_ENTITY_URL + TRANSACTION_PATH + OVERSEAS_ENTITY_PATH + PAYMENT; export const OVERSEAS_ENTITY_PAYMENT_WITH_TRANSACTION_URL = UPDATE_AN_OVERSEAS_ENTITY_URL + TRANSACTION_PATH + OVERSEAS_ENTITY_PATH + PAYMENT; diff --git a/src/controllers/beneficial.owner.delete.warning.controller.ts b/src/controllers/beneficial.owner.delete.warning.controller.ts index 18a2a5d5a..970cb9c26 100644 --- a/src/controllers/beneficial.owner.delete.warning.controller.ts +++ b/src/controllers/beneficial.owner.delete.warning.controller.ts @@ -1,31 +1,36 @@ import { NextFunction, Request, Response } from "express"; - +import { Session } from "@companieshouse/node-session-handler"; +import { createAndLogErrorRequest, logger } from "../utils/logger"; import * as config from "../config"; import { ApplicationData } from "../model"; +import { isActiveFeature } from "../utils/feature.flag"; +import { BeneficialOwnerGovKey } from "../model/beneficial.owner.gov.model"; +import { BeneficialOwnerIndividualKey } from "../model/beneficial.owner.individual.model"; +import { BeneficialOwnerOtherKey } from "../model/beneficial.owner.other.model"; +import { ManagingOfficerCorporateKey } from "../model/managing.officer.corporate.model"; +import { ManagingOfficerKey } from "../model/managing.officer.model"; +import { TrustKey } from "../model/trust.model"; +import { updateOverseasEntity } from "../service/overseas.entities.service"; + +import { getUrlWithParamsToPath, isRegistrationJourney } from "../utils/url"; + import { + setExtraData, + fetchApplicationData, checkBOsDetailsEntered, checkMOsDetailsEntered, - getApplicationData, - setExtraData } from "../utils/application.data"; -import { createAndLogErrorRequest, logger } from "../utils/logger"; import { BeneficialOwnersStatementType, BeneficialOwnersStatementTypes, BeneficialOwnerStatementKey } from "../model/beneficial.owner.statement.model"; -import { isActiveFeature } from "../utils/feature.flag"; -import { BeneficialOwnerGovKey } from "../model/beneficial.owner.gov.model"; -import { BeneficialOwnerIndividualKey } from "../model/beneficial.owner.individual.model"; -import { BeneficialOwnerOtherKey } from "../model/beneficial.owner.other.model"; -import { ManagingOfficerCorporateKey } from "../model/managing.officer.corporate.model"; -import { ManagingOfficerKey } from "../model/managing.officer.model"; -import { TrustKey } from "../model/trust.model"; -import { getUrlWithParamsToPath } from "../utils/url"; export const get = (req: Request, res: Response, next: NextFunction) => { + try { + logger.debugRequest(req, `GET ${config.BENEFICIAL_OWNER_DELETE_WARNING_PAGE}`); const beneficialOwnerStatement = req.query[BeneficialOwnerStatementKey] as string; @@ -46,30 +51,35 @@ export const get = (req: Request, res: Response, next: NextFunction) => { }; export const post = async (req: Request, res: Response, next: NextFunction): Promise => { + try { + logger.debugRequest(req, `POST ${config.BENEFICIAL_OWNER_DELETE_WARNING_PAGE}`); if (req.body["delete_beneficial_owners"] === '1') { const boStatement = req.body[BeneficialOwnerStatementKey]; - const appData: ApplicationData = await getApplicationData(req.session); + const isRegistration: boolean = isRegistrationJourney(req); + const appData: ApplicationData = await fetchApplicationData(req, isRegistration); - if ( boStatement === BeneficialOwnersStatementType.NONE_IDENTIFIED && checkBOsDetailsEntered(appData) ) { + if (boStatement === BeneficialOwnersStatementType.NONE_IDENTIFIED && checkBOsDetailsEntered(appData)) { appData[BeneficialOwnerIndividualKey] = []; appData[TrustKey] = []; appData[BeneficialOwnerOtherKey] = []; appData[BeneficialOwnerGovKey] = []; - } else if ( boStatement === BeneficialOwnersStatementType.ALL_IDENTIFIED_ALL_DETAILS && checkMOsDetailsEntered(appData) ) { + } else if (boStatement === BeneficialOwnersStatementType.ALL_IDENTIFIED_ALL_DETAILS && checkMOsDetailsEntered(appData)) { appData[ManagingOfficerKey] = []; appData[ManagingOfficerCorporateKey] = []; } appData[BeneficialOwnerStatementKey] = boStatement; - setExtraData(req.session, appData); - let nextPageUrl = config.BENEFICIAL_OWNER_TYPE_URL; - if (isActiveFeature(config.FEATURE_FLAG_ENABLE_REDIS_REMOVAL)) { - nextPageUrl = getUrlWithParamsToPath(config.BENEFICIAL_OWNER_TYPE_WITH_PARAMS_URL, req); + if (isActiveFeature(config.FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration) { + await updateOverseasEntity(req, req.session as Session, appData); } + setExtraData(req.session, appData); + const nextPageUrl = isActiveFeature(config.FEATURE_FLAG_ENABLE_REDIS_REMOVAL) + ? getUrlWithParamsToPath(config.BENEFICIAL_OWNER_TYPE_WITH_PARAMS_URL, req) + : config.BENEFICIAL_OWNER_TYPE_URL; return res.redirect(nextPageUrl); } diff --git a/src/controllers/check.your.answers.controller.ts b/src/controllers/check.your.answers.controller.ts index ebd8e9376..d3eb9f94d 100644 --- a/src/controllers/check.your.answers.controller.ts +++ b/src/controllers/check.your.answers.controller.ts @@ -81,7 +81,6 @@ export const post = async (req: Request, res: Response, next: NextFunction) => { logger.infoRequest(req, `Transaction Closed, ID: ${transactionID}`); const redirectPath = await startPaymentsSession(req, session, transactionID, overseasEntityID, transactionClosedResponse); logger.infoRequest(req, `Payments Session created with, Trans_ID: ${transactionID}, OE_ID: ${overseasEntityID}. Redirect to: ${redirectPath}`); - return res.redirect(redirectPath); } catch (error) { diff --git a/src/controllers/confirmation.controller.ts b/src/controllers/confirmation.controller.ts index 0166b8917..a751007d5 100644 --- a/src/controllers/confirmation.controller.ts +++ b/src/controllers/confirmation.controller.ts @@ -1,28 +1,36 @@ -import { Request, Response } from "express"; - +import { NextFunction, Request, Response } from "express"; import { logger } from "../utils/logger"; -import { CONFIRMATION_PAGE, PAYMENT_FEE } from "../config"; import { getLoggedInUserEmail } from "../utils/session"; -import { deleteApplicationData, getApplicationData } from "../utils/application.data"; import { ApplicationData } from "../model/application.model"; -import { Transactionkey } from "../model/data.types.model"; import { WhoIsRegisteringType } from "../model/who.is.making.filing.model"; +import { isRegistrationJourney } from "../utils/url"; +import { deleteApplicationData, fetchApplicationData } from "../utils/application.data"; +import { Transactionkey } from "../model/data.types.model"; +import { CONFIRMATION_PAGE, PAYMENT_FEE } from "../config"; + +export const get = async (req: Request, res: Response, next: NextFunction) => { + + try { -export const get = async (req: Request, res: Response) => { - logger.debugRequest(req, `GET ${CONFIRMATION_PAGE}`); + logger.debugRequest(req, `GET ${CONFIRMATION_PAGE}`); - const appData: ApplicationData = await getApplicationData(req.session); - const referenceNumber = appData[Transactionkey]; + const isRegistration: boolean = isRegistrationJourney(req); + const appData: ApplicationData = await fetchApplicationData(req, isRegistration, true); + const referenceNumber = appData[Transactionkey]; - deleteApplicationData(req.session); + deleteApplicationData(req.session); - return res.render(CONFIRMATION_PAGE, { - isAgentRegistering: appData.who_is_registering === WhoIsRegisteringType.AGENT, - referenceNumber, - entityEmail: appData.entity?.email, - userEmail: getLoggedInUserEmail(req.session), - verificationCheckDays: 14, - paymentFee: PAYMENT_FEE, - templateName: CONFIRMATION_PAGE - }); + return res.render(CONFIRMATION_PAGE, { + isAgentRegistering: appData.who_is_registering === WhoIsRegisteringType.AGENT, + referenceNumber, + entityEmail: appData.entity?.email, + userEmail: getLoggedInUserEmail(req.session), + verificationCheckDays: 14, + paymentFee: PAYMENT_FEE, + templateName: CONFIRMATION_PAGE + }); + } catch (error: any) { + logger.errorRequest(req, error); + next(error); + } }; diff --git a/src/controllers/payment.controller.ts b/src/controllers/payment.controller.ts index 823613b31..679037a28 100644 --- a/src/controllers/payment.controller.ts +++ b/src/controllers/payment.controller.ts @@ -1,7 +1,11 @@ import { NextFunction, Request, Response } from "express"; import { CreatePaymentRequest } from "@companieshouse/api-sdk-node/dist/services/payment"; - +import { isActiveFeature } from "../utils/feature.flag"; +import { ApplicationData } from "../model"; +import { fetchApplicationData } from "../utils/application.data"; import { logger, createAndLogErrorRequest } from "../utils/logger"; +import { OverseasEntityKey, PaymentKey } from "../model/data.types.model"; +import { getUrlWithParamsToPath, isRegistrationJourney } from "../utils/url"; import { CONFIRMATION_URL, CONFIRMATION_WITH_PARAMS_URL, @@ -10,47 +14,37 @@ import { PAYMENT_FAILED_WITH_PARAMS_URL, PAYMENT_PAID } from "../config"; -import { ApplicationData } from "../model"; -import { getApplicationData } from "../utils/application.data"; -import { OverseasEntityKey, PaymentKey } from "../model/data.types.model"; -import { isActiveFeature } from "../utils/feature.flag"; -import { getUrlWithParamsToPath } from "../utils/url"; // The Payment Platform will redirect the user's browser back to the `redirectUri` supplied when the payment session was created, // and this controller is dealing with the completion of the payment journey export const get = async (req: Request, res: Response, next: NextFunction): Promise => { + try { - const { status, state } = req.query; - const appData: ApplicationData = await getApplicationData(req.session); + const { status, state } = req.query; + const isRegistration: boolean = isRegistrationJourney(req); + const appData: ApplicationData = await fetchApplicationData(req, isRegistration); const savedPayment = appData[PaymentKey] || {} as CreatePaymentRequest; logger.infoRequest(req, `Returned state: ${ state }, saved state: ${savedPayment.state}, with status: ${ status }`); - // The application must ensure that the returned `state` matches the nonce // sent by the application to the Payment Platform. Protection against CSRF - if ( !savedPayment.state || savedPayment.state !== state) { + if (!savedPayment.state || savedPayment.state !== state) { return next(createAndLogErrorRequest(req, `Rejecting payment redirect, payment state does not match. Payment Request: ${ JSON.stringify(savedPayment)}`)); } - // Validate the status of the payment if (status === PAYMENT_PAID) { - let confirmationPageUrl = CONFIRMATION_URL; - if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL)) { - + if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration) { confirmationPageUrl = getUrlWithParamsToPath(CONFIRMATION_WITH_PARAMS_URL, req); } - logger.debugRequest(req, `Overseas Entity id: ${ appData[OverseasEntityKey] }, Payment status: ${status}, Redirecting to: ${confirmationPageUrl}`); - - // Payment Successful, redirect to confirmation page return res.redirect(confirmationPageUrl); } else { // Dealing with failures payment (User cancelled, Insufficient funds, Payment error ...) logger.debugRequest(req, `Overseas Entity id: ${ appData[OverseasEntityKey] }, Payment status: ${status}, Redirecting to: ${PAYMENT_FAILED_URL}`); let nextPageUrl = PAYMENT_FAILED_URL; - if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL)){ + if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration) { nextPageUrl = getUrlWithParamsToPath(PAYMENT_FAILED_WITH_PARAMS_URL, req); } return res.redirect(nextPageUrl); diff --git a/src/controllers/resume.submission.controller.ts b/src/controllers/resume.submission.controller.ts index 80027d2f9..adcdc134f 100644 --- a/src/controllers/resume.submission.controller.ts +++ b/src/controllers/resume.submission.controller.ts @@ -5,9 +5,9 @@ import { getResumePage } from "./shared/common.resume.submission.controller"; import { getUrlWithTransactionIdAndSubmissionId } from "../utils/url"; export const get = async (req: Request, res: Response, next: NextFunction) => { - const { transactionId, overseaEntityId } = req.params; + const { transactionId, overseasEntityId } = req.params; const resumeUrl: string = isActiveFeature(config.FEATURE_FLAG_ENABLE_REDIS_REMOVAL) - ? getUrlWithTransactionIdAndSubmissionId(config.SOLD_LAND_FILTER_WITH_PARAMS_URL, transactionId, overseaEntityId) + ? getUrlWithTransactionIdAndSubmissionId(config.SOLD_LAND_FILTER_WITH_PARAMS_URL, transactionId, overseasEntityId) : config.SOLD_LAND_FILTER_URL; await getResumePage(req, res, next, resumeUrl); }; diff --git a/src/controllers/shared/common.resume.submission.controller.ts b/src/controllers/shared/common.resume.submission.controller.ts index c2fe67c1f..2809d0e59 100644 --- a/src/controllers/shared/common.resume.submission.controller.ts +++ b/src/controllers/shared/common.resume.submission.controller.ts @@ -7,15 +7,15 @@ import { ApplicationData } from "../../model"; import { createAndLogErrorRequest, logger } from "../../utils/logger"; import { setExtraData } from "../../utils/application.data"; import { isActiveFeature } from "../../utils/feature.flag"; -import { getOverseasEntity } from "../../service/overseas.entities.service"; import { startPaymentsSession } from "../../service/payment.service"; import { getTransaction } from "../../service/transaction.service"; import { mapTrustApiReturnModelToWebModel } from "../../utils/trusts"; import { isRegistrationJourney } from "../../utils/url"; +import { DueDiligenceKey } from "../../model/due.diligence.model"; +import { OverseasEntityDueDiligenceKey } from "../../model/overseas.entity.due.diligence.model"; +import { getOverseasEntity, updateOverseasEntity } from "../../service/overseas.entities.service"; import { WhoIsRegisteringKey, WhoIsRegisteringType } from "../../model/who.is.making.filing.model"; -import { OverseasEntityDueDiligenceKey } from "../../model/overseas.entity.due.diligence.model"; -import { DueDiligenceKey } from "../../model/due.diligence.model"; import { BeneficialOwnerGov, BeneficialOwnerGovKey } from "../../model/beneficial.owner.gov.model"; import { BeneficialOwnerIndividual, BeneficialOwnerIndividualKey } from "../../model/beneficial.owner.individual.model"; import { BeneficialOwnerOther, BeneficialOwnerOtherKey } from "../../model/beneficial.owner.other.model"; @@ -24,32 +24,38 @@ import { ManagingOfficerIndividual, ManagingOfficerKey } from "../../model/manag import { ID, + Transactionkey, HasSoldLandKey, NonLegalFirmNoc, - IsSecureRegisterKey, OverseasEntityKey, - Transactionkey + IsSecureRegisterKey, } from "../../model/data.types.model"; export const getResumePage = async (req: Request, res: Response, next: NextFunction, resumePage: string) => { + try { + logger.debugRequest(req, `GET a saved OE submission`); - const { transactionId, overseaEntityId } = req.params; - const infoMsg = `Transaction ID: ${transactionId}, OverseasEntity ID: ${overseaEntityId}`; - const isRegistration: boolean = isRegistrationJourney(req); + // set missing request parameters used in the main journey + if (req.params[config.ROUTE_PARAM_SUBMISSION_ID]) { + req.params[config.ROUTE_PARAM_OVERSEAS_ENTITY_ID] = req.params[config.ROUTE_PARAM_SUBMISSION_ID]; + } else { + req.params[config.ROUTE_PARAM_SUBMISSION_ID] = req.params[config.ROUTE_PARAM_OVERSEAS_ENTITY_ID]; + } + const { transactionId, overseasEntityId } = req.params; + const infoMsg = `Transaction ID: ${transactionId}, OverseasEntity ID: ${overseasEntityId}`; + const isRegistration: boolean = isRegistrationJourney(req); logger.infoRequest(req, `Resuming OE - ${infoMsg}`); - - const appData: ApplicationData = await getOverseasEntity(req, transactionId, overseaEntityId); + const appData: ApplicationData = await getOverseasEntity(req, transactionId, overseasEntityId); if (!Object.keys(appData || {}).length) { throw createAndLogErrorRequest(req, `Error on resuming OE - ${infoMsg}`); } const session = req.session as Session; - setWebApplicationData(session, appData, transactionId, overseaEntityId, isRegistration); - + await setWebApplicationData(req, session, appData, transactionId, overseasEntityId, isRegistration); const transactionResource = await getTransaction(req, transactionId); if (transactionResource.status === config.CLOSED_PENDING_PAYMENT) { @@ -58,17 +64,14 @@ export const getResumePage = async (req: Request, res: Response, next: NextFunct [config.PAYMENT_REQUIRED_HEADER]: config.PAYMENTS_API_URL + config.PAYMENTS } }; - const baseURL = `${config.CHS_URL}${isRegistration ? config.REGISTER_AN_OVERSEAS_ENTITY_URL : config.UPDATE_AN_OVERSEAS_ENTITY_URL}`; - - const redirectPath = await startPaymentsSession(req, session, transactionId, overseaEntityId, headersPaymentUrl, baseURL); - - logger.infoRequest(req, `Payments Session created on Resume link with, Trans_ID: ${transactionId}, OE_ID: ${overseaEntityId}. Redirect to: ${redirectPath}`); - + const redirectPath = await startPaymentsSession(req, session, transactionId, overseasEntityId, headersPaymentUrl, baseURL); + logger.infoRequest(req, `Payments Session created on Resume link with, Trans_ID: ${transactionId}, OE_ID: ${overseasEntityId}. Redirect to: ${redirectPath}`); return res.redirect(redirectPath); } return res.redirect(resumePage); + } catch (error) { logger.errorRequest(req, error); next(error); @@ -83,9 +86,16 @@ export const getResumePage = async (req: Request, res: Response, next: NextFunct * @param session * @param appData * @param transactionId - * @param overseaEntityId + * @param overseasEntityId */ -const setWebApplicationData = (session: Session, appData: ApplicationData, transactionId: string, overseaEntityId: string, isRegistration: boolean) => { +const setWebApplicationData = async ( + req: Request, + session: Session, + appData: ApplicationData, + transactionId: string, + overseasEntityId: string, + isRegistration: boolean +) => { appData[BeneficialOwnerIndividualKey] = (appData[BeneficialOwnerIndividualKey] as BeneficialOwnerIndividual[]) .map(boi => { return { ...boi, [ID]: uuidv4() }; }); @@ -99,12 +109,8 @@ const setWebApplicationData = (session: Session, appData: ApplicationData, trans .map(moc => { return { ...moc, [ID]: uuidv4() }; }); if (!isRegistration || !isActiveFeature(config.FEATURE_FLAG_ENABLE_REDIS_REMOVAL)) { - appData[HasSoldLandKey] = '0'; appData[IsSecureRegisterKey] = '0'; - appData[Transactionkey] = transactionId; - appData[OverseasEntityKey] = overseaEntityId; - if (appData[OverseasEntityDueDiligenceKey] && Object.keys(appData[OverseasEntityDueDiligenceKey]).length) { appData[WhoIsRegisteringKey] = WhoIsRegisteringType.SOMEONE_ELSE; } else if (appData[DueDiligenceKey] && Object.keys(appData[DueDiligenceKey]).length) { @@ -112,6 +118,9 @@ const setWebApplicationData = (session: Session, appData: ApplicationData, trans } } + appData[Transactionkey] = transactionId; + appData[OverseasEntityKey] = overseasEntityId; + if (isActiveFeature(config.FEATURE_FLAG_ENABLE_TRUSTS_WEB)) { mapTrustApiReturnModelToWebModel(appData); } @@ -123,5 +132,9 @@ const setWebApplicationData = (session: Session, appData: ApplicationData, trans appData[BeneficialOwnerGovKey].forEach(bog => { delete bog[NonLegalFirmNoc]; }); } + if (isRegistration && isActiveFeature(config.FEATURE_FLAG_ENABLE_REDIS_REMOVAL)) { + await updateOverseasEntity(req, req.session as Session, appData, true); + } + setExtraData(session, appData); }; diff --git a/src/controllers/update/confirmation.controller.ts b/src/controllers/update/confirmation.controller.ts index b207b28e9..be94be090 100644 --- a/src/controllers/update/confirmation.controller.ts +++ b/src/controllers/update/confirmation.controller.ts @@ -1,21 +1,19 @@ import { NextFunction, Request, Response } from "express"; - import { logger } from "../../utils/logger"; -import { - CONFIRMATION_PAGE, - JourneyType -} from "../../config"; import { getLoggedInUserEmail } from "../../utils/session"; -import { deleteApplicationData, getApplicationData } from "../../utils/application.data"; import { ApplicationData } from "../../model/application.model"; import { Transactionkey } from "../../model/data.types.model"; import { WhoIsRegisteringType } from "../../model/who.is.making.filing.model"; import { isRemoveJourney } from "../../utils/url"; import { isNoChangeJourney } from "../../utils/update/no.change.journey"; +import { deleteApplicationData, getApplicationData } from "../../utils/application.data"; +import { CONFIRMATION_PAGE, JourneyType } from "../../config"; export const get = async (req: Request, res: Response, next: NextFunction) => { + try { - logger.debugRequest(req, `${req.method} ${req.route.path}`); + + logger.debugRequest(req, `${req?.method} ${req?.route?.path}`); const appData: ApplicationData = await getApplicationData(req.session); const referenceNumber = appData[Transactionkey]; diff --git a/src/service/overseas.entities.service.ts b/src/service/overseas.entities.service.ts index b53f3be38..6614fed85 100644 --- a/src/service/overseas.entities.service.ts +++ b/src/service/overseas.entities.service.ts @@ -37,7 +37,7 @@ export const createOverseasEntity = async ( return response.resource.id; }; -export const updateOverseasEntity = async (req: Request, session: Session, data?: ApplicationData) => { +export const updateOverseasEntity = async (req: Request, session: Session, data?: ApplicationData, forceUpdate: boolean = false) => { const appData: ApplicationData = data ?? await fetchApplicationData(req, isRegistrationJourney(req)); const transactionId = appData[Transactionkey] as string; @@ -52,7 +52,8 @@ export const updateOverseasEntity = async (req: Request, session: Session, data? session, transactionId, overseasEntityId, - appData + appData, + forceUpdate ); if (response.httpStatusCode !== 200) { @@ -66,7 +67,8 @@ export const updateOverseasEntity = async (req: Request, session: Session, data? export const getOverseasEntity = async ( req: Request, transactionId: string, - overseasEntityId: string + overseasEntityId: string, + forceFetch: boolean = false ): Promise => { logger.infoRequest(req, `Calling 'getOverseasEntity' for transaction id '${transactionId}' and overseas entity id '${overseasEntityId}'`); @@ -77,7 +79,8 @@ export const getOverseasEntity = async ( req, req.session as Session, transactionId, - overseasEntityId + overseasEntityId, + forceFetch ); if (response.httpStatusCode !== 200) { diff --git a/src/service/payment.service.ts b/src/service/payment.service.ts index e9984844c..91a1d5585 100644 --- a/src/service/payment.service.ts +++ b/src/service/payment.service.ts @@ -10,37 +10,36 @@ import { createOAuthApiClient } from "./api.service"; import { CreatePaymentRequest, Payment } from "@companieshouse/api-sdk-node/dist/services/payment"; import { - OverseasEntityKey, PaymentKey, Transactionkey, + OverseasEntityKey, } from "../model/data.types.model"; import { - fetchApplicationData, - setApplicationData, setExtraData, + setApplicationData, + fetchApplicationData, } from "../utils/application.data"; import { + isRegistrationJourney, getUrlWithParamsToPath, getUrlWithTransactionIdAndSubmissionId, - isRegistrationJourney, } from "../utils/url"; import { - PAYMENT_REQUIRED_HEADER, - REFERENCE, API_URL, CHS_URL, - REGISTER_AN_OVERSEAS_ENTITY_URL, + REFERENCE, PAYMENT, TRANSACTION, OVERSEAS_ENTITY, CONFIRMATION_URL, + PAYMENT_REQUIRED_HEADER, + ACTIVE_SUBMISSION_BASE_PATH, CONFIRMATION_WITH_PARAMS_URL, + REGISTER_AN_OVERSEAS_ENTITY_URL, FEATURE_FLAG_ENABLE_REDIS_REMOVAL, - ACTIVE_SUBMISSION_BASE_PATH, - LANDING_URL, } from "../config"; // If the transaction response is fee-bearing, a `X-Payment-Required` header will be received, @@ -56,32 +55,33 @@ export const startPaymentsSession = async ( ): Promise => { const isRegistration = isRegistrationJourney(req); - const appData: ApplicationData = await fetchApplicationData(req, isRegistration); - - setExtraData(session, { - ...appData, + const appData: ApplicationData = { + ...(await fetchApplicationData(req, isRegistration)), [Transactionkey]: transactionId, [OverseasEntityKey]: overseasEntityId - }); + }; + + setExtraData(session, appData); const paymentUrl = transactionRes.headers?.[PAYMENT_REQUIRED_HEADER]; if (!paymentUrl) { // Only if transaction does not have a fee let confirmationPageUrl = CONFIRMATION_URL; - - if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration){ + if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration) { confirmationPageUrl = getUrlWithParamsToPath(CONFIRMATION_WITH_PARAMS_URL, req); } - return confirmationPageUrl; } - const createPaymentRequest: CreatePaymentRequest = setPaymentRequest(transactionId, overseasEntityId, baseURL); - - // Save info into the session extra data field, including the state used as `nonce` against CSRF. - await setApplicationData(session, createPaymentRequest, PaymentKey); + const createPaymentRequest: CreatePaymentRequest = setPaymentRequest(req, transactionId, overseasEntityId, baseURL); + // Save app data including the state used as `nonce` against CSRF. + if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration) { + await setApplicationData(req, createPaymentRequest, PaymentKey); + } else { + await setApplicationData(session, createPaymentRequest, PaymentKey); + } // Create Payment Api Client by using the `paymentUrl` as baseURL const apiClient: ApiClient = createOAuthApiClient(session, paymentUrl); @@ -91,17 +91,16 @@ export const startPaymentsSession = async ( // Verify the state of the payment, success or failure (eg. cost not found, connection issues ...) if (paymentResult.isFailure()) { const errorResponse = paymentResult.value; - const msgErrorStatusCode = `http response status code=${ errorResponse?.httpStatusCode || "No Status Code found in response" }`; const msgErrorResponse = `http errors response=${ JSON.stringify(errorResponse?.errors || "No Errors found in response") }`; const msgError = `payment.service failure to create payment, ${msgErrorStatusCode}, ${msgErrorResponse}.`; throw createAndLogErrorRequest(req, msgError); + } else if (!paymentResult.value?.resource) { throw createAndLogErrorRequest(req, "No resource in payment response"); } else { const paymentResource: Payment = paymentResult.value.resource; - logger.infoRequest(req, `Create payment, status_code=${ paymentResult.value.httpStatusCode }, status=${ paymentResource.status }, links= ${ JSON.stringify(paymentResource.links ) } `); // To initiate the web journey through which the user will interact to make the payment @@ -109,7 +108,7 @@ export const startPaymentsSession = async ( } }; -const setPaymentRequest = (transactionId: string, overseasEntityId: string, baseURL?: string): CreatePaymentRequest => { +const setPaymentRequest = (req: Request, transactionId: string, overseasEntityId: string, baseURL?: string): CreatePaymentRequest => { const paymentResourceUri = `${API_URL}/transactions/${transactionId}/${PAYMENT}`; @@ -124,7 +123,7 @@ const setPaymentRequest = (transactionId: string, overseasEntityId: string, base let redirectUri = `${baseURL}${TRANSACTION}/${transactionId}/${OVERSEAS_ENTITY}/${overseasEntityId}/${PAYMENT}`; // TODO Remove this and the check for being on the registration journey when ids are in the Update journey URLs - const isRegistration: boolean = baseURL.includes(LANDING_URL); + const isRegistration = isRegistrationJourney(req); if (isActiveFeature(FEATURE_FLAG_ENABLE_REDIS_REMOVAL) && isRegistration) { const activeSubmissionBasePathWithIds = getUrlWithTransactionIdAndSubmissionId(ACTIVE_SUBMISSION_BASE_PATH, transactionId, overseasEntityId); diff --git a/src/utils/application.data.ts b/src/utils/application.data.ts index f0aed0986..31f5aee4d 100644 --- a/src/utils/application.data.ts +++ b/src/utils/application.data.ts @@ -25,6 +25,7 @@ import { TrusteesNoc, OverseasEntityKey, Transactionkey, + PaymentKey, } from '../model/data.types.model'; import { @@ -49,15 +50,15 @@ import { /** * @todo: remove this method after REDIS removal has been implemented for the Update/Remove journeys (ROE-2645) */ -export const fetchApplicationData = (req: Request, isRegistration: boolean): Promise => { +export const fetchApplicationData = (req: Request, isRegistration: boolean, forceFetch: boolean = false): Promise => { if (isRegistration) { - return getApplicationData(req); + return getApplicationData(req, forceFetch); } else { return getApplicationData(req.session); } }; -export const getApplicationData = async (sessionOrRequest: Session | Request | undefined): Promise => { +export const getApplicationData = async (sessionOrRequest: Session | Request | undefined, forceFetch: boolean = false): Promise => { const emptyAppData = {}; let req: Request; @@ -83,7 +84,7 @@ export const getApplicationData = async (sessionOrRequest: Session | Request | u return emptyAppData; } - const appData = await getOverseasEntity(req, transactionId, submissionId); + const appData = await getOverseasEntity(req, transactionId, submissionId, forceFetch); appData[Transactionkey] = transactionId; appData[OverseasEntityKey] = submissionId; @@ -129,7 +130,9 @@ export const setApplicationData = async (sessionOrRequest: Request | Session | u } setExtraData(req?.session as Session, appData); - return updateOverseasEntity(req as Request, req?.session as Session, appData); + + const forceUpdate: boolean = key === PaymentKey; + return updateOverseasEntity(req as Request, req?.session as Session, appData, forceUpdate); } catch (e) { logger.error(`Error setting application data, with error object: ${e}`); diff --git a/src/utils/check.your.answers.ts b/src/utils/check.your.answers.ts index 499d3a033..1d67815f9 100644 --- a/src/utils/check.your.answers.ts +++ b/src/utils/check.your.answers.ts @@ -1,10 +1,8 @@ import { NextFunction, Request, Response } from "express"; import { Session } from "@companieshouse/node-session-handler"; - import { logger } from "../utils/logger"; import { ApplicationData } from "../model"; import { isActiveFeature } from "../utils/feature.flag"; -import { OverseasEntityKey, Transactionkey } from "../model/data.types.model"; import { closeTransaction } from "../service/transaction.service"; import { startPaymentsSession } from "../service/payment.service"; import { fetchOverseasEntityEmailAddress } from "./update/fetch.overseas.entity.email"; @@ -16,31 +14,32 @@ import { checkRPStatementsExist } from "./relevant.period"; import { fetchApplicationData } from "../utils/application.data"; import { isRegistrationJourney, isRemoveJourney } from "./url"; +import { OverseasEntityKey, Transactionkey } from "../model/data.types.model"; import { checkEntityRequiresTrusts, checkEntityReviewRequiresTrusts } from "./trusts"; import { - CHS_URL, - JourneyType, + OVERSEAS_ENTITY_UPDATE_DETAILS_URL, + OVERSEAS_ENTITY_SECTION_HEADING, WHO_IS_MAKING_UPDATE_URL, UPDATE_AN_OVERSEAS_ENTITY_URL, + CHS_URL, + UPDATE_DO_YOU_WANT_TO_MAKE_OE_CHANGE_URL, FEATURE_FLAG_ENABLE_TRUSTS_WEB, UPDATE_CHECK_YOUR_ANSWERS_PAGE, UPDATE_REVIEW_STATEMENT_PAGE, - REMOVE_CONFIRM_STATEMENT_URL, - OVERSEAS_ENTITY_SECTION_HEADING, - OVERSEAS_ENTITY_UPDATE_DETAILS_URL, UPDATE_REGISTRABLE_BENEFICIAL_OWNER_URL, - UPDATE_DO_YOU_WANT_TO_MAKE_OE_CHANGE_URL, - FEATURE_FLAG_ENABLE_PROPERTY_OR_LAND_OWNER_NOC, UPDATE_NO_CHANGE_REGISTRABLE_BENEFICIAL_OWNER_URL, + JourneyType, + REMOVE_CONFIRM_STATEMENT_URL, + FEATURE_FLAG_ENABLE_PROPERTY_OR_LAND_OWNER_NOC } from "../config"; export const getDataForReview = async (req: Request, res: Response, next: NextFunction, isNoChangeJourney: boolean) => { const session = req.session as Session; - const isRemove: boolean = await isRemoveJourney(req); - const isRegistration = isRegistrationJourney(req); + const isRegistration: boolean = isRegistrationJourney(req); const appData: ApplicationData = await fetchApplicationData(req, isRegistration); + const isRemove: boolean = await isRemoveJourney(req); const hasAnyBosWithTrusteeNocs = isNoChangeJourney ? checkEntityReviewRequiresTrusts(appData) : checkEntityRequiresTrusts(appData); const backLinkUrl = getBackLinkUrl(isNoChangeJourney, hasAnyBosWithTrusteeNocs, isRemove); const templateName = getTemplateName(isNoChangeJourney); @@ -109,7 +108,7 @@ export const postDataForReview = async (req: Request, res: Response, next: NextF logger.debugRequest(req, `${req.method} ${req.route.path}`); const session = req.session as Session; - const isRegistration = isRegistrationJourney(req); + const isRegistration: boolean = isRegistrationJourney(req); const appData: ApplicationData = await fetchApplicationData(req, isRegistration); const noChangeReviewStatement = req.body["no_change_review_statement"]; @@ -120,9 +119,10 @@ export const postDataForReview = async (req: Request, res: Response, next: NextF const transactionID = appData[Transactionkey] as string; const overseasEntityID = appData[OverseasEntityKey] as string; const transactionClosedResponse = await closeTransaction(req, session, transactionID, overseasEntityID); + logger.infoRequest(req, `Transaction Closed, ID: ${transactionID}`); - const baseURL = `${CHS_URL}${UPDATE_AN_OVERSEAS_ENTITY_URL}`; + const baseURL = `${CHS_URL}${UPDATE_AN_OVERSEAS_ENTITY_URL}`; const redirectPath = await startPaymentsSession( req, session, @@ -135,7 +135,6 @@ export const postDataForReview = async (req: Request, res: Response, next: NextF logger.infoRequest(req, `Payments Session created with, Trans_ID: ${transactionID}, OE_ID: ${overseasEntityID}. Redirect to: ${redirectPath}`); return res.redirect(redirectPath); - } catch (error) { logger.errorRequest(req, error); next(error); @@ -160,6 +159,7 @@ const getBackLinkUrl = (isNoChangeJourney: boolean, hasAnyBosWithTrusteeNocs: bo }; const getTemplateName = (isNoChangeJourney: boolean) => ( - isNoChangeJourney ? UPDATE_REVIEW_STATEMENT_PAGE : UPDATE_CHECK_YOUR_ANSWERS_PAGE + isNoChangeJourney + ? UPDATE_REVIEW_STATEMENT_PAGE + : UPDATE_CHECK_YOUR_ANSWERS_PAGE ); - diff --git a/test/__mocks__/session.mock.ts b/test/__mocks__/session.mock.ts index f1097a199..d125ba131 100644 --- a/test/__mocks__/session.mock.ts +++ b/test/__mocks__/session.mock.ts @@ -74,18 +74,17 @@ export const MO_IND_ID = "0dccbd5e-cc09-4f8f-828e-f7cc9fc352ac"; export const MO_IND_ID_URL = "/" + MO_IND_ID; export const MO_CORP_ID = "2df18e59-74dd-42d7-9494-8d40b953ddbe"; export const MO_CORP_ID_URL = "/" + MO_CORP_ID; - export const COMPANY_NAME = "my company name"; export const COMPANY_NUMBER = "SA000392"; - export const EMAIL_ADDRESS = "user@domain.roe"; export const PUBLIC_REGISTER_NAME = "publicRegister"; export const PUBLIC_REGISTER_JURISDICTION = "jurisdiction"; export const REGISTRATION_NUMBER = "123"; - export const ERROR: Error = new Error(ANY_MESSAGE_ERROR); export const TRANSACTION_ID = "038138-572616-526863"; export const OVERSEAS_ENTITY_ID = "6281fe0790bdb128549bf09f"; +export const FORCE_UPDATE = false; +export const FORCE_FETCH = false; export const TRANSACTION = { id: TRANSACTION_ID, reference: `${REFERENCE}_${TRANSACTION_ID}`, diff --git a/test/controllers/beneficial.owner.delete.warning.controller.spec.ts b/test/controllers/beneficial.owner.delete.warning.controller.spec.ts index 3fb2f3c18..ab06c4657 100644 --- a/test/controllers/beneficial.owner.delete.warning.controller.spec.ts +++ b/test/controllers/beneficial.owner.delete.warning.controller.spec.ts @@ -5,46 +5,50 @@ jest.mock('../../src/middleware/navigation/has.beneficial.owners.statement.middl jest.mock('../../src/utils/feature.flag'); jest.mock('../../src/middleware/service.availability.middleware'); jest.mock("../../src/utils/url"); - -import mockCsrfProtectionMiddleware from "../__mocks__/csrfProtectionMiddleware.mock"; -import { describe, expect, test, beforeEach, jest } from "@jest/globals"; +jest.mock('../../src/service/overseas.entities.service'); import request from "supertest"; import { NextFunction, Request, Response } from "express"; +import mockCsrfProtectionMiddleware from "../__mocks__/csrfProtectionMiddleware.mock"; import app from "../../src/app"; -import * as config from "../../src/config"; +import * as config from "../../src/config"; import { hasBeneficialOwnersStatement } from "../../src/middleware/navigation/has.beneficial.owners.statement.middleware"; import { authentication } from "../../src/middleware/authentication.middleware"; +import { ErrorMessages } from "../../src/validation/error.messages"; +import { ManagingOfficerKey } from "../../src/model/managing.officer.model"; +import { ManagingOfficerCorporateKey } from "../../src/model/managing.officer.corporate.model"; +import { BeneficialOwnerGovKey } from "../../src/model/beneficial.owner.gov.model"; +import { BeneficialOwnerIndividualKey } from "../../src/model/beneficial.owner.individual.model"; +import { BeneficialOwnerOtherKey } from "../../src/model/beneficial.owner.other.model"; +import { TrustKey } from "../../src/model/trust.model"; +import { isActiveFeature } from "../../src/utils/feature.flag"; +import { updateOverseasEntity } from "../../src/service/overseas.entities.service"; +import { serviceAvailabilityMiddleware } from "../../src/middleware/service.availability.middleware"; + +import { getUrlWithParamsToPath, isRegistrationJourney } from "../../src/utils/url"; +import { APPLICATION_DATA_MOCK, ERROR } from "../__mocks__/session.mock"; + import { - getApplicationData, checkBOsDetailsEntered, checkMOsDetailsEntered, - setExtraData + setExtraData, + fetchApplicationData, } from "../../src/utils/application.data"; + import { BeneficialOwnersStatementType, BeneficialOwnersStatementTypes, BeneficialOwnerStatementKey, } from "../../src/model/beneficial.owner.statement.model"; + import { BACK_BUTTON_CLASS, BENEFICIAL_OWNER_DELETE_WARNING_PAGE_HEADING, PAGE_TITLE_ERROR, SERVICE_UNAVAILABLE, } from "../__mocks__/text.mock"; -import { APPLICATION_DATA_MOCK, ERROR } from "../__mocks__/session.mock"; -import { ErrorMessages } from "../../src/validation/error.messages"; -import { ManagingOfficerKey } from "../../src/model/managing.officer.model"; -import { ManagingOfficerCorporateKey } from "../../src/model/managing.officer.corporate.model"; -import { BeneficialOwnerGovKey } from "../../src/model/beneficial.owner.gov.model"; -import { BeneficialOwnerIndividualKey } from "../../src/model/beneficial.owner.individual.model"; -import { BeneficialOwnerOtherKey } from "../../src/model/beneficial.owner.other.model"; -import { TrustKey } from "../../src/model/trust.model"; -import { isActiveFeature } from "../../src/utils/feature.flag"; -import { serviceAvailabilityMiddleware } from "../../src/middleware/service.availability.middleware"; -import { getUrlWithParamsToPath } from "../../src/utils/url"; mockCsrfProtectionMiddleware.mockClear(); const mockHasBeneficialOwnersStatementMiddleware = hasBeneficialOwnersStatement as jest.Mock; @@ -54,7 +58,7 @@ const mockAuthenticationMiddleware = authentication as jest.Mock; mockAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); const mockSetExtraData = setExtraData as jest.Mock; -const mockGetApplicationData = getApplicationData as jest.Mock; +const mockFetchApplicationData = fetchApplicationData as jest.Mock; const mockCheckBOsDetailsEntered = checkBOsDetailsEntered as jest.Mock; const mockCheckMOsDetailsEntered = checkMOsDetailsEntered as jest.Mock; @@ -62,6 +66,7 @@ const boDeleteWarningURL = `${config.BENEFICIAL_OWNER_DELETE_WARNING_URL}?${Bene const boDeleteWarningWithParamsURL = `${config.BENEFICIAL_OWNER_DELETE_WARNING_WITH_PARAMS_URL}?${BeneficialOwnerStatementKey}=`; const mockIsActiveFeature = isActiveFeature as jest.Mock; +const mockUpdateOverseasEntity = updateOverseasEntity as jest.Mock; const mockServiceAvailabilityMiddleware = serviceAvailabilityMiddleware as jest.Mock; mockServiceAvailabilityMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); @@ -70,6 +75,9 @@ const NEXT_PAGE_URL = "/NEXT_PAGE"; const mockGetUrlWithParamsToPath = getUrlWithParamsToPath as jest.Mock; mockGetUrlWithParamsToPath.mockReturnValue(NEXT_PAGE_URL); +const mockIsRegistrationJourney = isRegistrationJourney as jest.Mock; +mockIsRegistrationJourney.mockReturnValue(true); + const req = {} as Request; describe("BENEFICIAL OWNER DELETE WARNING controller", () => { @@ -77,10 +85,12 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { beforeEach(() => { jest.clearAllMocks(); mockIsActiveFeature.mockReset(); + mockUpdateOverseasEntity.mockReset(); process.env.FEATURE_FLAG_ENABLE_REDIS_REMOVAL_27092023 = "false"; }); describe("GET tests", () => { + BeneficialOwnersStatementTypes.forEach(boStatementType => { test(`renders the ${config.BENEFICIAL_OWNER_DELETE_WARNING_PAGE} page if query param value is ${boStatementType}`, async () => { const resp = await request(app).get(`${boDeleteWarningURL}${boStatementType}`); @@ -95,14 +105,12 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { test(`throw error and redirect to the service is unavailable page if key of the query param is not correct`, async () => { const resp = await request(app).get(`${config.BENEFICIAL_OWNER_DELETE_WARNING_URL}?any=${BeneficialOwnersStatementType.ALL_IDENTIFIED_ALL_DETAILS}`); - expect(resp.status).toEqual(500); expect(resp.text).toContain(SERVICE_UNAVAILABLE); }); test(`throw error and redirect to the service is unavailable page if value in the query param is not correct`, async () => { const resp = await request(app).get(`${boDeleteWarningURL}"=*0"`); - expect(resp.status).toEqual(500); expect(resp.text).toContain(SERVICE_UNAVAILABLE); }); @@ -123,7 +131,6 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { test(`throw error and redirect to the service is unavailable page if key of the query param is not correct`, async () => { const resp = await request(app).get(`${config.BENEFICIAL_OWNER_DELETE_WARNING_WITH_PARAMS_URL}?any=${BeneficialOwnersStatementType.ALL_IDENTIFIED_ALL_DETAILS}`); - expect(resp.status).toEqual(500); expect(resp.text).toContain(SERVICE_UNAVAILABLE); }); @@ -147,12 +154,35 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { expect(mockSetExtraData).not.toHaveBeenCalled(); expect(mockCheckBOsDetailsEntered).not.toHaveBeenCalled(); expect(mockCheckMOsDetailsEntered).not.toHaveBeenCalled(); + expect(mockUpdateOverseasEntity).not.toHaveBeenCalled(); }); - test(`redirects to the beneficial owner type page if Yes option has been selected and + test(`redirects to the beneficial owner type page if Yes option has been selected and REDIS_removal flag is set to ON and ${BeneficialOwnersStatementType.SOME_IDENTIFIED_ALL_DETAILS} as statement type`, async () => { - mockGetApplicationData.mockReturnValueOnce(APPLICATION_DATA_MOCK); + mockFetchApplicationData.mockReturnValueOnce(APPLICATION_DATA_MOCK); + mockIsActiveFeature.mockReturnValueOnce(true); + mockIsActiveFeature.mockReturnValueOnce(true); + + const resp = await request(app) + .post(config.BENEFICIAL_OWNER_DELETE_WARNING_URL) + .send({ + delete_beneficial_owners: "1", + BeneficialOwnerStatementKey: BeneficialOwnersStatementType.SOME_IDENTIFIED_ALL_DETAILS + }); + + expect(resp.status).toEqual(302); + expect(mockSetExtraData).toHaveBeenCalledTimes(1); + expect(resp.header.location).toEqual(NEXT_PAGE_URL); + expect(mockUpdateOverseasEntity).toBeCalledTimes(1); + }); + + test(`redirects to the beneficial owner type page if Yes option has been selected and REDIS_removal flag is set to OFF and + ${BeneficialOwnersStatementType.SOME_IDENTIFIED_ALL_DETAILS} as statement type`, async () => { + + mockFetchApplicationData.mockReturnValueOnce(APPLICATION_DATA_MOCK); + mockIsActiveFeature.mockReturnValueOnce(false); + mockIsActiveFeature.mockReturnValueOnce(false); const resp = await request(app) .post(config.BENEFICIAL_OWNER_DELETE_WARNING_URL) @@ -164,6 +194,7 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { expect(resp.status).toEqual(302); expect(mockSetExtraData).toHaveBeenCalledTimes(1); expect(resp.header.location).toEqual(config.BENEFICIAL_OWNER_TYPE_URL); + expect(mockUpdateOverseasEntity).not.toHaveBeenCalled(); }); test("redirects to the beneficial owner type page after resetting all BOs object", async () => { @@ -173,7 +204,7 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { [BeneficialOwnerGovKey]: APPLICATION_DATA_MOCK.beneficial_owners_government_or_public_authority, [TrustKey]: APPLICATION_DATA_MOCK.trusts, }; - mockGetApplicationData.mockReturnValueOnce(appData); + mockFetchApplicationData.mockReturnValueOnce(appData); mockCheckBOsDetailsEntered.mockReturnValueOnce(true); const resp = await request(app) @@ -200,7 +231,7 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { [ManagingOfficerKey]: APPLICATION_DATA_MOCK.managing_officers_individual, [ManagingOfficerCorporateKey]: APPLICATION_DATA_MOCK.managing_officers_corporate }; - mockGetApplicationData.mockReturnValueOnce(appData); + mockFetchApplicationData.mockReturnValueOnce(appData); mockCheckMOsDetailsEntered.mockReturnValueOnce(true); const resp = await request(app) @@ -238,7 +269,7 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { }); test("catch error when posting data", async () => { - mockGetApplicationData.mockImplementationOnce(() => { throw ERROR; }); + mockFetchApplicationData.mockImplementationOnce(() => { throw ERROR; }); const resp = await request(app) .post(config.BENEFICIAL_OWNER_DELETE_WARNING_URL) .send({ delete_beneficial_owners: "1" }); @@ -264,7 +295,8 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { ${BeneficialOwnersStatementType.SOME_IDENTIFIED_ALL_DETAILS} as statement type`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL - mockGetApplicationData.mockReturnValueOnce(APPLICATION_DATA_MOCK); + mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockFetchApplicationData.mockReturnValueOnce(APPLICATION_DATA_MOCK); const resp = await request(app) .post(config.BENEFICIAL_OWNER_DELETE_WARNING_WITH_PARAMS_URL) @@ -282,6 +314,7 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { }); test("redirects to the beneficial owner type page after resetting all BOs object", async () => { + mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL const appData = { [BeneficialOwnerIndividualKey]: APPLICATION_DATA_MOCK.beneficial_owners_individual, @@ -289,7 +322,7 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { [BeneficialOwnerGovKey]: APPLICATION_DATA_MOCK.beneficial_owners_government_or_public_authority, [TrustKey]: APPLICATION_DATA_MOCK.trusts, }; - mockGetApplicationData.mockReturnValueOnce(appData); + mockFetchApplicationData.mockReturnValueOnce(appData); mockCheckBOsDetailsEntered.mockReturnValueOnce(true); const resp = await request(app) @@ -315,12 +348,13 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { }); test("redirects to the beneficial owner type page after resetting all MOs object", async () => { + mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL const appData = { [ManagingOfficerKey]: APPLICATION_DATA_MOCK.managing_officers_individual, [ManagingOfficerCorporateKey]: APPLICATION_DATA_MOCK.managing_officers_corporate }; - mockGetApplicationData.mockReturnValueOnce(appData); + mockFetchApplicationData.mockReturnValueOnce(appData); mockCheckMOsDetailsEntered.mockReturnValueOnce(true); const resp = await request(app) @@ -356,13 +390,12 @@ describe("BENEFICIAL OWNER DELETE WARNING controller", () => { test(`POST empty object and check for error in page title`, async () => { const resp = await request(app).post(config.BENEFICIAL_OWNER_DELETE_WARNING_WITH_PARAMS_URL); - expect(resp.status).toEqual(200); expect(resp.text).toContain(PAGE_TITLE_ERROR); }); test("catch error when posting data", async () => { - mockGetApplicationData.mockImplementationOnce(() => { throw ERROR; }); + mockFetchApplicationData.mockImplementationOnce(() => { throw ERROR; }); const resp = await request(app) .post(config.BENEFICIAL_OWNER_DELETE_WARNING_WITH_PARAMS_URL) .send({ delete_beneficial_owners: "1" }); diff --git a/test/controllers/confirmation.controller.spec.ts b/test/controllers/confirmation.controller.spec.ts index 6a0fcf2be..b90ce7240 100644 --- a/test/controllers/confirmation.controller.spec.ts +++ b/test/controllers/confirmation.controller.spec.ts @@ -3,22 +3,49 @@ jest.mock('../../src/middleware/authentication.middleware'); jest.mock('../../src/utils/application.data'); jest.mock('../../src/middleware/navigation/has.beneficial.owners.or.managing.officers.middleware'); jest.mock('../../src/utils/session'); +jest.mock('../../src/utils/feature.flag'); +jest.mock('../../src/utils/url'); +import { NextFunction, Request, Response } from "express"; import request from "supertest"; -import { describe, expect, jest, test, beforeEach } from "@jest/globals"; import { authentication } from "../../src/middleware/authentication.middleware"; -import { NextFunction, Request, Response } from "express"; - import app from "../../src/app"; -import { CONFIRMATION_URL, PAYMENT_FEE } from "../../src/config"; -import { CONFIRMATION_PAGE_TITLE, CONFIRMATION_NUMBER_OF_DAYS, CONFIRMATION_WHAT_YOU_NEED_TO_DO_NOW, CONFIRMATION_AGENT_MUST_THEN_COMPLETE_TEXT, CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT, CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT } from "../__mocks__/text.mock"; -import { deleteApplicationData, getApplicationData } from '../../src/utils/application.data'; -import { APPLICATION_DATA_MOCK, ENTITY_OBJECT_MOCK, getSessionRequestWithExtraData, TRANSACTION_ID, userMail } from "../__mocks__/session.mock"; +import * as config from "../../src/config"; import { get } from "../../src/controllers/confirmation.controller"; import { hasBOsOrMOs } from "../../src/middleware/navigation/has.beneficial.owners.or.managing.officers.middleware"; import { WhoIsRegisteringType } from "../../src/model/who.is.making.filing.model"; import { getLoggedInUserEmail } from "../../src/utils/session"; -import * as config from "../../src/config"; +import { isActiveFeature } from "../../src/utils/feature.flag"; +import { isRegistrationJourney } from "../../src/utils/url"; + +import { + deleteApplicationData, + fetchApplicationData, + getApplicationData +} from '../../src/utils/application.data'; + +import { + PAYMENT_FEE, + CONFIRMATION_URL, + CONFIRMATION_WITH_PARAMS_URL, +} from "../../src/config"; + +import { + CONFIRMATION_PAGE_TITLE, + CONFIRMATION_NUMBER_OF_DAYS, + CONFIRMATION_WHAT_YOU_NEED_TO_DO_NOW, + CONFIRMATION_AGENT_MUST_THEN_COMPLETE_TEXT, + CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT, + CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT +} from "../__mocks__/text.mock"; + +import { + userMail, + TRANSACTION_ID, + ENTITY_OBJECT_MOCK, + APPLICATION_DATA_MOCK, + getSessionRequestWithExtraData, +} from "../__mocks__/session.mock"; const mockHasBOsOrMOsMiddleware = hasBOsOrMOs as jest.Mock; mockHasBOsOrMOsMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); @@ -26,9 +53,13 @@ mockHasBOsOrMOsMiddleware.mockImplementation((req: Request, res: Response, next: const mockAuthenticationMiddleware = authentication as jest.Mock; mockAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); -const mockGetApplicationData = getApplicationData as jest.Mock; +const mockFetchApplicationData = fetchApplicationData as jest.Mock; const mockDeleteApplicationData = deleteApplicationData as jest.Mock; const mockGetLoggedInUserEmail = getLoggedInUserEmail as jest.Mock; +const mockIsActiveFeature = isActiveFeature as jest.Mock; + +const mockIsRegistrationJourney = isRegistrationJourney as jest.Mock; +mockIsRegistrationJourney.mockReturnValue(true); const req = {} as Request; const res = { render: jest.fn() as any } as Response; @@ -43,14 +74,17 @@ describe("Confirmation controller tests", () => { beforeEach(() => { jest.clearAllMocks(); mockGetLoggedInUserEmail.mockReturnValue(userMail); + mockIsRegistrationJourney.mockReset(); + mockIsActiveFeature.mockReset(); }); - test("renders the confirmation page for non agent", async () => { - mockGetApplicationData.mockReturnValue( - { ...APPLICATION_DATA_MOCK, - who_is_registering: WhoIsRegisteringType.SOMEONE_ELSE - } - ); + test("renders the confirmation page for non agent if the REDIS_removal flag is set to OFF", async () => { + mockIsActiveFeature.mockReturnValueOnce(false); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(false); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockFetchApplicationData.mockReturnValue({ + ...APPLICATION_DATA_MOCK, + who_is_registering: WhoIsRegisteringType.SOMEONE_ELSE, + }); const resp = await request(app).get(CONFIRMATION_URL); expect(resp.status).toEqual(200); @@ -69,16 +103,48 @@ describe("Confirmation controller tests", () => { expect(resp.text).toContain(SURVEY_LINK); expect(resp.text).toContain(CONFIRMATION_AGENT_MUST_THEN_COMPLETE_TEXT); expect(resp.text).toContain(CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT); + expect(resp.text).not.toContain(CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT); + expect(mockIsRegistrationJourney).toHaveBeenCalledTimes(2); + expect(mockIsActiveFeature).toHaveBeenCalledTimes(1); + }); + + test("renders the confirmation page for non agent if the REDIS_removal flag is set to ON", async () => { + mockIsRegistrationJourney.mockReturnValue(true); + mockIsActiveFeature.mockReturnValueOnce(false); + mockIsActiveFeature.mockReturnValueOnce(true); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(true); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockFetchApplicationData.mockReturnValue({ + ...APPLICATION_DATA_MOCK, + who_is_registering: WhoIsRegisteringType.SOMEONE_ELSE, + }); + const resp = await request(app).get(CONFIRMATION_WITH_PARAMS_URL); + expect(resp.status).toEqual(200); + expect(resp.text).toContain(CONFIRMATION_PAGE_TITLE); + expect(resp.text).toContain(TRANSACTION_ID); + expect(resp.text).toContain(CONFIRMATION_NUMBER_OF_DAYS); + expect(resp.text).toContain(CONFIRMATION_WHAT_YOU_NEED_TO_DO_NOW); + expect(resp.text).toContain(ENTITY_OBJECT_MOCK.email); + expect(resp.text).toContain(`£${PAYMENT_FEE}`); + expect(resp.text).toContain(userMail); + expect(resp.text).toContain(config.VF01_FORM_DOWNLOAD_URL); + expect(mockDeleteApplicationData).toHaveBeenCalledTimes(1); + expect(resp.text).toContain(APPLICATION_TO_REGISTER_TEXT); + expect(resp.text).toContain(NOTICE_OF_REGISTRATION_TEXT); + expect(resp.text).toContain(REGISTRATION_FEE_TEXT); + expect(resp.text).toContain(SURVEY_LINK); + expect(resp.text).toContain(CONFIRMATION_AGENT_MUST_THEN_COMPLETE_TEXT); + expect(resp.text).toContain(CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT); expect(resp.text).not.toContain(CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT); + expect(mockIsRegistrationJourney).toHaveBeenCalledTimes(2); + expect(mockIsActiveFeature).toHaveBeenCalledTimes(1); }); test("renders the confirmation page for agent", async () => { - mockGetApplicationData.mockReturnValue( - { ...APPLICATION_DATA_MOCK, - who_is_registering: WhoIsRegisteringType.AGENT - } - ); + mockFetchApplicationData.mockReturnValue({ + ...APPLICATION_DATA_MOCK, + who_is_registering: WhoIsRegisteringType.AGENT + }); const resp = await request(app).get(CONFIRMATION_URL); expect(resp.status).toEqual(200); @@ -94,7 +160,8 @@ describe("Confirmation controller tests", () => { }); test("should test that deleteApplicationData does the work", async () => { - mockGetApplicationData.mockReturnValueOnce( { ...APPLICATION_DATA_MOCK } ); + mockIsActiveFeature.mockReturnValueOnce(false); + mockFetchApplicationData.mockReturnValueOnce({ ...APPLICATION_DATA_MOCK }); req.session = getSessionRequestWithExtraData(); req.headers = {}; await get(req, res); diff --git a/test/controllers/payment.controller.spec.ts b/test/controllers/payment.controller.spec.ts index 6f8527aa8..7e3e30f35 100644 --- a/test/controllers/payment.controller.spec.ts +++ b/test/controllers/payment.controller.spec.ts @@ -7,22 +7,30 @@ jest.mock('../../src/middleware/service.availability.middleware'); jest.mock("../../src/utils/url"); import { NextFunction, Request, Response } from "express"; -import { describe, expect, jest, test, beforeEach } from "@jest/globals"; import request from "supertest"; import app from "../../src/app"; import { authentication } from "../../src/middleware/authentication.middleware"; import { serviceAvailabilityMiddleware } from "../../src/middleware/service.availability.middleware"; -import { getApplicationData } from '../../src/utils/application.data'; +import { fetchApplicationData } from '../../src/utils/application.data'; import { createAndLogErrorRequest, logger } from '../../src/utils/logger'; import { isActiveFeature } from "../../src/utils/feature.flag"; -import { getUrlWithParamsToPath } from "../../src/utils/url"; +import { PaymentKey } from "../../src/model/data.types.model"; + +import { getUrlWithParamsToPath, isRegistrationJourney } from "../../src/utils/url"; + +import { + FOUND_REDIRECT_TO, + MESSAGE_ERROR, + SERVICE_UNAVAILABLE +} from "../__mocks__/text.mock"; import { - PAYMENT_DECLINED_WITH_TRANSACTION_URL_AND_QUERY_STRING, PAYMENT_OBJECT_MOCK, + PAYMENT_DECLINED_WITH_TRANSACTION_URL_AND_QUERY_STRING, PAYMENT_WITH_TRANSACTION_URL_AND_QUERY_STRING } from "../__mocks__/session.mock"; + import { CONFIRMATION_PAGE, CONFIRMATION_URL, @@ -32,36 +40,41 @@ import { PAYMENT_FAILED_WITH_PARAMS_URL, PAYMENT_PAID } from "../../src/config"; -import { FOUND_REDIRECT_TO, MESSAGE_ERROR, SERVICE_UNAVAILABLE } from "../__mocks__/text.mock"; -import { PaymentKey } from "../../src/model/data.types.model"; const NEXT_PAGE_URL = "/NEXT_PAGE"; const mockLoggerDebugRequest = logger.debugRequest as jest.Mock; const mockLoggerInfoRequest = logger.infoRequest as jest.Mock; const mockCreateAndLogErrorRequest = createAndLogErrorRequest as jest.Mock; -const mockGetApplicationData = getApplicationData as jest.Mock; +const mockFetchApplicationData = fetchApplicationData as jest.Mock; + const mockAuthenticationMiddleware = authentication as jest.Mock; -mockAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); +mockAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); + const mockServiceAvailabilityMiddleware = serviceAvailabilityMiddleware as jest.Mock; -mockServiceAvailabilityMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); +mockServiceAvailabilityMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); + const mockIsActiveFeature = isActiveFeature as jest.Mock; const mockGetUrlWithParamsToPath = getUrlWithParamsToPath as jest.Mock; mockGetUrlWithParamsToPath.mockReturnValue(NEXT_PAGE_URL); +const mockIsRegistrationJourney = isRegistrationJourney as jest.Mock; +mockIsRegistrationJourney.mockReturnValue(true); + describe("Payment controller", () => { beforeEach(() => { jest.clearAllMocks(); mockIsActiveFeature.mockReset(); - mockGetApplicationData.mockReset(); + mockFetchApplicationData.mockReset(); mockLoggerDebugRequest.mockReset(); process.env.FEATURE_FLAG_ENABLE_REDIS_REMOVAL = "false"; }); describe("GET tests for Payment controller without params url", () => { + test("should rejecting redirect, state does not match", async () => { - mockGetApplicationData.mockReturnValueOnce( {} ); + mockFetchApplicationData.mockReturnValueOnce({}); await request(app).get(PAYMENT_WITH_TRANSACTION_URL_AND_QUERY_STRING); expect(mockLoggerInfoRequest).toHaveBeenCalledTimes(2); @@ -70,7 +83,7 @@ describe("Payment controller", () => { }); test(`should redirect to ${CONFIRMATION_PAGE} page, Payment Successful with status ${PAYMENT_PAID}`, async () => { - mockGetApplicationData.mockReturnValueOnce( { [PaymentKey]: PAYMENT_OBJECT_MOCK } ); + mockFetchApplicationData.mockReturnValueOnce({ [PaymentKey]: PAYMENT_OBJECT_MOCK }); const resp = await request(app).get(PAYMENT_WITH_TRANSACTION_URL_AND_QUERY_STRING); expect(resp.status).toEqual(302); @@ -81,7 +94,7 @@ describe("Payment controller", () => { }); test(`should redirect to ${PAYMENT_FAILED_PAGE} page, Payment failed somehow`, async () => { - mockGetApplicationData.mockReturnValueOnce( { [PaymentKey]: PAYMENT_OBJECT_MOCK } ); + mockFetchApplicationData.mockReturnValueOnce({ [PaymentKey]: PAYMENT_OBJECT_MOCK }); const resp = await request(app).get(PAYMENT_DECLINED_WITH_TRANSACTION_URL_AND_QUERY_STRING); expect(resp.status).toEqual(302); @@ -92,7 +105,7 @@ describe("Payment controller", () => { }); test(`Should render the error page`, async () => { - mockLoggerDebugRequest.mockImplementationOnce( () => { throw new Error(MESSAGE_ERROR); }); + mockLoggerDebugRequest.mockImplementationOnce(() => { throw new Error(MESSAGE_ERROR); }); const response = await request(app).get(PAYMENT_WITH_TRANSACTION_URL_AND_QUERY_STRING); expect(response.status).toEqual(500); @@ -106,7 +119,7 @@ describe("Payment controller", () => { test(`should redirect to ${CONFIRMATION_PAGE} page, Payment Successful with status ${PAYMENT_PAID}`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL - mockGetApplicationData.mockReturnValueOnce( { [PaymentKey]: PAYMENT_OBJECT_MOCK } ); + mockFetchApplicationData.mockReturnValueOnce({ [PaymentKey]: PAYMENT_OBJECT_MOCK }); const resp = await request(app).get(PAYMENT_WITH_TRANSACTION_URL_AND_QUERY_STRING); expect(resp.status).toEqual(302); @@ -120,7 +133,7 @@ describe("Payment controller", () => { test(`should redirect to ${PAYMENT_FAILED_PAGE} page, Payment failed somehow and feature flag active`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL - mockGetApplicationData.mockReturnValueOnce( { [PaymentKey]: PAYMENT_OBJECT_MOCK } ); + mockFetchApplicationData.mockReturnValueOnce({ [PaymentKey]: PAYMENT_OBJECT_MOCK }); const resp = await request(app).get(PAYMENT_DECLINED_WITH_TRANSACTION_URL_AND_QUERY_STRING); expect(resp.status).toEqual(302); diff --git a/test/controllers/resume.submission.controller.spec.ts b/test/controllers/resume.submission.controller.spec.ts index 1e82f5eb9..30004db7c 100644 --- a/test/controllers/resume.submission.controller.spec.ts +++ b/test/controllers/resume.submission.controller.spec.ts @@ -232,7 +232,7 @@ describe("Resume submission controller", () => { expect(mockSetExtraData).toHaveBeenCalledTimes(1); expect(mockCreateAndLogErrorRequest).not.toHaveBeenCalled(); expect(mockMapTrustApiReturnModelToWebModel).toHaveBeenCalledTimes(1); - expect(mockIsActiveFeature).toHaveBeenCalledTimes(4); + expect(mockIsActiveFeature).toHaveBeenCalledTimes(5); expect(mockGetUrlWithTransactionIdAndSubmissionId).not.toHaveBeenCalled(); }); @@ -266,7 +266,7 @@ describe("Resume submission controller", () => { expect(mockSetExtraData).toHaveBeenCalledTimes(1); expect(mockCreateAndLogErrorRequest).not.toHaveBeenCalled(); expect(mockMapTrustApiReturnModelToWebModel).toHaveBeenCalledTimes(1); - expect(mockIsActiveFeature).toHaveBeenCalledTimes(4); + expect(mockIsActiveFeature).toHaveBeenCalledTimes(5); expect(mockGetUrlWithTransactionIdAndSubmissionId).toHaveBeenCalledTimes(1); }); diff --git a/test/controllers/update/confirmation.controller.spec.ts b/test/controllers/update/confirmation.controller.spec.ts index 1c0888917..534294efe 100644 --- a/test/controllers/update/confirmation.controller.spec.ts +++ b/test/controllers/update/confirmation.controller.spec.ts @@ -4,20 +4,37 @@ jest.mock("../../../src/utils/logger"); jest.mock('../../../src/utils/application.data'); jest.mock('../../../src/middleware/service.availability.middleware'); jest.mock('../../../src/utils/session'); +jest.mock('../../../src/utils/url'); +jest.mock('../../../src/utils/feature.flag'); jest.mock('../../../src/middleware/company.authentication.middleware'); jest.mock('../../../src/middleware/navigation/update/has.beneficial.owners.or.managing.officers.update.middleware'); +import { NextFunction, Request, Response } from "express"; +import request from "supertest"; + // import remove journey middleware mock before app to prevent real function being used instead of mock import mockJourneyDetectionMiddleware from "../../__mocks__/journey.detection.middleware.mock"; - -import { authentication } from "../../../src/middleware/authentication.middleware"; -import { companyAuthentication } from "../../../src/middleware/company.authentication.middleware"; -import request from "supertest"; -import { NextFunction, Request, Response } from "express"; import app from "../../../src/app"; -import { REMOVE_SERVICE_NAME, UPDATE_CONFIRMATION_URL } from "../../../src/config"; import { logger } from "../../../src/utils/logger"; +import { authentication } from "../../../src/middleware/authentication.middleware"; +import { companyAuthentication } from "../../../src/middleware/company.authentication.middleware"; +import { get } from "../../../src/controllers/update/confirmation.controller"; +import { getLoggedInUserEmail } from "../../../src/utils/session"; +import { hasBOsOrMOsUpdate } from "../../../src/middleware/navigation/update/has.beneficial.owners.or.managing.officers.update.middleware"; import { serviceAvailabilityMiddleware } from "../../../src/middleware/service.availability.middleware"; +import { isActiveFeature } from "../../../src/utils/feature.flag"; + +import { deleteApplicationData, getApplicationData } from "../../../src/utils/application.data"; +import { REMOVE_SERVICE_NAME, UPDATE_CONFIRMATION_URL } from "../../../src/config"; +import { isRegistrationJourney, isRemoveJourney } from "../../../src/utils/url"; + +import { + userMail, + APPLICATION_DATA_MOCK, + APPLICATION_DATA_REMOVE_MOCK, + APPLICATION_DATA_MOCK_WITHOUT_UPDATE, + getSessionRequestWithExtraData, +} from "../../__mocks__/session.mock"; import { ANY_MESSAGE_ERROR, @@ -30,36 +47,31 @@ import { CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT, CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT } from "../../__mocks__/text.mock"; -import { deleteApplicationData, getApplicationData } from "../../../src/utils/application.data"; -import { - APPLICATION_DATA_MOCK, - APPLICATION_DATA_REMOVE_MOCK, - APPLICATION_DATA_MOCK_WITHOUT_UPDATE, - getSessionRequestWithExtraData, - userMail -} from "../../__mocks__/session.mock"; -import { get } from "../../../src/controllers/confirmation.controller"; -import { getLoggedInUserEmail } from "../../../src/utils/session"; -import { hasBOsOrMOsUpdate } from "../../../src/middleware/navigation/update/has.beneficial.owners.or.managing.officers.update.middleware"; const req = {} as Request; const res = { render: jest.fn() as any } as Response; +const next = jest.fn() as NextFunction; mockJourneyDetectionMiddleware.mockClear(); const mockLoggerDebugRequest = logger.debugRequest as jest.Mock; const mockAuthenticationMiddleware = authentication as jest.Mock; -mockAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); +mockAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); const mockCompanyAuthenticationMiddleware = companyAuthentication as jest.Mock; -mockCompanyAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); +mockCompanyAuthenticationMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); const mockServiceAvailabilityMiddleware = serviceAvailabilityMiddleware as jest.Mock; -mockServiceAvailabilityMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); +mockServiceAvailabilityMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); const mockHasBOsOrMOsUpdateMiddleware = hasBOsOrMOsUpdate as jest.Mock; -mockHasBOsOrMOsUpdateMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next() ); +mockHasBOsOrMOsUpdateMiddleware.mockImplementation((req: Request, res: Response, next: NextFunction) => next()); const mockGetApplicationData = getApplicationData as jest.Mock; const mockDeleteApplicationData = deleteApplicationData as jest.Mock; const mockGetLoggedInUserEmail = getLoggedInUserEmail as jest.Mock; +const mockIsActiveFeature = isActiveFeature as jest.Mock; +const mockIsRemoveJourney = isRemoveJourney as jest.Mock; + +const mockIsRegistrationJourney = isRegistrationJourney as jest.Mock; +mockIsRegistrationJourney.mockReturnValue(false); const UPDATE_STATEMENT_TEXT = "update statement"; const UPDATE_STATEMENT_WHAT_TO_DO_NOW = "What you need to do now"; @@ -73,6 +85,8 @@ describe("UPDATE CONFIRMATION controller", () => { beforeEach(() => { jest.clearAllMocks(); + mockIsActiveFeature.mockReset(); + mockLoggerDebugRequest.mockReset(); mockGetLoggedInUserEmail.mockReturnValue(userMail); }); @@ -88,11 +102,13 @@ describe("UPDATE CONFIRMATION controller", () => { expect(resp.text).toContain(userMail); expect(resp.text).toContain(UPDATE_STATEMENT_TEXT); expect(resp.text).toContain(UPDATE_SURVEY_LINK); - expect(mockGetApplicationData).toHaveBeenCalledTimes(2); + expect(mockGetApplicationData).toHaveBeenCalledTimes(1); expect(mockDeleteApplicationData).toHaveBeenCalledTimes(1); }); test("renders the remove confirmation page for a 'no change' submission", async () => { + mockIsRegistrationJourney.mockReturnValue(false); + mockIsRemoveJourney.mockReturnValue(true); mockGetApplicationData.mockReturnValue( { ...APPLICATION_DATA_REMOVE_MOCK } ); @@ -105,16 +121,15 @@ describe("UPDATE CONFIRMATION controller", () => { expect(resp.text).toContain(REMOVE_STATEMENT_TEXT); expect(resp.text).toContain(REMOVE_SURVEY_LINK); expect(resp.text).toContain(UPDATE_STATEMENT_WHAT_HAPPENS_NEXT); - - // This is a 'no change' scenario, so this text should not be output - expect(resp.text).not.toContain(UPDATE_STATEMENT_WHAT_TO_DO_NOW); + expect(resp.text).not.toContain(UPDATE_STATEMENT_WHAT_TO_DO_NOW); // This is a 'no change' scenario, so this text should not be output expect(resp.text).not.toContain(CONFIRMATION_AGENT_SPECIFIC_TEXT); - - expect(mockGetApplicationData).toHaveBeenCalledTimes(2); + expect(mockGetApplicationData).toHaveBeenCalledTimes(1); expect(mockDeleteApplicationData).toHaveBeenCalledTimes(1); }); test("renders the remove confirmation page for a 'change' submission (no agent)", async () => { + mockIsRemoveJourney.mockReturnValue(true); + mockIsRegistrationJourney.mockReturnValue(false); mockGetApplicationData.mockReturnValue( { ...APPLICATION_DATA_REMOVE_MOCK, @@ -133,19 +148,15 @@ describe("UPDATE CONFIRMATION controller", () => { expect(resp.text).toContain(UPDATE_STATEMENT_WHAT_HAPPENS_NEXT); expect(resp.text).toContain(CONFIRMATION_AGENT_MUST_THEN_COMPLETE_TEXT); expect(resp.text).toContain(CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT); - expect(resp.text).not.toContain(CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT); - - // This is a 'change' scenario, so this text should be output - expect(resp.text).toContain(UPDATE_STATEMENT_WHAT_TO_DO_NOW); + expect(resp.text).toContain(UPDATE_STATEMENT_WHAT_TO_DO_NOW); // This is a 'change' scenario, so this text should be output expect(resp.text).toContain(CONFIRMATION_AGENT_SPECIFIC_TEXT); - - expect(mockGetApplicationData).toHaveBeenCalledTimes(2); + expect(mockGetApplicationData).toHaveBeenCalledTimes(1); expect(mockDeleteApplicationData).toHaveBeenCalledTimes(1); }); test('catch error when page cannot be rendered', async () => { - mockLoggerDebugRequest.mockImplementationOnce( () => { throw new Error(ANY_MESSAGE_ERROR); }); + mockLoggerDebugRequest.mockImplementationOnce(() => { throw new Error(ANY_MESSAGE_ERROR); }); const resp = await request(app).get(UPDATE_CONFIRMATION_URL); expect(resp.status).toEqual(500); @@ -153,11 +164,14 @@ describe("UPDATE CONFIRMATION controller", () => { }); test("should test that deleteApplicationData does the work", async () => { - mockGetApplicationData.mockReturnValueOnce( { ...APPLICATION_DATA_MOCK } ); + mockIsRegistrationJourney.mockReturnValue(false); + mockIsRemoveJourney.mockReturnValue(false); + mockIsActiveFeature.mockReturnValue(false); + mockLoggerDebugRequest.mockReturnValue(true); + mockGetApplicationData.mockReturnValueOnce({ ...APPLICATION_DATA_MOCK }); req.session = getSessionRequestWithExtraData(); - await get(req, res); - + await get(req, res, next); const appData = await getApplicationData(req.session); expect(appData).toBeFalsy; @@ -166,12 +180,16 @@ describe("UPDATE CONFIRMATION controller", () => { }); test("agent related content is not displayed for no change submission", async () => { + mockIsRegistrationJourney.mockReturnValue(false); + mockIsRemoveJourney.mockReturnValue(false); + mockIsActiveFeature.mockReturnValue(false); + mockLoggerDebugRequest.mockReturnValue(true); mockGetApplicationData.mockReturnValue( { ...APPLICATION_DATA_MOCK, who_is_registering: undefined, update: { no_change: true } - } ); + }); const resp = await request(app).get(UPDATE_CONFIRMATION_URL); expect(resp.status).toEqual(200); expect(resp.text).toContain(UPDATE_STATEMENT_TEXT); @@ -186,7 +204,7 @@ describe("UPDATE CONFIRMATION controller", () => { ...APPLICATION_DATA_MOCK_WITHOUT_UPDATE, who_is_registering: undefined, update: { no_change: false } - } ); + }); const resp = await request(app).get(UPDATE_CONFIRMATION_URL); expect(resp.status).toEqual(200); expect(resp.text).toContain(UPDATE_STATEMENT_TEXT); @@ -195,7 +213,6 @@ describe("UPDATE CONFIRMATION controller", () => { expect(resp.text).toContain(CONFIRMATION_AGENT_SPECIFIC_TEXT); expect(resp.text).toContain(CONFIRMATION_AGENT_MUST_THEN_COMPLETE_TEXT); expect(resp.text).toContain(CONFIRMATION_COMPLETED_VERIFICATION_CHECKS_TEXT); - expect(resp.text).not.toContain(CONFIRMATION_COMPLETED_IDENTITY_CHECKS_TEXT); }); }); diff --git a/test/service/overseas.entities.service.spec.ts b/test/service/overseas.entities.service.spec.ts index 7943c544a..07f343084 100644 --- a/test/service/overseas.entities.service.spec.ts +++ b/test/service/overseas.entities.service.spec.ts @@ -25,6 +25,8 @@ import { OVERSEAS_ENTITY_ID, serviceNameOE, TRANSACTION_ID, + FORCE_UPDATE, + FORCE_FETCH, } from "../__mocks__/session.mock"; const mockFetchApplicationData = fetchApplicationData as jest.Mock; @@ -106,7 +108,7 @@ describe(`Update Overseas Entity Service test suite`, () => { expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Calling 'putOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}'`); expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Response from 'putOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}': ${JSON.stringify(mockResponse)}`); - expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNamePutOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, APPLICATION_DATA_MOCK); + expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNamePutOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, APPLICATION_DATA_MOCK, FORCE_UPDATE); }); test(`updateOverseasEntity should respond with created httpStatusCode when application data is supplied directly in the method call`, async () => { @@ -117,7 +119,7 @@ describe(`Update Overseas Entity Service test suite`, () => { expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Calling 'putOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}'`); expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Response from 'putOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}': ${JSON.stringify(mockResponse)}`); - expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNamePutOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, APPLICATION_DATA_MOCK); + expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNamePutOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, APPLICATION_DATA_MOCK, FORCE_UPDATE); }); test(`updateOverseasEntity should respond with 400 (Bad Request) error message`, async () => { @@ -128,7 +130,7 @@ describe(`Update Overseas Entity Service test suite`, () => { await expect( updateOverseasEntity(req, session) ).rejects.toThrow(ERROR); - expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNamePutOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, APPLICATION_DATA_MOCK); + expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNamePutOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, APPLICATION_DATA_MOCK, FORCE_UPDATE); expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Calling 'putOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}'`); expect(mockCreateAndLogErrorRequest).toBeCalledWith(req, errorMsg); }); @@ -147,7 +149,7 @@ describe(`Get Overseas Entity Service test suite`, () => { const response = await getOverseasEntity(req, TRANSACTION_ID, OVERSEAS_ENTITY_ID); - expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNameGetOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID); + expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNameGetOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, FORCE_FETCH); expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Calling 'getOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}'`); expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Response from 'getOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}': ${JSON.stringify(mockResponse)}`); expect(mockCreateAndLogErrorRequest).not.toHaveBeenCalled(); @@ -160,9 +162,9 @@ describe(`Get Overseas Entity Service test suite`, () => { mockMakeApiCallWithRetry.mockResolvedValueOnce(mockResponse); - await expect( getOverseasEntity(req, TRANSACTION_ID, OVERSEAS_ENTITY_ID) ).rejects.toThrow(ERROR); + await expect( getOverseasEntity(req, TRANSACTION_ID, OVERSEAS_ENTITY_ID, FORCE_FETCH) ).rejects.toThrow(ERROR); - expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNameGetOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID); + expect(mockMakeApiCallWithRetry).toBeCalledWith(serviceNameOE, fnNameGetOE, req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, FORCE_FETCH); expect(mockInfoRequestLog).toHaveBeenCalledWith(req, `Calling 'getOverseasEntity' for transaction id '${TRANSACTION_ID}' and overseas entity id '${OVERSEAS_ENTITY_ID}'`); expect(mockCreateAndLogErrorRequest).toBeCalledWith(req, errorMsg); }); diff --git a/test/service/payment.service.spec.ts b/test/service/payment.service.spec.ts index 95deedd3d..893e1952c 100644 --- a/test/service/payment.service.spec.ts +++ b/test/service/payment.service.spec.ts @@ -1,16 +1,20 @@ jest.mock("@companieshouse/api-sdk-node"); jest.mock("@companieshouse/api-sdk-node/dist/services/payment"); -jest.mock('../../src/utils/feature.flag'); -jest.mock('../../src/middleware/service.availability.middleware'); +jest.mock("../../src/utils/feature.flag"); +jest.mock("../../src/middleware/service.availability.middleware"); jest.mock("../../src/utils/url"); +jest.mock("../../src/service/overseas.entities.service"); +jest.mock("../../src/utils/application.data"); import { Request } from "express"; import { createApiClient } from "@companieshouse/api-sdk-node"; import { startPaymentsSession } from "../../src/service/payment.service"; import { isActiveFeature } from "../../src/utils/feature.flag"; +import { updateOverseasEntity } from "../../src/service/overseas.entities.service"; import { ApiResponse, ApiResult } from "@companieshouse/api-sdk-node/dist/services/resource"; import { Payment, PaymentService } from "@companieshouse/api-sdk-node/dist/services/payment"; +import { setApplicationData, fetchApplicationData, } from '../../src/utils/application.data'; import { getUrlWithParamsToPath, @@ -34,6 +38,7 @@ import { } from "../../src/config"; import { + APPLICATION_DATA_MOCK, getSessionRequestWithExtraData, OVERSEAS_ENTITY_ID, PAYMENT_FAILURE_MOCK_VALUE, @@ -70,6 +75,11 @@ const mockPaymentResult: ApiResult> = { const mockCreateApiClient = createApiClient as jest.Mock; mockCreateApiClient.mockReturnValue({ payment: PaymentService.prototype }); +const mockFetchApplicationData = fetchApplicationData as jest.Mock; + +const mockSetApplicationData = setApplicationData as jest.Mock; +mockSetApplicationData.mockReturnValue(true); + const mockGetUrlWithParamsToPath = getUrlWithParamsToPath as jest.Mock; mockGetUrlWithParamsToPath.mockReturnValue(NEXT_PAGE_URL); @@ -79,6 +89,8 @@ mockGetUrlWithTransactionIdAndSubmissionId.mockReturnValue(NEXT_PAGE_URL); const mockIsRegistrationJourney = isRegistrationJourney as jest.Mock; mockIsRegistrationJourney.mockReturnValue(true); +const mockUpdateOverseasEntity = updateOverseasEntity as jest.Mock; + const session = getSessionRequestWithExtraData(); const req: Request = { path: `${CHECK_YOUR_ANSWERS_URL}`, headers: {} } as Request; const updateReq: Request = { path: `${UPDATE_CHECK_YOUR_ANSWERS_URL}`, headers: {} } as Request; @@ -88,17 +100,19 @@ beforeEach (() => { mockIsActiveFeature.mockReset(); mockCreatePayment.mockReset(); mockIsFailure.mockReset(); + mockUpdateOverseasEntity.mockReset(); + mockFetchApplicationData.mockReset(); }); describe('Payment Service test suite', () => { test(`startPaymentsSession() should return ${CONFIRMATION_URL} if ${PAYMENT_REQUIRED_HEADER} blank`, async () => { - const response = await startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_CLOSED_RESPONSE ); + const response = await startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_CLOSED_RESPONSE); expect(response).toEqual(CONFIRMATION_URL); }); test(`startPaymentsSession() should return the first page to initiate the web journey ${PAYMENT_JOURNEY_URL} and with correct callback details, including a redirect URI`, async () => { - mockCreatePayment.mockResolvedValueOnce( mockPaymentResult ); + mockCreatePayment.mockResolvedValueOnce(mockPaymentResult); const response = await startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER); expect(response).toEqual(PAYMENT_JOURNEY_URL); const createPaymentResult = mockCreatePayment.mock.calls[0][0]; @@ -108,7 +122,7 @@ describe('Payment Service test suite', () => { }); test(`startPaymentsSession() should throw ${NO_RESOURCE_ON_PAYMENT_RESPONSE_MSG_ERROR} error msg`, async () => { - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: undefined } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: undefined }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(NO_RESOURCE_ON_PAYMENT_RESPONSE_MSG_ERROR); @@ -116,7 +130,7 @@ describe('Payment Service test suite', () => { test(`startPaymentsSession() should throw ${PAYMENT_RESPONSE_500_MSG_ERROR} error msg`, async () => { mockIsFailure.mockReturnValue(true); - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: PAYMENT_FAILURE_MOCK_VALUE } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: PAYMENT_FAILURE_MOCK_VALUE }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(PAYMENT_RESPONSE_500_MSG_ERROR); @@ -124,7 +138,7 @@ describe('Payment Service test suite', () => { test(`startPaymentsSession() should throw ${PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR} error msg`, async () => { mockIsFailure.mockReturnValue(true); - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: { errors: undefined, httpStatusCode: undefined } } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: { errors: undefined, httpStatusCode: undefined } }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR); @@ -132,7 +146,7 @@ describe('Payment Service test suite', () => { test(`startPaymentsSession() should throw ${PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR} error msg - No error response`, async () => { mockIsFailure.mockReturnValue(true); - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: undefined } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: undefined }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR); @@ -142,21 +156,41 @@ describe('Payment Service test suite', () => { describe('Payment Service test suite with params url', () => { - test(`startPaymentsSession() should return ${CONFIRMATION_WITH_PARAMS_URL} with substituted values if ${PAYMENT_REQUIRED_HEADER} blank`, async () => { + test(`startPaymentsSession() should return ${CONFIRMATION_WITH_PARAMS_URL} with substituted values if ${PAYMENT_REQUIRED_HEADER} blank if REDIS_removal f;ag is set to ON`, async () => { + mockIsActiveFeature.mockReturnValueOnce(true); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsActiveFeature.mockReturnValueOnce(true); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsActiveFeature.mockReturnValueOnce(true); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL - const response = await startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_CLOSED_RESPONSE ); + mockIsActiveFeature.mockReturnValueOnce(true); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockFetchApplicationData.mockReturnValue(APPLICATION_DATA_MOCK); + const response = await startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_CLOSED_RESPONSE); expect(response).toEqual(NEXT_PAGE_URL); + expect(mockFetchApplicationData).toHaveBeenCalledTimes(1); expect(mockGetUrlWithParamsToPath).toHaveBeenCalledTimes(1); expect(mockGetUrlWithParamsToPath.mock.calls[0][0]).toEqual(CONFIRMATION_WITH_PARAMS_URL); }); + test(`startPaymentsSession() should return ${CONFIRMATION_URL} with substituted values if ${PAYMENT_REQUIRED_HEADER} blank if REDIS_removal f;ag is set to OFF`, async () => { + mockIsActiveFeature.mockReturnValueOnce(false); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(false); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(false); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(false); // FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockFetchApplicationData.mockReturnValue(APPLICATION_DATA_MOCK); + const response = await startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_CLOSED_RESPONSE); + expect(response).toEqual(CONFIRMATION_URL); + expect(mockUpdateOverseasEntity).not.toHaveBeenCalled(); + expect(mockFetchApplicationData).toHaveBeenCalledTimes(1); + expect(mockGetUrlWithParamsToPath).not.toHaveBeenCalled(); + }); + test(`startPaymentsSession() should return ${CONFIRMATION_URL} without substituted values if ${PAYMENT_REQUIRED_HEADER} blank but not on the registration journey`, async () => { + mockFetchApplicationData.mockReturnValue(APPLICATION_DATA_MOCK); + mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL const response = await startPaymentsSession(updateReq, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_CLOSED_RESPONSE); - - expect(response).toEqual(CONFIRMATION_URL); - expect(mockGetUrlWithParamsToPath).toHaveBeenCalledTimes(0); + expect(response).toEqual(NEXT_PAGE_URL); + expect(mockGetUrlWithParamsToPath).toHaveBeenCalledTimes(1); }); test(`startPaymentsSession() should return the first page to initiate the web journey ${PAYMENT_JOURNEY_URL} and with correct callback details, including a redirect URI with substituted values`, async () => { @@ -174,7 +208,8 @@ describe('Payment Service test suite with params url', () => { }); test(`startPaymentsSession() should return the first page to initiate the web journey ${PAYMENT_JOURNEY_URL} and with correct callback details, including a redirect URI without substituted values if not on the registration journey`, async () => { - mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL + mockFetchApplicationData.mockReturnValue(APPLICATION_DATA_MOCK); + mockIsActiveFeature.mockReturnValueOnce(false); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockCreatePayment.mockResolvedValueOnce(mockPaymentResult); const updateBaseUrl = `${process.env.CHS_URL}${UPDATE_AN_OVERSEAS_ENTITY_URL}`; const response = await startPaymentsSession(updateReq, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER, updateBaseUrl); @@ -189,7 +224,7 @@ describe('Payment Service test suite with params url', () => { test(`startPaymentsSession() should throw ${NO_RESOURCE_ON_PAYMENT_RESPONSE_MSG_ERROR} error msg`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: undefined } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: undefined }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(NO_RESOURCE_ON_PAYMENT_RESPONSE_MSG_ERROR); @@ -198,7 +233,7 @@ describe('Payment Service test suite with params url', () => { test(`startPaymentsSession() should throw ${PAYMENT_RESPONSE_500_MSG_ERROR} error msg`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsFailure.mockReturnValue(true); - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: PAYMENT_FAILURE_MOCK_VALUE } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: PAYMENT_FAILURE_MOCK_VALUE }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(PAYMENT_RESPONSE_500_MSG_ERROR); @@ -207,7 +242,7 @@ describe('Payment Service test suite with params url', () => { test(`startPaymentsSession() should throw ${PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR} error msg`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsFailure.mockReturnValue(true); - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: { errors: undefined, httpStatusCode: undefined } } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: { errors: undefined, httpStatusCode: undefined } }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR); @@ -216,7 +251,7 @@ describe('Payment Service test suite with params url', () => { test(`startPaymentsSession() should throw ${PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR} error msg - No error response`, async () => { mockIsActiveFeature.mockReturnValueOnce(true); // For FEATURE_FLAG_ENABLE_REDIS_REMOVAL mockIsFailure.mockReturnValue(true); - mockCreatePayment.mockResolvedValueOnce( { ...mockPaymentResult, value: undefined } ); + mockCreatePayment.mockResolvedValueOnce({ ...mockPaymentResult, value: undefined }); await expect( startPaymentsSession(req, session, TRANSACTION_ID, OVERSEAS_ENTITY_ID, TRANSACTION_WITH_PAYMENT_HEADER) ).rejects.toThrow(PAYMENT_RESPONSE_NO_STATUS_CODE_MSG_ERROR);