Skip to content

Commit 4a6cd36

Browse files
committed
feat: test that email still sent despite pdf gen failure
1 parent 2ec1959 commit 4a6cd36

File tree

2 files changed

+196
-7
lines changed

2 files changed

+196
-7
lines changed

src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.service.spec.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
import dbHandler from '__tests__/unit/backend/helpers/jest-db'
33
import { ObjectId } from 'bson'
44
import mongoose from 'mongoose'
5-
import { ok, okAsync } from 'neverthrow'
5+
import { errAsync, ok, okAsync } from 'neverthrow'
66
import {
77
BasicField,
8-
EmailResponse, FormAuthType,
8+
EmailResponse,
9+
FormAuthType,
910
FormResponseMode,
1011
MyInfoAttribute,
11-
PaymentChannel
12+
PaymentChannel,
1213
} from 'shared/types'
1314

1415
import { getEncryptSubmissionModel } from 'src/app/models/submission.server.model'
@@ -28,6 +29,7 @@ import {
2829
createEncryptSubmissionWithoutSave,
2930
performEncryptPostSubmissionActions,
3031
} from '../encrypt-submission.service'
32+
import { AutoreplyPdfGenerationError } from 'src/app/services/mail/mail.errors'
3133

3234
const EncryptSubmission = getEncryptSubmissionModel(mongoose)
3335

@@ -148,6 +150,53 @@ describe('encrypt-submission.service', () => {
148150
)
149151
})
150152

