Skip to content

Commit ac1f46b

Browse files
committed
restructured
1 parent aa7aee6 commit ac1f46b

File tree

7 files changed

+70
-53
lines changed

7 files changed

+70
-53
lines changed

lambdas/id_sync/src/id_sync.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,18 @@
22
from common.clients import STREAM_NAME
33
from common.log_decorator import logging_decorator
44
from common.aws_lambda_event import AwsLambdaEvent
5+
from models.id_sync_exception import IdSyncException
56
from record_processor import process_record
67
'''
78
Lambda function handler for processing SQS events.Lambda for ID Sync. Fired by SQS
89
'''
910

1011

11-
class IdSyncException(Exception):
12-
"""Custom exception for ID Sync errors."""
13-
def __init__(self, message: str, nhs_numbers: list = None, exception=None):
14-
self.message = message
15-
self.nhs_numbers = nhs_numbers
16-
self.exception = exception
17-
super().__init__(message)
18-
19-
2012
@logging_decorator(prefix="id_sync", stream_name=STREAM_NAME)
2113
def handler(event_data, _):
2214

2315
try:
16+
logger.info("id_sync handler invoked")
2417
event = AwsLambdaEvent(event_data)
2518
record_count = len(event.records)
2619
if record_count > 0:

lambdas/id_sync/src/models/__init__.py

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class IdSyncException(Exception):
2+
"""Custom exception for ID Sync errors."""
3+
def __init__(self, message: str, nhs_numbers: list = None, exception=None):
4+
self.message = message
5+
self.nhs_numbers = nhs_numbers
6+
self.inner_exception = exception
7+
super().__init__(message)

lambdas/id_sync/src/pds_details.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
from os_vars import get_pds_env
88
from common.pds_service import PdsService
99
from common.authentication import AppRestrictedAuth, Service
10+
from models.id_sync_exception import IdSyncException
1011

1112
pds_env = get_pds_env()
1213
safe_tmp_dir = tempfile.mkdtemp(dir="/tmp") # NOSONAR
1314

1415

1516
def pds_get_patient_details(nhs_number: str) -> dict:
1617
try:
17-
logger.info(f"Get PDS patient details for {nhs_number}")
18+
logger.debug(f"pds_get_patient_details. nhs_number: {nhs_number}")
1819

