diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py index 7db74fe8d496..beeadc754461 100644 --- a/moto/sqs/responses.py +++ b/moto/sqs/responses.py @@ -351,14 +351,26 @@ def delete_message_batch(self): raise BatchEntryIdsNotDistinct(receipt_and_id["msg_user_id"]) receipt_seen.add(receipt) + success = [] + errors = [] for receipt_and_id in receipts: - self.sqs_backend.delete_message( - queue_name, receipt_and_id["receipt_handle"] - ) + try: + self.sqs_backend.delete_message( + queue_name, receipt_and_id["receipt_handle"] + ) + success.append(receipt_and_id["msg_user_id"]) + except ReceiptHandleIsInvalid: + errors.append( + { + "Id": receipt_and_id["msg_user_id"], + "SenderFault": "true", + "Code": "ReceiptHandleIsInvalid", + "Message": f'The input receipt handle "{receipt_and_id["receipt_handle"]}" is not a valid receipt handle.', + } + ) - message_ids = [r["msg_user_id"] for r in receipts] template = self.response_template(DELETE_MESSAGE_BATCH_RESPONSE) - return template.render(message_ids=message_ids) + return template.render(success=success, errors=errors) def purge_queue(self): queue_name = self._get_queue_name() @@ -669,11 +681,19 @@ def list_queue_tags(self): DELETE_MESSAGE_BATCH_RESPONSE = """ - {% for message_id in message_ids %} + {% for message_id in success %} {{ message_id }} {% endfor %} + {% for error_dict in errors %} + + {{ error_dict['Id'] }} + {{ error_dict['Code'] }} + {{ error_dict['Message'] }} + {{ error_dict['SenderFault'] }} + + {% endfor %} diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py index 17d84e5bf051..7cb8d0f60adb 100644 --- a/tests/test_sqs/test_sqs.py +++ b/tests/test_sqs/test_sqs.py @@ -2263,6 +2263,46 @@ def test_delete_message_batch_with_duplicates(): assert messages, "message still in the queue" +@mock_sqs +def test_delete_message_batch_with_invalid_receipt_id(): + client = boto3.client("sqs", region_name="us-east-1") + response = client.create_queue(QueueName=str(uuid4())[0:6]) + queue_url = response["QueueUrl"] + client.send_message(QueueUrl=queue_url, MessageBody="coucou") + + messages = client.receive_message( + QueueUrl=queue_url, WaitTimeSeconds=0, VisibilityTimeout=0 + )["Messages"] + assert messages, "at least one msg" + + # Try to delete the message from SQS but also include two invalid delete requests + entries = [ + {"Id": "fake-receipt-handle-1", "ReceiptHandle": "fake-receipt-handle-1"}, + {"Id": messages[0]["MessageId"], "ReceiptHandle": messages[0]["ReceiptHandle"]}, + {"Id": "fake-receipt-handle-2", "ReceiptHandle": "fake-receipt-handle-2"}, + ] + response = client.delete_message_batch(QueueUrl=queue_url, Entries=entries) + + assert response["Successful"] == [ + {"Id": messages[0]["MessageId"]} + ], "delete ok for real message" + + assert response["Failed"] == [ + { + "Id": "fake-receipt-handle-1", + "SenderFault": True, + "Code": "ReceiptHandleIsInvalid", + "Message": 'The input receipt handle "fake-receipt-handle-1" is not a valid receipt handle.', + }, + { + "Id": "fake-receipt-handle-2", + "SenderFault": True, + "Code": "ReceiptHandleIsInvalid", + "Message": 'The input receipt handle "fake-receipt-handle-2" is not a valid receipt handle.', + }, + ] + + @mock_sqs def test_message_attributes_in_receive_message(): sqs = boto3.resource("sqs", region_name="us-east-1")