From 48619d18591dd658480b4b4667f3d621b4969d67 Mon Sep 17 00:00:00 2001 From: Thomas Rausch Date: Fri, 26 Sep 2025 00:17:59 +0200 Subject: [PATCH 1/2] add sqs tests for ApproximateNumberOfMessagesNotVisible queue attribute --- .../localstack/services/sqs/models.py | 2 +- tests/aws/services/sqs/test_sqs.py | 128 ++++++++++++++++++ tests/aws/services/sqs/test_sqs.snapshot.json | 16 +++ .../aws/services/sqs/test_sqs.validation.json | 36 +++++ 4 files changed, 181 insertions(+), 1 deletion(-) diff --git a/localstack-core/localstack/services/sqs/models.py b/localstack-core/localstack/services/sqs/models.py index a9357b75d1249..702091290964e 100644 --- a/localstack-core/localstack/services/sqs/models.py +++ b/localstack-core/localstack/services/sqs/models.py @@ -153,7 +153,7 @@ def is_visible(self) -> bool: """ Returns false if the message has a visibility deadline that is in the future. - :return: whether the message is visibile or not. + :return: whether the message is visible or not. """ if self.visibility_deadline is None: return True diff --git a/tests/aws/services/sqs/test_sqs.py b/tests/aws/services/sqs/test_sqs.py index bd8645111c43c..e88db4adecd5a 100644 --- a/tests/aws/services/sqs/test_sqs.py +++ b/tests/aws/services/sqs/test_sqs.py @@ -1079,6 +1079,134 @@ def _assert(): retry(_assert) + @markers.aws.validated + def test_approximate_number_of_messages_not_visible(self, sqs_create_queue, aws_sqs_client): + # note that this test takes a bit longer when running on AWS, because we need to wait for propagation of + # queue attributes + queue_url = sqs_create_queue( + Attributes={ + "VisibilityTimeout": "60" if is_aws_cloud() else "5", + }, + ) + + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-1") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-2") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-3") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-4") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-5") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-6") + + # receive 1 message (from message group 1), now 5 messages should be visible + aws_sqs_client.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=1) + + if is_aws_cloud(): + # wait for aws to propagate the attributes + time.sleep(45) + + result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert result["Attributes"] == { + "ApproximateNumberOfMessages": "5", + "ApproximateNumberOfMessagesNotVisible": "1", + "ApproximateNumberOfMessagesDelayed": "0", + } + + def _assert_after(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "6", + "ApproximateNumberOfMessagesNotVisible": "0", + "ApproximateNumberOfMessagesDelayed": "0", + } + + retry(_assert_after, retries=15, sleep=1, sleep_before=15 if is_aws_cloud() else 0) + + @markers.aws.validated + def test_fifo_approximate_number_of_messages_not_visible( + self, sqs_create_queue, aws_sqs_client + ): + # note that this test takes a bit longer when running on AWS, because we need to wait for propagation of + # queue attributes + queue_url = sqs_create_queue( + QueueName=f"queue-{short_uid()}.fifo", + Attributes={ + "FifoQueue": "True", + "ContentBasedDeduplication": "True", + "VisibilityTimeout": "60" if is_aws_cloud() else "5", + }, + ) + + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-1", MessageGroupId="1") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-2", MessageGroupId="1") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-3", MessageGroupId="2") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-4", MessageGroupId="2") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-5", MessageGroupId="3") + aws_sqs_client.send_message(QueueUrl=queue_url, MessageBody="message-6", MessageGroupId="3") + + # receive 2 messages (from message group 1 and group 2), now two groups are invisible (4 messages), but only 2 + # messages should be marked as truly invisible (the ones received) + message_1 = aws_sqs_client.receive_message( + QueueUrl=queue_url, + MaxNumberOfMessages=1, + MessageSystemAttributeNames=["MessageGroupId"], + ) + message_2 = aws_sqs_client.receive_message( + QueueUrl=queue_url, + MaxNumberOfMessages=1, + MessageSystemAttributeNames=["MessageGroupId"], + ) + + assert message_1["Messages"][0]["Attributes"]["MessageGroupId"] == "1" + assert message_2["Messages"][0]["Attributes"]["MessageGroupId"] == "2" + + if is_aws_cloud(): + # wait for aws to propagate the attributes + time.sleep(45) + + result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert result["Attributes"] == { + "ApproximateNumberOfMessages": "4", + "ApproximateNumberOfMessagesNotVisible": "2", + "ApproximateNumberOfMessagesDelayed": "0", + } + + def _assert_after(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "6", + "ApproximateNumberOfMessagesNotVisible": "0", + "ApproximateNumberOfMessagesDelayed": "0", + } + + retry(_assert_after, retries=15, sleep=1, sleep_before=15 if is_aws_cloud() else 0) + @markers.aws.validated def test_receive_after_visibility_timeout(self, sqs_create_queue, aws_sqs_client): queue_url = sqs_create_queue(Attributes={"VisibilityTimeout": "1"}) diff --git a/tests/aws/services/sqs/test_sqs.snapshot.json b/tests/aws/services/sqs/test_sqs.snapshot.json index ef12d37f6f1f0..59b4b256f7276 100644 --- a/tests/aws/services/sqs/test_sqs.snapshot.json +++ b/tests/aws/services/sqs/test_sqs.snapshot.json @@ -4203,5 +4203,21 @@ } } } + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs]": { + "recorded-date": "25-09-2025, 21:46:19", + "recorded-content": {} + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs_query]": { + "recorded-date": "25-09-2025, 21:47:19", + "recorded-content": {} + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs]": { + "recorded-date": "25-09-2025, 21:53:42", + "recorded-content": {} + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs_query]": { + "recorded-date": "25-09-2025, 21:54:43", + "recorded-content": {} } } diff --git a/tests/aws/services/sqs/test_sqs.validation.json b/tests/aws/services/sqs/test_sqs.validation.json index 7d166f13b916c..1c6f0be48d292 100644 --- a/tests/aws/services/sqs/test_sqs.validation.json +++ b/tests/aws/services/sqs/test_sqs.validation.json @@ -1,4 +1,22 @@ { + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs]": { + "last_validated_date": "2025-09-25T21:46:19+00:00", + "durations_in_seconds": { + "setup": 0.17, + "call": 60.64, + "teardown": 0.07, + "total": 60.88 + } + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs_query]": { + "last_validated_date": "2025-09-25T21:47:19+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 60.47, + "teardown": 0.09, + "total": 60.56 + } + }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_aws_trace_header_propagation[sqs]": { "last_validated_date": "2025-06-27T10:55:55+00:00", "durations_in_seconds": { @@ -146,6 +164,24 @@ "total": 0.86 } }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs]": { + "last_validated_date": "2025-09-25T21:53:42+00:00", + "durations_in_seconds": { + "setup": 0.26, + "call": 60.54, + "teardown": 0.08, + "total": 60.88 + } + }, + "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs_query]": { + "last_validated_date": "2025-09-25T21:54:43+00:00", + "durations_in_seconds": { + "setup": 0.0, + "call": 60.57, + "teardown": 0.09, + "total": 60.66 + } + }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_change_to_high_throughput_after_creation[sqs]": { "last_validated_date": "2025-08-19T13:29:53+00:00", "durations_in_seconds": { From 0cee957c8957e06da14960ccd41fb2ca23eeab4c Mon Sep 17 00:00:00 2001 From: Thomas Rausch Date: Fri, 26 Sep 2025 18:53:20 +0200 Subject: [PATCH 2/2] simplify tests --- tests/aws/services/sqs/test_sqs.py | 70 +++++++++---------- .../aws/services/sqs/test_sqs.validation.json | 30 ++++---- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/tests/aws/services/sqs/test_sqs.py b/tests/aws/services/sqs/test_sqs.py index e88db4adecd5a..3550246595946 100644 --- a/tests/aws/services/sqs/test_sqs.py +++ b/tests/aws/services/sqs/test_sqs.py @@ -1099,25 +1099,24 @@ def test_approximate_number_of_messages_not_visible(self, sqs_create_queue, aws_ # receive 1 message (from message group 1), now 5 messages should be visible aws_sqs_client.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=1) - if is_aws_cloud(): - # wait for aws to propagate the attributes - time.sleep(45) + def _assert_attributes_before_visibility_timeout(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "5", + "ApproximateNumberOfMessagesNotVisible": "1", + "ApproximateNumberOfMessagesDelayed": "0", + } - result = aws_sqs_client.get_queue_attributes( - QueueUrl=queue_url, - AttributeNames=[ - "ApproximateNumberOfMessages", - "ApproximateNumberOfMessagesNotVisible", - "ApproximateNumberOfMessagesDelayed", - ], - ) - assert result["Attributes"] == { - "ApproximateNumberOfMessages": "5", - "ApproximateNumberOfMessagesNotVisible": "1", - "ApproximateNumberOfMessagesDelayed": "0", - } + retry(_assert_attributes_before_visibility_timeout, retries=60, sleep=1) - def _assert_after(): + def _assert_after_visibility_timeout(): _result = aws_sqs_client.get_queue_attributes( QueueUrl=queue_url, AttributeNames=[ @@ -1132,7 +1131,7 @@ def _assert_after(): "ApproximateNumberOfMessagesDelayed": "0", } - retry(_assert_after, retries=15, sleep=1, sleep_before=15 if is_aws_cloud() else 0) + retry(_assert_after_visibility_timeout, retries=60, sleep=1) @markers.aws.validated def test_fifo_approximate_number_of_messages_not_visible( @@ -1172,25 +1171,24 @@ def test_fifo_approximate_number_of_messages_not_visible( assert message_1["Messages"][0]["Attributes"]["MessageGroupId"] == "1" assert message_2["Messages"][0]["Attributes"]["MessageGroupId"] == "2" - if is_aws_cloud(): - # wait for aws to propagate the attributes - time.sleep(45) + def _assert_attributes_before_visibility_timeout(): + _result = aws_sqs_client.get_queue_attributes( + QueueUrl=queue_url, + AttributeNames=[ + "ApproximateNumberOfMessages", + "ApproximateNumberOfMessagesNotVisible", + "ApproximateNumberOfMessagesDelayed", + ], + ) + assert _result["Attributes"] == { + "ApproximateNumberOfMessages": "4", + "ApproximateNumberOfMessagesNotVisible": "2", + "ApproximateNumberOfMessagesDelayed": "0", + } - result = aws_sqs_client.get_queue_attributes( - QueueUrl=queue_url, - AttributeNames=[ - "ApproximateNumberOfMessages", - "ApproximateNumberOfMessagesNotVisible", - "ApproximateNumberOfMessagesDelayed", - ], - ) - assert result["Attributes"] == { - "ApproximateNumberOfMessages": "4", - "ApproximateNumberOfMessagesNotVisible": "2", - "ApproximateNumberOfMessagesDelayed": "0", - } + retry(_assert_attributes_before_visibility_timeout, retries=60, sleep=1) - def _assert_after(): + def _assert_after_visibility_timeout(): _result = aws_sqs_client.get_queue_attributes( QueueUrl=queue_url, AttributeNames=[ @@ -1205,7 +1203,7 @@ def _assert_after(): "ApproximateNumberOfMessagesDelayed": "0", } - retry(_assert_after, retries=15, sleep=1, sleep_before=15 if is_aws_cloud() else 0) + retry(_assert_after_visibility_timeout, retries=60, sleep=1) @markers.aws.validated def test_receive_after_visibility_timeout(self, sqs_create_queue, aws_sqs_client): diff --git a/tests/aws/services/sqs/test_sqs.validation.json b/tests/aws/services/sqs/test_sqs.validation.json index 1c6f0be48d292..3b62f59b54ae3 100644 --- a/tests/aws/services/sqs/test_sqs.validation.json +++ b/tests/aws/services/sqs/test_sqs.validation.json @@ -1,20 +1,20 @@ { "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs]": { - "last_validated_date": "2025-09-25T21:46:19+00:00", + "last_validated_date": "2025-09-26T16:41:28+00:00", "durations_in_seconds": { - "setup": 0.17, - "call": 60.64, + "setup": 0.19, + "call": 61.13, "teardown": 0.07, - "total": 60.88 + "total": 61.39 } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_approximate_number_of_messages_not_visible[sqs_query]": { - "last_validated_date": "2025-09-25T21:47:19+00:00", + "last_validated_date": "2025-09-26T16:42:30+00:00", "durations_in_seconds": { "setup": 0.0, - "call": 60.47, + "call": 61.63, "teardown": 0.09, - "total": 60.56 + "total": 61.72 } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_aws_trace_header_propagation[sqs]": { @@ -165,21 +165,21 @@ } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs]": { - "last_validated_date": "2025-09-25T21:53:42+00:00", + "last_validated_date": "2025-09-26T16:46:35+00:00", "durations_in_seconds": { - "setup": 0.26, - "call": 60.54, + "setup": 5.18, + "call": 62.17, "teardown": 0.08, - "total": 60.88 + "total": 67.43 } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_approximate_number_of_messages_not_visible[sqs_query]": { - "last_validated_date": "2025-09-25T21:54:43+00:00", + "last_validated_date": "2025-09-26T16:47:37+00:00", "durations_in_seconds": { "setup": 0.0, - "call": 60.57, - "teardown": 0.09, - "total": 60.66 + "call": 62.34, + "teardown": 0.07, + "total": 62.41 } }, "tests/aws/services/sqs/test_sqs.py::TestSqsProvider::test_fifo_change_to_high_throughput_after_creation[sqs]": {