Skip to content

Commit c09f878

Browse files
committed
comments addressed
pythonpath
1 parent 3a6a304 commit c09f878

File tree

12 files changed

+110
-121
lines changed

12 files changed

+110
-121
lines changed

.github/workflows/sonarcloud.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ jobs:
5959
- name: Run unittest with recordforwarder-coverage
6060
working-directory: backend
6161
id: recordforwarder
62+
env:
63+
PYTHONPATH: ${{ github.workspace }}/backend/src:${{ github.workspace }}/backend/tests
6264
continue-on-error: true
6365
run: |
6466
poetry env use 3.11

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ openapi.json
2929
!**/.vscode/settings.json.default
3030

3131
devtools/volume/
32+
backend/tests/.coverage

backend/src/constants.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,3 @@ class Urls:
2323

2424

2525
GENERIC_SERVER_ERROR_DIAGNOSTICS_MESSAGE = "Unable to process request. Issue may be transient."
26-
27-
VACCINE_TYPE_TO_DISEASES_HASH_KEY = "vacc_to_diseases"
28-
DISEASES_TO_VACCINE_TYPE_HASH_KEY = "diseases_to_vacc"

backend/src/models/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ class Constants:
4242

4343
SUPPLIER_PERMISSIONS_KEY = "supplier_permissions"
4444
VACCINE_TYPE_TO_DISEASES_HASH_KEY = "vacc_to_diseases"
45-
DISEASES_TO_VACCINE_TYPE_HASH_KEY = "diseases_to_vacc"
45+
DISEASES_TO_VACCINE_TYPE_HASH_KEY = "diseases_to_vacc"

backend/src/models/utils/validation_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
"""Utils for backend folder"""
22

33
import json
4-
import inspect
54

65
from typing import Union
76
from .generic_utils import create_diagnostics_error
87
from base_utils.base_utils import obtain_field_location
98
from models.obtain_field_value import ObtainFieldValue
109
from models.field_names import FieldNames
1110
from models.errors import MandatoryError
12-
from constants import Urls, DISEASES_TO_VACCINE_TYPE_HASH_KEY
11+
from constants import Urls
12+
from models.constants import Constants
1313
from clients import redis_client
1414

1515

