diff --git a/packages/augmentation-lambda/tests/test_augmentation_lambda_function.py b/packages/augmentation-lambda/tests/test_augmentation_lambda_function.py index aa9bc052..137df4e8 100644 --- a/packages/augmentation-lambda/tests/test_augmentation_lambda_function.py +++ b/packages/augmentation-lambda/tests/test_augmentation_lambda_function.py @@ -97,6 +97,44 @@ def test_handler_returns_success_result(mocker, mock_s3_client) -> None: } +def test_handler_creates_and_caches_s3_client_when_cache_is_empty(mocker) -> None: + """Tests that the handler creates the S3 client when the cache is empty and reuses it. + + :param mocker: The pytest-mock fixture for mocking objects. + """ + lambda_function._cached_s3_client = None + + created_s3_client = MagicMock() + create_s3_client_spy = mocker.patch.object( + lambda_function.lambda_handler, + "create_s3_client", + return_value=created_s3_client, + ) + mocker.patch.object(lambda_function, "EICRAugmenter", FakeAugmenter) + + event = { + "Records": [ + { + "messageId": "message-cache", + "body": json.dumps( + { + "eicr_id": "cached-eicr-id", + "eicr": "", + "nonstandard_codes": [], + } + ), + } + ] + } + + result = lambda_function.handler(event, None) + + assert create_s3_client_spy.call_count == 1 + assert lambda_function._cached_s3_client is created_s3_client + assert result["batchItemFailures"] == [] + assert result["results"][0]["status"] == "success" + + def test_handler_saves_outputs_to_s3(mocker, mock_s3_client) -> None: """Tests that the handler writes augmented eICR and metadata to S3. diff --git a/packages/augmentation/tests/unit/test_eicr_augmenter.py b/packages/augmentation/tests/unit/test_eicr_augmenter.py index 12b9061d..174f767d 100644 --- a/packages/augmentation/tests/unit/test_eicr_augmenter.py +++ b/packages/augmentation/tests/unit/test_eicr_augmenter.py @@ -12,6 +12,7 @@ from augmentation.models.config import ApplicationCode from augmentation.models.config import AugmenterConfig from augmentation.models.config import TTCAugmenterConfig +from augmentation.services.augmenter import Augmenter from augmentation.services.eicr_augmenter import EICRAugmenter from shared_models import Code from shared_models import DataField @@ -140,6 +141,22 @@ def test_eicr_related_doc(self, mocker: MockerFixture, snapshot: Snapshot): ], ) + def test_get_old_document_id_preserves_assigning_authority_name_when_present(self): + """Tests old document id preserves assigningAuthorityName when present.""" + eicr_with_assigning_authority_name = BASIC_ECR.replace( + ' assigningAuthorityName="original-document"', + ' assigningAuthorityName="original-document"', + ).replace( + ' assigningAuthorityName="TEXT_TO_CODE"', + "", + ) + + augmenter = EICRAugmenter(eicr_with_assigning_authority_name, []) + + parent_doc_id = augmenter._get_old_document_id() + + assert parent_doc_id.get("assigningAuthorityName") == "original-document" + def test_empty_eicr(self, mocker: MockerFixture): """Tests augmentor run method.""" doc_id = UUID("12345678-1234-5678-1234-567812345678") @@ -152,3 +169,56 @@ def test_empty_eicr(self, mocker: MockerFixture): match=r"Unable to find tag in eICR document for XPath: /ClinicalDocument/id/@root", ): EICRAugmenter(EMPTY_ECR, []) + + def test_get_old_document_id_sets_assigning_authority_name_when_missing(self): + """Tests old document id gets assigningAuthorityName when missing.""" + augmenter = EICRAugmenter(BASIC_ECR, []) + + parent_doc_id = augmenter._get_old_document_id() + + assert parent_doc_id.get("assigningAuthorityName") == "original-document" + + def test_get_old_xrfm_related_document_returns_none_when_missing(self): + """Tests old XFRM relatedDocument returns None when missing.""" + augmenter = EICRAugmenter(BASIC_ECR, []) + + related_document = augmenter._get_old_xrfm_related_document() + + assert related_document is None + + def test_get_old_xrfm_related_document_returns_element_when_present(self): + """Tests old XFRM relatedDocument is returned when present.""" + augmenter = EICRAugmenter(BASIC_ECR_RELATED_DOC, []) + + related_document = augmenter._get_old_xrfm_related_document() + + assert related_document is not None + assert related_document.tag == "relatedDocument" + assert related_document.get("typeCode") == "XFRM" + + def test_validate_config_raises_value_error_when_application_code_does_not_match(self): + """Tests config validation when application code does not match.""" + + class TestAugmenter(Augmenter): + def augment(self) -> Metadata: + return Metadata( + original_eicr_id="original-doc-id", + augmented_eicr_id="augmented-doc-id", + nonstandard_codes=[], + ) + + class InvalidConfig: + application_code = "wrong-application-code" + + with pytest.raises( + ValueError, + match=r"Config application code wrong-application-code does not match Augmenter application code ApplicationCode.TEXT_TO_CODE.", + ): + TestAugmenter( + BASIC_ECR, + InvalidConfig(), + ) + + def test_augment_base_method_returns_none(self): + """Tests abstract base augment method body.""" + assert Augmenter.augment(object()) is None diff --git a/packages/utils/tests/test_normalize.py b/packages/utils/tests/test_normalize.py index be91cc27..1b6ed4bd 100644 --- a/packages/utils/tests/test_normalize.py +++ b/packages/utils/tests/test_normalize.py @@ -139,3 +139,28 @@ def test_merge_enhancements(self, dict1, dict2, expected): """Test merge enhancements.""" merged = utils.merge_enhancements(dict1, dict2) assert merged == expected + + def test_merge_enhancements_uses_later_code_when_first_seen_code_is_none( + self, dict1, dict2, expected + ): + """Test merge enhancements.""" + merged = utils.merge_enhancements( + { + "EngRatFr": {"code": None, "abbrv": [], "synonyms": ["hello"]}, + }, + { + "EngRatFr": { + "code": "LP101814-4", + "abbrv": [], + "synonyms": ["goodbye"], + }, + }, + ) + + assert merged == { + "engratfr": { + "code": "LP101814-4", + "abbrv": [], + "synonyms": ["hello", "goodbye"], + } + } \ No newline at end of file diff --git a/packages/utils/tests/test_path.py b/packages/utils/tests/test_path.py index ec50984f..1af1e06b 100644 --- a/packages/utils/tests/test_path.py +++ b/packages/utils/tests/test_path.py @@ -56,6 +56,15 @@ def test_read_json_absolute(): os.unlink(path) +def test_load_loinc_enhancements_raises_when_project_root_missing(): + """Test load LOINC enhancements when project root is missing from cwd.""" + with pytest.raises( + ValueError, + match="Could not find 'dibbs-text-to-code' in current working directory path.", + ): + utils.load_loinc_enhancements("/tmp/not-the-project-root/tests") + + def test_load_loinc_enhancements(): """Test load LOINC enhancements CWD.""" print("test_load_loinc_enhancements cwd", os.getcwd())