66from freezegun import freeze_time
77from hamcrest import assert_that , empty , has_item
88
9- from eligibility_signposting_api .model .eligibility import ConditionName , DateOfBirth , NHSNumber , Status
10- from eligibility_signposting_api .model .rules import IterationDate , RuleType
9+ from eligibility_signposting_api .model .eligibility import ConditionName , DateOfBirth , NHSNumber , Postcode , Status
10+ from eligibility_signposting_api .model .rules import IterationDate , IterationRule , RuleComparator , RulePriority , RuleType
1111from eligibility_signposting_api .repos import EligibilityRepo , NotFoundError , RulesRepo
1212from eligibility_signposting_api .services import EligibilityService , UnknownPersonError
1313from tests .fixtures .builders .model import rule as rule_builder
@@ -359,9 +359,15 @@ def test_multiple_rule_types_cause_correct_status(faker: Faker):
359359 iterations = [
360360 rule_builder .IterationFactory .build (
361361 iteration_rules = [
362- rule_builder .PersonAgeSuppressionRuleFactory .build (type = RuleType .suppression ),
363- rule_builder .PersonAgeSuppressionRuleFactory .build (type = RuleType .filter ),
364- rule_builder .PersonAgeSuppressionRuleFactory .build (type = RuleType .suppression ),
362+ rule_builder .PersonAgeSuppressionRuleFactory .build (
363+ priority = RulePriority (5 ), type = RuleType .suppression
364+ ),
365+ rule_builder .PersonAgeSuppressionRuleFactory .build (
366+ priority = RulePriority (10 ), type = RuleType .filter
367+ ),
368+ rule_builder .PersonAgeSuppressionRuleFactory .build (
369+ priority = RulePriority (15 ), type = RuleType .suppression
370+ ),
365371 ],
366372 iteration_cohorts = [rule_builder .IterationCohortFactory .build (cohort_label = "cohort1" )],
367373 )
@@ -382,3 +388,95 @@ def test_multiple_rule_types_cause_correct_status(faker: Faker):
382388 has_item (is_condition ().with_condition_name (ConditionName ("RSV" )).and_status (Status .not_eligible ))
383389 ),
384390 )
391+
392+
393+ @pytest .mark .parametrize (
394+ ("test_comment" , "rule1" , "rule2" , "expected_status" ),
395+ [
396+ (
397+ "two rules, both exclude, same priority, should exclude" ,
398+ rule_builder .PersonAgeSuppressionRuleFactory .build (priority = RulePriority (5 )),
399+ rule_builder .PostcodeSuppressionRuleFactory .build (priority = RulePriority (5 )),
400+ Status .not_actionable ,
401+ ),
402+ (
403+ "two rules, rule 1 excludes, same priority, should allow" ,
404+ rule_builder .PersonAgeSuppressionRuleFactory .build (priority = RulePriority (5 )),
405+ rule_builder .PostcodeSuppressionRuleFactory .build (
406+ priority = RulePriority (5 ), comparator = RuleComparator ("NW1" )
407+ ),
408+ Status .actionable ,
409+ ),
410+ (
411+ "two rules, rule 2 excludes, same priority, should allow" ,
412+ rule_builder .PersonAgeSuppressionRuleFactory .build (
413+ priority = RulePriority (5 ), comparator = RuleComparator ("-65" )
414+ ),
415+ rule_builder .PostcodeSuppressionRuleFactory .build (priority = RulePriority (5 )),
416+ Status .actionable ,
417+ ),
418+ (
419+ "two rules, rule 1 excludes, different priority, should exclude" ,
420+ rule_builder .PersonAgeSuppressionRuleFactory .build (priority = RulePriority (5 )),
421+ rule_builder .PostcodeSuppressionRuleFactory .build (
422+ priority = RulePriority (10 ), comparator = RuleComparator ("NW1" )
423+ ),
424+ Status .not_actionable ,
425+ ),
426+ (
427+ "two rules, rule 2 excludes, different priority, should exclude" ,
428+ rule_builder .PersonAgeSuppressionRuleFactory .build (
429+ priority = RulePriority (5 ), comparator = RuleComparator ("-65" )
430+ ),
431+ rule_builder .PostcodeSuppressionRuleFactory .build (priority = RulePriority (10 )),
432+ Status .not_actionable ,
433+ ),
434+ (
435+ "two rules, both excludes, different priority, should exclude" ,
436+ rule_builder .PersonAgeSuppressionRuleFactory .build (priority = RulePriority (5 )),
437+ rule_builder .PostcodeSuppressionRuleFactory .build (priority = RulePriority (10 )),
438+ Status .not_actionable ,
439+ ),
440+ ],
441+ )
442+ def test_rules_with_same_priority_must_all_match_to_exclude (
443+ test_comment : str , rule1 : IterationRule , rule2 : IterationRule , expected_status : Status , faker : Faker
444+ ):
445+ # Given
446+ nhs_number = NHSNumber (f"5{ faker .random_int (max = 999999999 ):09d} " )
447+ date_of_birth = DateOfBirth (faker .date_of_birth (minimum_age = 66 , maximum_age = 74 ))
448+
449+ eligibility_repo = MagicMock (spec = EligibilityRepo )
450+ rules_repo = MagicMock (spec = RulesRepo )
451+ eligibility_repo .get_eligibility_data = MagicMock (
452+ return_value = eligibility_rows_builder (
453+ nhs_number , date_of_birth = date_of_birth , postcode = Postcode ("SW19 2BH" ), cohorts = ["cohort1" ]
454+ )
455+ )
456+ rules_repo .get_campaign_configs = MagicMock (
457+ return_value = [
458+ rule_builder .CampaignConfigFactory .build (
459+ target = "RSV" ,
460+ iterations = [
461+ rule_builder .IterationFactory .build (
462+ iteration_rules = [rule1 , rule2 ],
463+ iteration_cohorts = [rule_builder .IterationCohortFactory .build (cohort_label = "cohort1" )],
464+ )
465+ ],
466+ )
467+ ]
468+ )
469+
470+ service = EligibilityService (eligibility_repo , rules_repo )
471+
472+ # When
473+ actual = service .get_eligibility_status (NHSNumber (nhs_number ))
474+
475+ # Then
476+ assert_that (
477+ actual ,
478+ is_eligibility_status ().with_conditions (
479+ has_item (is_condition ().with_condition_name (ConditionName ("RSV" )).and_status (expected_status ))
480+ ),
481+ test_comment ,
482+ )
0 commit comments