153+
it('should send email confirmations and sendSubmissionToAdmin without pdf attachment when pdf generation fails', async () => {
154+
// Arrange
155+
MockMailUtils.generateAutoreplyPdf.mockReturnValue(
156+
errAsync(new AutoreplyPdfGenerationError()),
157+
)
158+
const mockSubmission = {
159+
_id: new ObjectId(),
160+
form: new ObjectId(),
161+
created: new Date(),
162+
} as IEncryptedSubmissionSchema
163+
const mockResponses: ProcessedFieldResponse[] = [
164+
{
165+
_id: new ObjectId().toHexString(),
166+
question: SgidFieldTitle.SgidNric,
167+
answer: MOCK_NRIC,
168+
fieldType: BasicField.Nric,
169+
},
170+
]
171+
MockFormService.retrieveFullFormById.mockReturnValue(
172+
okAsync(MOCK_NON_PAYMENT_ENCRYPT_FORM),
173+
)
174+
175+
// Act
176+
await performEncryptPostSubmissionActions({
177+
submission: mockSubmission,
178+
responses: mockResponses,
179+
emailFields: mockResponses,
180+
submissionAttachments: MOCK_SUBMISSION_ATTACHMENTS,
181+
respondentEmails: ['[email protected]', '[email protected]'], // presence of respondent emails means that form summary is enabled
182+
})
183+
184+
// Assert
185+
expect(MockMailUtils.generateAutoreplyPdf).toHaveBeenCalledOnce()
186+
expect(MockMailService.sendSubmissionToAdmin).toHaveBeenCalledWith(
187+
expect.objectContaining({
188+
submissionAttachments: MOCK_SUBMISSION_ATTACHMENTS,
189+
pdfAttachment: undefined,
190+
}),
191+
)
192+
expect(MockMailService.sendAutoReplyEmails).toHaveBeenCalledWith(
193+
expect.objectContaining({
194+
submissionAttachments: MOCK_SUBMISSION_ATTACHMENTS,
195+
pdfAttachment: undefined,
196+
}),
197+
)
198+
})
199+
151200
it('should not generate pdf attachment if payment is enabled and not pass to either sendSubmissionToAdmin or sendEmailConfirmations', async () => {
152201
// Arrange
153202
const mockSubmission = {

src/app/modules/submission/multirespondent-submission/__tests__/multirespondent-submission.service.spec.ts

Lines changed: 144 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import dbHandler from '__tests__/unit/backend/helpers/jest-db'
22
import { ObjectId } from 'bson'
3-
import { okAsync } from 'neverthrow'
3+
import { errAsync, okAsync } from 'neverthrow'
44
import {
55
BasicField,
66
FieldResponsesV3,
@@ -29,6 +29,7 @@ import {
2929
performMultiRespondentPostSubmissionUpdateActions,
3030
sendNextStepReminderEmail,
3131
} from '../multirespondent-submission.service'
32+
import { AutoreplyPdfGenerationError } from 'src/app/services/mail/mail.errors'
3233

3334
jest.mock('src/app/modules/datadog/datadog.utils')
3435
jest.mock('src/app/services/mail/mail.utils')
@@ -46,15 +47,18 @@ const MOCK_SUBMISSION_ATTACHMENTS = [
4647
fieldId: new ObjectId().toHexString(),
4748
},
4849
]
49-
MockMailUtils.generateAutoreplyPdf.mockReturnValue(
50-
okAsync(Buffer.from('mock pdf buffer')),
51-
)
5250

5351
describe('multirespondent-submission.service', () => {
5452
beforeAll(async () => {
5553
await dbHandler.connect()
5654
})
5755

56+
beforeEach(() => {
57+
MockMailUtils.generateAutoreplyPdf.mockReturnValue(
58+
okAsync(Buffer.from('mock pdf buffer')),
59+
)
60+
})
61+
5862
afterEach(async () => {
5963
jest.clearAllMocks()
6064
await dbHandler.clearDatabase()
@@ -706,6 +710,86 @@ describe('multirespondent-submission.service', () => {
706710
// Assert
707711
expect(sendMrfRespondentCopyEmailSpy).not.toHaveBeenCalled()
708712
})
713+
714+
it('sends respondent copy despite pdf generation error', async () => {
715+
// Arrange
716+
MockMailUtils.generateAutoreplyPdf.mockReturnValue(
717+
errAsync(new AutoreplyPdfGenerationError()),
718+
)
719+
const sendMrfRespondentCopyEmailSpy = jest.spyOn(
720+
MailService,
721+
'sendMrfRespondentCopyEmail',
722+
)
723+
const emailFieldWithFormSummaryStep1 = {
724+
_id: new ObjectId().toHexString(),
725+
fieldType: BasicField.Email,
726+
title: 'Step 1 Email Field',
727+
autoReplyOptions: {
728+
hasAutoReply: true,
729+
includeFormSummary: true,
730+
autoReplySubject: 'Test Subject',
731+
autoReplyMessage: 'Test Message',
732+
autoReplySender: 'Test Sender',
733+
},
734+
}
735+
736+
const workflow = [
737+
{
738+
_id: new ObjectId().toHexString(),
739+
workflow_type: WorkflowType.Static,
740+
emails: [],
741+
edit: [emailFieldWithFormSummaryStep1._id],
742+
},
743+
]
744+
745+
// Act
746+
await performMultiRespondentPostSubmissionCreateActions({
747+
submission: {
748+
_id: mockSubmissionId,
749+
} as unknown as IMultirespondentSubmissionSchema,
750+
submissionId: mockSubmissionId,
751+
form: {
752+
_id: mockFormId,
753+
title: 'Test Form',
754+
form_fields: [emailFieldWithFormSummaryStep1],
755+
stepsToNotify: [workflow[0]._id],
756+
workflow,
757+
admin: {
758+
agency: {
759+
fullName: 'Government Technology Agency',
760+
},
761+
},
762+
} as unknown as IPopulatedMultirespondentForm,
763+
encryptedPayload: {
764+
encryptedContent: 'encryptedContent',
765+
version: 1,
766+
submissionPublicKey: 'submissionPublicKey',
767+
encryptedSubmissionSecretKey: 'encryptedSubmissionSecretKey',
768+
responses: {
769+
[emailFieldWithFormSummaryStep1._id]: {
770+
fieldType: BasicField.Email,
771+
answer: {
772+
773+
},
774+
},
775+
},
776+
} as MultirespondentSubmissionDto,
777+
logMeta: {} as any,
778+
attachments: MOCK_SUBMISSION_ATTACHMENTS,
779+
})
780+
781+
// Assert
782+
// that sent to correct destination emails
783+
expect(sendMrfRespondentCopyEmailSpy).toHaveBeenCalledTimes(1)
784+
expect(
785+
sendMrfRespondentCopyEmailSpy.mock.calls[0][0].autoReplyMailData
786+
.email,
787+
).toEqual('[email protected]')
788+
// still sends without pdf attachment
789+
expect(
790+
sendMrfRespondentCopyEmailSpy.mock.calls[0][0].attachments,
791+
).toEqual([...MOCK_SUBMISSION_ATTACHMENTS])
792+
})
709793
})
710794

711795
describe('subsequent steps', () => {
@@ -2101,6 +2185,62 @@ describe('multirespondent-submission.service', () => {
21012185
})
21022186

21032187
describe('mrf completion email notification when no approval step exists', () => {
2188+
it('sends completion email without pdf attachment when pdf generation fails', async () => {
2189+
// Arrange
2190+
MockMailUtils.generateAutoreplyPdf.mockReturnValue(
2191+
errAsync(new AutoreplyPdfGenerationError()),
2192+
)
2193+
const sendMrfWorkflowCompletionEmailSpy = jest.spyOn(
2194+
MailService,
2195+
'sendMrfWorkflowCompletionEmail',
2196+
)
2197+
const singleStepWorkflow: FormWorkflowStepDto[] = [
2198+
{
2199+
_id: new ObjectId().toHexString(),
2200+
workflow_type: WorkflowType.Static,
2201+
emails: [],
2202+
edit: [],
2203+
},
2204+
]
2205+
2206+
// Act
2207+
await performMultiRespondentPostSubmissionCreateActions({
2208+
submission: {
2209+
_id: mockSubmissionId,
2210+
} as unknown as IMultirespondentSubmissionSchema,
2211+
submissionId: mockSubmissionId,
2212+
form: {
2213+
_id: mockFormId,
2214+
workflow: singleStepWorkflow,
2215+
emails: ['[email protected]'],
2216+
} as IPopulatedMultirespondentForm,
2217+
encryptedPayload: {
2218+
encryptedContent: 'encryptedContent',
2219+
version: 1,
2220+
submissionPublicKey: 'submissionPublicKey',
2221+
encryptedSubmissionSecretKey: 'encryptedSubmissionSecretKey',
2222+
} as MultirespondentSubmissionDto,
2223+
attachments: MOCK_SUBMISSION_ATTACHMENTS,
2224+
logMeta: {} as any,
2225+
})
2226+
2227+
// Assert
2228+
expect(sendMrfWorkflowCompletionEmailSpy).toHaveBeenCalledTimes(1)
2229+
// pdf generation is invoked
2230+
expect(MockMailUtils.generateAutoreplyPdf).toHaveBeenCalledTimes(1)
2231+
// submission attachments is sent without pdf attachment
2232+
expect(
2233+
sendMrfWorkflowCompletionEmailSpy.mock.calls[0][0].attachments,
2234+
).toEqual([...MOCK_SUBMISSION_ATTACHMENTS])
2235+
// the correct destination emails are included
2236+
expect(
2237+
sendMrfWorkflowCompletionEmailSpy.mock.calls[0][0].emails,
2238+
).toContainValues(['[email protected]'])
2239+
expect(
2240+
sendMrfWorkflowCompletionEmailSpy.mock.calls[0][0].emails.length,
2241+
).toBe(1)
2242+
})
2243+
21042244
it('sends completion email with pdf attachment when single step mrf is completed', async () => {
21052245
// Arrange
21062246
const sendMrfWorkflowCompletionEmailSpy = jest.spyOn(

0 commit comments

Comments
 (0)