11import { createHandler } from 'apis/sqs-trigger-lambda' ;
22import type { SQSEvent } from 'aws-lambda' ;
3- import { $TtlItemBusEvent , TtlItemBusEvent } from 'utils' ;
3+ import {
4+ ItemEnqueued ,
5+ MESHInboxMessageDownloaded ,
6+ } from 'digital-letters-events' ;
47import { randomUUID } from 'node:crypto' ;
58
69jest . mock ( 'node:crypto' , ( ) => ( {
@@ -18,35 +21,43 @@ describe('createHandler', () => {
1821 let logger : any ;
1922 let handler : any ;
2023
21- const validItem : TtlItemBusEvent = {
22- detail : {
23- profileversion : '1.0.0' ,
24- profilepublished : '2025-10' ,
25- id : '550e8400-e29b-41d4-a716-446655440001' ,
26- specversion : '1.0' ,
27- source :
28- '/nhs/england/notify/production/primary/data-plane/digital-letters' ,
29- subject :
30- 'customer/920fca11-596a-4eca-9c47-99f624614658/recipient/769acdd4-6a47-496f-999f-76a6fd2c3959' ,
31- type : 'uk.nhs.notify.digital.letters.sent.v1' ,
32- time : '2023-06-20T12:00:00Z' ,
33- recordedtime : '2023-06-20T12:00:00.250Z' ,
34- severitynumber : 2 ,
35- traceparent : '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01' ,
36- datacontenttype : 'application/json' ,
37- dataschema :
38- 'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10/digital-letter-base-data.schema.json' ,
39- dataschemaversion : '1.0' ,
40- severitytext : 'INFO' ,
41- data : {
42- messageUri : 'https://example.com/ttl/resource' ,
43- 'digital-letter-id' : '123e4567-e89b-12d3-a456-426614174000' ,
44- messageReference : 'ref1' ,
45- senderId : 'sender1' ,
46- } ,
24+ const messageDownloadedEvent : MESHInboxMessageDownloaded = {
25+ id : '550e8400-e29b-41d4-a716-446655440001' ,
26+ specversion : '1.0' ,
27+ source :
28+ '/nhs/england/notify/production/primary/data-plane/digitalletters/mesh' ,
29+ subject :
30+ 'customer/920fca11-596a-4eca-9c47-99f624614658/recipient/769acdd4-6a47-496f-999f-76a6fd2c3959' ,
31+ type : 'uk.nhs.notify.digital.letters.mesh.inbox.message.downloaded.v1' ,
32+ time : '2023-06-20T12:00:00Z' ,
33+ recordedtime : '2023-06-20T12:00:00.250Z' ,
34+ severitynumber : 2 ,
35+ traceparent : '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01' ,
36+ datacontenttype : 'application/json' ,
37+ dataschema :
38+ 'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-mesh-inbox-message-downloaded-data.schema.json' ,
39+ severitytext : 'INFO' ,
40+ data : {
41+ messageUri : 'https://example.com/ttl/resource' ,
42+ messageReference : 'ref1' ,
43+ senderId : 'sender1' ,
4744 } ,
4845 } ;
4946
47+ const eventBusEvent = {
48+ detail : messageDownloadedEvent ,
49+ } ;
50+
51+ const itemEnqueuedEvent : ItemEnqueued = {
52+ ...messageDownloadedEvent ,
53+ id : '550e8400-e29b-41d4-a716-446655440001' ,
54+ time : '2023-06-20T12:00:00.250Z' ,
55+ recordedtime : '2023-06-20T12:00:00.250Z' ,
56+ type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
57+ dataschema :
58+ 'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-queue-item-enqueued-data.schema.json' ,
59+ } ;
60+
5061 beforeEach ( ( ) => {
5162 createTtl = { send : jest . fn ( ) } ;
5263 eventPublisher = { sendEvents : jest . fn ( ) . mockResolvedValue ( [ ] ) } ;
@@ -55,27 +66,16 @@ describe('createHandler', () => {
5566 } ) ;
5667
5768 it ( 'processes a valid SQS event and returns success' , async ( ) => {
58- jest
59- . spyOn ( $TtlItemBusEvent , 'safeParse' )
60- . mockReturnValue ( { success : true , data : validItem } ) ;
6169 createTtl . send . mockResolvedValue ( 'sent' ) ;
6270 const event : SQSEvent = {
63- Records : [ { body : JSON . stringify ( validItem ) , messageId : 'msg1' } ] ,
71+ Records : [ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg1' } ] ,
6472 } as any ;
6573
6674 const res = await handler ( event ) ;
6775
6876 expect ( res . batchItemFailures ) . toEqual ( [ ] ) ;
69- expect ( createTtl . send ) . toHaveBeenCalledWith ( validItem . detail ) ;
70- expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [
71- {
72- ...validItem . detail ,
73- id : '550e8400-e29b-41d4-a716-446655440001' ,
74- time : '2023-06-20T12:00:00.250Z' ,
75- recordedtime : '2023-06-20T12:00:00.250Z' ,
76- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
77- } ,
78- ] ) ;
77+ expect ( createTtl . send ) . toHaveBeenCalledWith ( messageDownloadedEvent ) ;
78+ expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [ itemEnqueuedEvent ] ) ;
7979 expect ( logger . info ) . toHaveBeenCalledWith ( {
8080 description : 'Processed SQS Event.' ,
8181 failed : 0 ,
@@ -85,10 +85,6 @@ describe('createHandler', () => {
8585 } ) ;
8686
8787 it ( 'handles parse failure and logs error' , async ( ) => {
88- const zodError = { errors : [ ] } as any ;
89- jest
90- . spyOn ( $TtlItemBusEvent , 'safeParse' )
91- . mockReturnValue ( { success : false , error : zodError } ) ;
9288 const event : SQSEvent = {
9389 Records : [ { body : '{}' , messageId : 'msg2' } ] ,
9490 } as any ;
@@ -110,12 +106,9 @@ describe('createHandler', () => {
110106 } ) ;
111107
112108 it ( 'handles createTtl.send failure' , async ( ) => {
113- jest
114- . spyOn ( $TtlItemBusEvent , 'safeParse' )
115- . mockReturnValue ( { success : true , data : validItem } ) ;
116109 createTtl . send . mockResolvedValue ( 'failed' ) ;
117110 const event : SQSEvent = {
118- Records : [ { body : JSON . stringify ( validItem ) , messageId : 'msg3' } ] ,
111+ Records : [ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg3' } ] ,
119112 } as any ;
120113
121114 const res = await handler ( event ) ;
@@ -130,11 +123,9 @@ describe('createHandler', () => {
130123 } ) ;
131124
132125 it ( 'handles thrown error and logs' , async ( ) => {
133- jest . spyOn ( $TtlItemBusEvent , 'safeParse' ) . mockImplementation ( ( ) => {
134- throw new Error ( 'bad json' ) ;
135- } ) ;
126+ createTtl . send . mockRejectedValue ( new Error ( 'TTL service error' ) ) ;
136127 const event : SQSEvent = {
137- Records : [ { body : '{}' , messageId : 'msg4' } ] ,
128+ Records : [ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg4' } ] ,
138129 } as any ;
139130
140131 const res = await handler ( event ) ;
@@ -181,15 +172,12 @@ describe('createHandler', () => {
181172 } ) ;
182173
183174 it ( 'processes multiple successful events and sends them as a batch' , async ( ) => {
184- jest
185- . spyOn ( $TtlItemBusEvent , 'safeParse' )
186- . mockReturnValue ( { success : true , data : validItem } ) ;
187175 createTtl . send . mockResolvedValue ( 'sent' ) ;
188176 const sqsEvent : SQSEvent = {
189177 Records : [
190- { body : JSON . stringify ( validItem ) , messageId : 'msg1' } ,
191- { body : JSON . stringify ( validItem ) , messageId : 'msg2' } ,
192- { body : JSON . stringify ( validItem ) , messageId : 'msg3' } ,
178+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg1' } ,
179+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg2' } ,
180+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg3' } ,
193181 ] ,
194182 } as any ;
195183
@@ -198,27 +186,9 @@ describe('createHandler', () => {
198186 expect ( res . batchItemFailures ) . toEqual ( [ ] ) ;
199187 expect ( createTtl . send ) . toHaveBeenCalledTimes ( 3 ) ;
200188 expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [
201- {
202- ...validItem . detail ,
203- id : '550e8400-e29b-41d4-a716-446655440001' ,
204- time : '2023-06-20T12:00:00.250Z' ,
205- recordedtime : '2023-06-20T12:00:00.250Z' ,
206- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
207- } ,
208- {
209- ...validItem . detail ,
210- id : '550e8400-e29b-41d4-a716-446655440001' ,
211- time : '2023-06-20T12:00:00.250Z' ,
212- recordedtime : '2023-06-20T12:00:00.250Z' ,
213- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
214- } ,
215- {
216- ...validItem . detail ,
217- id : '550e8400-e29b-41d4-a716-446655440001' ,
218- time : '2023-06-20T12:00:00.250Z' ,
219- recordedtime : '2023-06-20T12:00:00.250Z' ,
220- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
221- } ,
189+ itemEnqueuedEvent ,
190+ itemEnqueuedEvent ,
191+ itemEnqueuedEvent ,
222192 ] ) ;
223193 expect ( logger . info ) . toHaveBeenCalledWith ( {
224194 description : 'Processed SQS Event.' ,
@@ -229,38 +199,23 @@ describe('createHandler', () => {
229199 } ) ;
230200
231201 it ( 'handles partial event publishing failures and logs warning' , async ( ) => {
232- jest
233- . spyOn ( $TtlItemBusEvent , 'safeParse' )
234- . mockReturnValue ( { success : true , data : validItem } ) ;
235202 createTtl . send . mockResolvedValue ( 'sent' ) ;
236- const failedEvents = [ validItem ] ;
203+ const failedEvents = [ messageDownloadedEvent ] ;
237204 eventPublisher . sendEvents . mockResolvedValue ( failedEvents ) ;
238205
239206 const event : SQSEvent = {
240207 Records : [
241- { body : JSON . stringify ( validItem ) , messageId : 'msg1' } ,
242- { body : JSON . stringify ( validItem ) , messageId : 'msg2' } ,
208+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg1' } ,
209+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg2' } ,
243210 ] ,
244211 } as any ;
245212
246213 const res = await handler ( event ) ;
247214
248215 expect ( res . batchItemFailures ) . toEqual ( [ ] ) ;
249216 expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [
250- {
251- ...validItem . detail ,
252- id : '550e8400-e29b-41d4-a716-446655440001' ,
253- time : '2023-06-20T12:00:00.250Z' ,
254- recordedtime : '2023-06-20T12:00:00.250Z' ,
255- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
256- } ,
257- {
258- ...validItem . detail ,
259- id : '550e8400-e29b-41d4-a716-446655440001' ,
260- time : '2023-06-20T12:00:00.250Z' ,
261- recordedtime : '2023-06-20T12:00:00.250Z' ,
262- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
263- } ,
217+ itemEnqueuedEvent ,
218+ itemEnqueuedEvent ,
264219 ] ) ;
265220 expect ( logger . warn ) . toHaveBeenCalledWith ( {
266221 description : 'Some events failed to publish' ,
@@ -270,29 +225,18 @@ describe('createHandler', () => {
270225 } ) ;
271226
272227 it ( 'handles event publishing exception and logs warning' , async ( ) => {
273- jest
274- . spyOn ( $TtlItemBusEvent , 'safeParse' )
275- . mockReturnValue ( { success : true , data : validItem } ) ;
276228 createTtl . send . mockResolvedValue ( 'sent' ) ;
277229 const publishError = new Error ( 'EventBridge error' ) ;
278230 eventPublisher . sendEvents . mockRejectedValue ( publishError ) ;
279231
280232 const event : SQSEvent = {
281- Records : [ { body : JSON . stringify ( validItem ) , messageId : 'msg1' } ] ,
233+ Records : [ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg1' } ] ,
282234 } as any ;
283235
284236 const res = await handler ( event ) ;
285237
286238 expect ( res . batchItemFailures ) . toEqual ( [ ] ) ;
287- expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [
288- {
289- ...validItem . detail ,
290- id : '550e8400-e29b-41d4-a716-446655440001' ,
291- time : '2023-06-20T12:00:00.250Z' ,
292- recordedtime : '2023-06-20T12:00:00.250Z' ,
293- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
294- } ,
295- ] ) ;
239+ expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [ itemEnqueuedEvent ] ) ;
296240 expect ( logger . warn ) . toHaveBeenCalledWith ( {
297241 err : publishError ,
298242 description : 'Failed to send events to EventBridge' ,
@@ -301,13 +245,10 @@ describe('createHandler', () => {
301245 } ) ;
302246
303247 it ( 'does not call eventPublisher when no successful events' , async ( ) => {
304- jest
305- . spyOn ( $TtlItemBusEvent , 'safeParse' )
306- . mockReturnValue ( { success : true , data : validItem } ) ;
307248 createTtl . send . mockResolvedValue ( 'failed' ) ;
308249
309250 const event : SQSEvent = {
310- Records : [ { body : JSON . stringify ( validItem ) , messageId : 'msg1' } ] ,
251+ Records : [ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg1' } ] ,
311252 } as any ;
312253
313254 const res = await handler ( event ) ;
@@ -323,20 +264,15 @@ describe('createHandler', () => {
323264 } ) ;
324265
325266 it ( 'handles mixed success and failure scenarios' , async ( ) => {
326- jest
327- . spyOn ( $TtlItemBusEvent , 'safeParse' )
328- . mockReturnValueOnce ( { success : true , data : validItem } )
329- . mockReturnValueOnce ( { success : false , error : { errors : [ ] } as any } )
330- . mockReturnValueOnce ( { success : true , data : validItem } ) ;
331267 createTtl . send
332268 . mockResolvedValueOnce ( 'sent' )
333269 . mockResolvedValueOnce ( 'failed' ) ;
334270
335271 const event : SQSEvent = {
336272 Records : [
337- { body : JSON . stringify ( validItem ) , messageId : 'msg1' } ,
273+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg1' } ,
338274 { body : '{}' , messageId : 'msg2' } ,
339- { body : JSON . stringify ( validItem ) , messageId : 'msg3' } ,
275+ { body : JSON . stringify ( eventBusEvent ) , messageId : 'msg3' } ,
340276 ] ,
341277 } as any ;
342278
@@ -346,15 +282,7 @@ describe('createHandler', () => {
346282 { itemIdentifier : 'msg2' } ,
347283 { itemIdentifier : 'msg3' } ,
348284 ] ) ;
349- expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [
350- {
351- ...validItem . detail ,
352- id : '550e8400-e29b-41d4-a716-446655440001' ,
353- time : '2023-06-20T12:00:00.250Z' ,
354- recordedtime : '2023-06-20T12:00:00.250Z' ,
355- type : 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1' ,
356- } ,
357- ] ) ;
285+ expect ( eventPublisher . sendEvents ) . toHaveBeenCalledWith ( [ itemEnqueuedEvent ] ) ;
358286 expect ( logger . info ) . toHaveBeenCalledWith ( {
359287 description : 'Processed SQS Event.' ,
360288 failed : 2 ,
0 commit comments