From 8f2f35749e4761f6d86e3d3099679f72d3f587bd Mon Sep 17 00:00:00 2001 From: Audrius Vaitonis Date: Fri, 31 May 2024 13:24:06 +0100 Subject: [PATCH] feat(DTFS2-7049): test unknown Gov.UK Notify responses --- .../govuk-notify/govuk-notify.service.test.ts | 34 +++++++++++++++++-- .../govuk-notify/govuk-notify.service.ts | 2 +- src/modules/emails/emails.controller.test.ts | 2 +- test/emails/post-emails.api-test.ts | 21 ++++++++++-- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/helper-modules/govuk-notify/govuk-notify.service.test.ts b/src/helper-modules/govuk-notify/govuk-notify.service.test.ts index 1c7808bb..a0fa7bdf 100644 --- a/src/helper-modules/govuk-notify/govuk-notify.service.test.ts +++ b/src/helper-modules/govuk-notify/govuk-notify.service.test.ts @@ -36,7 +36,7 @@ describe('GovukNotifyService', () => { }); describe('sendEmail', () => { - const generateNotifyError = (status: number, message: string) => { + const generateNotifyError = (status: number, message?: string) => { const response = { data: { status_code: status, @@ -96,8 +96,15 @@ describe('GovukNotifyService', () => { error: 'Internal Server Error', status: 500, }, + { + exceptionClass: InternalServerErrorException, + exceptionName: 'Internal Server Error Exception', + error: 'Internal Server Error', + status: 500, + }, ])('throws exception $exceptionName for unexpected $status', async ({ exceptionClass, exceptionName, error, status }) => { - jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(generateNotifyError(status, errorMessage))); + const notifyError = generateNotifyError(status, errorMessage); + jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(notifyError)); const resultPromise = service.sendEmail(govUkNotifyKey, { sendToEmailAddress, templateId, personalisation }); @@ -106,11 +113,14 @@ describe('GovukNotifyService', () => { await expect(resultPromise).rejects.toThrow(exceptionName); await expect(resultPromise).rejects.toHaveProperty('status', status); await expect(resultPromise).rejects.toHaveProperty('response', { message: [errorMessage], error, statusCode: status }); + expect(loggerError).toHaveBeenCalledTimes(1); + expect(loggerError).toHaveBeenCalledWith(notifyError); }); it('throws generic Error exception for unexpected status', async () => { const unexpectedStatus = valueGenerator.integer({ min: 900, max: 999 }); - jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(generateNotifyError(unexpectedStatus, errorMessage))); + const notifyError = generateNotifyError(unexpectedStatus, errorMessage); + jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(notifyError)); const resultPromise = service.sendEmail(govUkNotifyKey, { sendToEmailAddress, templateId, personalisation }); @@ -118,6 +128,22 @@ describe('GovukNotifyService', () => { await expect(resultPromise).rejects.toBeInstanceOf(Error); await expect(resultPromise).rejects.toThrow(errorMessage); await expect(resultPromise).rejects.toHaveProperty('message', errorMessage); + expect(loggerError).toHaveBeenCalledTimes(1); + expect(loggerError).toHaveBeenCalledWith(notifyError); + }); + + it('throws generic Error exception for error without error message', async () => { + const notifyError = generateNotifyError(400); + jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(notifyError)); + + const resultPromise = service.sendEmail(govUkNotifyKey, { sendToEmailAddress, templateId, personalisation }); + + expect(sendEmailMethodMock).toHaveBeenCalledTimes(1); + await expect(resultPromise).rejects.toBeInstanceOf(Error); + await expect(resultPromise).rejects.toThrow('NotifyClient failed with unexpected error %o'); + await expect(resultPromise).rejects.toHaveProperty('message', 'NotifyClient failed with unexpected error %o'); + expect(loggerError).toHaveBeenCalledTimes(1); + expect(loggerError).toHaveBeenCalledWith(notifyError); }); it('throws exception UnprocessableEntityException for empty response from GOV.UK Notify client', async () => { @@ -128,6 +154,8 @@ describe('GovukNotifyService', () => { expect(sendEmailMethodMock).toHaveBeenCalledTimes(1); await expect(resultPromise).rejects.toBeInstanceOf(UnprocessableEntityException); await expect(resultPromise).rejects.toThrow('No GOV.UK Notify response'); + expect(loggerError).toHaveBeenCalledTimes(1); + expect(loggerError).toHaveBeenCalledWith('Empty response from GOV.UK Notify'); }); }); }); diff --git a/src/helper-modules/govuk-notify/govuk-notify.service.ts b/src/helper-modules/govuk-notify/govuk-notify.service.ts index 78bec457..82f7d56f 100644 --- a/src/helper-modules/govuk-notify/govuk-notify.service.ts +++ b/src/helper-modules/govuk-notify/govuk-notify.service.ts @@ -56,7 +56,7 @@ export class GovukNotifyService { throw new Error(err.response.data.errors[0].message); } } else { - throw new Error(JSON.stringify(err.response.data)); + throw new Error('NotifyClient failed with unexpected error %o', err); } }); diff --git a/src/modules/emails/emails.controller.test.ts b/src/modules/emails/emails.controller.test.ts index 322799fa..ad768af5 100644 --- a/src/modules/emails/emails.controller.test.ts +++ b/src/modules/emails/emails.controller.test.ts @@ -29,7 +29,7 @@ describe('EmailsController', () => { const govUkNotifyKey = valueGenerator.string({ length: 10 }); it('returns receipt response for sent email', async () => { - when(emailsServiceSendEmail).calledWith(govUkNotifyKey, request[0]).mockResolvedValueOnce(postEmailsResponse[0]); + when(emailsServiceSendEmail).calledWith(govUkNotifyKey, request).mockResolvedValueOnce(postEmailsResponse[0]); const response = await controller.postEmail(govUkNotifyKey, request); diff --git a/test/emails/post-emails.api-test.ts b/test/emails/post-emails.api-test.ts index 2d06f37a..35260034 100644 --- a/test/emails/post-emails.api-test.ts +++ b/test/emails/post-emails.api-test.ts @@ -136,13 +136,28 @@ describe('POST /emails', () => { }); }); - it('returns a 500 response if the Notify client responds with status 500', async () => { - jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(generateNotifyError(500))); + it('returns a 500 response if the Notify client responds with unexpected status', async () => { + const unexpectedStatus = valueGenerator.integer({ min: 900, max: 999 }); + const error = generateNotifyError(unexpectedStatus, errorMessage); + jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(error)); const { status, body } = await api.post(mdmPath, request, { govUkNotifyKey }); expect(status).toBe(500); - expect(body).toStrictEqual({ + expect(body).toMatchObject({ + statusCode: 500, + message: 'Internal server error', + }); + }); + + it("returns a 500 response if the Notify client error doesn't have message attribute", async () => { + const error = generateNotifyError(400); + jest.mocked(sendEmailMethodMock).mockImplementation(() => Promise.reject(error)); + + const { status, body } = await api.post(mdmPath, request, { govUkNotifyKey }); + + expect(status).toBe(500); + expect(body).toMatchObject({ statusCode: 500, message: 'Internal server error', });