diff --git a/moto/sqs/models.py b/moto/sqs/models.py
index 0d4f93cc1..8f27193cb 100644
--- a/moto/sqs/models.py
+++ b/moto/sqs/models.py
@@ -44,6 +44,8 @@ MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
MAXIMUM_MESSAGE_SIZE_ATTR_LOWER_BOUND = 1024
MAXIMUM_MESSAGE_SIZE_ATTR_UPPER_BOUND = MAXIMUM_MESSAGE_LENGTH
+MAXIMUM_MESSAGE_DELAY = 900
+
TRANSPORT_TYPE_ENCODINGS = {
"String": b"\x01",
"Binary": b"\x02",
@@ -799,6 +801,13 @@ class SQSBackend(BaseBackend):
if message_attributes:
message.message_attributes = message_attributes
+ if delay_seconds > MAXIMUM_MESSAGE_DELAY:
+ msg = (
+ f"Value {delay_seconds} for parameter DelaySeconds is invalid. "
+ "Reason: DelaySeconds must be >= 0 and <= 900."
+ )
+ raise InvalidParameterValue(msg)
+
message.mark_sent(delay_seconds=delay_seconds)
queue.add_message(message)
@@ -834,21 +843,24 @@ class SQSBackend(BaseBackend):
raise TooManyEntriesInBatchRequest(len(entries))
messages = []
+ failedInvalidDelay = []
for entry in entries.values():
- # Loop through looking for messages
- message = self.send_message(
- queue_name,
- entry["MessageBody"],
- message_attributes=entry["MessageAttributes"],
- delay_seconds=entry["DelaySeconds"],
- group_id=entry.get("MessageGroupId"),
- deduplication_id=entry.get("MessageDeduplicationId"),
- )
- message.user_id = entry["Id"]
+ try:
+ # Loop through looking for messages
+ message = self.send_message(
+ queue_name,
+ entry["MessageBody"],
+ message_attributes=entry["MessageAttributes"],
+ delay_seconds=entry["DelaySeconds"],
+ group_id=entry.get("MessageGroupId"),
+ deduplication_id=entry.get("MessageDeduplicationId"),
+ )
+ message.user_id = entry["Id"]
+ messages.append(message)
+ except InvalidParameterValue:
+ failedInvalidDelay.append(entry)
- messages.append(message)
-
- return messages
+ return messages, failedInvalidDelay
def _get_first_duplicate_id(self, ids):
unique_ids = set()
diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py
index c8546b2ab..192544a08 100644
--- a/moto/sqs/responses.py
+++ b/moto/sqs/responses.py
@@ -299,10 +299,23 @@ class SQSResponse(BaseResponse):
if entries == {}:
raise EmptyBatchRequest()
- messages = self.sqs_backend.send_message_batch(queue_name, entries)
+ messages, failedInvalidDelay = self.sqs_backend.send_message_batch(
+ queue_name, entries
+ )
+
+ errors = []
+ for entry in failedInvalidDelay:
+ errors.append(
+ {
+ "Id": entry["Id"],
+ "SenderFault": "true",
+ "Code": "InvalidParameterValue",
+ "Message": "Value 1800 for parameter DelaySeconds is invalid. Reason: DelaySeconds must be >= 0 and <= 900.",
+ }
+ )
template = self.response_template(SEND_MESSAGE_BATCH_RESPONSE)
- return template.render(messages=messages)
+ return template.render(messages=messages, errors=errors)
def delete_message(self):
queue_name = self._get_queue_name()
@@ -650,6 +663,7 @@ RECEIVE_MESSAGE_RESPONSE = """
"""
+# UPDATED Line 681-688
SEND_MESSAGE_BATCH_RESPONSE = """
{% for message in messages %}
@@ -662,6 +676,14 @@ SEND_MESSAGE_BATCH_RESPONSE = """
{% endif %}
{% 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 ad215e736..ae3ca1d19 100644
--- a/tests/test_sqs/test_sqs.py
+++ b/tests/test_sqs/test_sqs.py
@@ -3115,3 +3115,68 @@ def test_message_has_windows_return():
messages = queue.receive_messages()
messages.should.have.length_of(1)
messages[0].body.should.match(message)
+
+
+@mock_sqs
+def test_message_delay_is_more_than_15_minutes():
+ client = boto3.client("sqs", region_name="us-east-1")
+ response = client.create_queue(
+ QueueName=f"{str(uuid4())[0:6]}.fifo", Attributes={"FifoQueue": "true"}
+ )
+ queue_url = response["QueueUrl"]
+
+ response = client.send_message_batch(
+ QueueUrl=queue_url,
+ Entries=[
+ {
+ "Id": "id_1",
+ "MessageBody": "body_1",
+ "DelaySeconds": 3,
+ "MessageAttributes": {
+ "attribute_name_1": {
+ "StringValue": "attribute_value_1",
+ "DataType": "String",
+ }
+ },
+ "MessageGroupId": "message_group_id_1",
+ "MessageDeduplicationId": "message_deduplication_id_1",
+ },
+ {
+ "Id": "id_2",
+ "MessageBody": "body_2",
+ "DelaySeconds": 1800,
+ "MessageAttributes": {
+ "attribute_name_2": {"StringValue": "123", "DataType": "Number"}
+ },
+ "MessageGroupId": "message_group_id_2",
+ "MessageDeduplicationId": "message_deduplication_id_2",
+ },
+ ],
+ )
+
+ sorted([entry["Id"] for entry in response["Successful"]]).should.equal(["id_1"])
+
+ sorted([entry["Id"] for entry in response["Failed"]]).should.equal(["id_2"])
+
+ # print(response)
+
+ time.sleep(4)
+
+ response = client.receive_message(
+ QueueUrl=queue_url,
+ MaxNumberOfMessages=10,
+ MessageAttributeNames=["attribute_name_1", "attribute_name_2"],
+ AttributeNames=["MessageDeduplicationId", "MessageGroupId"],
+ )
+
+ response["Messages"].should.have.length_of(1)
+ response["Messages"][0]["Body"].should.equal("body_1")
+ response["Messages"][0]["MessageAttributes"].should.equal(
+ {"attribute_name_1": {"StringValue": "attribute_value_1", "DataType": "String"}}
+ )
+ response["Messages"][0]["Attributes"]["MessageGroupId"].should.equal(
+ "message_group_id_1"
+ )
+ response["Messages"][0]["Attributes"]["MessageDeduplicationId"].should.equal(
+ "message_deduplication_id_1"
+ )