@@ -11,13 +11,16 @@ import {constructPSUDataItem, constructPSUDataItemMessage} from "./testHelpers"
1111const mockAddPrescriptionMessagesToNotificationStateStore = jest . fn ( )
1212const mockClearCompletedSQSMessages = jest . fn ( )
1313const mockDrainQueue = jest . fn ( )
14+ const mockCheckCooldownForUpdate = jest . fn ( )
15+
1416jest . unstable_mockModule (
1517 "../src/utils" ,
1618 async ( ) => ( {
1719 __esModule : true ,
1820 drainQueue : mockDrainQueue ,
1921 addPrescriptionMessagesToNotificationStateStore : mockAddPrescriptionMessagesToNotificationStateStore ,
20- clearCompletedSQSMessages : mockClearCompletedSQSMessages
22+ clearCompletedSQSMessages : mockClearCompletedSQSMessages ,
23+ checkCooldownForUpdate : mockCheckCooldownForUpdate
2124 } )
2225)
2326
@@ -60,6 +63,7 @@ describe("Unit test for NHS Notify lambda handler", () => {
6063 mockDrainQueue . mockImplementation ( ( ) => Promise . resolve ( [ ] ) )
6164 await expect ( lambdaHandler ( mockEventBridgeEvent ) ) . resolves . not . toThrow ( )
6265
66+ expect ( mockCheckCooldownForUpdate ) . not . toHaveBeenCalled ( )
6367 expect ( mockInfo ) . toHaveBeenCalledWith ( "No messages to process" )
6468 } )
6569
@@ -72,9 +76,13 @@ describe("Unit test for NHS Notify lambda handler", () => {
7276 mockDrainQueue . mockImplementationOnce ( ( ) => Promise . resolve ( [ msg1 , msg2 ] ) )
7377 // deletion succeeds
7478 mockClearCompletedSQSMessages . mockImplementationOnce ( ( ) => Promise . resolve ( undefined ) )
79+ // Checking cooldown
80+ mockCheckCooldownForUpdate . mockImplementation ( ( ) => Promise . resolve ( true ) )
7581
7682 await expect ( lambdaHandler ( mockEventBridgeEvent ) ) . resolves . not . toThrow ( )
7783
84+ expect ( mockCheckCooldownForUpdate ) . toHaveBeenCalledTimes ( 2 )
85+
7886 // ensure we logged the fetched notifications
7987 expect ( mockInfo ) . toHaveBeenCalledWith (
8088 "Fetched prescription notification messages" ,
@@ -97,6 +105,7 @@ describe("Unit test for NHS Notify lambda handler", () => {
97105 const item = constructPSUDataItem ( { TaskID : "tx" , RequestID : "rx" } )
98106 const msg = constructPSUDataItemMessage ( { PSUDataItem : item } )
99107 mockDrainQueue . mockImplementationOnce ( ( ) => Promise . resolve ( [ msg ] ) )
108+ mockCheckCooldownForUpdate . mockImplementation ( ( ) => Promise . resolve ( true ) )
100109
101110 const deletionError = new Error ( "Delete failed" )
102111 mockClearCompletedSQSMessages . mockImplementationOnce ( ( ) => Promise . reject ( deletionError ) )
@@ -111,6 +120,7 @@ describe("Unit test for NHS Notify lambda handler", () => {
111120
112121 it ( "Throws and logs if addPrescriptionMessagesToNotificationStateStore fails" , async ( ) => {
113122 mockDrainQueue . mockImplementationOnce ( ( ) => Promise . resolve ( [ constructPSUDataItemMessage ( ) ] ) )
123+ mockCheckCooldownForUpdate . mockImplementation ( ( ) => Promise . resolve ( true ) )
114124 const thrownError = new Error ( "Failed" )
115125 mockAddPrescriptionMessagesToNotificationStateStore . mockImplementationOnce (
116126 ( ) => Promise . reject ( thrownError )
@@ -133,6 +143,7 @@ describe("Unit test for NHS Notify lambda handler", () => {
133143 mockDrainQueue . mockImplementation ( ( ) =>
134144 Promise . resolve ( [ message ] )
135145 )
146+ mockCheckCooldownForUpdate . mockImplementation ( ( ) => Promise . resolve ( true ) )
136147
137148 await expect ( lambdaHandler ( mockEventBridgeEvent ) ) . resolves . not . toThrow ( )
138149
@@ -151,4 +162,65 @@ describe("Unit test for NHS Notify lambda handler", () => {
151162 }
152163 )
153164 } )
165+
166+ it ( "Filters out messages inside cooldown" , async ( ) => {
167+ const fresh = constructPSUDataItem ( { RequestID : "fresh" , TaskID : "t1" } )
168+ const stale = constructPSUDataItem ( { RequestID : "stale" , TaskID : "t2" } )
169+ const msgFresh = constructPSUDataItemMessage ( { PSUDataItem : fresh } )
170+ const msgStale = constructPSUDataItemMessage ( { PSUDataItem : stale } )
171+
172+ mockDrainQueue . mockImplementation ( ( ) => Promise . resolve ( [ msgFresh , msgStale ] ) )
173+
174+ // returns true if the request ID is "fresh"
175+ mockCheckCooldownForUpdate . mockImplementation ( ( logger , update ) => {
176+ const u = update as { RequestID : string }
177+ return Promise . resolve ( u . RequestID === "fresh" )
178+ } )
179+
180+ mockClearCompletedSQSMessages . mockImplementation ( ( ) => Promise . resolve ( ) )
181+ mockAddPrescriptionMessagesToNotificationStateStore . mockImplementation ( ( ) => Promise . resolve ( ) )
182+
183+ await expect ( lambdaHandler ( mockEventBridgeEvent ) ) . resolves . not . toThrow ( )
184+
185+ // we should only persist & delete the fresh one
186+ expect ( mockAddPrescriptionMessagesToNotificationStateStore )
187+ . toHaveBeenCalledWith ( expect . any ( Object ) , [ msgFresh ] )
188+
189+ expect ( mockClearCompletedSQSMessages )
190+ . toHaveBeenCalledWith ( expect . any ( Object ) , [ msgFresh ] )
191+
192+ // and log how many were suppressed
193+ expect ( mockInfo ) . toHaveBeenCalledWith (
194+ "Suppressed 1 messages due to cooldown" ,
195+ { suppressedCount : 1 , totalFetched : 2 }
196+ )
197+ } )
198+
199+ it ( "Logs a message when all messages are inside cooldown" , async ( ) => {
200+ const stale = constructPSUDataItem ( { RequestID : "stale" , TaskID : "t1" } )
201+ const msgStale = constructPSUDataItemMessage ( { PSUDataItem : stale } )
202+
203+ mockDrainQueue . mockImplementation ( ( ) => Promise . resolve ( [ msgStale ] ) )
204+
205+ // returns true if the request ID is "fresh"
206+ mockCheckCooldownForUpdate . mockImplementation ( ( logger , update ) => {
207+ const u = update as { RequestID : string }
208+ return Promise . resolve ( u . RequestID === "fresh" )
209+ } )
210+
211+ mockClearCompletedSQSMessages . mockImplementation ( ( ) => Promise . resolve ( ) )
212+ mockAddPrescriptionMessagesToNotificationStateStore . mockImplementation ( ( ) => Promise . resolve ( ) )
213+
214+ await expect ( lambdaHandler ( mockEventBridgeEvent ) ) . resolves . not . toThrow ( )
215+
216+ expect ( mockAddPrescriptionMessagesToNotificationStateStore ) . not . toHaveBeenCalled ( )
217+ expect ( mockClearCompletedSQSMessages ) . not . toHaveBeenCalled ( )
218+
219+ // and log that everything was suppressed
220+ expect ( mockInfo )
221+ . toHaveBeenCalledWith (
222+ "All messages suppressed by cooldown; nothing to notify" ,
223+ { suppressedCount : 1 , totalFetched : 1 }
224+ )
225+ } )
154226} )
0 commit comments