diff --git a/api.planx.uk/lib/notify/index.ts b/api.planx.uk/lib/notify/index.ts index a3963d4d9d..7970f70cc3 100644 --- a/api.planx.uk/lib/notify/index.ts +++ b/api.planx.uk/lib/notify/index.ts @@ -66,7 +66,7 @@ const sendEmail = async ( expiryDate?: string; } = { message: "Success" }; if (template === "expiry") - softDeleteSession(config.personalisation.sessionId!); + await softDeleteSession(config.personalisation.sessionId!); if (template === "save") returnValue.expiryDate = config.personalisation.expiryDate; return returnValue; diff --git a/api.planx.uk/modules/admin/session/bops.test.ts b/api.planx.uk/modules/admin/session/bops.test.ts index f4441e20dc..245ae59063 100644 --- a/api.planx.uk/modules/admin/session/bops.test.ts +++ b/api.planx.uk/modules/admin/session/bops.test.ts @@ -37,6 +37,18 @@ describe("BOPS payload admin endpoint", () => { .expect(403); }); + it("returns an error if the BOPS payload generation fails", async () => { + mockGenerateBOPSPayload.mockRejectedValueOnce("Error!"); + + await supertest(app) + .get(endpoint`123`) + .set(authHeader({ role: "platformAdmin" })) + .expect(500) + .then((res) => { + expect(res.body.error).toMatch(/Failed to get BOPS payload/); + }); + }); + it("returns a JSON payload", async () => { await supertest(app) .get(endpoint`123`) diff --git a/api.planx.uk/modules/admin/session/digitalPlanningData.test.ts b/api.planx.uk/modules/admin/session/digitalPlanningData.test.ts index 09d3f114c9..9197f45b14 100644 --- a/api.planx.uk/modules/admin/session/digitalPlanningData.test.ts +++ b/api.planx.uk/modules/admin/session/digitalPlanningData.test.ts @@ -40,6 +40,22 @@ describe("Digital Planning Application payload admin endpoint", () => { .expect(403); }); + it("returns an error if the XML generation fails", async () => { + mockGenerateDigitalPlanningApplicationPayload.mockRejectedValueOnce( + "Error!", + ); + + await supertest(app) + .get(endpoint`123`) + .set(authHeader({ role: "platformAdmin" })) + .expect(500) + .then((res) => { + expect(res.body.error).toMatch( + /Failed to make Digital Planning Application payload/, + ); + }); + }); + it("returns a valid JSON payload", async () => { await supertest(app) .get(endpoint`123`) diff --git a/api.planx.uk/modules/admin/session/oneAppXML.test.ts b/api.planx.uk/modules/admin/session/oneAppXML.test.ts index 5988ba1778..af18f0d186 100644 --- a/api.planx.uk/modules/admin/session/oneAppXML.test.ts +++ b/api.planx.uk/modules/admin/session/oneAppXML.test.ts @@ -38,6 +38,18 @@ describe("OneApp XML endpoint", () => { .expect(403); }); + it("returns an error if the XML generation fails", async () => { + mockGenerateOneAppXML.mockRejectedValueOnce("Error!"); + + await supertest(app) + .get(endpoint`123`) + .set(authHeader({ role: "platformAdmin" })) + .expect(500) + .then((res) => { + expect(res.body.error).toMatch(/Failed to get OneApp XML/); + }); + }); + it("returns XML", async () => { await supertest(app) .get(endpoint`123`) diff --git a/api.planx.uk/modules/admin/session/summary.test.ts b/api.planx.uk/modules/admin/session/summary.test.ts index a74cbd9844..75551c8cac 100644 --- a/api.planx.uk/modules/admin/session/summary.test.ts +++ b/api.planx.uk/modules/admin/session/summary.test.ts @@ -39,6 +39,10 @@ describe("Session summary admin endpoint", () => { .expect(403); }); + it.todo("returns an error if the service fails"); + + it.todo("returns an error if the session can't be found"); + it("returns JSON", async () => { await supertest(app) .get(endpoint`abc123`) diff --git a/api.planx.uk/modules/admin/session/zip.test.ts b/api.planx.uk/modules/admin/session/zip.test.ts index dccf00e126..0444e5734a 100644 --- a/api.planx.uk/modules/admin/session/zip.test.ts +++ b/api.planx.uk/modules/admin/session/zip.test.ts @@ -40,4 +40,6 @@ describe("zip data admin endpoint", () => { .expect(200) .expect("content-type", "application/zip"); }); + + it.todo("returns an error if the service fails"); }); diff --git a/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.test.ts b/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.test.ts index cc2c593d67..d836ff4c21 100644 --- a/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.test.ts +++ b/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.test.ts @@ -133,9 +133,14 @@ describe("Send email endpoint for invite to pay templates", () => { }); describe("'Payment Expiry' templates", () => { - const _templates = ["payment-expiry", "payment-expiry-agent"]; - it.todo( - "soft deletes the payment_request when a payment expiry email is sent", - ); + const templates = ["payment-expiry", "payment-expiry-agent"]; + + for (const template of templates) { + describe(`${template} Template`, () => { + it.todo( + "soft deletes the payment_request when a payment expiry email is sent", + ); + }); + } }); }); diff --git a/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.ts b/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.ts index c23a806896..7f0899a204 100644 --- a/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.ts +++ b/api.planx.uk/modules/pay/service/inviteToPay/sendPaymentEmail.ts @@ -38,8 +38,6 @@ const sendSinglePaymentEmail = async ({ paymentRequestId, template, ); - if (!session || !paymentRequest) - throw Error(`Invalid payment request: ${paymentRequestId}`); const config = await getInviteToPayNotifyConfig(session, paymentRequest); const recipient = template.includes("-agent") ? session.email diff --git a/api.planx.uk/modules/saveAndReturn/service/utils.test.ts b/api.planx.uk/modules/saveAndReturn/service/utils.test.ts index 2d12b1e69a..d272205f34 100644 --- a/api.planx.uk/modules/saveAndReturn/service/utils.test.ts +++ b/api.planx.uk/modules/saveAndReturn/service/utils.test.ts @@ -1,5 +1,11 @@ -import { Team } from "../../../types"; -import { convertSlugToName, getResumeLink } from "./utils"; +import { queryMock } from "../../../tests/graphqlQueryMock"; +import { LowCalSession, LowCalSessionData, Team } from "../../../types"; +import { + convertSlugToName, + getResumeLink, + getSessionDetails, + setupEmailEventTriggers, +} from "./utils"; describe("convertSlugToName util function", () => { it("should return the correct value", () => { @@ -41,3 +47,66 @@ describe("getResumeLink util function", () => { expect(testCase).toEqual(expectedResult); }); }); + +describe("getSessionDetails() util function", () => { + it("sets defaults for values not in the session", async () => { + const result = await getSessionDetails({ + data: { id: "flowId", passport: { data: {} }, breadcrumbs: {} }, + id: "abc123", + created_at: "2023-01-01", + submitted_at: "2023-02-02", + has_user_saved: true, + } as LowCalSession); + + expect(result.address).toEqual("Address not submitted"); + expect(result.projectType).toEqual("Project type not submitted"); + }); + + it("defaults to address title if no single line address is present", async () => { + const sessionData: LowCalSessionData = { + id: "flowId", + passport: { + data: { + _address: { + title: "Address title", + }, + }, + }, + breadcrumbs: {}, + }; + + const result = await getSessionDetails({ + data: sessionData, + id: "abc123", + created_at: "2023-01-01", + submitted_at: "2023-02-02", + has_user_saved: true, + } as LowCalSession); + + expect(result.address).toEqual("Address title"); + }); +}); + +describe("setupEmailEventTriggers util function", () => { + it("handles GraphQL errors", async () => { + queryMock.mockQuery({ + name: "SetupEmailNotifications", + data: { + session: { + id: "123", + hasUserSaved: true, + }, + }, + variables: { + sessionId: "123", + }, + graphqlErrors: [ + { + message: "Something went wrong", + }, + ], + }); + + await expect(setupEmailEventTriggers("123")).rejects.toThrow(); + }); +}); diff --git a/api.planx.uk/modules/saveAndReturn/service/utils.ts b/api.planx.uk/modules/saveAndReturn/service/utils.ts index a46968a7b0..b1b26b5f38 100644 --- a/api.planx.uk/modules/saveAndReturn/service/utils.ts +++ b/api.planx.uk/modules/saveAndReturn/service/utils.ts @@ -147,7 +147,7 @@ interface SessionDetails { /** * Parse session details into an object which will be read by email template */ -const getSessionDetails = async ( +export const getSessionDetails = async ( session: LowCalSession, ): Promise => { const passportProtectTypes = @@ -248,7 +248,7 @@ interface SetupEmailNotifications { // Update lowcal_sessions.has_user_saved column to kick-off the setup_lowcal_expiry_events & // setup_lowcal_reminder_events event triggers in Hasura // Should only run once on initial save of a session -const setupEmailEventTriggers = async (sessionId: string) => { +export const setupEmailEventTriggers = async (sessionId: string) => { try { const mutation = gql` mutation SetupEmailNotifications($sessionId: uuid!) { diff --git a/api.planx.uk/modules/sendEmail/index.test.ts b/api.planx.uk/modules/sendEmail/index.test.ts index 96debfbfcc..8ae931321a 100644 --- a/api.planx.uk/modules/sendEmail/index.test.ts +++ b/api.planx.uk/modules/sendEmail/index.test.ts @@ -7,6 +7,7 @@ import { mockSetupEmailNotifications, mockSoftDeleteLowcalSession, mockValidateSingleSessionRequest, + mockValidateSingleSessionRequestMissingSession, } from "../../tests/mocks/saveAndReturnMocks"; import { CoreDomainClient } from "@opensystemslab/planx-core"; @@ -200,6 +201,71 @@ describe("Send Email endpoint", () => { }); describe("'Expiry' template", () => { + it("returns an error if unable to delete the session", async () => { + queryMock.mockQuery({ + name: "ValidateSingleSessionRequest", + data: { + flows_by_pk: mockFlow, + lowcalSessions: [ + { + ...mockLowcalSession, + id: "456", + }, + ], + }, + variables: { + sessionId: "456", + }, + }); + + queryMock.mockQuery({ + name: "SetupEmailNotifications", + data: { + session: { + id: "456", + hasUserSaved: true, + }, + }, + variables: { + sessionId: "456", + }, + }); + + queryMock.mockQuery({ + name: "SoftDeleteLowcalSession", + data: { + update_lowcal_sessions_by_pk: { + id: "456", + }, + }, + variables: { + sessionId: "456", + }, + matchOnVariables: true, + graphqlErrors: [ + { + message: "Something went wrong", + }, + ], + }); + + const data = { + payload: { + sessionId: "456", + email: TEST_EMAIL, + }, + }; + + await supertest(app) + .post(`/send-email/expiry`) + .set("Authorization", "testtesttest") + .send(data) + .expect(500) + .then((res) => { + expect(res.body.error).toMatch(/Error deleting session/); + }); + }); + it("soft deletes the session when an expiry email is sent", async () => { const data = { payload: { @@ -239,6 +305,18 @@ describe("Setting up send email events", () => { queryMock.mockQuery(mockSetupEmailNotifications); }); + test("Missing sessions are handled", async () => { + queryMock.mockQuery(mockValidateSingleSessionRequestMissingSession); + + await supertest(app) + .post(SAVE_ENDPOINT) + .send(data) + .expect(500) + .then((res) => { + expect(res.body.error).toMatch(/Unable to find session/); + }); + }); + test("Initial save sets ups email notifications", async () => { queryMock.mockQuery(mockValidateSingleSessionRequest); diff --git a/api.planx.uk/tests/mocks/saveAndReturnMocks.ts b/api.planx.uk/tests/mocks/saveAndReturnMocks.ts index ec75fae2d2..199acc438a 100644 --- a/api.planx.uk/tests/mocks/saveAndReturnMocks.ts +++ b/api.planx.uk/tests/mocks/saveAndReturnMocks.ts @@ -174,6 +174,18 @@ export const mockValidateSingleSessionRequest = { variables: { sessionId: mockLowcalSession.id, }, + matchOnVariables: true, +}; + +export const mockValidateSingleSessionRequestMissingSession = { + name: "ValidateSingleSessionRequest", + data: { + flows_by_pk: mockFlow, + lowcalSessions: [], + }, + variables: { + sessionId: mockLowcalSession.id, + }, }; export const mockSoftDeleteLowcalSession = { @@ -186,6 +198,7 @@ export const mockSoftDeleteLowcalSession = { variables: { sessionId: "123", }, + matchOnVariables: true, }; export const mockSetupEmailNotifications = {