From a7c3c9bba244ca276a58e58622b860a47b9cdacb Mon Sep 17 00:00:00 2001 From: NogaNHS <127490765+NogaNHS@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:03:42 +0000 Subject: [PATCH] PRMP-1192 - New delete pointer nrl (#465) * [PRMP-1129] add delete pointer --------- Co-authored-by: Steph Torres --- lambdas/enums/nrl_sqs_upload.py | 6 + lambdas/enums/snomed_codes.py | 9 + .../handlers/manage_nrl_pointer_handler.py | 31 +-- lambdas/services/base/sqs_service.py | 5 + lambdas/services/document_deletion_service.py | 26 ++- lambdas/services/nrl_api_service.py | 51 ++++- lambdas/tests/unit/conftest.py | 2 + .../test_manage_nrl_pointer_handler.py | 17 +- .../unit/services/base/test_sqs_service.py | 31 ++- .../test_document_deletion_service.py | 32 ++- .../unit/services/test_nrl_api_service.py | 191 ++++++++++++++++++ 11 files changed, 365 insertions(+), 36 deletions(-) create mode 100644 lambdas/enums/nrl_sqs_upload.py create mode 100644 lambdas/enums/snomed_codes.py diff --git a/lambdas/enums/nrl_sqs_upload.py b/lambdas/enums/nrl_sqs_upload.py new file mode 100644 index 000000000..07957f4d7 --- /dev/null +++ b/lambdas/enums/nrl_sqs_upload.py @@ -0,0 +1,6 @@ +from enum import StrEnum, auto + + +class NrlActionTypes(StrEnum): + CREATE = auto() + DELETE = auto() diff --git a/lambdas/enums/snomed_codes.py b/lambdas/enums/snomed_codes.py new file mode 100644 index 000000000..9b1f3f995 --- /dev/null +++ b/lambdas/enums/snomed_codes.py @@ -0,0 +1,9 @@ +from enum import StrEnum + + +class SnomedCodesType(StrEnum): + LLOYD_GEORGE = "16521000000101" + + +class SnomedCodesCategory(StrEnum): + CARE_PLAN = "734163000" diff --git a/lambdas/handlers/manage_nrl_pointer_handler.py b/lambdas/handlers/manage_nrl_pointer_handler.py index b5af8041a..615359bb1 100644 --- a/lambdas/handlers/manage_nrl_pointer_handler.py +++ b/lambdas/handlers/manage_nrl_pointer_handler.py @@ -1,5 +1,6 @@ import json +from enums.nrl_sqs_upload import NrlActionTypes from models.nrl_fhir_document_reference import FhirDocumentReference from models.nrl_sqs_message import NrlSqsMessage from services.base.nhs_oauth_service import NhsOauthService @@ -31,11 +32,7 @@ def lambda_handler(event, context): ssm_service = SSMService() oauth_service = NhsOauthService(ssm_service) nrl_api_service = NrlApiService(ssm_service, oauth_service) - actions_options = { - "POST": nrl_api_service.create_new_pointer, - "UPDATE": nrl_api_service.update_pointer, - "DELETE": nrl_api_service.delete_pointer, - } + unhandled_messages = [] for sqs_message in sqs_messages: @@ -50,14 +47,22 @@ def lambda_handler(event, context): nrl_verified_message = nrl_message.model_dump( by_alias=True, exclude_none=True, exclude_defaults=True ) - document = ( - FhirDocumentReference( - **nrl_verified_message, custodian=nrl_api_service.end_user_ods_code - ) - .build_fhir_dict() - .json() - ) - actions_options[nrl_message.action](json.loads(document)) + match nrl_message.action: + case NrlActionTypes.CREATE: + document = ( + FhirDocumentReference( + **nrl_verified_message, + custodian=nrl_api_service.end_user_ods_code, + ) + .build_fhir_dict() + .json() + ) + + nrl_api_service.create_new_pointer(json.loads(document)) + case NrlActionTypes.DELETE: + nrl_api_service.delete_pointer( + nrl_message.nhs_number, nrl_message.snomed_code_doc_type + ) except Exception as error: unhandled_messages.append(sqs_message) logger.info(f"Failed to process current message due to error: {error}") diff --git a/lambdas/services/base/sqs_service.py b/lambdas/services/base/sqs_service.py index e75d06082..ff9cd34ec 100644 --- a/lambdas/services/base/sqs_service.py +++ b/lambdas/services/base/sqs_service.py @@ -8,6 +8,11 @@ def __init__(self, *args, **kwargs): self.client = boto3.client("sqs", config=config) super().__init__(*args, **kwargs) + def send_message_fifo(self, queue_url: str, message_body: str, group_id: str): + self.client.send_message( + QueueUrl=queue_url, MessageBody=message_body, MessageGroupId=group_id + ) + def send_message_standard(self, queue_url: str, message_body: str): self.client.send_message(QueueUrl=queue_url, MessageBody=message_body) diff --git a/lambdas/services/document_deletion_service.py b/lambdas/services/document_deletion_service.py index 53d97ed65..09ba07e2d 100644 --- a/lambdas/services/document_deletion_service.py +++ b/lambdas/services/document_deletion_service.py @@ -1,10 +1,16 @@ +import os +import uuid from typing import Literal from botocore.exceptions import ClientError from enums.lambda_error import LambdaError +from enums.nrl_sqs_upload import NrlActionTypes from enums.s3_lifecycle_tags import S3LifecycleTags +from enums.snomed_codes import SnomedCodesCategory, SnomedCodesType from enums.supported_document_types import SupportedDocumentTypes from models.document_reference import DocumentReference +from models.nrl_sqs_message import NrlSqsMessage +from services.base.sqs_service import SQSService from services.document_service import DocumentService from services.lloyd_george_stitch_job_service import LloydGeorgeStitchJobService from utils.audit_logging_setup import LoggingService @@ -19,6 +25,7 @@ class DocumentDeletionService: def __init__(self): self.document_service = DocumentService() self.stitch_service = LloydGeorgeStitchJobService() + self.sqs_service = SQSService() def handle_delete( self, nhs_number: str, doc_types: list[SupportedDocumentTypes] @@ -27,6 +34,8 @@ def handle_delete( for doc_type in doc_types: files_deleted += self.delete_specific_doc_type(nhs_number, doc_type) self.delete_documents_references_in_stitch_table(nhs_number) + if SupportedDocumentTypes.LG in doc_types: + self.send_sqs_message_to_remove_pointer(nhs_number) return files_deleted def get_documents_references_in_storage( @@ -41,7 +50,7 @@ def get_documents_references_in_storage( def delete_documents_references_in_stitch_table(self, nhs_number: str): documents_in_stitch_table = ( - self.stitch_service.query_stitch_trace_with_nhs_number(nhs_number) + self.stitch_service.query_stitch_trace_with_nhs_number(nhs_number) or [] ) for record in documents_in_stitch_table: @@ -77,3 +86,18 @@ def delete_specific_doc_type( {"Results": "Failed to delete documents"}, ) raise DocumentDeletionServiceException(500, LambdaError.DocDelClient) + + def send_sqs_message_to_remove_pointer(self, nhs_number: str): + delete_nrl_message = NrlSqsMessage( + nhs_number=nhs_number, + action=NrlActionTypes.DELETE, + snomed_code_doc_type=SnomedCodesType.LLOYD_GEORGE, + snomed_code_category=SnomedCodesCategory.CARE_PLAN, + ) + sqs_group_id = f"NRL_delete_{uuid.uuid4()}" + nrl_queue_url = os.environ["NRL_SQS_QUEUE_URL"] + self.sqs_service.send_message_fifo( + queue_url=nrl_queue_url, + message_body=delete_nrl_message.model_dump_json(), + group_id=sqs_group_id, + ) diff --git a/lambdas/services/nrl_api_service.py b/lambdas/services/nrl_api_service.py index ef4f98887..9676645e1 100644 --- a/lambdas/services/nrl_api_service.py +++ b/lambdas/services/nrl_api_service.py @@ -18,7 +18,7 @@ def __init__(self, ssm_service, auth_service): retry_strategy = Retry( total=3, status_forcelist=[429, 500, 502, 503, 504], - allowed_methods=["GET"], + allowed_methods=["GET", "POST", "DELETE", "OPTIONS"], backoff_factor=1, ) adapter = HTTPAdapter(max_retries=retry_strategy) @@ -29,6 +29,7 @@ def __init__(self, ssm_service, auth_service): self.headers = { "Authorization": f"Bearer {self.auth_service.get_active_access_token()}", "NHSD-End-User-Organisation-ODS": self.end_user_ods_code, + "Accept": "application/json", } def get_end_user_ods_code(self): @@ -40,13 +41,11 @@ def get_end_user_ods_code(self): def create_new_pointer(self, body, retry_on_expired: bool = True): try: self.set_x_request_id() - self.headers["Accept"] = "application/json" response = self.session.post( url=self.endpoint, headers=self.headers, json=body ) response.raise_for_status() logger.info("Successfully created new pointer") - self.headers.pop("Accept") except HTTPError as e: logger.error(e.response) if e.response.status_code == 401 and retry_on_expired: @@ -57,11 +56,49 @@ def create_new_pointer(self, body, retry_on_expired: bool = True): else: raise NrlApiException("Error while creating new NRL Pointer") - def update_pointer(self): - self.set_x_request_id() + def get_pointer(self, nhs_number, record_type=None, retry_on_expired: bool = True): + try: + self.set_x_request_id() + params = { + "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{nhs_number}" + } + if record_type: + params["type"] = f"http://snomed.info/sct|{record_type}" + response = self.session.get( + url=self.endpoint, params=params, headers=self.headers + ) + response.raise_for_status() + return response.json() + except HTTPError as e: + logger.error(e.response.json()) + if e.response.status_code == 401 and retry_on_expired: + self.headers["Authorization"] = ( + f"Bearer {self.auth_service.get_active_access_token()}" + ) + self.get_pointer(nhs_number, record_type, retry_on_expired=False) + else: + raise NrlApiException("Error while getting NRL Pointer") - def delete_pointer(self): - self.set_x_request_id() + def delete_pointer(self, nhs_number, record_type): + search_results = self.get_pointer(nhs_number, record_type).get("entry", []) + for entry in search_results: + self.set_x_request_id() + pointer_id = entry.get("resource", {}).get("id") + url_endpoint = self.endpoint + f"/{pointer_id}" + try: + response = self.session.delete(url=url_endpoint, headers=self.headers) + logger.info(response.json()) + response.raise_for_status() + except HTTPError as e: + logger.error(e.response.json()) + if e.response.status_code == 401: + self.headers["Authorization"] = ( + f"Bearer {self.auth_service.get_active_access_token()}" + ) + self.session.delete(url=self.endpoint, headers=self.headers) + else: + logger.error(f"Unable to delete NRL Pointer: {entry}") + continue def set_x_request_id(self): self.headers["X-Request-ID"] = str(uuid.uuid4()) diff --git a/lambdas/tests/unit/conftest.py b/lambdas/tests/unit/conftest.py index c9f3c34be..43951c6c7 100644 --- a/lambdas/tests/unit/conftest.py +++ b/lambdas/tests/unit/conftest.py @@ -106,6 +106,7 @@ MOCK_PRESIGNED_URL_ROLE_ARN_VALUE = "arn:aws:iam::test123" STITCH_METADATA_DYNAMODB_NAME_VALUE = "test_stitch_metadata" +NRL_SQS_URL = "https://sqs.us-east-1.amazonaws.com/177715257436/MyQueue" @pytest.fixture @@ -167,6 +168,7 @@ def set_env(monkeypatch): ) monkeypatch.setenv("NRL_API_ENDPOINT", FAKE_URL) monkeypatch.setenv("NRL_END_USER_ODS_CODE", "test_nrl_user_ods_ssm_key") + monkeypatch.setenv("NRL_SQS_QUEUE_URL", NRL_SQS_URL) EXPECTED_PARSED_PATIENT_BASE_CASE = PatientDetails( diff --git a/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py b/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py index 9a7932f6f..c115a2202 100644 --- a/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py +++ b/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py @@ -13,7 +13,7 @@ def mock_service(mocker): return mocked_instance -def build_test_sqs_message(action="POST"): +def build_test_sqs_message(action="create"): SQS_Message = { "nhs_number": "123456789", "snomed_code_doc_type": "16521000000101", @@ -31,22 +31,15 @@ def build_test_sqs_message(action="POST"): def test_process_event_with_one_message(mock_service, context, set_env): - event = {"Records": [build_test_sqs_message("POST")]} + event = {"Records": [build_test_sqs_message("create")]} lambda_handler(event, context) mock_service.create_new_pointer.assert_called_once() -def test_process_update_event_with_one_message(mock_service, context, set_env): - event = {"Records": [build_test_sqs_message("UPDATE")]} - lambda_handler(event, context) - - mock_service.update_pointer.assert_called_once() - - def test_process_delete_event_with_one_message(mock_service, context, set_env): - event = {"Records": [build_test_sqs_message("DELETE")]} + event = {"Records": [build_test_sqs_message("delete")]} lambda_handler(event, context) @@ -55,7 +48,7 @@ def test_process_delete_event_with_one_message(mock_service, context, set_env): def test_process_event_with_multiple_messages(mock_service, context, set_env): event = { - "Records": [build_test_sqs_message("POST"), build_test_sqs_message("DELETE")] + "Records": [build_test_sqs_message("create"), build_test_sqs_message("delete")] } lambda_handler(event, context) @@ -65,7 +58,7 @@ def test_process_event_with_multiple_messages(mock_service, context, set_env): def test_failed_to_create_a_pointer(mock_service, context, set_env, caplog): - event = {"Records": [build_test_sqs_message("POST")]} + event = {"Records": [build_test_sqs_message("create")]} mock_service.create_new_pointer.side_effect = NrlApiException("test exception") lambda_handler(event, context) diff --git a/lambdas/tests/unit/services/base/test_sqs_service.py b/lambdas/tests/unit/services/base/test_sqs_service.py index 0fe33c744..02034efe0 100644 --- a/lambdas/tests/unit/services/base/test_sqs_service.py +++ b/lambdas/tests/unit/services/base/test_sqs_service.py @@ -1,10 +1,12 @@ import json +import pytest from services.base.sqs_service import SQSService from tests.unit.conftest import MOCK_LG_METADATA_SQS_QUEUE, TEST_NHS_NUMBER -def test_send_message_with_nhs_number_attr(set_env, mocker): +@pytest.fixture() +def mocked_sqs_client(mocker): mocked_sqs_client = mocker.MagicMock() def return_mock(service_name, **_kwargs): @@ -13,7 +15,16 @@ def return_mock(service_name, **_kwargs): mocker.patch("boto3.client", side_effect=return_mock) + return mocked_sqs_client + + +@pytest.fixture() +def service(mocker, mocked_sqs_client): service = SQSService() + return service + + +def test_send_message_with_nhs_number_attr(set_env, mocked_sqs_client, service): test_message_body = json.dumps( {"NHS-NO": "1234567890", "files": ["file1.pdf", "file2.pdf"]} @@ -34,3 +45,21 @@ def return_mock(service_name, **_kwargs): MessageBody=test_message_body, MessageGroupId="test_group_id", ) + + +def test_send_message_fifo(set_env, mocked_sqs_client, service): + test_message_body = json.dumps( + {"NHS-NO": "1234567890", "files": ["file1.pdf", "file2.pdf"]} + ) + + service.send_message_fifo( + group_id="test_group_id", + queue_url=MOCK_LG_METADATA_SQS_QUEUE, + message_body=test_message_body, + ) + + mocked_sqs_client.send_message.assert_called_with( + QueueUrl=MOCK_LG_METADATA_SQS_QUEUE, + MessageBody=test_message_body, + MessageGroupId="test_group_id", + ) diff --git a/lambdas/tests/unit/services/test_document_deletion_service.py b/lambdas/tests/unit/services/test_document_deletion_service.py index c70f5efae..e68c7e1a4 100644 --- a/lambdas/tests/unit/services/test_document_deletion_service.py +++ b/lambdas/tests/unit/services/test_document_deletion_service.py @@ -4,7 +4,12 @@ from enums.s3_lifecycle_tags import S3LifecycleTags from enums.supported_document_types import SupportedDocumentTypes from services.document_deletion_service import DocumentDeletionService -from tests.unit.conftest import MOCK_ARF_TABLE_NAME, MOCK_LG_TABLE_NAME, TEST_NHS_NUMBER +from tests.unit.conftest import ( + MOCK_ARF_TABLE_NAME, + MOCK_LG_TABLE_NAME, + NRL_SQS_URL, + TEST_NHS_NUMBER, +) from tests.unit.helpers.data.test_documents import ( create_test_doc_store_refs, create_test_lloyd_george_doc_store_refs, @@ -35,7 +40,7 @@ def mocked_document_query( def mock_deletion_service(set_env, mocker): mocker.patch("services.document_deletion_service.DocumentService") mocker.patch("services.document_deletion_service.LloydGeorgeStitchJobService") - + mocker.patch("services.document_deletion_service.SQSService") yield DocumentDeletionService() @@ -215,3 +220,26 @@ def test_delete_documents_references_in_stitch_table(mocker, mock_deletion_servi mock_deletion_service.document_service.dynamo_service.update_item.assert_has_calls( expected_calls ) + + +def test_send_sqs_message_to_remove_pointer(mocker, mock_deletion_service): + mocker.patch("uuid.uuid4", return_value="test_uuid") + + expected_message_body = ( + '{{"nhs_number":"{}",' + '"snomed_code_doc_type":"16521000000101",' + '"snomed_code_category":"734163000",' + '"description":"",' + '"attachment":null,' + '"action":"delete"}}' + ).format(TEST_NHS_NUMBER) + + mock_deletion_service.send_sqs_message_to_remove_pointer(TEST_NHS_NUMBER) + + assert mock_deletion_service.sqs_service.send_message_fifo.call_count == 1 + + mock_deletion_service.sqs_service.send_message_fifo.assert_called_with( + group_id="NRL_delete_test_uuid", + message_body=expected_message_body, + queue_url=NRL_SQS_URL, + ) diff --git a/lambdas/tests/unit/services/test_nrl_api_service.py b/lambdas/tests/unit/services/test_nrl_api_service.py index 6d5bb75f8..997a3d839 100644 --- a/lambdas/tests/unit/services/test_nrl_api_service.py +++ b/lambdas/tests/unit/services/test_nrl_api_service.py @@ -1,6 +1,7 @@ import pytest from requests import Response from services.nrl_api_service import NrlApiService +from tests.unit.conftest import FAKE_URL, TEST_NHS_NUMBER from tests.unit.helpers.mock_services import FakeSSMService, FakOAuthService from utils.exceptions import NrlApiException @@ -39,3 +40,193 @@ def test_create_new_pointer_raise_error(nrl_service): def test_get_end_user_ods_code(nrl_service): actual = nrl_service.get_end_user_ods_code() assert actual == "test_value_test_nrl_user_ods_ssm_key" + + +def test_get_pointer_with_record_type(mocker, nrl_service): + mock_type = 11111111 + mocker.patch("uuid.uuid4", return_value="test_uuid") + + mock_params = { + "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}", + "type": f"http://snomed.info/sct|{mock_type}", + } + mock_headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "NHSD-End-User-Organisation-ODS": "test_value_test_nrl_user_ods_ssm_key", + "Accept": "application/json", + "X-Request-ID": "test_uuid", + } + nrl_service.get_pointer(TEST_NHS_NUMBER, mock_type) + + nrl_service.session.get.assert_called_with( + params=mock_params, url=FAKE_URL, headers=mock_headers + ) + + +def test_get_pointer_with_record_type_no_retry(mocker, nrl_service): + mock_type = 11111111 + mocker.patch("uuid.uuid4", return_value="test_uuid") + mock_params = { + "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}", + "type": f"http://snomed.info/sct|{mock_type}", + } + mock_headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "NHSD-End-User-Organisation-ODS": "test_value_test_nrl_user_ods_ssm_key", + "Accept": "application/json", + "X-Request-ID": "test_uuid", + } + response = Response() + response.status_code = 401 + response._content = b"{}" + nrl_service.session.get.return_value = response + with pytest.raises(NrlApiException): + nrl_service.get_pointer(TEST_NHS_NUMBER, mock_type, retry_on_expired=False) + + nrl_service.session.get.assert_called_with( + params=mock_params, url=FAKE_URL, headers=mock_headers + ) + nrl_service.session.get.assert_called_once() + + +def test_get_pointer_with_record_type_with_retry(mocker, nrl_service): + mock_type = 11111111 + mocker.patch("uuid.uuid4", return_value="test_uuid") + mock_params = { + "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}", + "type": f"http://snomed.info/sct|{mock_type}", + } + mock_headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "NHSD-End-User-Organisation-ODS": "test_value_test_nrl_user_ods_ssm_key", + "Accept": "application/json", + "X-Request-ID": "test_uuid", + } + response = Response() + response.status_code = 401 + response._content = b"{}" + nrl_service.session.get.return_value = response + with pytest.raises(NrlApiException): + nrl_service.get_pointer(TEST_NHS_NUMBER, mock_type, retry_on_expired=True) + + nrl_service.session.get.assert_called_with( + params=mock_params, url=FAKE_URL, headers=mock_headers + ) + assert nrl_service.session.get.call_count == 2 + + +def test_get_pointer_raise_error(nrl_service): + response = Response() + response.status_code = 400 + response._content = b"{}" + + mock_type = 11111111 + + nrl_service.session.get.return_value = response + pytest.raises(NrlApiException, nrl_service.get_pointer, TEST_NHS_NUMBER, mock_type) + + nrl_service.session.get.assert_called_once() + + +def test_delete_pointer_with_record_type_no_record(mocker, nrl_service): + mock_type = 11111111 + mocker.patch("uuid.uuid4", return_value="test_uuid") + + nrl_response = { + "resourceType": "Bundle", + "type": "searchset", + "total": 0, + "entry": [], + } + nrl_service.get_pointer = mocker.MagicMock(return_value=nrl_response) + nrl_service.delete_pointer(TEST_NHS_NUMBER, mock_type) + + nrl_service.session.delete.assert_not_called() + + +def test_delete_pointer_with_record_type_one_record(mocker, nrl_service): + mock_type = 11111111 + mocker.patch("uuid.uuid4", return_value="test_uuid") + mock_pointer_id = "ODSCODE-1111bfb1-1111-2222-3333-4444555c666f" + mock_headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "NHSD-End-User-Organisation-ODS": "test_value_test_nrl_user_ods_ssm_key", + "Accept": "application/json", + "X-Request-ID": "test_uuid", + } + nrl_response = { + "resourceType": "Bundle", + "type": "searchset", + "total": 1, + "entry": [ + { + "resource": { + "resourceType": "DocumentReference", + "id": mock_pointer_id, + } + } + ], + } + nrl_service.get_pointer = mocker.MagicMock(return_value=nrl_response) + nrl_service.delete_pointer(TEST_NHS_NUMBER, mock_type) + + nrl_service.session.delete.assert_called_with( + url=FAKE_URL + "/" + mock_pointer_id, headers=mock_headers + ) + + +def test_delete_pointer_with_record_type_more_than_one_record(mocker, nrl_service): + mock_type = 11111111 + mocker.patch("uuid.uuid4", return_value="test_uuid") + mock_pointer_id = "ODSCODE-1111bfb1-1111-2222-3333-4444555c666" + + nrl_response = { + "resourceType": "Bundle", + "type": "searchset", + "total": 1, + "entry": [ + { + "resource": { + "resourceType": "DocumentReference", + "id": mock_pointer_id + "1", + } + }, + { + "resource": { + "resourceType": "DocumentReference", + "id": mock_pointer_id + "2", + } + }, + ], + } + nrl_service.get_pointer = mocker.MagicMock(return_value=nrl_response) + nrl_service.delete_pointer(TEST_NHS_NUMBER, mock_type) + + assert nrl_service.session.delete.call_count == 2 + + +def test_delete_pointer_not_raise_error(mocker, nrl_service): + response = Response() + response.status_code = 400 + response._content = b"{}" + mock_type = 11111111 + nrl_response = { + "resourceType": "Bundle", + "type": "searchset", + "total": 1, + "entry": [ + { + "resource": { + "resourceType": "DocumentReference", + "id": "222", + } + } + ], + } + nrl_service.get_pointer = mocker.MagicMock(return_value=nrl_response) + nrl_service.session.delete.return_value = response + try: + nrl_service.delete_pointer(TEST_NHS_NUMBER, mock_type) + except Exception: + assert False + nrl_service.session.delete.assert_called_once()