@@ -111,6 +111,19 @@ def assert_audit_entry_status_equals(self, message_id: str, status: str) -> None
111111 actual_status = audit_entry .get ("status" , {}).get ("S" )
112112 self .assertEqual (actual_status , status )
113113
114+ def assert_audit_entry_counts_equal (self , message_id : str , expected_counts : dict ) -> None :
115+ """Checks the audit entry counts are as expected"""
116+ audit_entry = self .dynamodb_client .get_item (
117+ TableName = AUDIT_TABLE_NAME , Key = {"message_id" : {"S" : message_id }}
118+ ).get ("Item" )
119+
120+ actual_counts = {}
121+ actual_counts ["record_count" ] = audit_entry .get ("record_count" , {}).get ("N" )
122+ actual_counts ["records_succeeded" ] = audit_entry .get ("records_succeeded" , {}).get ("N" )
123+ actual_counts ["records_failed" ] = audit_entry .get ("records_failed" , {}).get ("N" )
124+
125+ self .assertEqual (actual_counts , expected_counts )
126+
114127 def test_lambda_handler_main_multiple_records (self ):
115128 """Test lambda handler with multiple records."""
116129 # Set up an audit entry which does not yet have record_count recorded
@@ -164,6 +177,11 @@ def test_lambda_handler_main_multiple_records(self):
164177 {"body" : json .dumps (array_of_mixed_success_and_failure_messages )},
165178 ]
166179 }
180+ expected_entry_counts = {
181+ "record_count" : None ,
182+ "records_succeeded" : None ,
183+ "records_failed" : "6" ,
184+ }
167185
168186 response = lambda_handler (event = event , context = {})
169187
@@ -177,6 +195,7 @@ def test_lambda_handler_main_multiple_records(self):
177195 ],
178196 existing_file_content = ValidValues .ack_headers ,
179197 )
198+ self .assert_audit_entry_counts_equal ("row" , expected_entry_counts )
180199
181200 def test_lambda_handler_main (self ):
182201 """Test lambda handler with consitent ack_file_name and message_template."""
@@ -217,12 +236,20 @@ def test_lambda_handler_main(self):
217236 "messages" : [{"row_id" : "row^1" , "diagnostics" : "SHOULD BE A DICTIONARY, NOT A STRING" }],
218237 },
219238 ]
239+ expected_records_failed = [None , "3" , "8" , "8" , "9" ]
240+ expected_entry_counts = {
241+ "record_count" : None ,
242+ "records_succeeded" : None ,
243+ "records_failed" : None ,
244+ }
220245
221- for test_case in test_cases :
246+ for test_case , expected_failures in zip (test_cases , expected_records_failed ):
247+ expected_entry_counts ["records_failed" ] = expected_failures
222248 # Test scenario where there is no existing ack file
223249 with self .subTest (msg = f"No existing ack file: { test_case ['description' ]} " ):
224250 response = lambda_handler (event = self .generate_event (test_case ["messages" ]), context = {})
225251 self .assertEqual (response , EXPECTED_ACK_LAMBDA_RESPONSE_FOR_SUCCESS )
252+ self .assert_audit_entry_counts_equal ("row" , expected_entry_counts )
226253 validate_ack_file_content (self .s3_client , test_case ["messages" ])
227254
228255 self .s3_client .delete_object (
@@ -252,6 +279,11 @@ def test_lambda_handler_updates_ack_file_but_does_not_mark_complete_when_records
252279 for i in range (1 , 4 )
253280 ]
254281 test_event = {"Records" : [{"body" : json .dumps (array_of_success_messages )}]}
282+ expected_entry_counts = {
283+ "record_count" : "100" ,
284+ "records_succeeded" : None ,
285+ "records_failed" : None ,
286+ }
255287
256288 response = lambda_handler (event = test_event , context = {})
257289
@@ -268,6 +300,7 @@ def test_lambda_handler_updates_ack_file_but_does_not_mark_complete_when_records
268300 is_complete = False ,
269301 )
270302 self .assert_audit_entry_status_equals (mock_batch_message_id , "Preprocessed" )
303+ self .assert_audit_entry_counts_equal (mock_batch_message_id , expected_entry_counts )
271304
272305 def test_lambda_handler_updates_ack_file_and_marks_complete_when_all_records_processed (self ):
273306 """
@@ -305,6 +338,11 @@ def test_lambda_handler_updates_ack_file_and_marks_complete_when_all_records_pro
305338 all_messages_plus_eof = deepcopy (array_of_success_messages )
306339 all_messages_plus_eof .append (MOCK_MESSAGE_DETAILS .eof_message )
307340 test_event = {"Records" : [{"body" : json .dumps (all_messages_plus_eof )}]}
341+ expected_entry_counts = {
342+ "record_count" : "100" ,
343+ "records_succeeded" : "100" ,
344+ "records_failed" : None ,
345+ }
308346
309347 response = lambda_handler (event = test_event , context = {})
310348
@@ -319,6 +357,61 @@ def test_lambda_handler_updates_ack_file_and_marks_complete_when_all_records_pro
319357 is_complete = True ,
320358 )
321359 self .assert_audit_entry_status_equals (mock_batch_message_id , "Processed" )
360+ self .assert_audit_entry_counts_equal (mock_batch_message_id , expected_entry_counts )
361+
362+ def test_lambda_handler_sets_records_succeeded (self ):
363+ """
364+ Test that the records_succeeded count is set when all records have been processed.
365+ """
366+ mock_batch_message_id = "75db20e6-c0b5-4012-a8bc-f861a1dd4b22"
367+
368+ # Original source file had 100 records
369+ add_audit_entry_to_table (self .dynamodb_client , mock_batch_message_id , record_count = 100 )
370+
371+ # Previous invocations have already created and added to the temp ack file
372+ existing_ack_content = generate_sample_existing_ack_content ()
373+
374+ self .s3_client .put_object (
375+ Bucket = BucketNames .DESTINATION ,
376+ Key = MOCK_MESSAGE_DETAILS .temp_ack_file_key ,
377+ Body = StringIO (existing_ack_content ).getvalue (),
378+ )
379+
380+ array_of_success_messages = [
381+ {
382+ ** BASE_SUCCESS_MESSAGE ,
383+ "row_id" : f"{ mock_batch_message_id } ^{ i } " ,
384+ "imms_id" : f"imms_{ i } " ,
385+ "local_id" : f"local^{ i } " ,
386+ }
387+ for i in range (50 , 75 )
388+ ]
389+ array_of_failure_messages = [
390+ {
391+ ** BASE_FAILURE_MESSAGE ,
392+ "row_id" : f"{ mock_batch_message_id } ^{ i } " ,
393+ "local_id" : f"local^{ i } " ,
394+ "diagnostics" : DiagnosticsDictionaries .UNHANDLED_ERROR ,
395+ }
396+ for i in range (75 , 100 )
397+ ]
398+
399+ # Include the EoF message in the event
400+ array_of_messages = deepcopy (array_of_success_messages ) + deepcopy (array_of_failure_messages )
401+ all_messages_plus_eof = array_of_messages
402+ all_messages_plus_eof .append (MOCK_MESSAGE_DETAILS .eof_message )
403+ test_event = {"Records" : [{"body" : json .dumps (all_messages_plus_eof )}]}
404+ expected_entry_counts = {
405+ "record_count" : "100" ,
406+ "records_succeeded" : "75" ,
407+ "records_failed" : "25" ,
408+ }
409+
410+ response = lambda_handler (event = test_event , context = {})
411+
412+ self .assertEqual (response , EXPECTED_ACK_LAMBDA_RESPONSE_FOR_SUCCESS )
413+ self .assert_audit_entry_status_equals (mock_batch_message_id , "Processed" )
414+ self .assert_audit_entry_counts_equal (mock_batch_message_id , expected_entry_counts )
322415
323416 def test_lambda_handler_error_scenarios (self ):
324417 """Test that the lambda handler raises appropriate exceptions for malformed event data."""
0 commit comments