@@ -2,12 +2,31 @@ import { SNSMessage, SQSEvent } from "aws-lambda";
22import pino from "pino" ;
33import { LetterRepository } from "internal/datastore/src" ;
44import { LetterRequestPreparedEventV2 } from "@nhsdigital/nhs-notify-event-schemas-letter-rendering" ;
5+ import { LetterRequestPreparedEvent } from "@nhsdigital/nhs-notify-event-schemas-letter-rendering-v1" ;
56import createUpsertLetterHandler from "../upsert-handler" ;
67import { Deps } from "../../config/deps" ;
78import { EnvVars } from "../../config/env" ;
89
10+ function createSqsRecord ( msgId : string , body : string ) {
11+ return {
12+ messageId : msgId ,
13+ receiptHandle : "" ,
14+ body,
15+ attributes : {
16+ ApproximateReceiveCount : "" ,
17+ SentTimestamp : "" ,
18+ SenderId : "" ,
19+ ApproximateFirstReceiveTimestamp : "" ,
20+ } ,
21+ messageAttributes : { } ,
22+ md5OfBody : "" ,
23+ eventSource : "" ,
24+ eventSourceARN : "" ,
25+ awsRegion : "" ,
26+ } ;
27+ }
928function createNotification (
10- event : LetterRequestPreparedEventV2 ,
29+ event : LetterRequestPreparedEventV2 | LetterRequestPreparedEvent ,
1130) : Partial < SNSMessage > {
1231 return {
1332 SignatureVersion : "" ,
@@ -25,7 +44,7 @@ function createNotification(
2544 } ;
2645}
2746
28- function createValidEvent (
47+ function createValidV2Event (
2948 overrides : Partial < any > = { } ,
3049) : LetterRequestPreparedEventV2 {
3150 // minimal valid event matching the prepared letter schema
@@ -65,12 +84,52 @@ function createValidEvent(
6584 } ;
6685}
6786
87+ function createValidV1Event (
88+ overrides : Partial < any > = { } ,
89+ ) : LetterRequestPreparedEvent {
90+ // minimal valid event matching the prepared letter schema
91+ const now = new Date ( ) . toISOString ( ) ;
92+
93+ return {
94+ specversion : "1.0" ,
95+ id : overrides . id ?? "7b9a03ca-342a-4150-b56b-989109c45613" ,
96+ source : "/data-plane/letter-rendering/test" ,
97+ subject : "client/client1/letter-request/letterRequest1" ,
98+ type : "uk.nhs.notify.letter-rendering.letter-request.prepared.v1" ,
99+ time : now ,
100+ dataschema :
101+ "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.1.0.0.schema.json" ,
102+ dataschemaversion : "1.0.0" ,
103+ data : {
104+ domainId : overrides . domainId ?? "letter1" ,
105+ letterVariantId : overrides . letterVariantId ?? "lv1" ,
106+ requestId : overrides . requestId ?? "request1" ,
107+ requestItemId : overrides . requestItemId ?? "requestItem1" ,
108+ requestItemPlanId : overrides . requestItemPlanId ?? "requestItemPlan1" ,
109+ clientId : overrides . clientId ?? "client1" ,
110+ campaignId : overrides . campaignId ?? "campaign1" ,
111+ templateId : overrides . templateId ?? "template1" ,
112+ url : overrides . url ?? "s3://letterDataBucket/letter1.pdf" ,
113+ sha256Hash :
114+ "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6" ,
115+ createdAt : now ,
116+ pageCount : 1 ,
117+ status : "PREPARED" ,
118+ } ,
119+ traceparent : "00-0af7651916cd43dd8448eb211c803191-b7ad6b7169203331-01" ,
120+ recordedtime : now ,
121+ severitynumber : 2 ,
122+ severitytext : "INFO" ,
123+ plane : "data" ,
124+ } ;
125+ }
126+
68127describe ( "createUpsertLetterHandler" , ( ) => {
69128 const mockedDeps : jest . Mocked < Deps > = {
70129 letterRepo : {
71130 upsertLetter : jest . fn ( ) ,
72131 } as unknown as LetterRepository ,
73- logger : { error : jest . fn ( ) } as unknown as pino . Logger ,
132+ logger : { error : jest . fn ( ) , info : jest . fn ( ) } as unknown as pino . Logger ,
74133 env : {
75134 LETTERS_TABLE_NAME : "LETTERS_TABLE_NAME" ,
76135 LETTER_TTL_HOURS : 12_960 ,
@@ -90,46 +149,22 @@ describe("createUpsertLetterHandler", () => {
90149 test ( "processes all records successfully and returns no batch failures" , async ( ) => {
91150 const evt : SQSEvent = {
92151 Records : [
93- {
94- messageId : "msg1" ,
95- receiptHandle : "rh1" ,
96- body : JSON . stringify ( createNotification ( createValidEvent ( ) ) ) ,
97- attributes : {
98- ApproximateReceiveCount : "" ,
99- SentTimestamp : "" ,
100- SenderId : "" ,
101- ApproximateFirstReceiveTimestamp : "" ,
102- } ,
103- messageAttributes : { } ,
104- md5OfBody : "" ,
105- eventSource : "" ,
106- eventSourceARN : "" ,
107- awsRegion : "" ,
108- } ,
109- {
110- messageId : "msg2" ,
111- receiptHandle : "rh2" ,
112- body : JSON . stringify (
152+ createSqsRecord (
153+ "msg1" ,
154+ JSON . stringify ( createNotification ( createValidV2Event ( ) ) ) ,
155+ ) ,
156+ createSqsRecord (
157+ "msg2" ,
158+ JSON . stringify (
113159 createNotification (
114- createValidEvent ( {
160+ createValidV2Event ( {
115161 id : "7b9a03ca-342a-4150-b56b-989109c45614" ,
116162 domainId : "letter2" ,
117163 url : "s3://letterDataBucket/letter2.pdf" ,
118164 } ) ,
119165 ) ,
120166 ) ,
121- attributes : {
122- ApproximateReceiveCount : "" ,
123- SentTimestamp : "" ,
124- SenderId : "" ,
125- ApproximateFirstReceiveTimestamp : "" ,
126- } ,
127- messageAttributes : { } ,
128- md5OfBody : "" ,
129- eventSource : "" ,
130- eventSourceARN : "" ,
131- awsRegion : "" ,
132- } ,
167+ ) ,
133168 ] ,
134169 } ;
135170
@@ -167,25 +202,25 @@ describe("createUpsertLetterHandler", () => {
167202 expect ( firstArg . source ) . toBe ( "/data-plane/letter-rendering/test" ) ;
168203 } ) ;
169204
170- test ( "invalid JSON body produces batch failure and logs error " , async ( ) => {
205+ test ( "processes all v1 records successfully and returns no batch failures " , async ( ) => {
171206 const evt : SQSEvent = {
172207 Records : [
173- {
174- messageId : "bad-json ",
175- receiptHandle : "rh1" ,
176- body : "this-is-not-json" ,
177- attributes : {
178- ApproximateReceiveCount : " ",
179- SentTimestamp : "" ,
180- SenderId : "" ,
181- ApproximateFirstReceiveTimestamp : "" ,
182- } ,
183- messageAttributes : { } ,
184- md5OfBody : "" ,
185- eventSource : "" ,
186- eventSourceARN : "" ,
187- awsRegion : "" ,
188- } ,
208+ createSqsRecord (
209+ "msg1 ",
210+ JSON . stringify ( createNotification ( createValidV1Event ( ) ) ) ,
211+ ) ,
212+ createSqsRecord (
213+ "msg2 ",
214+ JSON . stringify (
215+ createNotification (
216+ createValidV1Event ( {
217+ id : "7b9a03ca-342a-4150-b56b-989109c45614" ,
218+ domainId : "letter2" ,
219+ url : "s3://letterDataBucket/letter2.pdf " ,
220+ } ) ,
221+ ) ,
222+ ) ,
223+ ) ,
189224 ] ,
190225 } ;
191226
@@ -195,6 +230,45 @@ describe("createUpsertLetterHandler", () => {
195230 { } as any ,
196231 ) ;
197232
233+ expect ( result ) . toBeDefined ( ) ;
234+ if ( ! result ) throw new Error ( "expected BatchResponse, got void" ) ;
235+ expect ( result . batchItemFailures ) . toHaveLength ( 0 ) ;
236+
237+ expect ( mockedDeps . letterRepo . upsertLetter ) . toHaveBeenCalledTimes ( 2 ) ;
238+
239+ const firstArg = ( mockedDeps . letterRepo . upsertLetter as jest . Mock ) . mock
240+ . calls [ 0 ] [ 0 ] ;
241+ expect ( firstArg . id ) . toBe ( "letter1" ) ;
242+ expect ( firstArg . supplierId ) . toBe ( "supplier1" ) ;
243+ expect ( firstArg . specificationId ) . toBe ( "spec1" ) ;
244+ expect ( firstArg . url ) . toBe ( "s3://letterDataBucket/letter1.pdf" ) ;
245+ expect ( firstArg . status ) . toBe ( "PENDING" ) ;
246+ expect ( firstArg . groupId ) . toBe ( "client1campaign1template1" ) ;
247+ expect ( firstArg . source ) . toBe ( "/data-plane/letter-rendering/test" ) ;
248+
249+ const secondArg = ( mockedDeps . letterRepo . upsertLetter as jest . Mock ) . mock
250+ . calls [ 1 ] [ 0 ] ;
251+ expect ( secondArg . id ) . toBe ( "letter2" ) ;
252+ expect ( secondArg . supplierId ) . toBe ( "supplier1" ) ;
253+ expect ( secondArg . specificationId ) . toBe ( "spec1" ) ;
254+ expect ( secondArg . url ) . toBe ( "s3://letterDataBucket/letter2.pdf" ) ;
255+ expect ( secondArg . status ) . toBe ( "PENDING" ) ;
256+ expect ( secondArg . groupId ) . toBe ( "client1campaign1template1" ) ;
257+ expect ( secondArg . groupId ) . toBe ( "client1campaign1template1" ) ;
258+ expect ( firstArg . source ) . toBe ( "/data-plane/letter-rendering/test" ) ;
259+ } ) ;
260+
261+ test ( "invalid JSON body produces batch failure and logs error" , async ( ) => {
262+ const evt : SQSEvent = {
263+ Records : [ createSqsRecord ( "bad-json" , "this-is-not-json" ) ] ,
264+ } ;
265+
266+ const result = await createUpsertLetterHandler ( mockedDeps ) (
267+ evt ,
268+ { } as any ,
269+ { } as any ,
270+ ) ;
271+
198272 expect ( result ) . toBeDefined ( ) ;
199273 if ( ! result ) throw new Error ( "expected BatchResponse, got void" ) ;
200274 expect ( result . batchItemFailures ) . toHaveLength ( 1 ) ;
@@ -209,22 +283,10 @@ describe("createUpsertLetterHandler", () => {
209283 test ( "invalid notification schema produces batch failure and logs error" , async ( ) => {
210284 const evt : SQSEvent = {
211285 Records : [
212- {
213- messageId : "bad-schema" ,
214- receiptHandle : "rh1" ,
215- body : JSON . stringify ( { not : "the expected shape" } ) ,
216- attributes : {
217- ApproximateReceiveCount : "" ,
218- SentTimestamp : "" ,
219- SenderId : "" ,
220- ApproximateFirstReceiveTimestamp : "" ,
221- } ,
222- messageAttributes : { } ,
223- md5OfBody : "" ,
224- eventSource : "" ,
225- eventSourceARN : "" ,
226- awsRegion : "" ,
227- } ,
286+ createSqsRecord (
287+ "bad-schema" ,
288+ JSON . stringify ( { not : "the expected shape" } ) ,
289+ ) ,
228290 ] ,
229291 } ;
230292
@@ -248,25 +310,13 @@ describe("createUpsertLetterHandler", () => {
248310 test ( "invalid event schema produces batch failure and logs error" , async ( ) => {
249311 const evt : SQSEvent = {
250312 Records : [
251- {
252- messageId : "bad-schema" ,
253- receiptHandle : "rh1" ,
254- body : JSON . stringify ( {
313+ createSqsRecord (
314+ "bad-schema" ,
315+ JSON . stringify ( {
255316 Type : "Notification" ,
256317 Message : JSON . stringify ( { bad : "shape" } ) ,
257318 } ) ,
258- attributes : {
259- ApproximateReceiveCount : "" ,
260- SentTimestamp : "" ,
261- SenderId : "" ,
262- ApproximateFirstReceiveTimestamp : "" ,
263- } ,
264- messageAttributes : { } ,
265- md5OfBody : "" ,
266- eventSource : "" ,
267- eventSourceARN : "" ,
268- awsRegion : "" ,
269- } ,
319+ ) ,
270320 ] ,
271321 } ;
272322
@@ -281,6 +331,9 @@ describe("createUpsertLetterHandler", () => {
281331 expect ( result . batchItemFailures ) . toHaveLength ( 1 ) ;
282332 expect ( result . batchItemFailures [ 0 ] . itemIdentifier ) . toBe ( "bad-schema" ) ;
283333
334+ expect ( ( mockedDeps . logger . info as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ) . toBe (
335+ "Trying to parse message with V1 schema" ,
336+ ) ;
284337 expect ( ( mockedDeps . logger . error as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ) . toBe (
285338 "Error parsing letter event in upsert" ,
286339 ) ;
@@ -294,52 +347,28 @@ describe("createUpsertLetterHandler", () => {
294347
295348 const evt : SQSEvent = {
296349 Records : [
297- {
298- messageId : "ok-msg" ,
299- receiptHandle : "rh1" ,
300- body : JSON . stringify (
350+ createSqsRecord (
351+ "ok-msg" ,
352+ JSON . stringify (
301353 createNotification (
302- createValidEvent ( {
354+ createValidV2Event ( {
303355 id : "7b9a03ca-342a-4150-b56b-989109c45615" ,
304356 data : { domainId : "ok" } ,
305357 } ) ,
306358 ) ,
307359 ) ,
308- attributes : {
309- ApproximateReceiveCount : "" ,
310- SentTimestamp : "" ,
311- SenderId : "" ,
312- ApproximateFirstReceiveTimestamp : "" ,
313- } ,
314- messageAttributes : { } ,
315- md5OfBody : "" ,
316- eventSource : "" ,
317- eventSourceARN : "" ,
318- awsRegion : "" ,
319- } ,
320- {
321- messageId : "fail-msg" ,
322- receiptHandle : "rh2" ,
323- body : JSON . stringify (
360+ ) ,
361+ createSqsRecord (
362+ "fail-msg" ,
363+ JSON . stringify (
324364 createNotification (
325- createValidEvent ( {
365+ createValidV2Event ( {
326366 id : "7b9a03ca-342a-4150-b56b-989109c45616" ,
327367 data : { domainId : "ok" } ,
328368 } ) ,
329369 ) ,
330370 ) ,
331- attributes : {
332- ApproximateReceiveCount : "" ,
333- SentTimestamp : "" ,
334- SenderId : "" ,
335- ApproximateFirstReceiveTimestamp : "" ,
336- } ,
337- messageAttributes : { } ,
338- md5OfBody : "" ,
339- eventSource : "" ,
340- eventSourceARN : "" ,
341- awsRegion : "" ,
342- } ,
371+ ) ,
343372 ] ,
344373 } ;
345374
0 commit comments