1920
cache = Cache(directory=safe_tmp_dir)
2021
authenticator = AppRestrictedAuth(
@@ -33,6 +34,7 @@ def pds_get_patient_details(nhs_number: str) -> dict:
3334
else:
3435
logger.info(f"No patient details found for ID: {nhs_number}")
3536
return None
36-
except Exception:
37-
logger.exception(f"Error getting PDS patient details for {nhs_number}")
38-
return None
37+
except Exception as e:
38+
msg = f"Error getting PDS patient details for {nhs_number}"
39+
logger.exception(msg)
40+
raise IdSyncException(message=msg, exception=e)

lambdas/id_sync/src/record_processor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
def process_record(event_record):
1313

14-
logger.info("Processing record: %s", event_record)
14+
logger.info("process_record. Processing record: %s", event_record)
1515
body_text = event_record.get('body', '')
1616

1717
# convert body to json
@@ -37,6 +37,7 @@ def process_record(event_record):
3737

3838
def process_nhs_number(nhs_number: str) -> Optional[str]:
3939
# get patient details from PDS
40+
logger.debug(f"process_nhs_number. Processing NHS number: {nhs_number}")
4041
patient_details = pds_get_patient_details(nhs_number)
4142
if not patient_details:
4243
return {"status": "error", "message": f"No records returned for ID: {nhs_number}"}

lambdas/id_sync/tests/test_id_sync.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
with patch('common.log_decorator.logging_decorator') as mock_decorator:
66
mock_decorator.return_value = lambda f: f # Pass-through decorator
77
from id_sync import handler
8-
from id_sync import IdSyncException
8+
from models.id_sync_exception import IdSyncException
99

1010

1111
class TestIdSyncHandler(unittest.TestCase):
@@ -174,7 +174,7 @@ def test_handler_empty_records(self):
174174

175175
# Assertions
176176
self.mock_aws_lambda_event.assert_called_once_with(self.empty_event)
177-
self.mock_logger.info.assert_called_once_with("No records found in event")
177+
self.mock_logger.info.assert_any_call("No records found in event")
178178
self.mock_process_record.assert_not_called()
179179

180180
self.assertEqual(result["status"], "success")
@@ -192,7 +192,7 @@ def test_handler_no_records_key(self):
192192

193193
# Assertions
194194
self.mock_aws_lambda_event.assert_called_once_with(self.no_records_event)
195-
self.mock_logger.info.assert_called_once_with("No records found in event")
195+
self.mock_logger.info.assert_any_call("No records found in event")
196196
self.mock_process_record.assert_not_called()
197197

198198
self.assertEqual(result["status"], "success")

lambdas/id_sync/tests/test_pds_details.py

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
from unittest.mock import patch, MagicMock
33
from pds_details import pds_get_patient_details
4+
from models.id_sync_exception import IdSyncException
45

56

67
class TestGetPdsPatientDetails(unittest.TestCase):
@@ -58,9 +59,6 @@ def test_pds_get_patient_details_success(self):
5859
# Assert
5960
self.assertEqual(result, "9912003888")
6061

61-
# Verify logger was called
62-
self.mock_logger.info.assert_called_once_with(f"Get PDS patient details for {self.test_patient_id}")
63-
6462
# Verify Cache was initialized correctly
6563
self.mock_cache_class.assert_called_once()
6664

@@ -79,9 +77,7 @@ def test_pds_get_patient_details_no_patient_found(self):
7977
self.assertIsNone(result)
8078

8179
# Verify both logger calls
82-
self.mock_logger.info.assert_any_call(f"Get PDS patient details for {self.test_patient_id}")
8380
self.mock_logger.info.assert_any_call(f"No patient details found for ID: {self.test_patient_id}")
84-
self.assertEqual(self.mock_logger.info.call_count, 2)
8581

8682
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(self.test_patient_id)
8783

@@ -97,13 +93,12 @@ def test_pds_get_patient_details_empty_response(self):
9793
self.assertIsNone(result)
9894

9995
# Verify both logger calls
100-
self.mock_logger.info.assert_any_call(f"Get PDS patient details for {self.test_patient_id}")
10196
self.mock_logger.info.assert_any_call(f"No patient details found for ID: {self.test_patient_id}")
102-
self.assertEqual(self.mock_logger.info.call_count, 2)
10397

10498
def test_pds_get_patient_details_missing_identifier_field(self):
10599
"""Test when PDS response doesn't contain 'identifier' field"""
106100
# Arrange
101+
test_nhs_number = "my-nhs-number"
107102
patient_data_without_identifier = {
108103
"name": "John Doe",
109104
"birthDate": "1990-01-01",
@@ -113,14 +108,18 @@ def test_pds_get_patient_details_missing_identifier_field(self):
113108
self.mock_pds_service_instance.get_patient_details.return_value = patient_data_without_identifier
114109

115110
# Act
116-
result = pds_get_patient_details(self.test_patient_id)
111+
with self.assertRaises(IdSyncException) as context:
112+
pds_get_patient_details(test_nhs_number)
113+
114+
exception = context.exception
117115

118116
# Assert
119-
self.assertIsNone(result)
117+
self.assertEqual(exception.message, f"Error getting PDS patient details for {test_nhs_number}")
118+
self.assertEqual(exception.nhs_numbers, None)
120119

121120
# Verify exception was logged
122121
self.mock_logger.exception.assert_called_once_with(
123-
f"Error getting PDS patient details for {self.test_patient_id}")
122+
f"Error getting PDS patient details for {test_nhs_number}")
124123

125124
def test_pds_get_patient_details_empty_identifier_array(self):
126125
"""Test when identifier array is empty"""
@@ -132,10 +131,13 @@ def test_pds_get_patient_details_empty_identifier_array(self):
132131
self.mock_pds_service_instance.get_patient_details.return_value = patient_data_empty_identifier
133132

134133
# Act
135-
result = pds_get_patient_details(self.test_patient_id)
134+
with self.assertRaises(IdSyncException) as context:
135+
pds_get_patient_details(self.test_patient_id)
136136

137137
# Assert
138-
self.assertIsNone(result)
138+
exception = context.exception
139+
self.assertEqual(exception.message, f"Error getting PDS patient details for {self.test_patient_id}")
140+
self.assertEqual(exception.nhs_numbers, None)
139141

140142
# Verify exception was logged due to IndexError
141143
self.mock_logger.exception.assert_called_once_with(
@@ -152,10 +154,14 @@ def test_pds_get_patient_details_identifier_missing_value(self):
152154
self.mock_pds_service_instance.get_patient_details.return_value = patient_data_missing_value
153155

154156
# Act
155-
result = pds_get_patient_details(self.test_patient_id)
157+
with self.assertRaises(IdSyncException) as context:
158+
pds_get_patient_details(self.test_patient_id)
159+
160+
exception = context.exception
156161

157162
# Assert
158-
self.assertIsNone(result)
163+
self.assertEqual(exception.message, f"Error getting PDS patient details for {self.test_patient_id}")
164+
self.assertEqual(exception.nhs_numbers, None)
159165

160166
# Verify exception was logged due to KeyError
161167
self.mock_logger.exception.assert_called_once_with(
@@ -181,13 +187,19 @@ def test_pds_get_patient_details_multiple_identifiers(self):
181187
def test_pds_get_patient_details_pds_service_exception(self):
182188
"""Test when PdsService.get_patient_details raises an exception"""
183189
# Arrange
184-
self.mock_pds_service_instance.get_patient_details.side_effect = Exception("PDS API error")
190+
mock_exception = Exception("My custom error")
191+
self.mock_pds_service_instance.get_patient_details.side_effect = mock_exception
185192

186193
# Act
187-
result = pds_get_patient_details(self.test_patient_id)
194+
with self.assertRaises(IdSyncException) as context:
195+
pds_get_patient_details(self.test_patient_id)
196+
197+
exception = context.exception
188198

189199
# Assert
190-
self.assertIsNone(result)
200+
self.assertEqual(exception.inner_exception, mock_exception)
201+
self.assertEqual(exception.message, f"Error getting PDS patient details for {self.test_patient_id}")
202+
self.assertEqual(exception.nhs_numbers, None)
191203

192204
# Verify exception was logged
193205
self.mock_logger.exception.assert_called_once_with(
@@ -201,10 +213,13 @@ def test_pds_get_patient_details_cache_initialization_error(self):
201213
self.mock_cache_class.side_effect = OSError("Cannot write to /tmp")
202214

203215
# Act
204-
result = pds_get_patient_details(self.test_patient_id)
216+
with self.assertRaises(IdSyncException) as context:
217+
pds_get_patient_details(self.test_patient_id)
205218

206219
# Assert
207-
self.assertIsNone(result)
220+
exception = context.exception
221+
self.assertEqual(exception.message, f"Error getting PDS patient details for {self.test_patient_id}")
222+
self.assertEqual(exception.nhs_numbers, None)
208223

209224
# Verify exception was logged
210225
self.mock_logger.exception.assert_called_once_with(
@@ -218,37 +233,37 @@ def test_pds_get_patient_details_auth_initialization_error(self):
218233
self.mock_auth_class.side_effect = ValueError("Invalid authentication parameters")
219234

220235
# Act
221-
result = pds_get_patient_details(self.test_patient_id)
236+
with self.assertRaises(IdSyncException) as context:
237+
pds_get_patient_details(self.test_patient_id)
222238

223239
# Assert
224-
self.assertIsNone(result)
240+
exception = context.exception
241+
self.assertEqual(exception.message, f"Error getting PDS patient details for {self.test_patient_id}")
242+
self.assertEqual(exception.nhs_numbers, None)
225243

226244
# Verify exception was logged
227245
self.mock_logger.exception.assert_called_once_with(
228246
f"Error getting PDS patient details for {self.test_patient_id}")
229247

230-
def test_pds_get_patient_details_logger_info_exception(self):
248+
def test_pds_get_patient_details_exception(self):
231249
"""Test when logger.info throws an exception"""
232250
# Arrange
233-
self.mock_logger.info.side_effect = Exception("Logging system failure")
251+
test_exception = Exception("some-random-error")
252+
self.mock_pds_service_class.side_effect = test_exception
253+
test_nhs_number = "another-nhs-number"
234254

235255
# Act
236-
result = pds_get_patient_details(self.test_patient_id)
256+
with self.assertRaises(Exception) as context:
257+
pds_get_patient_details(test_nhs_number)
237258

259+
exception = context.exception
238260
# Assert
239-
self.assertIsNone(result)
240-
241-
# Verify logger.info was called and raised exception
242-
self.mock_logger.info.assert_called_once_with(f"Get PDS patient details for {self.test_patient_id}")
243-
261+
self.assertEqual(exception.inner_exception, test_exception)
262+
self.assertEqual(exception.message, f"Error getting PDS patient details for {test_nhs_number}")
263+
self.assertEqual(exception.nhs_numbers, None)
244264
# Verify logger.exception was called due to the caught exception
245265
self.mock_logger.exception.assert_called_once_with(
246-
f"Error getting PDS patient details for {self.test_patient_id}")
247-
248-
# Verify that no other services were initialized since exception occurred early
249-
self.mock_cache_class.assert_not_called()
250-
self.mock_auth_class.assert_not_called()
251-
self.mock_pds_service_class.assert_not_called()
266+
f"Error getting PDS patient details for {test_nhs_number}")
252267

253268
def test_pds_get_patient_details_different_patient_ids(self):
254269
"""Test with different patient ID formats"""
@@ -272,7 +287,6 @@ def test_pds_get_patient_details_different_patient_ids(self):
272287

273288
# Assert
274289
self.assertEqual(result, patient_id)
275-
self.mock_logger.info.assert_called_once_with(f"Get PDS patient details for {patient_id}")
276290
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(patient_id)
277291

278292
def test_pds_get_patient_details_service_dependencies(self):

0 commit comments

Comments
 (0)