Skip to content

Commit 7a99d82

Browse files
consumer with no mappings is a valid scenario
1 parent 2dacd5e commit 7a99d82

6 files changed

Lines changed: 74 additions & 88 deletions

File tree

src/eligibility_signposting_api/services/eligibility_services.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ class InvalidQueryParamError(Exception):
2020
pass
2121

2222

23-
class NoPermittedCampaignsError(Exception):
24-
pass
25-
26-
2723
@service
2824
class EligibilityService:
2925
def __init__(
@@ -74,4 +70,4 @@ def __collect_permitted_campaign_configs(
7470
campaign for campaign in campaign_configs if campaign.id in permitted_campaign_ids
7571
]
7672
return permitted_campaign_configs
77-
raise NoPermittedCampaignsError
73+
return []

src/eligibility_signposting_api/views/eligibility.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from eligibility_signposting_api.model.consumer_mapping import ConsumerId
2121
from eligibility_signposting_api.model.eligibility_status import Condition, EligibilityStatus, NHSNumber, Status
2222
from eligibility_signposting_api.services import EligibilityService, UnknownPersonError
23-
from eligibility_signposting_api.services.eligibility_services import NoPermittedCampaignsError
2423
from eligibility_signposting_api.views.response_model import eligibility_response
2524
from eligibility_signposting_api.views.response_model.eligibility_response import ProcessedSuggestion
2625

@@ -66,8 +65,6 @@ def check_eligibility(
6665
)
6766
except UnknownPersonError:
6867
return handle_unknown_person_error(nhs_number)
69-
except NoPermittedCampaignsError:
70-
return handle_no_permitted_campaigns_for_the_consumer_error(consumer_id)
7168
else:
7269
response: eligibility_response.EligibilityResponse = build_eligibility_response(eligibility_status)
7370
AuditContext.write_to_firehose(audit_service)

tests/integration/in_process/test_eligibility_endpoint.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -836,8 +836,9 @@ def test_response_when_no_campaign_config_is_present(
836836
headers = {"nhs-login-nhs-number": str(persisted_77yo_person), CONSUMER_ID: "23-mic7heal-jor6don"}
837837

838838
# When
839-
response = client.get(f'/patient-check/{persisted_77yo_person}?includeActions=N&conditions=FLU',
840-
headers=headers)
839+
response = client.get(
840+
f"/patient-check/{persisted_77yo_person}?includeActions=N&conditions=FLU", headers=headers
841+
)
841842

842843
# Then
843844
assert_that(
@@ -848,9 +849,7 @@ def test_response_when_no_campaign_config_is_present(
848849
is_json_that(
849850
has_entry(
850851
"processedSuggestions",
851-
equal_to(
852-
[]
853-
),
852+
equal_to([]),
854853
)
855854
)
856855
),

tests/unit/repos/test_consumer_mapping_repo.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import json
2-
import pytest
32
from unittest.mock import MagicMock
43

4+
import pytest
5+
56
from eligibility_signposting_api.model.consumer_mapping import ConsumerId
6-
from eligibility_signposting_api.repos.consumer_mapping_repo import ConsumerMappingRepo, BucketName
7+
from eligibility_signposting_api.repos.consumer_mapping_repo import BucketName, ConsumerMappingRepo
78

89

910
class TestConsumerMappingRepo:
@@ -13,38 +14,26 @@ def mock_s3_client(self):
1314

1415
@pytest.fixture
1516
def repo(self, mock_s3_client):
16-
return ConsumerMappingRepo(
17-
s3_client=mock_s3_client,
18-
bucket_name=BucketName("test-bucket")
19-
)
17+
return ConsumerMappingRepo(s3_client=mock_s3_client, bucket_name=BucketName("test-bucket"))
2018

2119
def test_get_permitted_campaign_ids_success(self, repo, mock_s3_client):
2220
# Given
2321
consumer_id = "user-123"
2422
expected_campaigns = ["flu-2024", "covid-2024"]
25-
mapping_data = {
26-
consumer_id: expected_campaigns
27-
}
23+
mapping_data = {consumer_id: expected_campaigns}
2824

29-
mock_s3_client.list_objects.return_value = {
30-
"Contents": [{"Key": "mappings.json"}]
31-
}
25+
mock_s3_client.list_objects.return_value = {"Contents": [{"Key": "mappings.json"}]}
3226

3327
body_json = json.dumps(mapping_data).encode("utf-8")
34-
mock_s3_client.get_object.return_value = {
35-
"Body": MagicMock(read=lambda: body_json)
36-
}
28+
mock_s3_client.get_object.return_value = {"Body": MagicMock(read=lambda: body_json)}
3729

3830
# When
3931
result = repo.get_permitted_campaign_ids(ConsumerId(consumer_id))
4032

4133
# Then
4234
assert result == expected_campaigns
4335
mock_s3_client.list_objects.assert_called_once_with(Bucket="test-bucket")
44-
mock_s3_client.get_object.assert_called_once_with(
45-
Bucket="test-bucket",
46-
Key="mappings.json"
47-
)
36+
mock_s3_client.get_object.assert_called_once_with(Bucket="test-bucket", Key="mappings.json")
4837

4938
def test_get_permitted_campaign_ids_returns_none_when_missing(self, repo, mock_s3_client):
5039
# Setup data where the consumer_id doesn't exist

tests/unit/services/test_eligibility_services.py

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,32 @@
33
import pytest
44
from hamcrest import assert_that, empty
55

6-
from eligibility_signposting_api.model.campaign_config import CampaignID, CampaignConfig
6+
from eligibility_signposting_api.model.campaign_config import CampaignConfig, CampaignID
77
from eligibility_signposting_api.model.eligibility_status import NHSNumber
88
from eligibility_signposting_api.repos import CampaignRepo, NotFoundError, PersonRepo
99
from eligibility_signposting_api.repos.consumer_mapping_repo import ConsumerMappingRepo
1010
from eligibility_signposting_api.services import EligibilityService, UnknownPersonError
1111
from eligibility_signposting_api.services.calculators.eligibility_calculator import EligibilityCalculatorFactory
12-
from eligibility_signposting_api.services.eligibility_services import NoPermittedCampaignsError
1312
from tests.fixtures.matchers.eligibility import is_eligibility_status
1413

14+
1515
@pytest.fixture
1616
def mock_repos():
1717
return {
1818
"person": MagicMock(spec=PersonRepo),
1919
"campaign": MagicMock(spec=CampaignRepo),
2020
"consumer": MagicMock(spec=ConsumerMappingRepo),
21-
"factory": MagicMock(spec=EligibilityCalculatorFactory)
21+
"factory": MagicMock(spec=EligibilityCalculatorFactory),
2222
}
2323

24+
2425
@pytest.fixture
2526
def service(mock_repos):
2627
return EligibilityService(
27-
mock_repos["person"],
28-
mock_repos["campaign"],
29-
mock_repos["consumer"],
30-
mock_repos["factory"]
28+
mock_repos["person"], mock_repos["campaign"], mock_repos["consumer"], mock_repos["factory"]
3129
)
3230

31+
3332
def test_eligibility_service_returns_from_repo():
3433
# Given
3534
person_repo = MagicMock(spec=PersonRepo)
@@ -66,48 +65,58 @@ def test_eligibility_service_for_nonexistent_nhs_number():
6665
)
6766

6867

69-
def test_get_eligibility_status_filters_permitted_campaigns(service, mock_repos):
70-
"""Tests that ONLY permitted campaigns reach the calculator factory."""
71-
# Given
72-
nhs_number = NHSNumber("1234567890")
73-
person_data = {"age": 65, "vulnerable": True}
74-
mock_repos["person"].get_eligibility_data.return_value = person_data
75-
76-
# Available campaigns in system
77-
camp_a = MagicMock(spec=CampaignConfig, id=CampaignID("CAMP_A"))
78-
camp_b = MagicMock(spec=CampaignConfig, id=CampaignID("CAMP_B"))
79-
mock_repos["campaign"].get_campaign_configs.return_value = [camp_a, camp_b]
80-
81-
# Consumer is only permitted to see CAMP_B
82-
mock_repos["consumer"].get_permitted_campaign_ids.return_value = [CampaignID("CAMP_B")]
83-
84-
# Mock calculator behavior
85-
mock_calc = MagicMock()
86-
mock_repos["factory"].get.return_value = mock_calc
87-
mock_calc.get_eligibility_status.return_value = "eligible_result"
68+
def test_raises_unknown_person_error_on_repo_not_found(service, mock_repos):
69+
"""Tests that NotFoundError from repo is translated to UnknownPersonError."""
70+
mock_repos["person"].get_eligibility_data.side_effect = NotFoundError
8871

89-
# When
90-
result = service.get_eligibility_status(nhs_number, "Y", ["FLU"], "G1", "consumer_xyz")
72+
with pytest.raises(UnknownPersonError):
73+
service.get_eligibility_status(NHSNumber("999"), "Y", [], "", "any")
9174

92-
# Then
93-
# Verify the factory was called ONLY with camp_b
94-
mock_repos["factory"].get.assert_called_once_with(person_data, [camp_b])
95-
assert result == "eligible_result"
9675

97-
def test_raises_no_permitted_campaigns_error(service, mock_repos):
98-
"""Tests the scenario where the consumer mapping exists but returns nothing."""
99-
mock_repos["person"].get_eligibility_data.return_value = {"data": "exists"}
100-
mock_repos["campaign"].get_campaign_configs.return_value = [MagicMock()]
76+
@pytest.mark.parametrize(
77+
"available_campaign_ids, permitted_campaign_ids, expected_campaign_ids",
78+
[
79+
# permitted campaigns is NOT empty → only permitted campaigns used
80+
(
81+
["CAMP_A", "CAMP_B"],
82+
["CAMP_B"],
83+
["CAMP_B"],
84+
),
85+
# permitted campaigns is empty → no campaigns used
86+
(
87+
["CAMP_A", "CAMP_B"],
88+
[],
89+
[],
90+
),
91+
# no campaigns in system → no campaigns used
92+
(
93+
[],
94+
[],
95+
[],
96+
),
97+
],
98+
)
99+
def test_get_eligibility_status_campaign_filtering(
100+
service,
101+
mock_repos,
102+
available_campaign_ids,
103+
permitted_campaign_ids,
104+
expected_campaign_ids,
105+
):
106+
# Given
107+
nhs_number = NHSNumber("1234567890")
108+
person_data = {"age": 65, "vulnerable": True}
109+
mock_repos["person"].get_eligibility_data.return_value = person_data
101110

102-
# Consumer has no permitted IDs mapped
103-
mock_repos["consumer"].get_permitted_campaign_ids.return_value = []
111+
# Build campaign configs
112+
campaigns = [MagicMock(spec=CampaignConfig, id=CampaignID(camp_id)) for camp_id in available_campaign_ids]
113+
mock_repos["campaign"].get_campaign_configs.return_value = campaigns
104114

105-
with pytest.raises(NoPermittedCampaignsError):
106-
service.get_eligibility_status(NHSNumber("1"), "Y", [], "", "bad_consumer")
115+
permitted_ids = [CampaignID(camp_id) for camp_id in permitted_campaign_ids]
116+
mock_repos["consumer"].get_permitted_campaign_ids.return_value = permitted_ids
107117

108-
def test_raises_unknown_person_error_on_repo_not_found(service, mock_repos):
109-
"""Tests that NotFoundError from repo is translated to UnknownPersonError."""
110-
mock_repos["person"].get_eligibility_data.side_effect = NotFoundError
118+
expected_ids = [CampaignID(camp_id) for camp_id in expected_campaign_ids]
119+
expected_campaigns = [camp for camp in campaigns if camp.id in expected_ids]
111120

112-
with pytest.raises(UnknownPersonError):
113-
service.get_eligibility_status(NHSNumber("999"), "Y", [], "", "any")
121+
mock_calc = MagicMock()
122+
mock_repos["factory"].get.return_v

tests/unit/views/test_eligibility.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def get_eligibility_status(
9494
) -> EligibilityStatus:
9595
raise ValueError
9696

97+
9798
class FakeNoPermittedCampaignsService(EligibilityService):
9899
def __init__(self):
99100
pass
@@ -109,6 +110,7 @@ def get_eligibility_status(
109110
# Simulate the new error scenario
110111
raise NoPermittedCampaignsError
111112

113+
112114
def test_security_headers_present_on_successful_response(app: Flask, client: FlaskClient):
113115
"""Test that security headers are present on successful eligibility check response."""
114116
# Given
@@ -610,10 +612,7 @@ def test_no_permitted_campaigns_for_consumer_error(app: Flask, client: FlaskClie
610612
get_app_container(app).override.service(EligibilityService, new=FakeNoPermittedCampaignsService()),
611613
get_app_container(app).override.service(AuditService, new=FakeAuditService()),
612614
):
613-
headers = {
614-
"nhs-login-nhs-number": "9876543210",
615-
"Consumer-Id": "unrecognized_consumer"
616-
}
615+
headers = {"nhs-login-nhs-number": "9876543210", "Consumer-Id": "unrecognized_consumer"}
617616

618617
# When
619618
response = client.get("/patient-check/9876543210", headers=headers)
@@ -632,12 +631,12 @@ def test_no_permitted_campaigns_for_consumer_error(app: Flask, client: FlaskClie
632631
has_entries(
633632
severity="error",
634633
code="forbidden",
635-
diagnostics="Consumer ID 'unrecognized_consumer' was not recognised by the Eligibility Signposting API"
634+
diagnostics="Consumer ID 'unrecognized_consumer' was not recognised by the Eligibility Signposting API",
636635
)
637-
)
636+
),
638637
)
639638
)
640-
)
639+
),
641640
)
642641

643642

@@ -654,10 +653,7 @@ def test_consumer_id_is_passed_to_service(app: Flask, client: FlaskClient):
654653
get_app_container(app).override.service(EligibilityService, new=mock_service),
655654
get_app_container(app).override.service(AuditService, new=FakeAuditService()),
656655
):
657-
headers = {
658-
"nhs-login-nhs-number": "1234567890",
659-
"Consumer-Id": "specific_consumer_123"
660-
}
656+
headers = {"nhs-login-nhs-number": "1234567890", "Consumer-Id": "specific_consumer_123"}
661657

662658
# When
663659
client.get("/patient-check/1234567890", headers=headers)

0 commit comments

Comments
 (0)