@@ -653,6 +653,38 @@ def persisted_person_with_no_person_attribute_type(
653653 person_table .delete_item (Key = {"NHS_NUMBER" : row ["NHS_NUMBER" ], "ATTRIBUTE_TYPE" : row ["ATTRIBUTE_TYPE" ]})
654654
655655
656+ @pytest .fixture
657+ def person_with_covid_vaccination (
658+ person_table : Any , faker : Faker , hashing_service : HashingService
659+ ) -> Generator [eligibility_status .NHSNumber ]:
660+ """
661+ Fixture for a person with a COVID vaccination on 2026-01-28.
662+ Used for testing derived values (ADD_DAYS function).
663+ """
664+ nhs_num = faker .nhs_number ()
665+ nhs_number = eligibility_status .NHSNumber (nhs_num )
666+ nhs_num_hash = hashing_service .hash_with_current_secret (nhs_num )
667+
668+ date_of_birth = eligibility_status .DateOfBirth (faker .date_of_birth (minimum_age = 77 , maximum_age = 77 ))
669+
670+ for row in (
671+ rows := person_rows_builder (
672+ nhs_number = nhs_num_hash ,
673+ date_of_birth = date_of_birth ,
674+ postcode = "HP1" ,
675+ cohorts = ["cohort_label1" ],
676+ vaccines = {"COVID" : {"LAST_SUCCESSFUL_DATE" : "20260128" }},
677+ icb = "QE1" ,
678+ ).data
679+ ):
680+ person_table .put_item (Item = row )
681+
682+ yield nhs_number
683+
684+ for row in rows :
685+ person_table .delete_item (Key = {"NHS_NUMBER" : row ["NHS_NUMBER" ], "ATTRIBUTE_TYPE" : row ["ATTRIBUTE_TYPE" ]})
686+
687+
656688@pytest .fixture (scope = "session" )
657689def rules_bucket (s3_client : BaseClient ) -> Generator [BucketName ]:
658690 bucket_name = BucketName (os .getenv ("RULES_BUCKET_NAME" , "test-rules-bucket" ))
@@ -981,6 +1013,87 @@ def campaign_config_with_invalid_tokens(s3_client: BaseClient, rules_bucket: Buc
9811013 s3_client .delete_object (Bucket = rules_bucket , Key = f"{ campaign .name } .json" )
9821014
9831015
1016+ @pytest .fixture
1017+ def campaign_config_with_derived_values (s3_client : BaseClient , rules_bucket : BucketName ) -> Generator [CampaignConfig ]:
1018+ """Campaign config with derived values for testing ADD_DAYS function."""
1019+ campaign : CampaignConfig = rule .CampaignConfigFactory .build (
1020+ target = "COVID" ,
1021+ iterations = [
1022+ rule .IterationFactory .build (
1023+ default_comms_routing = "DERIVED_VALUES_TEST|DERIVED_VALUES_NEXT_DOSE" ,
1024+ actions_mapper = rule .ActionsMapperFactory .build (
1025+ root = {
1026+ "DERIVED_VALUES_TEST" : AvailableAction (
1027+ ActionType = "DataValue" ,
1028+ ExternalRoutingCode = "DateOfLastVaccination" ,
1029+ ActionDescription = "[[TARGET.COVID.LAST_SUCCESSFUL_DATE]]" ,
1030+ ),
1031+ "DERIVED_VALUES_NEXT_DOSE" : AvailableAction (
1032+ ActionType = "DataValue" ,
1033+ ExternalRoutingCode = "DateOfNextEarliestVaccination" ,
1034+ ActionDescription = "[[TARGET.COVID.NEXT_DOSE_DUE:ADD_DAYS(91)]]" ,
1035+ ),
1036+ }
1037+ ),
1038+ iteration_rules = [],
1039+ iteration_cohorts = [
1040+ rule .IterationCohortFactory .build (
1041+ cohort_label = "cohort_label1" ,
1042+ cohort_group = "cohort_group1" ,
1043+ positive_description = "Positive Description" ,
1044+ negative_description = "Negative Description" ,
1045+ )
1046+ ],
1047+ )
1048+ ],
1049+ )
1050+ campaign_data = {"CampaignConfig" : campaign .model_dump (by_alias = True )}
1051+ s3_client .put_object (
1052+ Bucket = rules_bucket , Key = f"{ campaign .name } .json" , Body = json .dumps (campaign_data ), ContentType = "application/json"
1053+ )
1054+ yield campaign
1055+ s3_client .delete_object (Bucket = rules_bucket , Key = f"{ campaign .name } .json" )
1056+
1057+
1058+ @pytest .fixture
1059+ def campaign_config_with_derived_values_formatted (
1060+ s3_client : BaseClient , rules_bucket : BucketName
1061+ ) -> Generator [CampaignConfig ]:
1062+ """Campaign config with derived values and date formatting."""
1063+ campaign : CampaignConfig = rule .CampaignConfigFactory .build (
1064+ target = "COVID" ,
1065+ iterations = [
1066+ rule .IterationFactory .build (
1067+ default_comms_routing = "DERIVED_VALUES_FORMATTED" ,
1068+ actions_mapper = rule .ActionsMapperFactory .build (
1069+ root = {
1070+ "DERIVED_VALUES_FORMATTED" : AvailableAction (
1071+ ActionType = "DataValue" ,
1072+ ExternalRoutingCode = "DateOfNextEarliestVaccination" ,
1073+ ActionDescription = "[[TARGET.COVID.NEXT_DOSE_DUE:ADD_DAYS(91):DATE(%d %B %Y)]]" ,
1074+ ),
1075+ }
1076+ ),
1077+ iteration_rules = [],
1078+ iteration_cohorts = [
1079+ rule .IterationCohortFactory .build (
1080+ cohort_label = "cohort_label1" ,
1081+ cohort_group = "cohort_group1" ,
1082+ positive_description = "Positive Description" ,
1083+ negative_description = "Negative Description" ,
1084+ )
1085+ ],
1086+ )
1087+ ],
1088+ )
1089+ campaign_data = {"CampaignConfig" : campaign .model_dump (by_alias = True )}
1090+ s3_client .put_object (
1091+ Bucket = rules_bucket , Key = f"{ campaign .name } .json" , Body = json .dumps (campaign_data ), ContentType = "application/json"
1092+ )
1093+ yield campaign
1094+ s3_client .delete_object (Bucket = rules_bucket , Key = f"{ campaign .name } .json" )
1095+
1096+
9841097@pytest .fixture (scope = "class" )
9851098def multiple_campaign_configs (s3_client : BaseClient , rules_bucket : BucketName ) -> Generator [list [CampaignConfig ]]:
9861099 """Create and upload multiple campaign configs to S3, then clean up after tests."""
0 commit comments