@@ -53,7 +53,7 @@ def convert_disease_codes_to_vaccine_type(disease_codes_input: list) -> Union[st
5353
otherwise raises a value error
5454
"""
5555
key = ":".join(sorted(disease_codes_input))
56-
vaccine_type = redis_client.hget(DISEASES_TO_VACCINE_TYPE_HASH_KEY, key)
56+
vaccine_type = redis_client.hget(Constants.DISEASES_TO_VACCINE_TYPE_HASH_KEY, key)
5757

5858
if not vaccine_type:
5959
raise ValueError(

backend/src/parameter_parser.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from clients import redis_client
1010
from models.errors import ParameterException
11-
from constants import VACCINE_TYPE_TO_DISEASES_HASH_KEY
11+
from models.constants import Constants
1212

1313
ParamValue = list[str]
1414
ParamContainer = dict[str, ParamValue]
@@ -109,8 +109,8 @@ def process_search_params(params: ParamContainer) -> SearchParams:
109109
if len(vaccine_types) < 1:
110110
raise ParameterException(f"Search parameter {immunization_target_key} must have one or more values.")
111111

112-
valid_vaccine_types = redis_client.hkeys(VACCINE_TYPE_TO_DISEASES_HASH_KEY)
113-
if any([x not in valid_vaccine_types for x in vaccine_types]):
112+
valid_vaccine_types = redis_client.hkeys(Constants.VACCINE_TYPE_TO_DISEASES_HASH_KEY)
113+
if any(x not in valid_vaccine_types for x in vaccine_types):
114114
raise ParameterException(
115115
f"immunization-target must be one or more of the following: {', '.join(valid_vaccine_types)}")
116116

backend/tests/.coverage

-52 KB
Binary file not shown.

backend/tests/sample_data/permissions_config.py

Whitespace-only changes.

backend/tests/test_fhir_controller.py

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,33 @@
3333
from tests.utils.generic_utils import load_json_data
3434
from tests.utils.values_for_tests import ValidValues
3535

36-
"test"
36+
class TestFhirControllerBase(unittest.TestCase):
37+
"""Base class for all tests to set up common fixtures"""
3738

38-
39-
class TestFhirController(unittest.TestCase):
4039
def setUp(self):
40+
super().setUp()
41+
self.redis_patcher = patch("parameter_parser.redis_client")
42+
self.mock_redis_client = self.redis_patcher.start()
43+
self.logger_info_patcher = patch("logging.Logger.info")
44+
self.mock_logger_info = self.logger_info_patcher.start()
45+
46+
def tearDown(self):
47+
self.redis_patcher.stop()
48+
self.logger_info_patcher.stop()
49+
super().tearDown()
50+
51+
class TestFhirController(TestFhirControllerBase):
52+
def setUp(self):
53+
super().setUp()
4154
self.service = create_autospec(FhirService)
4255
self.repository = create_autospec(ImmunizationRepository)
4356
self.authorizer = create_autospec(Authorization)
4457
self.controller = FhirController(self.authorizer, self.service)
4558

59+
def tearDown(self):
60+
self.redis_patcher.stop()
61+
super().tearDown()
62+
4663
def test_create_response(self):
4764
"""it should return application/fhir+json with correct status code"""
4865
body = {"message": "a body"}
@@ -133,7 +150,6 @@ def test_get_imms_by_identifer_header_missing(self):
133150
response = self.controller.get_immunization_by_identifier(lambda_event)
134151

135152
self.assertEqual(response["statusCode"], 403)
136-
137153
@patch("fhir_controller.get_supplier_permissions")
138154
def test_not_found_for_identifier(self, mock_get_permissions):
139155
"""it should return not-found OperationOutcome if it doesn't exist"""
@@ -165,7 +181,6 @@ def test_not_found_for_identifier(self, mock_get_permissions):
165181

166182
imms = identifier.replace("|", "#")
167183
# When
168-
169184
response = self.controller.get_immunization_by_identifier(lambda_event)
170185

171186
# Then
@@ -200,7 +215,6 @@ def test_get_imms_by_identifer_patient_identifier_and_element_present(self, mock
200215
self.assertEqual(response["statusCode"], 400)
201216
body = json.loads(response["body"])
202217
self.assertEqual(body["resourceType"], "OperationOutcome")
203-
204218
@patch("fhir_controller.get_supplier_permissions")
205219
def test_get_imms_by_identifer_both_body_and_query_params_present(self, mock_get_supplier_permissions):
206220
"""it should return Immunization Id if it exists"""
@@ -422,7 +436,6 @@ def test_validate_immunization_identifier_having_whitespace(self,mock_get_suppli
422436
self.assertEqual(response["statusCode"], 400)
423437
outcome = json.loads(response["body"])
424438
self.assertEqual(outcome["resourceType"], "OperationOutcome")
425-
426439
@patch("fhir_controller.get_supplier_permissions")
427440
def test_validate_imms_id_invalid_vaccinetype(self, mock_get_permissions):
428441
"""it should validate lambda's Immunization id"""
@@ -738,7 +751,6 @@ def test_validate_immunization_identifier_having_whitespace(self,mock_get_suppli
738751
self.assertEqual(response["statusCode"], 400)
739752
outcome = json.loads(response["body"])
740753
self.assertEqual(outcome["resourceType"], "OperationOutcome")
741-
742754
@patch("fhir_controller.get_supplier_permissions")
743755
def test_validate_imms_id_invalid_vaccinetype(self, mock_get_permissions):
744756
"""it should validate lambda's Immunization id"""
@@ -820,7 +832,6 @@ def test_get_imms_by_id_unauthorised_vax_error(self,mock_permissions):
820832
# Then
821833
mock_permissions.assert_called_once_with("test")
822834
self.assertEqual(response["statusCode"], 403)
823-
824835
@patch("fhir_controller.get_supplier_permissions")
825836
def test_get_imms_by_id_no_vax_permission(self, mock_permissions):
826837
"""it should return Immunization Id if it exists"""
@@ -1142,7 +1153,6 @@ def test_update_immunization_UnauthorizedVaxError(self, mock_get_supplier_permis
11421153
response = self.controller.update_immunization(aws_event)
11431154
mock_get_supplier_permissions.assert_called_once_with("Test")
11441155
self.assertEqual(response["statusCode"], 403)
1145-
11461156
@patch("fhir_controller.get_supplier_permissions")
11471157
def test_update_immunization_UnauthorizedVaxError_check_for_non_batch(self, mock_get_supplier_permissions):
11481158
"""it should not update the Immunization record"""
@@ -1573,7 +1583,6 @@ def test_immunization_exception_not_found(self, mock_get_permissions):
15731583
body = json.loads(response["body"])
15741584
self.assertEqual(body["resourceType"], "OperationOutcome")
15751585
self.assertEqual(body["issue"][0]["code"], "not-found")
1576-
15771586
@patch("fhir_controller.get_supplier_permissions")
15781587
def test_immunization_unhandled_error(self, mock_get_supplier_permissions):
15791588
"""it should return server-error OperationOutcome if service throws UnhandledResponseError"""
@@ -1596,8 +1605,22 @@ def test_immunization_unhandled_error(self, mock_get_supplier_permissions):
15961605
self.assertEqual(body["resourceType"], "OperationOutcome")
15971606
self.assertEqual(body["issue"][0]["code"], "exception")
15981607

1599-
class TestSearchImmunizations(unittest.TestCase):
1608+
class TestSearchImmunizations(TestFhirControllerBase):
1609+
MOCK_REDIS_V2D_RESPONSE = {
1610+
"PERTUSSIS": "[{\"code\": \"27836007\", \"term\": \"Pertussis (disorder)\"}]",
1611+
"RSV": "[{\"code\": \"55735004\", \"term\": \"Respiratory syncytial virus infection (disorder)\"}]",
1612+
"3in1": "[{\"code\": \"398102009\", \"term\": \"Acute poliomyelitis\"}, {\"code\": \"397430003\", \"term\": \"Diphtheria caused by Corynebacterium diphtheriae\"}, {\"code\": \"76902006\", \"term\": \"Tetanus (disorder)\"}]",
1613+
"MMR": "[{\"code\": \"14189004\", \"term\": \"Measles (disorder)\"}, {\"code\": \"36989005\", \"term\": \"Mumps (disorder)\"}, {\"code\": \"36653000\", \"term\": \"Rubella (disorder)\"}]",
1614+
"HPV": "[{\"code\": \"240532009\", \"term\": \"Human papillomavirus infection\"}]",
1615+
"MMRV": "[{\"code\": \"14189004\", \"term\": \"Measles (disorder)\"}, {\"code\": \"36989005\", \"term\": \"Mumps (disorder)\"}, {\"code\": \"36653000\", \"term\": \"Rubella (disorder)\"}, {\"code\": \"38907003\", \"term\": \"Varicella (disorder)\"}]",
1616+
"PCV13": "[{\"code\": \"16814004\", \"term\": \"Pneumococcal infectious disease\"}]",
1617+
"SHINGLES": "[{\"code\": \"4740000\", \"term\": \"Herpes zoster\"}]",
1618+
"COVID19": "[{\"code\": \"840539006\", \"term\": \"Disease caused by severe acute respiratory syndrome coronavirus 2\"}]",
1619+
"FLU": "[{\"code\": \"6142004\", \"term\": \"Influenza caused by seasonal influenza virus (disorder)\"}]",
1620+
"MENACWY": "[{\"code\": \"23511006\", \"term\": \"Meningococcal infectious disease\"}]"
1621+
}
16001622
def setUp(self):
1623+
super().setUp()
16011624
self.service = create_autospec(FhirService)
16021625
self.authorizer = create_autospec(Authorization)
16031626
self.controller = FhirController(self.authorizer, self.service)
@@ -1607,11 +1630,14 @@ def setUp(self):
16071630
self.date_to_key = "-date.to"
16081631
self.nhs_number_valid_value = "9000000009"
16091632
self.patient_identifier_valid_value = f"{patient_identifier_system}|{self.nhs_number_valid_value}"
1633+
self.mock_redis_client.hkeys.return_value = self.MOCK_REDIS_V2D_RESPONSE
1634+
1635+
def tearDown(self):
1636+
return super().tearDown()
16101637

16111638
@patch("fhir_controller.get_supplier_permissions")
16121639
def test_get_search_immunizations(self, mock_get_supplier_permissions):
16131640
"""it should search based on patient_identifier and immunization_target"""
1614-
16151641
mock_get_supplier_permissions.return_value = ["COVID19.S"]
16161642
search_result = Bundle.construct()
16171643
self.service.search_immunizations.return_value = search_result
@@ -1795,7 +1821,6 @@ def test_post_search_immunizations(self,mock_get_supplier_permissions):
17951821
"headers": {"Content-Type": "application/x-www-form-urlencoded", "SupplierSystem": "Test"},
17961822
"body": base64_encoded_body,
17971823
}
1798-
17991824
# When
18001825
response = self.controller.search_immunizations(lambda_event)
18011826
# Then
@@ -1906,6 +1931,7 @@ def test_post_search_immunizations_for_unauthorized_vaccine_type_search_403(self
19061931

19071932
@patch("fhir_controller.process_search_params", wraps=process_search_params)
19081933
def test_uses_parameter_parser(self, process_search_params: Mock):
1934+
self.mock_redis_client.hkeys.return_value = self.MOCK_REDIS_V2D_RESPONSE
19091935
lambda_event = {
19101936
"multiValueQueryStringParameters": {
19111937
self.patient_identifier_key: ["https://fhir.nhs.uk/Id/nhs-number|9000000009"],

backend/tests/test_fhir_service.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import datetime
44
import unittest
55
from copy import deepcopy
6+
from unittest.mock import create_autospec, patch
67
from unittest import skip
78
from unittest.mock import create_autospec
89
from decimal import Decimal
@@ -23,10 +24,25 @@
2324
create_covid_19_immunization_dict_no_id,
2425
VALID_NHS_NUMBER,
2526
)
26-
from .utils.generic_utils import load_json_data
27-
from src.constants import NHS_NUMBER_USED_IN_SAMPLE_DATA
27+
from utils.generic_utils import load_json_data
28+
from constants import NHS_NUMBER_USED_IN_SAMPLE_DATA
29+
30+
class TestFhirServiceBase(unittest.TestCase):
31+
"""Base class for all tests to set up common fixtures"""
32+
33+
def setUp(self):
34+
super().setUp()
35+
self.redis_patcher = patch("models.utils.validation_utils.redis_client")
36+
self.mock_redis_client = self.redis_patcher.start()
37+
self.logger_info_patcher = patch("logging.Logger.info")
38+
self.mock_logger_info = self.logger_info_patcher.start()
39+
40+
def tearDown(self):
41+
self.redis_patcher.stop()
42+
self.logger_info_patcher.stop()
43+
super().tearDown()
44+
2845

29-
"test"
3046
class TestServiceUrl(unittest.TestCase):
3147
def test_get_service_url(self):
3248
"""it should create service url"""
@@ -51,15 +67,20 @@ def test_get_service_url(self):
5167
self.assertEqual(url, f"https://internal-dev.api.service.nhs.uk/{base_path}")
5268

5369

54-
class TestGetImmunizationByAll(unittest.TestCase):
70+
class TestGetImmunizationByAll(TestFhirServiceBase):
5571
"""Tests for FhirService.get_immunization_by_id"""
5672

5773
def setUp(self):
74+
super().setUp()
5875
self.imms_repo = create_autospec(ImmunizationRepository)
5976
self.pds_service = create_autospec(PdsService)
6077
self.validator = create_autospec(ImmunizationValidator)
6178
self.fhir_service = FhirService(self.imms_repo, self.pds_service, self.validator)
6279

80+
def tearDown(self):
81+
super().tearDown()
82+
patch.stopall()
83+
6384
def test_get_immunization_by_id_by_all(self):
6485
"""it should find an Immunization by id"""
6586
imms_id = "an-id"
@@ -119,7 +140,8 @@ def test_pre_validation_failed(self):
119140
self.assertEqual(error.exception.message, expected_msg)
120141
self.imms_repo.update_immunization.assert_not_called()
121142

122-
def test_post_validation_failed(self):
143+
def test_post_validation_failed_get_by_all(self):
144+
self.mock_redis_client.hget.side_effect = [None, 'COVID-19']
123145
valid_imms = create_covid_19_immunization_dict("an-id", VALID_NHS_NUMBER)
124146

125147
bad_target_disease_imms = deepcopy(valid_imms)
@@ -150,14 +172,19 @@ def test_post_validation_failed(self):
150172
self.imms_repo.get_immunization_by_id_all.assert_not_called()
151173

152174

153-
class TestGetImmunization(unittest.TestCase):
175+
class TestGetImmunization(TestFhirServiceBase):
154176
"""Tests for FhirService.get_immunization_by_id"""
155177

156178
def setUp(self):
179+
super().setUp()
157180
self.imms_repo = create_autospec(ImmunizationRepository)
158181
self.pds_service = create_autospec(PdsService)
159182
self.validator = create_autospec(ImmunizationValidator)
160183
self.fhir_service = FhirService(self.imms_repo, self.pds_service, self.validator)
184+
self.logger_info_patcher = patch("logging.Logger.info")
185+
self.mock_logger_info = self.logger_info_patcher.start()
186+
def tearDown(self):
187+
patch.stopall()
161188

162189
def test_get_immunization_by_id(self):
163190
"""it should find an Immunization by id"""
@@ -234,7 +261,8 @@ def test_pre_validation_failed(self):
234261
self.imms_repo.update_immunization.assert_not_called()
235262
self.pds_service.get_patient_details.assert_not_called()
236263

237-
def test_post_validation_failed(self):
264+
def test_post_validation_failed_get(self):
265+
self.mock_redis_client.hget.side_effect = [None, 'COVID-19']
238266
valid_imms = create_covid_19_immunization_dict("an-id", VALID_NHS_NUMBER)
239267

240268
bad_target_disease_imms = deepcopy(valid_imms)
@@ -309,10 +337,11 @@ def test_immunization_not_found(self):
309337
self.assertEqual(act_imms["entry"], [])
310338

311339

312-
class TestCreateImmunization(unittest.TestCase):
340+
class TestCreateImmunization(TestFhirServiceBase):
313341
"""Tests for FhirService.create_immunization"""
314342

315343
def setUp(self):
344+
super().setUp()
316345
self.imms_repo = create_autospec(ImmunizationRepository)
317346
self.pds_service = create_autospec(PdsService)
318347
self.validator = create_autospec(ImmunizationValidator)
@@ -323,6 +352,10 @@ def setUp(self):
323352
ImmunizationValidator(add_post_validators=False),
324353
)
325354

355+
def tearDown(self):
356+
patch.stopall()
357+
super().tearDown()
358+
326359
def test_create_immunization(self):
327360
"""it should create Immunization and validate it"""
328361
imms_id = "an-id"
@@ -382,7 +415,7 @@ def test_pre_validation_failed(self):
382415

383416
def test_post_validation_failed(self):
384417
"""it should throw exception if Immunization is not valid"""
385-
418+
self.mock_redis_client.hget.side_effect = [None, 'COVID-19']
386419
valid_imms = create_covid_19_immunization_dict_no_id(VALID_NHS_NUMBER)
387420

388421
bad_target_disease_imms = deepcopy(valid_imms)

0 commit comments

Comments
 (0)