Skip to content

Commit b59a022

Browse files
committed
Add dq reporter tests. Needs work because they are causing global_s3_client tests to fail
1 parent 839fd21 commit b59a022

File tree

3 files changed

+137
-9
lines changed

3 files changed

+137
-9
lines changed

lambdas/shared/src/common/data_quality/reporter.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
@dataclass
1313
class DataQualityReport:
14-
data_quality_report_id: uuid.UUID
14+
data_quality_report_id: str
1515
validationDate: str
1616
completeness: MissingFields
1717
validity: list[str]
@@ -30,12 +30,12 @@ def __init__(self, is_batch_csv: bool, bucket: str):
3030
def generate_and_send_report(self, immunisation: dict) -> None:
3131
"""Formats and sends a data quality report to the S3 bucket."""
3232
dq_output = self.dq_checker.run_checks(immunisation)
33-
event_id = uuid.uuid4()
34-
file_key = f"{event_id}.json"
33+
dq_report_id = str(uuid.uuid4())
34+
file_key = f"{dq_report_id}.json"
3535

3636
# Build report
3737
dq_report = DataQualityReport(
38-
data_quality_report_id=event_id,
38+
data_quality_report_id=dq_report_id,
3939
validationDate=dq_output.validation_datetime,
4040
completeness=dq_output.missing_fields,
4141
validity=dq_output.invalid_fields,
@@ -51,9 +51,9 @@ def generate_and_send_report(self, immunisation: dict) -> None:
5151
except ClientError as error:
5252
# We only log the error here because we want the data quality checks to have minimal impact on the API's
5353
# functionality. This should only happen in the case of AWS infrastructure issues.
54-
logger.error("error whilst sending data quality for report id: %s with error: %s", file_key, str(error))
54+
logger.error("error whilst sending data quality for report id: %s with error: %s", dq_report_id, str(error))
5555
return None
5656

57-
logger.info("data quality report sent successfully for report id: %s", file_key)
57+
logger.info("data quality report sent successfully for report id: %s", dq_report_id)
5858

5959
return None

lambdas/shared/tests/test_common/data_quality/test_completeness.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
class TestDataQualityCompletenessChecker(unittest.TestCase):
99
def setUp(self):
10-
super().setUp()
1110
self.DataQualityCompletenessChecker = DataQualityCompletenessChecker()
1211

1312
def test_check_completeness_no_missing_fields(self):
Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,135 @@
1+
import datetime
2+
import json
13
import unittest
4+
import uuid
5+
from copy import deepcopy
6+
from dataclasses import asdict
7+
from unittest.mock import patch
28

9+
import boto3
10+
from moto import mock_aws
311

12+
from common.data_quality.completeness import MissingFields
13+
from common.data_quality.reporter import DataQualityReport, DataQualityReporter
14+
from test_common.data_quality.sample_values import VALID_BATCH_IMMUNISATION, VALID_FHIR_IMMUNISATION
15+
16+
17+
@mock_aws
418
class TestDataQualityReporter(unittest.TestCase):
5-
def test_something(self):
6-
self.assertEqual(True, True) # add assertion here
19+
def setUp(self):
20+
# Fix date.today() for all validation tests
21+
date_today_patcher = patch("common.data_quality.models.immunization_batch_row_model.datetime", wraps=datetime)
22+
self.mock_date_today = date_today_patcher.start()
23+
self.mock_date_today.date.today.return_value = datetime.date(2024, 5, 20)
24+
25+
# Fix datetime.now
26+
self.mock_fixed_datetime = datetime.datetime(2024, 5, 20, 14, 12, 30, 123, tzinfo=datetime.timezone.utc)
27+
datetime_now_patcher = patch("common.data_quality.checker.datetime", wraps=datetime.datetime)
28+
self.mock_datetime_now = datetime_now_patcher.start()
29+
self.mock_datetime_now.now.return_value = self.mock_fixed_datetime
30+
31+
# Fix generated UUID
32+
self.example_uuid = uuid.UUID("fa711f35-c08b-48c8-b498-3b151e686ddf")
33+
uuid_patcher = patch("uuid.uuid4", return_value=self.example_uuid)
34+
self.mock_uuid = uuid_patcher.start()
35+
36+
# Set up mock S3 bucket
37+
self.bucket = "test_bucket"
38+
self.s3_client = boto3.client("s3")
39+
self.s3_client.create_bucket(Bucket=self.bucket)
40+
41+
# Instantiate reporters
42+
self.batch_dq_reporter = DataQualityReporter(is_batch_csv=True, bucket=self.bucket)
43+
self.fhir_json_dq_reporter = DataQualityReporter(is_batch_csv=False, bucket=self.bucket)
44+
45+
# Expected reports
46+
self.expected_dq_report_no_issues = DataQualityReport(
47+
data_quality_report_id=str(self.example_uuid),
48+
validationDate="2024-05-20T14:12:30.000Z",
49+
completeness=MissingFields(required_fields=[], mandatory_fields=[], optional_fields=[]),
50+
validity=[],
51+
timeliness_recorded_days=4,
52+
timeliness_ingested_seconds=785550,
53+
)
54+
self.expected_dq_report_with_issues = DataQualityReport(
55+
data_quality_report_id=str(self.example_uuid),
56+
validationDate="2024-05-20T14:12:30.000Z",
57+
completeness=MissingFields(
58+
required_fields=["NHS_NUMBER", "INDICATION_CODE"],
59+
mandatory_fields=["PERSON_FORENAME", "PERSON_SURNAME"],
60+
optional_fields=["PERFORMING_PROFESSIONAL_FORENAME", "PERFORMING_PROFESSIONAL_SURNAME"],
61+
),
62+
validity=["NHS_NUMBER", "DOSE_AMOUNT", "INDICATION_CODE"],
63+
timeliness_recorded_days=4,
64+
timeliness_ingested_seconds=785550,
65+
)
66+
67+
def generate_and_send_report_test_logic(
68+
self, expected_dq_report: DataQualityReport, immunisation: dict, is_batch_csv: bool
69+
):
70+
# run generate report
71+
if is_batch_csv:
72+
self.batch_dq_reporter.generate_and_send_report(immunisation)
73+
else:
74+
self.fhir_json_dq_reporter.generate_and_send_report(immunisation)
75+
76+
expected_json = json.dumps(asdict(expected_dq_report))
77+
78+
actual_json_object = self.s3_client.get_object(Bucket=self.bucket, Key=f"{str(self.example_uuid)}.json")
79+
actual_json = actual_json_object.get("Body").read().decode("utf-8")
80+
81+
self.assertEqual(expected_json, actual_json)
82+
83+
def test_generate_and_send_report_no_issues_batch(self):
84+
self.generate_and_send_report_test_logic(
85+
expected_dq_report=self.expected_dq_report_no_issues,
86+
immunisation=VALID_BATCH_IMMUNISATION,
87+
is_batch_csv=True,
88+
)
89+
90+
def test_generate_and_send_report_no_issues_api(self):
91+
self.generate_and_send_report_test_logic(
92+
expected_dq_report=self.expected_dq_report_no_issues,
93+
immunisation=VALID_FHIR_IMMUNISATION,
94+
is_batch_csv=False,
95+
)
96+
97+
def test_generate_and_send_report_with_issues_batch(self):
98+
batch_immunisation_with_issues = deepcopy(VALID_BATCH_IMMUNISATION)
99+
100+
# Missing fields
101+
batch_immunisation_with_issues.pop("NHS_NUMBER") # required
102+
batch_immunisation_with_issues.pop("INDICATION_CODE") # required
103+
batch_immunisation_with_issues.pop("PERSON_FORENAME") # mandatory
104+
batch_immunisation_with_issues.pop("PERSON_SURNAME") # mandatory
105+
batch_immunisation_with_issues.pop("PERFORMING_PROFESSIONAL_FORENAME") # optional
106+
batch_immunisation_with_issues.pop("PERFORMING_PROFESSIONAL_SURNAME") # optional
107+
108+
# Invalid fields
109+
batch_immunisation_with_issues["DOSE_AMOUNT"] = "6.789"
110+
111+
self.generate_and_send_report_test_logic(
112+
expected_dq_report=self.expected_dq_report_with_issues,
113+
immunisation=batch_immunisation_with_issues,
114+
is_batch_csv=True,
115+
)
116+
117+
def test_generate_and_send_report_with_issues_api(self):
118+
fhir_immunisation_with_issues = deepcopy(VALID_FHIR_IMMUNISATION)
119+
120+
# Missing fields
121+
fhir_immunisation_with_issues["contained"][1]["identifier"][0]["value"] = "" # required
122+
fhir_immunisation_with_issues["reasonCode"][0]["coding"][0]["code"] = "" # required
123+
fhir_immunisation_with_issues["contained"][1]["name"][0]["given"][0] = "" # mandatory
124+
fhir_immunisation_with_issues["contained"][1]["name"][0]["family"] = "" # mandatory
125+
fhir_immunisation_with_issues["contained"][0]["name"][0]["given"][0] = "" # optional
126+
fhir_immunisation_with_issues["contained"][0]["name"][0]["family"] = "" # optional
127+
128+
# Invalid fields
129+
fhir_immunisation_with_issues["doseQuantity"]["value"] = "6.789"
130+
131+
self.generate_and_send_report_test_logic(
132+
expected_dq_report=self.expected_dq_report_with_issues,
133+
immunisation=fhir_immunisation_with_issues,
134+
is_batch_csv=False,
135+
)

0 commit comments

Comments
 (0)