Skip to content

Commit 876485f

Browse files
committed
VED-755: improve coverage
1 parent af29a78 commit 876485f

File tree

2 files changed

+184
-4
lines changed

2 files changed

+184
-4
lines changed

lambdas/id_sync/src/record_processor.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,19 @@ def normalize_strings(item: Any) -> str | None:
154154
ieds_gender = normalize_strings(patient.get("gender"))
155155
ieds_birth = patient.get("birthDate")
156156

157-
if pds_birth and ieds_birth and pds_birth != ieds_birth:
157+
if not all([pds_name, pds_gender, pds_birth, ieds_name, ieds_gender, ieds_birth]):
158+
logger.debug("demographics_match: missing required demographics")
159+
return False
160+
161+
if pds_birth != ieds_birth:
158162
logger.debug("demographics_match: birthDate mismatch %s != %s", pds_birth, ieds_birth)
159163
return False
160164

161-
if pds_gender and ieds_gender and pds_gender != ieds_gender:
165+
if pds_gender != ieds_gender:
162166
logger.debug("demographics_match: gender mismatch %s != %s", pds_gender, ieds_gender)
163167
return False
164168

165-
if pds_name and ieds_name and pds_name != ieds_name:
169+
if pds_name != ieds_name:
166170
logger.debug("demographics_match: name mismatch %s != %s", pds_name, ieds_name)
167171
return False
168172

lambdas/id_sync/tests/test_record_processor.py

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,84 @@ def test_process_record_demographics_mismatch_skips_update(self):
127127
# Act
128128
result = process_record(test_sqs_record)
129129

130-
# Assert: update skipped
130+
# Assert
131131
self.assertEqual(result["status"], "success")
132132
self.assertIn("update skipped", result["message"])
133133

134+
def test_invalid_body_parsing_returns_error(self):
135+
"""When body is a malformed string, process_record should return an error"""
136+
bad_record = {"body": "not-a-json-or-python-literal"}
137+
result = process_record(bad_record)
138+
self.assertEqual(result["status"], "error")
139+
self.assertIn("Invalid body format", result["message"])
140+
141+
def test_no_subject_in_body_returns_error(self):
142+
"""When body doesn't contain a subject, return an error"""
143+
result = process_record({"body": {"other": "value"}})
144+
self.assertEqual(result["status"], "error")
145+
self.assertIn("No NHS number found", result["message"])
146+
147+
def test_pds_details_exception_aborts_update(self):
148+
"""If fetching PDS details raises, function should return error"""
149+
nhs_number = "nhs-exc-1"
150+
test_sqs_record = {"body": {"subject": nhs_number}}
151+
# pds returns a different id to force update path
152+
self.mock_pds_get_patient_id.return_value = "pds-new"
153+
self.mock_ieds_check_exist.return_value = True
154+
self.mock_pds_get_patient_details.side_effect = Exception("pds fail")
155+
156+
result = process_record(test_sqs_record)
157+
self.assertEqual(result["status"], "error")
158+
self.assertIn("Failed to fetch PDS details", result["message"])
159+
160+
def test_get_items_exception_aborts_update(self):
161+
"""If fetching IEDS items raises, function should return error"""
162+
nhs_number = "nhs-exc-2"
163+
test_sqs_record = {"body": {"subject": nhs_number}}
164+
self.mock_pds_get_patient_id.return_value = "pds-new"
165+
self.mock_ieds_check_exist.return_value = True
166+
self.mock_pds_get_patient_details.return_value = {
167+
"name": [{"given": ["J"], "family": "K"}],
168+
"gender": "male", "birthDate": "2000-01-01"
169+
}
170+
self.mock_get_items_from_patient_id.side_effect = Exception("dynamo fail")
171+
172+
result = process_record(test_sqs_record)
173+
self.assertEqual(result["status"], "error")
174+
self.assertIn("Failed to fetch IEDS items", result["message"])
175+
176+
def test_update_called_on_match(self):
177+
"""Verify ieds_update_patient_id is called when demographics match"""
178+
pds_id = "pds-match"
179+
nhs_number = "nhs-match"
180+
test_sqs_record = {"body": {"subject": nhs_number}}
181+
self.mock_pds_get_patient_id.return_value = pds_id
182+
self.mock_pds_get_patient_details.return_value = {
183+
"name": [
184+
{
185+
"given": ["Tom"],
186+
"family": "Hanks"}
187+
],
188+
"gender": "male",
189+
"birthDate": "1956-07-09"
190+
}
191+
item = {
192+
"Resource": {
193+
"resourceType": "Immunization",
194+
"contained": [{
195+
"resourceType": "Patient",
196+
"id": "PatM",
197+
"name": [{"given": ["Tom"], "family": "Hanks"}],
198+
"gender": "male", "birthDate": "1956-07-09"}
199+
]}
200+
}
201+
self.mock_get_items_from_patient_id.return_value = [item]
202+
self.mock_ieds_update_patient_id.return_value = {"status": "success"}
203+
204+
result = process_record(test_sqs_record)
205+
self.assertEqual(result["status"], "success")
206+
self.mock_ieds_update_patient_id.assert_called_once_with(nhs_number, pds_id)
207+
134208
def test_process_record_no_records_exist(self):
135209
"""Test when no records exist for the patient ID"""
136210
# Arrange
@@ -205,3 +279,105 @@ def test_body_is_string(self):
205279

