Skip to content

Commit 2718f5f

Browse files
test for rules stop (#111)
* test for rules stop * using JSON for rulestop test * Testcase refinement for rules stop * Update src/eligibility_signposting_api/model/rules.py Co-authored-by: Simon B <[email protected]> --------- Co-authored-by: Simon B <[email protected]>
1 parent a99c00d commit 2718f5f

File tree

3 files changed

+44
-31
lines changed

3 files changed

+44
-31
lines changed

src/eligibility_signposting_api/model/rules.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ class IterationRule(BaseModel):
100100
operator: RuleOperator = Field(..., alias="Operator")
101101
comparator: RuleComparator = Field(..., alias="Comparator")
102102
attribute_target: RuleAttributeTarget | None = Field(None, alias="AttributeTarget")
103-
rule_stop: RuleStop | None = Field(None, alias="RuleStop")
103+
rule_stop: RuleStop = Field(RuleStop(False), alias="RuleStop") # noqa: FBT003
104104

105105
@field_validator("rule_stop", mode="before")
106-
def parse_yn_to_bool(cls, v: str) -> bool: # noqa: N805
106+
def parse_yn_to_bool(cls, v: str | bool) -> bool: # noqa: N805
107107
if isinstance(v, str):
108108
return v.upper() == "Y"
109-
return False
109+
return v
110110

111111
model_config = {"populate_by_name": True, "extra": "ignore"}
112112

tests/unit/model/test_rules.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import json
2+
13
import pytest
24
from dateutil.relativedelta import relativedelta
35
from faker import Faker
6+
from hamcrest import assert_that
47

8+
from eligibility_signposting_api.model.rules import IterationRule
59
from tests.fixtures.builders.model.rule import IterationFactory, RawCampaignConfigFactory
10+
from tests.fixtures.matchers.rules import is_iteration_rule
611

712

813
def test_campaign_must_have_at_least_one_iteration():
@@ -59,3 +64,31 @@ def test_iteration_must_have_active_iteration_from_its_start(faker: Faker):
5964
r".*1st iteration starts later",
6065
):
6166
RawCampaignConfigFactory.build(start_date=start_date, iterations=[iteration])
67+
68+
69+
@pytest.mark.parametrize(
70+
("rule_stop", "expected"),
71+
[
72+
("Y", True),
73+
("N", False),
74+
("", False),
75+
(None, False),
76+
],
77+
)
78+
def test_iteration_rule_deserialisation(rule_stop: str, expected):
79+
# Given
80+
rule_json = f"""{{"Type": "F",
81+
"Name": "Exclude TOO YOUNG",
82+
"Description": "Exclude too Young less than 75 on the day of run",
83+
"Priority": 110,
84+
"AttributeLevel": "PERSON",
85+
"AttributeName": "DATE_OF_BIRTH",
86+
"Operator": "Y>",
87+
"Comparator": "-75",
88+
"RuleStop": "{rule_stop if rule_stop is not None else "null"}"}}"""
89+
90+
# When
91+
actual = IterationRule.model_validate(json.loads(rule_json))
92+
93+
# Then
94+
assert_that(actual, is_iteration_rule().with_rule_stop(expected))

tests/unit/services/calculators/test_eligibility_calculator.py

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from eligibility_signposting_api.model import rules
99
from eligibility_signposting_api.model import rules as rules_model
1010
from eligibility_signposting_api.model.eligibility import ConditionName, DateOfBirth, NHSNumber, Postcode, Status
11-
from eligibility_signposting_api.model.rules import IterationRule
1211
from eligibility_signposting_api.services.calculators.eligibility_calculator import EligibilityCalculator
1312
from tests.fixtures.builders.model import rule as rule_builder
1413
from tests.fixtures.builders.repos.person import person_rows_builder
@@ -817,49 +816,30 @@ def test_status_if_iteration_rules_contains_cohort_label_field(
817816
@pytest.mark.parametrize(
818817
("rule_stop", "expected_status", "test_comment"),
819818
[
820-
("Y", Status.not_actionable, "Stops at the first rule"),
821-
("N", Status.not_eligible, "Both the rules are executed"),
822-
("", Status.not_eligible, "Both the rules are executed"),
823-
(None, Status.not_eligible, "Both the rules are executed"),
819+
(True, Status.not_actionable, "Stops at the first rule"),
820+
(False, Status.not_eligible, "Both the rules are executed"),
824821
],
825822
)
826-
def test_rules_stop_behavior(rule_stop: str | None, expected_status: Status, test_comment: str, faker: Faker) -> None:
823+
def test_rules_stop_behavior(rule_stop: bool, expected_status: Status, test_comment: str, faker: Faker) -> None: # noqa: FBT001
827824
# Given
828825
nhs_number = NHSNumber(faker.nhs_number())
829826
date_of_birth = DateOfBirth(faker.date_of_birth(minimum_age=18, maximum_age=74))
830827
person_rows = person_rows_builder(nhs_number, date_of_birth=date_of_birth, cohorts=["cohort1"])
831828

832-
# Base rule template
833-
# Not using model factory to create Iteration rules since it sets boolean values for "Y"/"N"
834-
simple_age_data = {
835-
"Name": "Exclude too young less than 75",
836-
"Description": "Exclude too young less than 75",
837-
"AttributeLevel": "PERSON",
838-
"AttributeName": "DATE_OF_BIRTH",
839-
"Operator": "Y>",
840-
"Comparator": "-75",
841-
}
842-
843-
# Build rule variations
844-
rule_variants = [
845-
{"Type": "S", "Priority": 10, "RuleStop": rule_stop},
846-
{"Type": "S", "Priority": 10},
847-
{"Type": "F", "Priority": 15},
848-
]
849-
850-
iteration_rules = [IterationRule.model_validate({**simple_age_data, **variant}) for variant in rule_variants]
851-
852829
# Build campaign configuration
853830
campaign_config = rule_builder.CampaignConfigFactory.build(
854831
target="RSV",
855832
iterations=[
856833
rule_builder.IterationFactory.build(
857-
iteration_rules=[],
834+
iteration_rules=[
835+
rule_builder.PersonAgeSuppressionRuleFactory.build(priority=10, rule_stop=rule_stop),
836+
rule_builder.PersonAgeSuppressionRuleFactory.build(priority=10),
837+
rule_builder.PersonAgeSuppressionRuleFactory.build(type=rules.RuleType.filter, priority=15),
838+
],
858839
iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")],
859840
)
860841
],
861842
)
862-
campaign_config.iterations[0].iteration_rules.extend(iteration_rules)
863843

864844
calculator = EligibilityCalculator(person_rows, [campaign_config])
865845

0 commit comments

Comments
 (0)