From 0dee6e1583d780cfbd559c2d8fe6d18761306a0c Mon Sep 17 00:00:00 2001 From: Megan Date: Fri, 13 Feb 2026 09:42:38 +0000 Subject: [PATCH 1/3] NDR-365 Allow title to be optional for pdm fhir requests --- lambdas/models/document_reference.py | 14 +- .../post_fhir_document_reference_service.py | 28 +- .../test_upload_document_fhir_api_success.py | 36 +- .../tests/e2e/api/test_upload_document_api.py | 64 +++- lambdas/tests/e2e/helpers/data_helper.py | 39 ++- ..._fhir_document_reference_search_service.py | 74 ++++ ...dm_post_fhir_document_reference_service.py | 329 ++++++++++++++---- ...t_pdm_upload_document_reference_service.py | 134 ++++--- ...st_post_fhir_document_reference_service.py | 239 +++++++++---- 9 files changed, 736 insertions(+), 221 deletions(-) diff --git a/lambdas/models/document_reference.py b/lambdas/models/document_reference.py index ecd2cd224e..613d88343c 100644 --- a/lambdas/models/document_reference.py +++ b/lambdas/models/document_reference.py @@ -102,7 +102,7 @@ class DocumentReference(BaseModel): author: str | None = None content_type: str = Field(default=DEFAULT_CONTENT_TYPE) created: str = Field( - default_factory=lambda: datetime.now(timezone.utc).strftime(DATE_FORMAT) + default_factory=lambda: datetime.now(timezone.utc).strftime(DATE_FORMAT), ) document_scan_creation: Optional[str] = Field( default_factory=lambda: datetime.date(datetime.now()).isoformat(), @@ -125,10 +125,10 @@ class DocumentReference(BaseModel): ] = Field(default="preliminary") doc_type: str = Field(default=None, exclude=True) document_snomed_code_type: Optional[str] = Field( - default=SnomedCodes.LLOYD_GEORGE.value.code + default=SnomedCodes.LLOYD_GEORGE.value.code, ) file_location: str = "" - file_name: str + file_name: str | None file_size: int | None = Field(default=None) last_updated: int = Field( default_factory=lambda: int(datetime.now(timezone.utc).timestamp()), @@ -140,11 +140,12 @@ class DocumentReference(BaseModel): s3_version_id: Optional[str] = Field(default=None, alias="S3VersionID") s3_upload_key: str = Field(default=None, exclude=True) status: Literal["current", "superseded", "entered-in-error"] = Field( - default="current" + default="current", ) sub_folder: str = Field(default=None, exclude=True) ttl: Optional[int] = Field( - alias=str(DocumentReferenceMetadataFields.TTL.value), default=None + alias=str(DocumentReferenceMetadataFields.TTL.value), + default=None, ) uploaded: bool = Field(default=None) uploading: bool = Field(default=None) @@ -174,7 +175,8 @@ def set_location_properties(cls, data, *args, **kwargs): if "s3_file_key" not in data: data["s3_file_key"] = cls._build_final_s3_key(data) data["file_location"] = cls._build_s3_location( - data["s3_bucket_name"], current_s3_file_key + data["s3_bucket_name"], + current_s3_file_key, ) return data diff --git a/lambdas/services/post_fhir_document_reference_service.py b/lambdas/services/post_fhir_document_reference_service.py index 255c1c6bde..bec8b561de 100644 --- a/lambdas/services/post_fhir_document_reference_service.py +++ b/lambdas/services/post_fhir_document_reference_service.py @@ -3,7 +3,9 @@ from enums.mtls import MtlsCommonNames from enums.snomed_codes import SnomedCode, SnomedCodes from models.document_reference import DocumentReference -from models.fhir.R4.fhir_document_reference import SNOMED_URL +from models.fhir.R4.fhir_document_reference import ( + SNOMED_URL, +) from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) @@ -25,7 +27,9 @@ def __init__(self): super().__init__() def process_fhir_document_reference( - self, fhir_document: str, api_request_context: dict = {} + self, + fhir_document: str, + api_request_context: dict = {}, ) -> str: """ Process a FHIR Document Reference request @@ -40,7 +44,7 @@ def process_fhir_document_reference( common_name = validate_common_name_in_mtls(api_request_context) validated_fhir_doc = FhirDocumentReference.model_validate_json( - fhir_document + fhir_document, ) # Extract NHS number and author from the FHIR document @@ -72,7 +76,9 @@ def process_fhir_document_reference( ) presigned_url = self._handle_document_save( - document_reference, validated_fhir_doc, dynamo_table + document_reference, + validated_fhir_doc, + dynamo_table, ) return self._create_fhir_response(document_reference, presigned_url) @@ -107,7 +113,9 @@ def _extract_author_from_fhir(self, fhir_doc: FhirDocumentReference) -> str | No raise DocumentRefException(400, LambdaError.DocRefNoParse) def _determine_document_type( - self, fhir_doc: FhirDocumentReference, common_name: MtlsCommonNames | None + self, + fhir_doc: FhirDocumentReference, + common_name: MtlsCommonNames | None, ) -> SnomedCode: if not common_name: """Determine the document type based on SNOMED code in the FHIR document""" @@ -119,7 +127,7 @@ def _determine_document_type( return snomed_code else: logger.error( - f"SNOMED code {coding.code} - {coding.display} is not supported" + f"SNOMED code {coding.code} - {coding.display} is not supported", ) raise DocumentRefException(400, LambdaError.DocRefInvalidType) logger.error("SNOMED code not found in FHIR document") @@ -147,6 +155,12 @@ def _create_document_reference( if not custodian: custodian = current_gp_ods + title = fhir_doc.content[0].attachment.title or None + + if doc_type != SnomedCodes.PATIENT_DATA.value and title is None: + logger.error("FHIR document validation error: attachment.title missing") + raise DocumentRefException(400, LambdaError.DocRefNoParse) + sub_folder, raw_request = ( ("user_upload", None) if doc_type != SnomedCodes.PATIENT_DATA.value @@ -161,7 +175,7 @@ def _create_document_reference( s3_bucket_name=self.staging_bucket_name, author=author, content_type=fhir_doc.content[0].attachment.contentType, - file_name=fhir_doc.content[0].attachment.title, + file_name=title, document_snomed_code_type=doc_type.code, doc_status="preliminary", status="current", diff --git a/lambdas/tests/e2e/api/fhir/test_upload_document_fhir_api_success.py b/lambdas/tests/e2e/api/fhir/test_upload_document_fhir_api_success.py index 41812f0642..dd1a7d4204 100644 --- a/lambdas/tests/e2e/api/fhir/test_upload_document_fhir_api_success.py +++ b/lambdas/tests/e2e/api/fhir/test_upload_document_fhir_api_success.py @@ -1,4 +1,5 @@ import base64 +import json import logging import os @@ -42,7 +43,8 @@ def condition(response_json): return response_json["content"][0]["attachment"].get("data", False) raw_retrieve_response = retrieve_document_with_retry( - upload_response["id"], condition + upload_response["id"], + condition, ) retrieve_response = raw_retrieve_response.json() @@ -85,7 +87,8 @@ def test_create_document_without_author_or_type(test_data): with open(sample_pdf_path, "rb") as f: record["data"] = base64.b64encode(f.read()).decode("utf-8") payload = pdm_data_helper.create_upload_payload( - record=record, exclude=["type", "author"] + record=record, + exclude=["type", "author"], ) for field in ["type", "author"]: @@ -103,3 +106,32 @@ def test_create_document_without_author_or_type(test_data): assert doc_ref["Item"]["RawRequest"] == payload for field in ["type", "author"]: assert field not in doc_ref["Item"]["RawRequest"] + + +def test_create_document_without_title(test_data): + record = { + "ods": "H81109", + "nhs_number": "9912003071", + } + + sample_pdf_path = os.path.join(os.path.dirname(__file__), "files", "dummy.pdf") + with open(sample_pdf_path, "rb") as f: + record["data"] = base64.b64encode(f.read()).decode("utf-8") + payload = pdm_data_helper.create_upload_payload(record=record, exclude=["title"]) + assert "title" not in payload + + raw_upload_response = upload_document(payload) + assert raw_upload_response.status_code == 201 + record["id"] = raw_upload_response.json()["id"].split("~")[1] + test_data.append(record) + + doc_ref = pdm_data_helper.retrieve_document_reference(record=record) + assert "Item" in doc_ref + assert "RawRequest" in doc_ref["Item"] + assert doc_ref["Item"]["RawRequest"] == payload + raw_request = json.loads(doc_ref["Item"]["RawRequest"]) + assert "content" in raw_request + content = raw_request["content"] + assert "attachment" in content[0] + attachment = raw_request["content"][0]["attachment"] + assert "title" not in attachment diff --git a/lambdas/tests/e2e/api/test_upload_document_api.py b/lambdas/tests/e2e/api/test_upload_document_api.py index cc9319bc23..204eea9368 100644 --- a/lambdas/tests/e2e/api/test_upload_document_api.py +++ b/lambdas/tests/e2e/api/test_upload_document_api.py @@ -18,7 +18,7 @@ data_helper = LloydGeorgeDataHelper() -def create_upload_payload(lloyd_george_record): +def create_upload_payload(lloyd_george_record, exclude: list[str] | None = None): sample_payload = { "resourceType": "DocumentReference", "type": { @@ -27,28 +27,28 @@ def create_upload_payload(lloyd_george_record): "system": "http://snomed.info/sct", "code": f"{LLOYD_GEORGE_SNOMED}", "display": "Lloyd George record folder", - } - ] + }, + ], }, "subject": { "identifier": { "system": "https://fhir.nhs.uk/Id/nhs-number", "value": lloyd_george_record["nhs_number"], - } + }, }, "author": [ { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": lloyd_george_record["ods"], - } - } + }, + }, ], "custodian": { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": lloyd_george_record["ods"], - } + }, }, "content": [ { @@ -57,11 +57,18 @@ def create_upload_payload(lloyd_george_record): "contentType": "application/pdf", "language": "en-GB", "title": "1of1_Lloyd_George_Record_[Paula Esme VESEY]_[9730153973]_[22-01-1960].pdf", - } - } + }, + }, ], } + if exclude: + for field in exclude: + if field == "title": + sample_payload["content"][0]["attachment"].pop(field, None) + else: + sample_payload.pop(field, None) + if "data" in lloyd_george_record: sample_payload["content"][0]["attachment"]["data"] = lloyd_george_record["data"] return json.dumps(sample_payload) @@ -105,10 +112,10 @@ def condition(response_json): assert base64.b64decode(base64_data, validate=True) assert upload_response == snapshot_json( - exclude=paths("id", "date", "content.0.attachment.url") + exclude=paths("id", "date", "content.0.attachment.url"), ) assert retrieve_response == snapshot_json( - exclude=paths("id", "date", "content.0.attachment.data") + exclude=paths("id", "date", "content.0.attachment.data"), ) @@ -153,8 +160,11 @@ def condition(response_json): assert upload_response == snapshot_json(exclude=paths("id", "date")) assert retrieve_response == snapshot_json( exclude=paths( - "id", "date", "content.0.attachment.url", "content.0.attachment.size" - ) + "id", + "date", + "content.0.attachment.url", + "content.0.attachment.size", + ), ) @@ -195,7 +205,7 @@ def condition(response_json): retrieve_response = raw_retrieve_response.json() assert upload_response == snapshot_json( - exclude=paths("id", "date", "content.0.attachment.url") + exclude=paths("id", "date", "content.0.attachment.url"), ) assert retrieve_response == snapshot_json(exclude=paths("id", "date")) @@ -221,3 +231,29 @@ def test_create_document_does_not_save_raw(test_data): doc_ref = data_helper.retrieve_document_reference(record=lloyd_george_record) assert "Item" in doc_ref assert "RawRequest" not in doc_ref["Item"] + + +def test_create_document_without_title_raises_error(test_data): + lloyd_george_record = {} + lloyd_george_record["ods"] = "H81109" + lloyd_george_record["nhs_number"] = "9449303304" + + sample_pdf_path = os.path.join(os.path.dirname(__file__), "files", "dummy.pdf") + with open(sample_pdf_path, "rb") as f: + lloyd_george_record["data"] = base64.b64encode(f.read()).decode("utf-8") + payload = create_upload_payload(lloyd_george_record, exclude=["title"]) + + url = f"https://{API_ENDPOINT}/FhirDocumentReference" + headers = {"Authorization": "Bearer 123", "X-Api-Key": API_KEY} + + retrieve_response = requests.post(url, headers=headers, data=payload) + assert retrieve_response.status_code == 400 + + json_response = retrieve_response.json() + assert ( + json_response["issue"][0]["details"]["coding"][0]["code"] == "VALIDATION_ERROR" + ) + assert ( + json_response["issue"][0]["diagnostics"] + == "Failed to parse document upload request data" + ) diff --git a/lambdas/tests/e2e/helpers/data_helper.py b/lambdas/tests/e2e/helpers/data_helper.py index c810b16823..5314e8ad73 100644 --- a/lambdas/tests/e2e/helpers/data_helper.py +++ b/lambdas/tests/e2e/helpers/data_helper.py @@ -43,7 +43,8 @@ def build_env(self, table_name, bucket_name): "ndr-dev": "internal-dev.api.service.nhs.uk", } self.apim_url = apim_map.get( - str(self.workspace), "internal-dev.api.service.nhs.uk" + str(self.workspace), + "internal-dev.api.service.nhs.uk", ) domain = ( @@ -61,7 +62,11 @@ def build_env(self, table_name, bucket_name): self.mtls_endpoint = f"mtls.{self.workspace}.{domain}" def build_record( - self, nhs_number="9912003071", data=None, doc_status=None, size=None + self, + nhs_number="9912003071", + data=None, + doc_status=None, + size=None, ): record = { "id": str(uuid.uuid4()), @@ -109,7 +114,8 @@ def create_resource(self, record): def retrieve_document_reference(self, record): return self.dynamo_service.get_item( - table_name=self.dynamo_table, key={"ID": record["id"]} + table_name=self.dynamo_table, + key={"ID": record["id"]}, ) def create_upload_payload(self, record, exclude=[], return_json=False): @@ -122,28 +128,28 @@ def create_upload_payload(self, record, exclude=[], return_json=False): "system": "https://snomed.info/sct", "code": f"{self.snomed_code}", "display": "Confidential patient data", - } - ] + }, + ], }, "subject": { "identifier": { "system": "https://fhir.nhs.uk/Id/nhs-number", "value": record["nhs_number"], - } + }, }, "author": [ { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": record["ods"], - } - } + }, + }, ], "custodian": { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": record["ods"], - } + }, }, "content": [ { @@ -152,13 +158,16 @@ def create_upload_payload(self, record, exclude=[], return_json=False): "contentType": "application/pdf", "language": "en-GB", "title": "1of1_pdm_record_[Paula Esme VESEY]_[9730153973]_[22-01-1960].pdf", - } - } + }, + }, ], } for field in exclude: - payload.pop(field, None) + if field == "title": + payload["content"][0]["attachment"].pop(field, None) + else: + payload.pop(field, None) if "data" in record: payload["content"][0]["attachment"]["data"] = record["data"] @@ -260,7 +269,7 @@ def add_virus_scan_tag(self, key, result, date): "TagSet": [ {"Key": "scan-result", "Value": result}, {"Key": "scan-date", "Value": date}, - ] + ], }, ) @@ -275,7 +284,9 @@ def check_record_exists_in_s3_with_version(self, key, version_id): try: if version_id: _ = s3_client.head_object( - Bucket=self.s3_bucket, Key=key, VersionId=version_id + Bucket=self.s3_bucket, + Key=key, + VersionId=version_id, ) else: _ = s3_client.head_object(Bucket=self.s3_bucket, Key=key) diff --git a/lambdas/tests/unit/services/test_pdm_get_fhir_document_reference_search_service.py b/lambdas/tests/unit/services/test_pdm_get_fhir_document_reference_search_service.py index c0e275b41e..b75c50f199 100644 --- a/lambdas/tests/unit/services/test_pdm_get_fhir_document_reference_search_service.py +++ b/lambdas/tests/unit/services/test_pdm_get_fhir_document_reference_search_service.py @@ -256,3 +256,77 @@ def test_create_document_reference_fhir_response_integration( assert isinstance(result, dict) assert result == expected_fhir_response + + +@freeze_time("2023-05-01T12:00:00Z") +def test_create_document_reference_fhir_response_no_title( + mock_document_service, + mocker, +): + mock_document_reference = mocker.MagicMock() + mock_document_reference.nhs_number = "9000000009" + mock_document_reference.file_name = None + mock_document_reference.created = "2023-05-01T12:00:00" + mock_document_reference.document_scan_creation = "2023-05-01" + mock_document_reference.id = "Y05868-1634567890" + mock_document_reference.current_gp_ods = "Y12345" + mock_document_reference.author = "Y12345" + mock_document_reference.doc_status = "final" + mock_document_reference.custodian = "Y12345" + mock_document_reference.document_snomed_code_type = "717391000000106" + mock_document_reference.version = "1" + + expected_fhir_response = { + "id": "717391000000106~Y05868-1634567890", + "resourceType": "DocumentReference", + "status": "current", + "docStatus": "final", + "subject": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000009", + }, + }, + "date": "2023-05-01T12:00:00", + "content": [ + { + "attachment": { + "contentType": "application/pdf", + "language": "en-GB", + "creation": "2023-05-01", + "url": f"{APIM_API_URL}/DocumentReference/717391000000106~Y05868-1634567890", + }, + }, + ], + "author": [ + { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + }, + }, + ], + "custodian": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y12345", + }, + }, + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "717391000000106", + "display": "Confidential patient data", + }, + ], + }, + "meta": {"versionId": "1"}, + } + + result = mock_document_service.create_document_reference_fhir_response( + mock_document_reference, + ) + + assert isinstance(result, dict) + assert result == expected_fhir_response diff --git a/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py b/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py index ca143721c9..427cecf029 100644 --- a/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py +++ b/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py @@ -6,22 +6,30 @@ from enums.mtls import MtlsCommonNames from enums.snomed_codes import SnomedCode, SnomedCodes from models.fhir.R4.base_models import Identifier, Reference -from models.fhir.R4.fhir_document_reference import Attachment +from models.fhir.R4.fhir_document_reference import ( + Attachment, +) from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) -from models.fhir.R4.fhir_document_reference import DocumentReferenceContent +from models.fhir.R4.fhir_document_reference import ( + DocumentReferenceContent, +) from services.fhir_document_reference_service_base import ( FhirDocumentReferenceServiceBase, ) from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) -from tests.unit.conftest import APIM_API_URL +from tests.unit.conftest import ( + APIM_API_URL, +) from tests.unit.conftest import ( EXPECTED_PARSED_PATIENT_BASE_CASE as mock_pds_patient_details, ) -from tests.unit.conftest import TEST_UUID +from tests.unit.conftest import ( + TEST_UUID, +) from utils.lambda_exceptions import DocumentRefException from utils.request_context import request_context @@ -45,13 +53,13 @@ def mock_post_fhir_doc_ref_service(set_env): @pytest.fixture def mock_fhir_doc_ref_base_service(mocker, setup_request_context): mock_document_service = mocker.patch( - "services.fhir_document_reference_service_base.DocumentService" + "services.fhir_document_reference_service_base.DocumentService", ) mock_s3_service = mocker.patch( - "services.fhir_document_reference_service_base.S3Service" + "services.fhir_document_reference_service_base.S3Service", ) mock_dynamo_service = mocker.patch( - "services.fhir_document_reference_service_base.DynamoDBService" + "services.fhir_document_reference_service_base.DynamoDBService", ) service = FhirDocumentReferenceServiceBase() service.document_service = mock_document_service.return_value @@ -70,8 +78,8 @@ def mock_mtls_common_names(monkeypatch): "PDM": [ "ndrclient.main.int.pdm.national.nhs.uk", "client.dev.ndr.national.nhs.uk", - ] - } + ], + }, ), ) @@ -111,7 +119,7 @@ def valid_fhir_doc_json(): "identifier": { "system": "https://fhir.nhs.uk/Id/nhs-number", "value": "9000000009", - } + }, }, "type": { "coding": [ @@ -119,22 +127,22 @@ def valid_fhir_doc_json(): "system": "http://snomed.info/sct", "code": SnomedCodes.LLOYD_GEORGE.value.code, "display": SnomedCodes.LLOYD_GEORGE.value.display_name, - } - ] + }, + ], }, "custodian": { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } + }, }, "author": [ { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } - } + }, + }, ], "content": [ { @@ -143,10 +151,10 @@ def valid_fhir_doc_json(): "language": "en-GB", "title": "test-file.pdf", "creation": "2023-01-01T12:00:00Z", - } - } + }, + }, ], - } + }, ) @@ -161,13 +169,13 @@ def valid_fhir_doc_json_only_required(): "identifier": { "system": "https://fhir.nhs.uk/Id/nhs-number", "value": "9000000009", - } + }, }, "custodian": { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } + }, }, "content": [ { @@ -176,10 +184,10 @@ def valid_fhir_doc_json_only_required(): "language": "en-GB", "title": "test-file.pdf", "creation": "2023-01-01T12:00:00Z", - } - } + }, + }, ], - } + }, ) @@ -225,7 +233,7 @@ def valid_mtls_fhir_doc_json(): "identifier": { "system": "https://fhir.nhs.uk/Id/nhs-number", "value": "9000000009", - } + }, }, "type": { "coding": [ @@ -233,22 +241,22 @@ def valid_mtls_fhir_doc_json(): "system": "http://snomed.info/sct", "code": SnomedCodes.PATIENT_DATA.value.code, "display": SnomedCodes.PATIENT_DATA.value.display_name, - } - ] + }, + ], }, "custodian": { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } + }, }, "author": [ { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } - } + }, + }, ], "content": [ { @@ -257,10 +265,91 @@ def valid_mtls_fhir_doc_json(): "language": "en-GB", "title": "test-file.pdf", "creation": "2023-01-01T12:00:00Z", - } - } + }, + }, ], - } + }, + ) + + +@pytest.fixture +def invalid_fhir_doc_json_missing_content(): + return json.dumps( + { + "resourceType": "DocumentReference", + "docStatus": "final", + "status": "current", + "subject": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000009", + }, + }, + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": SnomedCodes.PATIENT_DATA.value.code, + "display": SnomedCodes.PATIENT_DATA.value.display_name, + }, + ], + }, + "custodian": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "A12345", + }, + }, + "author": [ + { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "A12345", + }, + }, + ], + }, + ) + + +@pytest.fixture +def invalid_fhir_doc_json_missing_content_attachment(): + return json.dumps( + { + "resourceType": "DocumentReference", + "docStatus": "final", + "status": "current", + "subject": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9000000009", + }, + }, + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": SnomedCodes.PATIENT_DATA.value.code, + "display": SnomedCodes.PATIENT_DATA.value.display_name, + }, + ], + }, + "custodian": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "A12345", + }, + }, + "author": [ + { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "A12345", + }, + }, + ], + "content": [{}], + }, ) @@ -298,20 +387,22 @@ def valid_fhir_doc_object_without_optional(valid_fhir_doc_json_only_required): def test_get_dynamo_table_for_patient_data_doc_type( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test _get_dynamo_table_for_doc_type method with a non-Lloyd George document type.""" patient_data_code = SnomedCodes.PATIENT_DATA.value result = mock_post_fhir_doc_ref_service._get_dynamo_table_for_doc_type( - patient_data_code + patient_data_code, ) assert result == str(DynamoTables.CORE) def test_get_dynamo_table_for_unsupported_doc_type( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test _get_dynamo_table_for_doc_type method with a non-Lloyd George document type.""" @@ -325,7 +416,8 @@ def test_get_dynamo_table_for_unsupported_doc_type( def test_get_dynamo_table_for_lloyd_george_doc_type( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test _get_dynamo_table_for_doc_type method with Lloyd George document type.""" lg_code = SnomedCodes.LLOYD_GEORGE.value @@ -346,7 +438,8 @@ def test_process_mtls_fhir_document_reference_with_binary( custom_endpoint = f"{APIM_API_URL}/DocumentReference" result = mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_mtls_fhir_doc_with_binary, valid_mtls_request_context + valid_mtls_fhir_doc_with_binary, + valid_mtls_request_context, ) assert isinstance(result, str) @@ -357,20 +450,25 @@ def test_process_mtls_fhir_document_reference_with_binary( def test_determine_document_type_with_correct_common_name( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _determine_document_type method when type is missing entirely.""" fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) fhir_doc.type = None result = mock_post_fhir_doc_ref_service._determine_document_type( - fhir_doc, MtlsCommonNames.PDM + fhir_doc, + MtlsCommonNames.PDM, ) assert result == SnomedCodes.PATIENT_DATA.value def test_s3_file_key_for_pdm( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _create_document_reference method without custodian information.""" @@ -381,15 +479,16 @@ def test_s3_file_key_for_pdm( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.author = [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="B67890" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), ] fhir_doc.custodian = None @@ -414,7 +513,9 @@ def test_s3_file_key_for_pdm( def test_create_pdm_document_reference_with_raw_request( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _create_document_reference method with raw_request included (pdm).""" @@ -425,20 +526,22 @@ def test_create_pdm_document_reference_with_raw_request( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.custodian = Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="A12345" - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="A12345", + ), ) fhir_doc.author = [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="B67890" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), ] doc_type = SnomedCodes.PATIENT_DATA.value @@ -461,7 +564,9 @@ def test_create_pdm_document_reference_with_raw_request( def test_create_lg_document_reference_with_raw_request( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _create_document_reference method with raw_request included (LG, should be empty).""" @@ -472,20 +577,22 @@ def test_create_lg_document_reference_with_raw_request( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.custodian = Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="A12345" - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="A12345", + ), ) fhir_doc.author = [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="B67890" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), ] doc_type = SnomedCodes.LLOYD_GEORGE.value @@ -508,9 +615,11 @@ def test_create_lg_document_reference_with_raw_request( def test_create_pdm_document_reference_without_author_or_type( - mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): - """Test _create_document_reference method with raw_request included (LG, should be empty).""" + """Test _create_document_reference method without author or type.""" fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) fhir_doc.content = [ @@ -519,13 +628,14 @@ def test_create_pdm_document_reference_without_author_or_type( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.custodian = Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="A12345" - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="A12345", + ), ) fhir_doc.author = [] fhir_doc.type = [] @@ -546,3 +656,88 @@ def test_create_pdm_document_reference_without_author_or_type( assert result.custodian == "A12345" assert result.current_gp_ods == "C13579" assert result.author is None + + +def test_create_pdm_document_reference_without_title( + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, +): + """Test _create_document_reference method without title""" + + fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) + fhir_doc.content = [ + DocumentReferenceContent( + attachment=Attachment( + contentType="application/pdf", + creation="2023-01-01T12:00:00Z", + ), + ), + ] + fhir_doc.custodian = Reference( + identifier=Identifier( + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="A12345", + ), + ) + fhir_doc.author = [ + Reference( + identifier=Identifier( + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), + ] + + doc_type = SnomedCodes.PATIENT_DATA.value + result = mock_post_fhir_doc_ref_service._create_document_reference( + nhs_number="9000000009", + author="B67890", + doc_type=doc_type, + fhir_doc=fhir_doc, + current_gp_ods="C13579", + raw_fhir_doc=json.dumps({"foo": "bar"}), + ) + + assert result.raw_request == json.dumps({"foo": "bar"}) + assert result.nhs_number == "9000000009" + assert result.document_snomed_code_type == SnomedCodes.PATIENT_DATA.value.code + assert result.custodian == "A12345" + assert result.current_gp_ods == "C13579" + assert result.file_name is None + + +def test_process_fhir_document_reference_without_content_raises_error( + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mock_mtls_common_names, + invalid_fhir_doc_json_missing_content, + valid_mtls_request_context, +): + with pytest.raises(DocumentRefException) as excinfo: + mock_post_fhir_doc_ref_service.process_fhir_document_reference( + invalid_fhir_doc_json_missing_content, + valid_mtls_request_context, + ) + + assert excinfo.value.status_code == 400 + assert excinfo.value.error == LambdaError.DocRefNoParse + assert excinfo.value.message == "Failed to parse document upload request data" + + +def test_process_fhir_document_reference_without_content_attachment_raises_error( + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mock_mtls_common_names, + invalid_fhir_doc_json_missing_content_attachment, + valid_mtls_request_context, +): + with pytest.raises(DocumentRefException) as excinfo: + mock_post_fhir_doc_ref_service.process_fhir_document_reference( + invalid_fhir_doc_json_missing_content_attachment, + valid_mtls_request_context, + ) + + assert excinfo.value.status_code == 400 + assert excinfo.value.error == LambdaError.DocRefNoParse + assert excinfo.value.message == "Failed to parse document upload request data" diff --git a/lambdas/tests/unit/services/test_pdm_upload_document_reference_service.py b/lambdas/tests/unit/services/test_pdm_upload_document_reference_service.py index c0b3d14383..908cb7218e 100644 --- a/lambdas/tests/unit/services/test_pdm_upload_document_reference_service.py +++ b/lambdas/tests/unit/services/test_pdm_upload_document_reference_service.py @@ -34,7 +34,7 @@ def mock_document_reference(): doc_ref.doc_status = "uploading" doc_ref.version = "1" doc_ref._build_s3_location = Mock( - return_value="s3://test-lg-bucket/9000000001/test-doc-id" + return_value="s3://test-lg-bucket/9000000001/test-doc-id", ) return doc_ref @@ -44,7 +44,7 @@ def mock_virus_scan_service( mocker, ): mock = mocker.patch( - "services.upload_document_reference_service.get_virus_scan_service" + "services.upload_document_reference_service.get_virus_scan_service", ) yield mock @@ -79,7 +79,7 @@ def mock_pdm_document_reference(): doc_ref.file_size = 1234567890 doc_ref.doc_status = "uploading" doc_ref._build_s3_location = Mock( - return_value=f"s3://test-staging-bucket/fhir_upload/{SnomedCodes.PATIENT_DATA.value.code}/9000000001/test-doc-id" + return_value=f"s3://test-staging-bucket/fhir_upload/{SnomedCodes.PATIENT_DATA.value.code}/9000000001/test-doc-id", ) return doc_ref @@ -118,7 +118,9 @@ def test_handle_upload_document_reference_request_with_none_object_key(pdm_servi def test_handle_upload_document_reference_request_success( - service, mock_pdm_document_reference, mocker + service, + mock_pdm_document_reference, + mocker, ): """Test successful handling of the upload document reference request""" object_key = ( @@ -130,7 +132,7 @@ def test_handle_upload_document_reference_request_success( [mock_pdm_document_reference], ] service.virus_scan_service.scan_file = mocker.MagicMock( - return_value=VirusScanResult.CLEAN + return_value=VirusScanResult.CLEAN, ) service.handle_upload_document_reference_request(object_key, object_size) @@ -146,24 +148,26 @@ def test_handle_upload_document_reference_request_with_exception(pdm_service): object_key = "staging/test-doc-id" pdm_service.document_service.fetch_documents_from_table.side_effect = Exception( - "Test error" + "Test error", ) pdm_service.handle_upload_document_reference_request(object_key) def test_fetch_preliminary_document_reference_success( - pdm_service, mock_pdm_document_reference + pdm_service, + mock_pdm_document_reference, ): """Test successful document reference fetching""" document_key = "test-doc-id" nhs_number = "12345" pdm_service.document_service.fetch_documents_from_table.return_value = [ - mock_pdm_document_reference + mock_pdm_document_reference, ] result = pdm_service._fetch_preliminary_document_reference( - document_key=document_key, nhs_number=nhs_number + document_key=document_key, + nhs_number=nhs_number, ) assert result == mock_pdm_document_reference @@ -182,7 +186,8 @@ def test_fetch_preliminary_document_reference_no_documents_found(pdm_service): pdm_service.document_service.fetch_documents_from_table.return_value = [] result = pdm_service._fetch_preliminary_document_reference( - document_key=document_key, nhs_number=nhs_number + document_key=document_key, + nhs_number=nhs_number, ) assert result is None @@ -194,12 +199,14 @@ def test_fetch_preliminary_document_reference_no_nhs_number(pdm_service): nhs_number = None with pytest.raises(FileProcessingException): pdm_service._fetch_preliminary_document_reference( - document_key=document_key, nhs_number=nhs_number + document_key=document_key, + nhs_number=nhs_number, ) def test_fetch_preliminary_document_reference_multiple_documents_warning( - pdm_service, mock_document_reference + pdm_service, + mock_document_reference, ): """Test handling when multiple documents are found""" document_key = "test-doc-id" @@ -211,7 +218,8 @@ def test_fetch_preliminary_document_reference_multiple_documents_warning( ] result = pdm_service._fetch_preliminary_document_reference( - document_key=document_key, nhs_number=nhs_number + document_key=document_key, + nhs_number=nhs_number, ) assert result == mock_document_reference @@ -227,27 +235,35 @@ def test_fetch_preliminary_document_reference_exception(pdm_service): with pytest.raises(DocumentServiceException): pdm_service._fetch_preliminary_document_reference( - document_key=document_key, nhs_number=nhs_number + document_key=document_key, + nhs_number=nhs_number, ) def test__process_preliminary_document_reference_clean_virus_scan( - pdm_service, mock_pdm_document_reference, mocker + pdm_service, + mock_pdm_document_reference, + mocker, ): """Test processing document reference with a clean virus scan""" object_key = "12345/test-doc-id" mocker.patch.object( - pdm_service, "_perform_virus_scan", return_value=VirusScanResult.CLEAN + pdm_service, + "_perform_virus_scan", + return_value=VirusScanResult.CLEAN, ) mock_process_clean = mocker.patch.object(pdm_service, "_process_clean_document") mock_finalize_transaction = mocker.patch.object( - pdm_service, "_finalize_and_supersede_with_transaction" + pdm_service, + "_finalize_and_supersede_with_transaction", ) mock_delete = mocker.patch.object(pdm_service, "delete_file_from_staging_bucket") pdm_service._process_preliminary_document_reference( - mock_pdm_document_reference, object_key, 1222 + mock_pdm_document_reference, + object_key, + 1222, ) mock_process_clean.assert_called_once() @@ -257,21 +273,28 @@ def test__process_preliminary_document_reference_clean_virus_scan( assert mock_pdm_document_reference.uploading is False mock_delete.assert_called_once() + def test__process_preliminary_document_reference_infected_virus_scan( - pdm_service, mock_document_reference, mocker + pdm_service, + mock_document_reference, + mocker, ): """Test processing document reference with an infected virus scan""" object_key = "staging/test-doc-id" mocker.patch.object( - pdm_service, "_perform_virus_scan", return_value=VirusScanResult.INFECTED + pdm_service, + "_perform_virus_scan", + return_value=VirusScanResult.INFECTED, ) mock_delete = mocker.patch.object(pdm_service, "delete_file_from_staging_bucket") mock_process_clean = mocker.patch.object(pdm_service, "_process_clean_document") mock_update_dynamo = mocker.patch.object(pdm_service, "_update_dynamo_table") pdm_service._process_preliminary_document_reference( - mock_document_reference, object_key, 1222 + mock_document_reference, + object_key, + 1222, ) mock_process_clean.assert_not_called() @@ -280,7 +303,8 @@ def test__process_preliminary_document_reference_infected_virus_scan( def test_perform_virus_scan_returns_clean_hardcoded( - pdm_service, mock_document_reference + pdm_service, + mock_document_reference, ): """Test virus scan returns hardcoded CLEAN result""" object_key = "staging/test-doc-id" @@ -289,7 +313,9 @@ def test_perform_virus_scan_returns_clean_hardcoded( def test_perform_virus_scan_exception_returns_infected( - pdm_service, mock_document_reference, mocker + pdm_service, + mock_document_reference, + mocker, ): """Test virus scan exception handling returns INFECTED for safety""" mock_virus_service = mocker.patch.object(pdm_service, "virus_scan_service") @@ -316,7 +342,9 @@ def test_process_clean_document_success(pdm_service, mock_document_reference, mo def test_process_clean_document_exception_restores_original_values( - pdm_service, mock_document_reference, mocker + pdm_service, + mock_document_reference, + mocker, ): """Test that original values are restored when processing fails""" object_key = "staging/test-doc-id" @@ -342,7 +370,8 @@ def test_process_clean_document_exception_restores_original_values( def test_copy_files_from_staging_bucket_success( - pdm_service, mock_pdm_document_reference + pdm_service, + mock_pdm_document_reference, ): """Test successful file copying from staging bucket""" source_file_key = ( @@ -351,7 +380,8 @@ def test_copy_files_from_staging_bucket_success( expected_dest_key = "9000000001/test-doc-id" pdm_service.copy_files_from_staging_bucket( - mock_pdm_document_reference, source_file_key + mock_pdm_document_reference, + source_file_key, ) pdm_service.s3_service.copy_across_bucket.assert_called_once_with( @@ -366,13 +396,14 @@ def test_copy_files_from_staging_bucket_success( def test_copy_files_from_staging_bucket_client_error( - pdm_service, mock_document_reference + pdm_service, + mock_document_reference, ): """Test handling of ClientError during file copying""" source_file_key = "staging/test-doc-id" client_error = ClientError( error_response={ - "Error": {"Code": "NoSuchBucket", "Message": "Bucket does not exist"} + "Error": {"Code": "NoSuchBucket", "Message": "Bucket does not exist"}, }, operation_name="CopyObject", ) @@ -380,7 +411,8 @@ def test_copy_files_from_staging_bucket_client_error( with pytest.raises(FileProcessingException): pdm_service.copy_files_from_staging_bucket( - mock_document_reference, source_file_key + mock_document_reference, + source_file_key, ) @@ -391,7 +423,8 @@ def test_delete_file_from_staging_bucket_success(pdm_service): pdm_service.delete_file_from_staging_bucket(source_file_key) pdm_service.s3_service.delete_object.assert_called_once_with( - MOCK_STAGING_STORE_BUCKET, source_file_key + MOCK_STAGING_STORE_BUCKET, + source_file_key, ) @@ -404,7 +437,8 @@ def test_delete_pdm_file_from_staging_bucket_success(pdm_service): pdm_service.delete_file_from_staging_bucket(source_file_key) pdm_service.s3_service.delete_object.assert_called_once_with( - MOCK_STAGING_STORE_BUCKET, source_file_key + MOCK_STAGING_STORE_BUCKET, + source_file_key, ) @@ -413,7 +447,7 @@ def test_delete_file_from_staging_bucket_client_error(pdm_service): source_file_key = "staging/test-doc-id" client_error = ClientError( error_response={ - "Error": {"Code": "NoSuchKey", "Message": "Key does not exist"} + "Error": {"Code": "NoSuchKey", "Message": "Key does not exist"}, }, operation_name="DeleteObject", ) @@ -427,7 +461,8 @@ def test_delete_file_from_staging_bucket_client_error(pdm_service): def test_update_dynamo_table_clean_scan_result( - pdm_service, mock_pdm_document_reference + pdm_service, + mock_pdm_document_reference, ): """Test updating DynamoDB table with a clean scan result""" pdm_service._update_dynamo_table(mock_pdm_document_reference) @@ -459,7 +494,10 @@ def test_update_dynamo_table_client_error(pdm_service, mock_document_reference): """Test handling of ClientError during DynamoDB update""" client_error = ClientError( error_response={ - "Error": {"Code": "ResourceNotFoundException", "Message": "Table not found"} + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Table not found", + }, }, operation_name="UpdateItem", ) @@ -485,13 +523,17 @@ def test_handle_upload_document_reference_request_no_document_found(pdm_service) def test_process_preliminary_document_reference_exception_during_processing( - pdm_service, mock_document_reference, mocker + pdm_service, + mock_document_reference, + mocker, ): """Test that exceptions during processing are properly raised""" object_key = "staging/test-doc-id" mocker.patch.object( - pdm_service, "_perform_virus_scan", return_value=VirusScanResult.CLEAN + pdm_service, + "_perform_virus_scan", + return_value=VirusScanResult.CLEAN, ) mocker.patch.object( pdm_service, @@ -501,7 +543,9 @@ def test_process_preliminary_document_reference_exception_during_processing( with pytest.raises(Exception) as exc_info: pdm_service._process_preliminary_document_reference( - mock_document_reference, object_key, 1222 + mock_document_reference, + object_key, + 1222, ) assert "Processing failed" in str(exc_info.value) @@ -511,7 +555,7 @@ def test_get_infrastructure_for_document_key_pdm(service): assert service.table_name == "" assert service.destination_bucket_name == MOCK_LG_BUCKET service._get_infrastructure_for_document_key( - object_parts=["fhir_upload", SnomedCodes.PATIENT_DATA.value.code, "1234"] + object_parts=["fhir_upload", SnomedCodes.PATIENT_DATA.value.code, "1234"], ) assert service.table_name == str(DynamoTables.CORE) assert service.destination_bucket_name == MOCK_PDM_BUCKET @@ -607,17 +651,19 @@ def test_document_type_extraction_from_object_key( def test_handle_upload_pdm_document_reference_request_success( - service, mock_document_reference, mocker + service, + mock_document_reference, + mocker, ): """Test successful handling of the upload document reference request""" pdm_snomed = SnomedCodes.PATIENT_DATA.value object_key = f"fhir_upload/{pdm_snomed.code}/staging/test-doc-id" object_size = 1111 service.document_service.fetch_documents_from_table.return_value = [ - mock_document_reference + mock_document_reference, ] service.virus_scan_service.scan_file = mocker.MagicMock( - return_value=VirusScanResult.CLEAN + return_value=VirusScanResult.CLEAN, ) service.handle_upload_document_reference_request(object_key, object_size) @@ -630,7 +676,8 @@ def test_handle_upload_pdm_document_reference_request_success( def test_copy_files_from_staging_bucket_to_pdm_success( - pdm_service, mock_pdm_document_reference + pdm_service, + mock_pdm_document_reference, ): """Test successful file copying from staging bucket""" source_file_key = ( @@ -640,7 +687,8 @@ def test_copy_files_from_staging_bucket_to_pdm_success( f"{mock_pdm_document_reference.nhs_number}/{mock_pdm_document_reference.id}" ) pdm_service.copy_files_from_staging_bucket( - mock_pdm_document_reference, source_file_key + mock_pdm_document_reference, + source_file_key, ) pdm_service.s3_service.copy_across_bucket.assert_called_once_with( source_bucket=MOCK_STAGING_STORE_BUCKET, diff --git a/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py b/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py index 4db5aced91..397f8467a4 100644 --- a/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py +++ b/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py @@ -6,22 +6,32 @@ from enums.snomed_codes import SnomedCode, SnomedCodes from models.document_reference import DocumentReference from models.fhir.R4.base_models import CodeableConcept, Identifier, Reference -from models.fhir.R4.fhir_document_reference import SNOMED_URL, Attachment +from models.fhir.R4.fhir_document_reference import ( + SNOMED_URL, + Attachment, +) from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) -from models.fhir.R4.fhir_document_reference import DocumentReferenceContent +from models.fhir.R4.fhir_document_reference import ( + DocumentReferenceContent, +) from services.fhir_document_reference_service_base import ( FhirDocumentReferenceServiceBase, ) from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) -from tests.unit.conftest import APIM_API_URL +from tests.unit.conftest import ( + APIM_API_URL, +) from tests.unit.conftest import ( EXPECTED_PARSED_PATIENT_BASE_CASE as mock_pds_patient_details, ) -from tests.unit.conftest import MOCK_LG_TABLE_NAME, TEST_UUID +from tests.unit.conftest import ( + MOCK_LG_TABLE_NAME, + TEST_UUID, +) from utils.exceptions import FhirDocumentReferenceException from utils.lambda_exceptions import DocumentRefException from utils.request_context import request_context @@ -36,16 +46,16 @@ def mock_post_fhir_doc_ref_service(set_env): @pytest.fixture def mock_fhir_doc_ref_base_service(mocker, setup_request_context): mock_document_service = mocker.patch( - "services.fhir_document_reference_service_base.DocumentService" + "services.fhir_document_reference_service_base.DocumentService", ) mock_s3_service = mocker.patch( - "services.fhir_document_reference_service_base.S3Service" + "services.fhir_document_reference_service_base.S3Service", ) mock_dynamo_service = mocker.patch( - "services.fhir_document_reference_service_base.DynamoDBService" + "services.fhir_document_reference_service_base.DynamoDBService", ) mock_doc_type_table_router = mocker.patch( - "services.fhir_document_reference_service_base.DocTypeTableRouter" + "services.fhir_document_reference_service_base.DocTypeTableRouter", ) service = FhirDocumentReferenceServiceBase() service.document_service = mock_document_service.return_value @@ -100,7 +110,7 @@ def valid_fhir_doc_json(): "identifier": { "system": "https://fhir.nhs.uk/Id/nhs-number", "value": "9000000009", - } + }, }, "type": { "coding": [ @@ -108,22 +118,22 @@ def valid_fhir_doc_json(): "system": "http://snomed.info/sct", "code": SnomedCodes.LLOYD_GEORGE.value.code, "display": SnomedCodes.LLOYD_GEORGE.value.display_name, - } - ] + }, + ], }, "custodian": { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } + }, }, "author": [ { "identifier": { "system": "https://fhir.nhs.uk/Id/ods-organization-code", "value": "A12345", - } - } + }, + }, ], "content": [ { @@ -132,10 +142,10 @@ def valid_fhir_doc_json(): "language": "en-GB", "title": "test-file.pdf", "creation": "2023-01-01T12:00:00Z", - } - } + }, + }, ], - } + }, ) @@ -165,7 +175,7 @@ def test_process_fhir_document_reference_with_presigned_url( mock_handle_document_save.return_value = mock_presigned_url_response result = mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_json + valid_fhir_doc_json, ) expected_pre_sign_url = mock_presigned_url_response @@ -187,7 +197,7 @@ def test_process_fhir_document_reference_with_binary( custom_endpoint = f"{APIM_API_URL}/DocumentReference" mock_handle_document_save.return_value = None result = mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_with_binary + valid_fhir_doc_with_binary, ) assert isinstance(result, str) @@ -198,7 +208,8 @@ def test_process_fhir_document_reference_with_binary( def test_validation_error( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test handling of an invalid FHIR document.""" with pytest.raises(DocumentRefException) as excinfo: @@ -222,6 +233,7 @@ def test_validation_error( ], ) def test_doc_ref_no_parse_message_includes_details_format( + mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, valid_fhir_doc_json, nhs_number, @@ -234,7 +246,7 @@ def test_doc_ref_no_parse_message_includes_details_format( with pytest.raises(DocumentRefException) as error: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - invalid_nhs_doc_json + invalid_nhs_doc_json, ) assert error.value.status_code == 400 @@ -268,8 +280,8 @@ def test_doc_ref_no_parse_message_includes_details_format( "system": "http://snomed.info/sct", "code": "invalid-code", "display": "Invalid", - } - ] + }, + ], }, }, LambdaError.DocRefInvalidType, @@ -297,7 +309,9 @@ def test_document_validation_errors( def test_raise_dynamo_error( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, valid_fhir_doc_json + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + valid_fhir_doc_json, ): """Test handling of DynamoDB error.""" mock_fhir_doc_ref_base_service.dynamo_service.create_item.side_effect = ClientError( @@ -307,7 +321,7 @@ def test_raise_dynamo_error( with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_json + valid_fhir_doc_json, ) assert excinfo.value.status_code == 500 @@ -341,7 +355,9 @@ def test_save_document_reference_to_dynamo_error( with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service._handle_document_save( - document_ref, valid_fhir_doc_object, "test_table" + document_ref, + valid_fhir_doc_object, + "test_table", ) assert excinfo.value.status_code == 500 @@ -359,12 +375,12 @@ def test_process_fhir_document_reference_with_pds_error( """Test process_fhir_document_reference with a real PDS error (PatientNotFoundException).""" mock_check_nhs_number_with_pds.side_effect = FhirDocumentReferenceException( - "Patient not found" + "Patient not found", ) with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_json + valid_fhir_doc_json, ) assert excinfo.value.status_code == 400 @@ -372,7 +388,9 @@ def test_process_fhir_document_reference_with_pds_error( def test_s3_presigned_url_error( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, valid_fhir_doc_json + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + valid_fhir_doc_json, ): """Test handling of S3 presigned URL error.""" mock_fhir_doc_ref_base_service.s3_service.create_put_presigned_url.side_effect = ( @@ -384,7 +402,7 @@ def test_s3_presigned_url_error( with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_json + valid_fhir_doc_json, ) assert excinfo.value.status_code == 500 @@ -398,12 +416,13 @@ def test_s3_upload_error( ): """Test handling of S3 upload error.""" mock_fhir_doc_ref_base_service.s3_service.upload_file_obj.side_effect = ClientError( - {"Error": {"Code": "InternalServerError", "Message": "Test error"}}, "PutObject" + {"Error": {"Code": "InternalServerError", "Message": "Test error"}}, + "PutObject", ) with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_with_binary + valid_fhir_doc_with_binary, ) assert excinfo.value.status_code == 500 @@ -419,12 +438,12 @@ def test_extract_nhs_number_from_fhir_with_invalid_system( """Test _extract_nhs_number_from_fhir method with an invalid NHS number system.""" valid_fhir_doc_object.subject = Reference( - identifier=Identifier(system="invalid-system", value="9000000009") + identifier=Identifier(system="invalid-system", value="9000000009"), ) with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_object.model_dump_json() + valid_fhir_doc_object.model_dump_json(), ) assert excinfo.value.status_code == 400 @@ -442,7 +461,7 @@ def test_extract_nhs_number_from_fhir_with_missing_identifier( with pytest.raises(DocumentRefException) as excinfo: mock_post_fhir_doc_ref_service.process_fhir_document_reference( - valid_fhir_doc_object.model_dump_json() + valid_fhir_doc_object.model_dump_json(), ) assert excinfo.value.status_code == 400 @@ -450,7 +469,9 @@ def test_extract_nhs_number_from_fhir_with_missing_identifier( def test_create_document_reference_with_author( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _create_document_reference method with author information included.""" @@ -461,20 +482,22 @@ def test_create_document_reference_with_author( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.custodian = Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="A12345" - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="A12345", + ), ) fhir_doc.author = [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="B67890" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), ] doc_type = SnomedCode(code="test-code", display_name="Test Type") @@ -496,7 +519,9 @@ def test_create_document_reference_with_author( def test_create_document_reference_without_custodian( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _create_document_reference method without custodian information.""" @@ -507,15 +532,16 @@ def test_create_document_reference_without_custodian( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.author = [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="B67890" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), ] fhir_doc.custodian = None @@ -543,8 +569,8 @@ def test_create_document_reference_without_custodian( identifier=Identifier( system="https://fhir.nhs.uk/Id/ods-organization-code", value="A12345", - ) - ) + ), + ), ], "A12345", ), @@ -579,16 +605,19 @@ def test_extract_author_from_fhir( [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + ), + ), ] ), ([Reference(identifier=None)]), ], ) def test_extract_author_from_fhir_raises_error( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker, fhir_author + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, + fhir_author, ): """Test _extract_author_from_fhir method with malformed json returns Validation errors.""" fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) @@ -601,7 +630,9 @@ def test_extract_author_from_fhir_raises_error( def test_determine_document_type_with_missing_type( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _determine_document_type method when type is missing entirely.""" fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) @@ -615,7 +646,9 @@ def test_determine_document_type_with_missing_type( def test_determine_document_type_with_unknown_config( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _determine_document_type method when type is missing entirely.""" fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) @@ -624,7 +657,7 @@ def test_determine_document_type_with_unknown_config( "system": SNOMED_URL, "code": "1234567890", "display": "unknown code", - } + }, ] fhir_doc.type = CodeableConcept(coding=mock_coding) @@ -636,7 +669,9 @@ def test_determine_document_type_with_unknown_config( def test_determine_document_type_with_missing_coding( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _determine_document_type method when coding is missing.""" fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) @@ -651,7 +686,8 @@ def test_determine_document_type_with_missing_coding( def test_process_fhir_document_reference_with_malformed_json( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test process_fhir_document_reference with malformed JSON.""" malformed_json = '{"resourceType": "DocumentReference", "invalid": }' @@ -664,7 +700,8 @@ def test_process_fhir_document_reference_with_malformed_json( def test_process_fhir_document_reference_with_empty_string( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test process_fhir_document_reference with an empty string.""" with pytest.raises(DocumentRefException) as excinfo: @@ -675,7 +712,8 @@ def test_process_fhir_document_reference_with_empty_string( def test_process_fhir_document_reference_with_none( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, ): """Test process_fhir_document_reference with None input.""" with pytest.raises(DocumentRefException) as excinfo: @@ -685,8 +723,28 @@ def test_process_fhir_document_reference_with_none( assert excinfo.value.error == LambdaError.DocRefNoParse +def test_create_lg_document_reference_without_content_raises_error( + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, +): + """Test _create_document_reference method without content attachment raises error.""" + + fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) + fhir_doc.content = None + + with pytest.raises(DocumentRefException) as excinfo: + mock_post_fhir_doc_ref_service.process_fhir_document_reference(fhir_doc) + + assert excinfo.value.status_code == 400 + assert excinfo.value.error == LambdaError.DocRefNoParse + assert excinfo.value.message == "Failed to parse document upload request data" + + def test_s3_file_key_for_lg( - mock_fhir_doc_ref_base_service, mock_post_fhir_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, ): """Test _create_document_reference method without custodian information.""" @@ -697,15 +755,16 @@ def test_s3_file_key_for_lg( contentType="application/pdf", title="test-file.pdf", creation="2023-01-01T12:00:00Z", - ) - ) + ), + ), ] fhir_doc.author = [ Reference( identifier=Identifier( - system="https://fhir.nhs.uk/Id/ods-organization-code", value="B67890" - ) - ) + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), ] fhir_doc.custodian = None @@ -724,3 +783,47 @@ def test_s3_file_key_for_lg( assert "user_upload/9000000009" in result.s3_upload_key assert f"9000000009/{result.id}" in result.s3_file_key assert result.sub_folder == "user_upload" + + +def test_create_lg_document_reference_without_title_raises_error( + mock_fhir_doc_ref_base_service, + mock_post_fhir_doc_ref_service, + mocker, +): + """Test _create_document_reference method without title information raises error.""" + + fhir_doc = mocker.MagicMock(spec=FhirDocumentReference) + fhir_doc.content = [ + DocumentReferenceContent( + attachment=Attachment( + contentType="application/pdf", + creation="2023-01-01T12:00:00Z", + ), + ), + ] + fhir_doc.author = [ + Reference( + identifier=Identifier( + system="https://fhir.nhs.uk/Id/ods-organization-code", + value="B67890", + ), + ), + ] + fhir_doc.custodian = None + + doc_type = SnomedCodes.LLOYD_GEORGE.value + current_gp_ods = "C13579" + + with pytest.raises(DocumentRefException) as excinfo: + mock_post_fhir_doc_ref_service._create_document_reference( + nhs_number="9000000009", + author="B67890", + doc_type=doc_type, + fhir_doc=fhir_doc, + current_gp_ods=current_gp_ods, + raw_fhir_doc=json.dumps({"foo": "bar"}), + ) + + assert excinfo.value.status_code == 400 + assert excinfo.value.error == LambdaError.DocRefNoParse + assert excinfo.value.message == "Failed to parse document upload request data" From c59139e35bbaa00f635b85a86264ca7f31c29869 Mon Sep 17 00:00:00 2001 From: Megan Date: Fri, 13 Feb 2026 09:52:57 +0000 Subject: [PATCH 2/3] NDR-365 format --- .../post_fhir_document_reference_service.py | 4 +--- lambdas/tests/e2e/helpers/data_helper.py | 3 +-- ...pdm_post_fhir_document_reference_service.py | 16 ++++------------ ...est_post_fhir_document_reference_service.py | 18 ++++-------------- 4 files changed, 10 insertions(+), 31 deletions(-) diff --git a/lambdas/services/post_fhir_document_reference_service.py b/lambdas/services/post_fhir_document_reference_service.py index bec8b561de..575dc2e934 100644 --- a/lambdas/services/post_fhir_document_reference_service.py +++ b/lambdas/services/post_fhir_document_reference_service.py @@ -3,9 +3,7 @@ from enums.mtls import MtlsCommonNames from enums.snomed_codes import SnomedCode, SnomedCodes from models.document_reference import DocumentReference -from models.fhir.R4.fhir_document_reference import ( - SNOMED_URL, -) +from models.fhir.R4.fhir_document_reference import SNOMED_URL from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) diff --git a/lambdas/tests/e2e/helpers/data_helper.py b/lambdas/tests/e2e/helpers/data_helper.py index 5314e8ad73..3d6fc87d18 100644 --- a/lambdas/tests/e2e/helpers/data_helper.py +++ b/lambdas/tests/e2e/helpers/data_helper.py @@ -294,5 +294,4 @@ def check_record_exists_in_s3_with_version(self, key, version_id): except s3_client.exceptions.ClientError as e: if e.response["Error"]["Code"] == "404": return False - else: - raise + raise diff --git a/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py b/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py index 427cecf029..bbe85d0505 100644 --- a/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py +++ b/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py @@ -6,30 +6,22 @@ from enums.mtls import MtlsCommonNames from enums.snomed_codes import SnomedCode, SnomedCodes from models.fhir.R4.base_models import Identifier, Reference -from models.fhir.R4.fhir_document_reference import ( - Attachment, -) +from models.fhir.R4.fhir_document_reference import Attachment from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) -from models.fhir.R4.fhir_document_reference import ( - DocumentReferenceContent, -) +from models.fhir.R4.fhir_document_reference import DocumentReferenceContent from services.fhir_document_reference_service_base import ( FhirDocumentReferenceServiceBase, ) from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) -from tests.unit.conftest import ( - APIM_API_URL, -) +from tests.unit.conftest import APIM_API_URL from tests.unit.conftest import ( EXPECTED_PARSED_PATIENT_BASE_CASE as mock_pds_patient_details, ) -from tests.unit.conftest import ( - TEST_UUID, -) +from tests.unit.conftest import TEST_UUID from utils.lambda_exceptions import DocumentRefException from utils.request_context import request_context diff --git a/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py b/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py index 397f8467a4..9a8a8dad5b 100644 --- a/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py +++ b/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py @@ -6,32 +6,22 @@ from enums.snomed_codes import SnomedCode, SnomedCodes from models.document_reference import DocumentReference from models.fhir.R4.base_models import CodeableConcept, Identifier, Reference -from models.fhir.R4.fhir_document_reference import ( - SNOMED_URL, - Attachment, -) +from models.fhir.R4.fhir_document_reference import SNOMED_URL, Attachment from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) -from models.fhir.R4.fhir_document_reference import ( - DocumentReferenceContent, -) +from models.fhir.R4.fhir_document_reference import DocumentReferenceContent from services.fhir_document_reference_service_base import ( FhirDocumentReferenceServiceBase, ) from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) -from tests.unit.conftest import ( - APIM_API_URL, -) +from tests.unit.conftest import APIM_API_URL from tests.unit.conftest import ( EXPECTED_PARSED_PATIENT_BASE_CASE as mock_pds_patient_details, ) -from tests.unit.conftest import ( - MOCK_LG_TABLE_NAME, - TEST_UUID, -) +from tests.unit.conftest import MOCK_LG_TABLE_NAME, TEST_UUID from utils.exceptions import FhirDocumentReferenceException from utils.lambda_exceptions import DocumentRefException from utils.request_context import request_context From a48dfd266e62db2b27d227abe1cbeabdfe2d1c16 Mon Sep 17 00:00:00 2001 From: Megan Date: Fri, 13 Feb 2026 09:57:02 +0000 Subject: [PATCH 3/3] NDR-365 format --- .../post_fhir_document_reference_service.py | 4 +++- ...pdm_post_fhir_document_reference_service.py | 16 ++++++++++++---- ...est_post_fhir_document_reference_service.py | 18 ++++++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lambdas/services/post_fhir_document_reference_service.py b/lambdas/services/post_fhir_document_reference_service.py index 575dc2e934..bec8b561de 100644 --- a/lambdas/services/post_fhir_document_reference_service.py +++ b/lambdas/services/post_fhir_document_reference_service.py @@ -3,7 +3,9 @@ from enums.mtls import MtlsCommonNames from enums.snomed_codes import SnomedCode, SnomedCodes from models.document_reference import DocumentReference -from models.fhir.R4.fhir_document_reference import SNOMED_URL +from models.fhir.R4.fhir_document_reference import ( + SNOMED_URL, +) from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) diff --git a/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py b/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py index bbe85d0505..427cecf029 100644 --- a/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py +++ b/lambdas/tests/unit/services/test_pdm_post_fhir_document_reference_service.py @@ -6,22 +6,30 @@ from enums.mtls import MtlsCommonNames from enums.snomed_codes import SnomedCode, SnomedCodes from models.fhir.R4.base_models import Identifier, Reference -from models.fhir.R4.fhir_document_reference import Attachment +from models.fhir.R4.fhir_document_reference import ( + Attachment, +) from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) -from models.fhir.R4.fhir_document_reference import DocumentReferenceContent +from models.fhir.R4.fhir_document_reference import ( + DocumentReferenceContent, +) from services.fhir_document_reference_service_base import ( FhirDocumentReferenceServiceBase, ) from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) -from tests.unit.conftest import APIM_API_URL +from tests.unit.conftest import ( + APIM_API_URL, +) from tests.unit.conftest import ( EXPECTED_PARSED_PATIENT_BASE_CASE as mock_pds_patient_details, ) -from tests.unit.conftest import TEST_UUID +from tests.unit.conftest import ( + TEST_UUID, +) from utils.lambda_exceptions import DocumentRefException from utils.request_context import request_context diff --git a/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py b/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py index 9a8a8dad5b..397f8467a4 100644 --- a/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py +++ b/lambdas/tests/unit/services/test_post_fhir_document_reference_service.py @@ -6,22 +6,32 @@ from enums.snomed_codes import SnomedCode, SnomedCodes from models.document_reference import DocumentReference from models.fhir.R4.base_models import CodeableConcept, Identifier, Reference -from models.fhir.R4.fhir_document_reference import SNOMED_URL, Attachment +from models.fhir.R4.fhir_document_reference import ( + SNOMED_URL, + Attachment, +) from models.fhir.R4.fhir_document_reference import ( DocumentReference as FhirDocumentReference, ) -from models.fhir.R4.fhir_document_reference import DocumentReferenceContent +from models.fhir.R4.fhir_document_reference import ( + DocumentReferenceContent, +) from services.fhir_document_reference_service_base import ( FhirDocumentReferenceServiceBase, ) from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) -from tests.unit.conftest import APIM_API_URL +from tests.unit.conftest import ( + APIM_API_URL, +) from tests.unit.conftest import ( EXPECTED_PARSED_PATIENT_BASE_CASE as mock_pds_patient_details, ) -from tests.unit.conftest import MOCK_LG_TABLE_NAME, TEST_UUID +from tests.unit.conftest import ( + MOCK_LG_TABLE_NAME, + TEST_UUID, +) from utils.exceptions import FhirDocumentReferenceException from utils.lambda_exceptions import DocumentRefException from utils.request_context import request_context