206280
# Assert
207281
self.assertEqual(result["status"], "success")
282+
283+
def test_process_record_birthdate_mismatch_skips_update(self):
284+
"""If birthDate differs between PDS and IEDS, update should be skipped"""
285+
pds_id = "pds-2"
286+
nhs_number = "nhs-2"
287+
test_sqs_record = {"body": {"subject": nhs_number}}
288+
289+
self.mock_pds_get_patient_id.return_value = pds_id
290+
self.mock_pds_get_patient_details.return_value = {
291+
"name": [{"given": ["John"], "family": "Doe"}],
292+
"gender": "male",
293+
"birthDate": "1980-01-01",
294+
}
295+
296+
# IEDS has different birthDate
297+
item = {
298+
"Resource": {
299+
"resourceType": "Immunization",
300+
"contained": [
301+
{
302+
"resourceType": "Patient",
303+
"id": "PatX",
304+
"name":
305+
[{
306+
"given": ["John"],
307+
"family": "Doe"
308+
}],
309+
"gender": "male",
310+
"birthDate": "1980-01-02"}
311+
]
312+
}
313+
}
314+
self.mock_get_items_from_patient_id.return_value = [item]
315+
316+
result = process_record(test_sqs_record)
317+
self.assertEqual(result["status"], "success")
318+
self.assertIn("update skipped", result["message"])
319+
320+
def test_process_record_gender_mismatch_skips_update(self):
321+
"""If gender differs between PDS and IEDS, update should be skipped"""
322+
pds_id = "pds-3"
323+
nhs_number = "nhs-3"
324+
test_sqs_record = {"body": {"subject": nhs_number}}
325+
326+
self.mock_pds_get_patient_id.return_value = pds_id
327+
self.mock_pds_get_patient_details.return_value = {
328+
"name": [{"given": ["Alex"], "family": "Smith"}],
329+
"gender": "female",
330+
"birthDate": "1992-03-03",
331+
}
332+
333+
# IEDS has different gender
334+
item = {
335+
"Resource": {
336+
"resourceType": "Immunization",
337+
"contained": [
338+
{
339+
"resourceType": "Patient",
340+
"id": "PatY",
341+
"name": [{
342+
"given": ["Alex"],
343+
"family": "Smith"
344+
}],
345+
"gender": "male", "birthDate": "1992-03-03"}
346+
]
347+
}}
348+
self.mock_get_items_from_patient_id.return_value = [item]
349+
350+
result = process_record(test_sqs_record)
351+
self.assertEqual(result["status"], "success")
352+
self.assertIn("update skipped", result["message"])
353+
354+
def test_process_record_no_comparable_fields_skips_update(self):
355+
"""If PDS provides no comparable fields, do not update (skip)"""
356+
pds_id = "pds-4"
357+
nhs_number = "nhs-4"
358+
test_sqs_record = {"body": {"subject": nhs_number}}
359+
360+
self.mock_pds_get_patient_id.return_value = pds_id
361+
# PDS returns minimal/empty details
362+
self.mock_pds_get_patient_details.return_value = {}
363+
364+
item = {
365+
"Resource": {
366+
"resourceType": "Immunization",
367+
"contained": [
368+
{
369+
"resourceType": "Patient",
370+
"id": "PatZ",
371+
"name": [{
372+
"given": ["Zoe"],
373+
"family": "Lee"
374+
}],
375+
"gender": "female",
376+
"birthDate": "2000-01-01"}
377+
]
378+
}}
379+
self.mock_get_items_from_patient_id.return_value = [item]
380+
381+
result = process_record(test_sqs_record)
382+
self.assertEqual(result["status"], "success")
383+
self.assertIn("update skipped", result["message"])

0 commit comments

Comments
 (0)