diff --git a/lambdas/delta_backend/src/delta.py b/lambdas/delta_backend/src/delta.py index 9623a95a1..8379e59d0 100644 --- a/lambdas/delta_backend/src/delta.py +++ b/lambdas/delta_backend/src/delta.py @@ -232,7 +232,6 @@ def handler(event, _context): "diagnostics": "Delta Lambda failure: Incorrect invocation of Lambda", } logger.exception(operation_outcome["diagnostics"]) - send_message(event) # Send failed records to DLQ log_data = {"function_name": "delta_sync", "operation_outcome": operation_outcome} send_log_to_firehose(STREAM_NAME, log_data) diff --git a/lambdas/delta_backend/tests/test_delta.py b/lambdas/delta_backend/tests/test_delta.py index 94bef09ae..a09c6e8b6 100644 --- a/lambdas/delta_backend/tests/test_delta.py +++ b/lambdas/delta_backend/tests/test_delta.py @@ -2,7 +2,7 @@ import json import os import unittest -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, call, patch from botocore.exceptions import ClientError @@ -114,6 +114,25 @@ def test_handler_success_insert(self): self.assertEqual(put_item_data["SupplierSystem"], supplier) self.mock_sqs_client.send_message.assert_not_called() + def test_handler_exception(self): + """Ensure that sqs_client exceptions do not cause the lambda handler itself to raise an exception""" + + # Arrange + self.mock_sqs_client.send_message.side_effect = Exception("SQS error") + event = {"invalid_format": True} + + # Act + result = handler(event, None) + + # Assert + self.assertFalse(result) + self.mock_logger_exception.assert_has_calls( + [ + call("Delta Lambda failure: Incorrect invocation of Lambda"), + call("Error sending record to DLQ"), + ] + ) + def test_handler_overall_failure(self): # Arrange event = {"invalid_format": True} diff --git a/lambdas/recordforwarder/tests/test_forwarding_batch_lambda.py b/lambdas/recordforwarder/tests/test_forwarding_batch_lambda.py index ccb22e373..dde50a6b2 100644 --- a/lambdas/recordforwarder/tests/test_forwarding_batch_lambda.py +++ b/lambdas/recordforwarder/tests/test_forwarding_batch_lambda.py @@ -863,20 +863,48 @@ def test_forward_request_to_dynamo( expected_values = test_case[0]["expected_values"] assert expected_values.items() <= call_data.items() - def clear_test_tables(self): - """Clear DynamoDB table after each test.""" - scan = self.table.scan() - items = scan.get("Items", []) - while items: - for item in items: - self.table.delete_item(Key={"PK": item["PK"]}) - scan = self.table.scan() - items = scan.get("Items", []) - - def teardown(self): - """Deletes mock dynamodb resource""" - self.table.delete() - self.dynamodb_resource = None + @patch("forwarding_batch_lambda.sqs_client.send_message") + def test_forward_lambda_handler_exception_handler(self, mock_send_message): + """Test exception handling when sqs_client fails""" + # Arrange + # Ensure there is at least one failure, so that sqs_client is called + test_cases = [ + { + "name": "Row 1: Duplication Error: Create failure ", + "input": self.generate_input(row_id=1, operation_requested="CREATE", include_fhir_json=True), + "expected_keys": ForwarderValues.EXPECTED_KEYS_DIAGNOSTICS, + "expected_values": { + "row_id": "row-1", + "diagnostics": create_diagnostics_dictionary( + IdentifierDuplicationError("https://www.ravs.england.nhs.uk/#RSV_002") + ), + }, + "is_failure": True, + }, + ] + + self.table.put_item( + Item={ + "PK": "Immunization#4d2ac1eb-080f-4e54-9598-f2d53334681c", + "PatientPK": "Patient#9732928395", + "PatientSK": "RSV#4d2ac1eb-080f-4e54-9598-f2d53334681c", + "IdentifierPK": "https://www.ravs.england.nhs.uk/#RSV_002", + "Version": 1, + } + ) + + mock_send_message.side_effect = Exception("Unknown Exception in SQS client") + + event = self.generate_event(test_cases) + + self.mock_redis.hget.return_value = "RSV" + self.mock_redis_getter.return_value = self.mock_redis + + # Act & Assert + with self.assertRaises(Exception) as context: + forward_lambda_handler(event, {}) + + self.assertEqual("Unknown Exception in SQS client", str(context.exception)) if __name__ == "__main__":