Skip to content

Commit 053269c

Browse files
Eli 786/status text integration tests and config (#659)
* adding unit tests * updated tests * added more test scenarios * added rule parameters * ordered test scenarios added postcode rule * Removing commented tests * fixed parameterized actions * cleaned filter and supression rules * Tidied up action rules * Removed test_comment parameter and added id for each test * removed duplicate scenario * updated duplicate test * corrected test comments * updated id names * updated to add scenario ids coupled with each scenario * added integration test * added integration test * updated override unit tests, added audit log test * added audit log test to integration test * fixed format issues * added new test config --------- Co-authored-by: ThomasH-NHS <thomas.hunt14@nhs.net>
1 parent 78902f1 commit 053269c

5 files changed

Lines changed: 1059 additions & 0 deletions

File tree

tests/fixtures/builders/model/rule.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,16 @@ class ICBNonActionableActionRuleFactory(IterationRuleFactory):
255255
attribute_name = RuleAttributeName("ICB")
256256
comparator = RuleComparator("QE1")
257257
comms_routing = CommsRouting("ActionCode1")
258+
259+
260+
class PostcodeNonActionableRuleFactory(IterationRuleFactory):
261+
type = RuleType.not_actionable_actions
262+
name = RuleName("Excluded postcode In SW19")
263+
code = None
264+
description = RuleDescription("In SW19")
265+
priority = RulePriority(20)
266+
operator = RuleOperator.starts_with
267+
attribute_level = RuleAttributeLevel.PERSON
268+
attribute_name = RuleAttributeName("POSTCODE")
269+
comparator = RuleComparator("SW19")
270+
comms_routing = CommsRouting("ActionCode1")

tests/integration/conftest.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,55 @@ def campaign_config_with_custom_target_attributes(
11231123
s3_client.delete_object(Bucket=rules_bucket, Key=f"{campaign.name}.json")
11241124

11251125

1126+
@pytest.fixture
1127+
def campaign_config_with_status_text_override(
1128+
s3_client: BaseClient, rules_bucket: BucketName
1129+
) -> Generator[CampaignConfig]:
1130+
"""Campaign config that overrides status text via a no-render action."""
1131+
campaign: CampaignConfig = rule.CampaignConfigFactory.build(
1132+
target="COVID",
1133+
iterations=[
1134+
rule.IterationFactory.build(
1135+
default_comms_routing="VISIBLE_ACTION|STATUS_TEXT_OVERRIDE",
1136+
status_text=StatusText(
1137+
NotEligible="Original not eligible text",
1138+
NotActionable="Original not actionable text",
1139+
Actionable="Original actionable text",
1140+
),
1141+
actions_mapper=rule.ActionsMapperFactory.build(
1142+
root={
1143+
"VISIBLE_ACTION": AvailableAction(
1144+
ActionType="DataValue",
1145+
ExternalRoutingCode="VisibleAction",
1146+
ActionDescription="Visible action description",
1147+
),
1148+
"STATUS_TEXT_OVERRIDE": AvailableAction(
1149+
ActionType="norender_StatusTextOverride",
1150+
ExternalRoutingCode="StatusTextOverride",
1151+
ActionDescription="Overridden actionable status text",
1152+
),
1153+
}
1154+
),
1155+
iteration_rules=[],
1156+
iteration_cohorts=[
1157+
rule.IterationCohortFactory.build(
1158+
cohort_label="cohort_label1",
1159+
cohort_group="cohort_group1",
1160+
positive_description="Positive Description",
1161+
negative_description="Negative Description",
1162+
)
1163+
],
1164+
)
1165+
],
1166+
)
1167+
campaign_data = {"CampaignConfig": campaign.model_dump(by_alias=True)}
1168+
s3_client.put_object(
1169+
Bucket=rules_bucket, Key=f"{campaign.name}.json", Body=json.dumps(campaign_data), ContentType="application/json"
1170+
)
1171+
yield campaign
1172+
s3_client.delete_object(Bucket=rules_bucket, Key=f"{campaign.name}.json")
1173+
1174+
11261175
@pytest.fixture
11271176
def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) -> Generator[list[CampaignConfig]]:
11281177
"""Create and upload multiple campaign configs to S3, then clean up after tests."""
@@ -1499,6 +1548,20 @@ def consumer_to_active_campaign_config_with_custom_target_attributes_mapping(
14991548
s3_client.delete_object(Bucket=consumer_mapping_bucket, Key="consumer_mapping.json")
15001549

15011550

1551+
@pytest.fixture
1552+
def consumer_to_active_campaign_config_with_status_text_override_mapping(
1553+
s3_client: BaseClient,
1554+
consumer_mapping_bucket: ConsumerMapping,
1555+
campaign_config_with_status_text_override: CampaignConfig,
1556+
consumer_id: ConsumerId,
1557+
):
1558+
consumer_mapping = create_and_put_consumer_mapping_in_s3(
1559+
campaign_config_with_status_text_override, consumer_id, consumer_mapping_bucket, s3_client
1560+
)
1561+
yield consumer_mapping
1562+
s3_client.delete_object(Bucket=consumer_mapping_bucket, Key="consumer_mapping_config.json")
1563+
1564+
15021565
@pytest.fixture
15031566
def consumer_to_campaign_having_inactive_iteration_mapping(
15041567
s3_client: BaseClient,
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import json
2+
from http import HTTPStatus
3+
4+
from botocore.client import BaseClient
5+
from brunns.matchers.data import json_matching as is_json_that
6+
from brunns.matchers.werkzeug import is_werkzeug_response as is_response
7+
from flask.testing import FlaskClient
8+
from hamcrest import assert_that, equal_to, has_entries, has_item, has_key, is_, is_not, none
9+
10+
from eligibility_signposting_api.model.consumer_mapping import ConsumerId, ConsumerMapping
11+
from eligibility_signposting_api.model.eligibility_status import NHSNumber
12+
from eligibility_signposting_api.repos.campaign_repo import BucketName
13+
from tests.integration.conftest import UNIQUE_CONSUMER_HEADER, bridge_latest_kinesis_record_to_firehose
14+
15+
16+
class TestStatusTextOverride:
17+
def test_status_text_override_replaces_iteration_status_text_and_is_not_returned_as_action( # noqa: PLR0913
18+
self,
19+
client: FlaskClient,
20+
audit_bucket: BucketName,
21+
s3_client: BaseClient,
22+
person_with_covid_vaccination: NHSNumber,
23+
consumer_id: ConsumerId,
24+
kinesis_client,
25+
firehose_client,
26+
consumer_to_active_campaign_config_with_status_text_override_mapping: ConsumerMapping, # noqa: ARG002
27+
secretsmanager_client: BaseClient, # noqa: ARG002
28+
):
29+
headers = {
30+
"nhs-login-nhs-number": str(person_with_covid_vaccination),
31+
UNIQUE_CONSUMER_HEADER: str(consumer_id),
32+
}
33+
34+
response = client.get(
35+
f"/patient-check/{person_with_covid_vaccination}?includeActions=Y",
36+
headers=headers,
37+
)
38+
39+
assert_that(
40+
response,
41+
is_response().with_status_code(HTTPStatus.OK).and_text(is_json_that(has_key("processedSuggestions"))),
42+
)
43+
44+
body = response.get_json()
45+
assert_that(body, is_not(none()))
46+
processed_suggestions = body.get("processedSuggestions", [])
47+
48+
covid_suggestion = next(
49+
(suggestion for suggestion in processed_suggestions if suggestion.get("condition") == "COVID"),
50+
None,
51+
)
52+
assert_that(covid_suggestion, is_not(none()))
53+
assert covid_suggestion["status"] == "Actionable"
54+
assert covid_suggestion["statusText"] == "Overridden actionable status text"
55+
56+
actions = covid_suggestion.get("actions", [])
57+
assert_that(
58+
actions,
59+
has_item(
60+
has_entries(
61+
actionType="DataValue",
62+
actionCode="VisibleAction",
63+
description="Visible action description",
64+
)
65+
),
66+
)
67+
assert all(action.get("actionType") != "norender_StatusTextOverride" for action in actions)
68+
69+
bridge_latest_kinesis_record_to_firehose(
70+
kinesis_client=kinesis_client,
71+
kinesis_stream_name="test-kinesis-audit-stream",
72+
firehose_client=firehose_client,
73+
firehose_delivery_stream_name="test_firehose_audit_stream_to_s3",
74+
)
75+
76+
# Then
77+
objects = s3_client.list_objects_v2(Bucket=audit_bucket).get("Contents", [])
78+
object_keys = [obj["Key"] for obj in objects]
79+
latest_key = sorted(object_keys)[-1]
80+
audit_data = json.loads(s3_client.get_object(Bucket=audit_bucket, Key=latest_key)["Body"].read())
81+
82+
status_text_override = "Overridden actionable status text"
83+
status_text = "Overridden actionable status text"
84+
85+
assert_that(len(audit_data["response"]["condition"]), equal_to(1))
86+
assert_that(audit_data["response"]["condition"][0].get("statusTextOverride"), equal_to(status_text_override))
87+
assert_that(audit_data["response"]["condition"][0].get("statusText"), equal_to(status_text))
88+
89+
def test_no_status_text_override_as_no_action( # noqa: PLR0913
90+
self,
91+
client: FlaskClient,
92+
audit_bucket: BucketName,
93+
s3_client: BaseClient,
94+
person_with_covid_vaccination: NHSNumber,
95+
consumer_id: ConsumerId,
96+
kinesis_client,
97+
firehose_client,
98+
consumer_to_active_campaign_config_with_status_text_override_mapping: ConsumerMapping, # noqa: ARG002
99+
secretsmanager_client: BaseClient, # noqa: ARG002
100+
):
101+
headers = {
102+
"nhs-login-nhs-number": str(person_with_covid_vaccination),
103+
UNIQUE_CONSUMER_HEADER: str(consumer_id),
104+
}
105+
106+
response = client.get(
107+
f"/patient-check/{person_with_covid_vaccination}?includeActions=N",
108+
headers=headers,
109+
)
110+
111+
assert_that(
112+
response,
113+
is_response().with_status_code(HTTPStatus.OK).and_text(is_json_that(has_key("processedSuggestions"))),
114+
)
115+
116+
body = response.get_json()
117+
assert_that(body, is_not(none()))
118+
processed_suggestions = body.get("processedSuggestions", [])
119+
120+
covid_suggestion = next(
121+
(suggestion for suggestion in processed_suggestions if suggestion.get("condition") == "COVID"),
122+
None,
123+
)
124+
assert_that(covid_suggestion, is_not(none()))
125+
assert covid_suggestion["status"] == "Actionable"
126+
assert covid_suggestion["statusText"] == "Original actionable text"
127+
128+
actions = covid_suggestion.get("actions", [])
129+
130+
assert_that(actions, is_([]))
131+
132+
assert all(action.get("actionType") != "norender_StatusTextOverride" for action in actions)
133+
134+
bridge_latest_kinesis_record_to_firehose(
135+
kinesis_client=kinesis_client,
136+
kinesis_stream_name="test-kinesis-audit-stream",
137+
firehose_client=firehose_client,
138+
firehose_delivery_stream_name="test_firehose_audit_stream_to_s3",
139+
)
140+
141+
# Then
142+
objects = s3_client.list_objects_v2(Bucket=audit_bucket).get("Contents", [])
143+
object_keys = [obj["Key"] for obj in objects]
144+
latest_key = sorted(object_keys)[-1]
145+
audit_data = json.loads(s3_client.get_object(Bucket=audit_bucket, Key=latest_key)["Body"].read())
146+
147+
status_text_override = None
148+
status_text = "Original actionable text"
149+
150+
assert_that(len(audit_data["response"]["condition"]), equal_to(1))
151+
assert_that(audit_data["response"]["condition"][0].get("statusTextOverride"), equal_to(status_text_override))
152+
assert_that(audit_data["response"]["condition"][0].get("statusText"), equal_to(status_text))

0 commit comments

Comments
 (0)