@@ -270,6 +270,7 @@ def validate_imms_delta_table_for_dpsfull_records(context):
270270 )
271271
272272
273+ @then ("The delta table will be populated with the correct data for reinstated record" )
273274@then ("The delta table will be populated with the correct data for all updated records in batch file" )
274275def validate_imms_delta_table_for_updated_records (context ):
275276 if context .delta_cache is None :
@@ -289,14 +290,63 @@ def validate_imms_delta_table_for_deleted_records(context):
289290 "The imms event table will be populated with the correct data for '{operation}' event for records in batch file"
290291 )
291292)
292- def validate_imms_event_table_for_all_records_in_batch_file (context , operation : Operation ):
293+ def validate_imms_event_table_for_given_operation_event (context , operation ):
294+ validate_imms_event_table_for_all_records_in_batch_file (context , operation )
295+
296+
297+ @then ("The imms event table will be populated with the correct data for reinstated record in batch file" )
298+ def validate_imms_event_table_for_reinstated_event (context ):
299+ validate_imms_event_table_for_all_records_in_batch_file (context , "updated" , reinstated = True )
300+
301+
302+ @then ("all rejected records are listed in the csv bus ack file and no imms id is generated" )
303+ def all_record_are_rejected_for_given_field_name (context ):
304+ file_rows = read_and_validate_csv_bus_ack_file_content (context )
305+ all_valid = validate_bus_ack_file_for_error (context , file_rows )
306+ assert all_valid , "One or more records failed validation checks"
307+
308+
309+ @then (parsers .parse ("MNS event will be triggered with correct data for all '{event_type}' events where NHS is not null" ))
310+ def mns_event_will_be_triggered_with_correct_data_for_created_events_in_batch_file (context , event_type ):
311+ if context .mns_validation_required .strip ().lower () != "true" :
312+ print (
313+ f"MNS event validation is skipped since mns_validation_required is set to { context .mns_validation_required } "
314+ )
315+ return
316+
317+ action = event_type .upper () if event_type .upper () in ["CREATE" , "UPDATE" ] else "CREATE"
318+
319+ df = context .vaccine_df .dropna (subset = ["IMMS_ID" ]).copy ()
320+ df ["IMMS_ID_CLEAN" ] = df ["IMMS_ID" ].astype (str ).str .replace ("Immunization#" , "" , regex = False )
321+
322+ valid_rows = list (df .itertuples (index = False ))
323+
324+ if not valid_rows :
325+ print ("No valid NHS rows found — skipping MNS validation." )
326+ return
327+
328+ mns_event_will_be_triggered_for_batch_record (context = context , action = action , valid_rows = valid_rows )
329+
330+
331+ @then ("Api updated event will trigger MNS event with correct data" )
332+ def mns_event_will_be_triggered_with_correct_data_for_api_updated_events (context ):
333+ mns_event_will_be_triggered_with_correct_data (context = context , action = "UPDATE" )
334+
335+
336+ def normalize (value ):
337+ return "" if pd .isna (value ) or value == "" else value
338+
339+
340+ def validate_imms_event_table_for_all_records_in_batch_file (context , operation : Operation , reinstated = False ):
293341 mapping = ActionMap [operation .lower ()]
294342 df = context .vaccine_df [context .vaccine_df ["ACTION_FLAG" ].str .lower () == mapping .action_flag .value .lower ()]
295343
296344 df ["UNIQUE_ID_COMBINED" ] = df ["UNIQUE_ID_URI" ].astype (str ) + "#" + df ["UNIQUE_ID" ].astype (str )
297345 valid_rows = df [df ["UNIQUE_ID_COMBINED" ].notnull () & (df ["UNIQUE_ID_COMBINED" ] != "nan#nan" )]
298346
299- for idx , row in valid_rows .iterrows ():
347+ unique_rows = valid_rows .drop_duplicates (subset = ["UNIQUE_ID_COMBINED" ])
348+
349+ for idx , row in unique_rows .iterrows ():
300350 unique_id_combined = row ["UNIQUE_ID_COMBINED" ]
301351 batch_record = {k : normalize (v ) for k , v in row .to_dict ().items ()}
302352
@@ -339,48 +389,28 @@ def validate_imms_event_table_for_all_records_in_batch_file(context, operation:
339389 ("Version" , int (context .expected_version ), int (item .get ("Version" ))),
340390 ]
341391
392+ actualDeletedAt = item .get ("DeletedAt" )
393+
342394 for name , expected , actual in fields_to_compare :
343395 check .is_true (expected == actual , f"Expected { name } : { expected } , Actual { actual } " )
344396
345- validate_to_compare_batch_record_with_event_table_record (context , batch_record , created_event )
346-
347-
348- @then ("all rejected records are listed in the csv bus ack file and no imms id is generated" )
349- def all_record_are_rejected_for_given_field_name (context ):
350- file_rows = read_and_validate_csv_bus_ack_file_content (context )
351- all_valid = validate_bus_ack_file_for_error (context , file_rows )
352- assert all_valid , "One or more records failed validation checks"
353-
354-
355- @then (parsers .parse ("MNS event will be triggered with correct data for all '{event_type}' events where NHS is not null" ))
356- def mns_event_will_be_triggered_with_correct_data_for_created_events_in_batch_file (context , event_type ):
357- if context .mns_validation_required .strip ().lower () != "true" :
358- print (
359- f"MNS event validation is skipped since mns_validation_required is set to { context .mns_validation_required } "
360- )
361- return
362-
363- action = event_type .upper () if event_type .upper () in ["CREATE" , "UPDATE" ] else "CREATE"
364-
365- df = context .vaccine_df .dropna (subset = ["IMMS_ID" ]).copy ()
366- df ["IMMS_ID_CLEAN" ] = df ["IMMS_ID" ].astype (str ).str .replace ("Immunization#" , "" , regex = False )
367-
368- valid_rows = list (df .itertuples (index = False ))
369-
370- if not valid_rows :
371- print ("No valid NHS rows found — skipping MNS validation." )
372- return
373-
374- mns_event_will_be_triggered_for_batch_record (context = context , action = action , valid_rows = valid_rows )
375-
376-
377- @then ("Api updated event will trigger MNS event with correct data" )
378- def mns_event_will_be_triggered_with_correct_data_for_api_updated_events (context ):
379- mns_event_will_be_triggered_with_correct_data (context = context , action = "UPDATE" )
380-
397+ if Operation [operation ].value == "DELETE" :
398+ check .is_true (
399+ actualDeletedAt is not None and actualDeletedAt > 0 ,
400+ f"Expected DeletedAt to be a Unix timestamp, got { actualDeletedAt } " ,
401+ )
402+ elif reinstated :
403+ check .is_true (
404+ actualDeletedAt == "reinstated" ,
405+ f"Expected DeletedAt: None for reinstated record, got { actualDeletedAt } " ,
406+ )
407+ else :
408+ check .is_true (
409+ actualDeletedAt is None ,
410+ f"Expected DeletedAt: None, Actual { actualDeletedAt } " ,
411+ )
381412
382- def normalize (value ):
383- return "" if pd .isna (value ) or value == "" else value
413+ validate_to_compare_batch_record_with_event_table_record (context , batch_record , created_event )
384414
385415
386416def create_batch_file (context , file_ext : str = "csv" , fileName : str = None , delimiter : str = "|" ):
@@ -450,19 +480,19 @@ def preload_delta_data(context):
450480 context .delta_cache [clean_id ] = {"rows" : group , "delta_items" : delta_items }
451481
452482
453- def validate_imms_delta_table_for_newly_created_records_in_batch_file (context ):
483+ def validate_imms_delta_table_for_newly_created_records_in_batch_file (context , expected_number_of_items = 1 ):
454484 for clean_id , data in context .delta_cache .items ():
455485 rows = data ["rows" ]
456486 delta_items = data ["delta_items" ]
457487
458488 create_items = [i for i in delta_items if i .get ("Operation" ) == "CREATE" ]
459489
460490 check .is_true (
461- len (create_items ) == 1 ,
462- f"Expected exactly 1 CREATE record for IMMS_ID { clean_id } , found { len (create_items )} " ,
491+ len (create_items ) == expected_number_of_items ,
492+ f"Expected exactly { expected_number_of_items } CREATE record(s) for IMMS_ID { clean_id } , found { len (create_items )} " ,
463493 )
464494
465- create_item = create_items [ 0 ]
495+ create_item = max ( create_items , key = lambda x : x . get ( "SequenceNumber" , - 1 ))
466496
467497 for _ , row in rows [rows ["ACTION_FLAG" ] == "NEW" ].iterrows ():
468498 batch_record = {k : normalize (v ) for k , v in row .to_dict ().items ()}
@@ -497,23 +527,29 @@ def validate_imms_delta_table_for_updated_records_in_batch_file(context):
497527 )
498528
499529
500- def validate_imms_delta_table_for_deleted_records_in_batch_file (context ):
530+ def validate_imms_delta_table_for_deleted_records_in_batch_file (context , expected_number_of_items = 1 ):
501531 for clean_id , data in context .delta_cache .items ():
502532 rows = data ["rows" ]
503533 delta_items = data ["delta_items" ]
504534
505- delete_item = next (( i for i in delta_items if i .get ("Operation" ) == "DELETE" ), None )
535+ delete_items = [ i for i in delta_items if i .get ("Operation" ) == "DELETE" ]
506536
507- check .is_true (delete_item , f"No DELETE record for IMMS_ID { clean_id } " )
537+ check .is_true (
538+ len (delete_items ) == expected_number_of_items ,
539+ f"Expected exactly { expected_number_of_items } DELETE record(s) for IMMS_ID { clean_id } , found { len (delete_items )} " ,
540+ )
541+
542+ delete_item = max (delete_items , key = lambda x : x .get ("SequenceNumber" , - 1 ))
508543
509544 delete_rows = rows [rows ["ACTION_FLAG" ] == "DELETE" ]
510545
511546 check .is_true (
512- len (delete_rows ) == 1 ,
513- f"Expected exactly 1 DELETE row in batch file for IMMS_ID { clean_id } , found { len (delete_rows )} " ,
547+ len (delete_rows ) == expected_number_of_items ,
548+ f"Expected exactly { expected_number_of_items } DELETE row(s) in batch file for IMMS_ID { clean_id } , found { len (delete_rows )} " ,
514549 )
515550
516551 row = delete_rows .iloc [0 ]
552+
517553 batch_record = {k : normalize (v ) for k , v in row .to_dict ().items ()}
518554
519555 validate_imms_delta_record_with_batch_record (
0 commit comments