diff --git a/localstack/services/sns/provider.py b/localstack/services/sns/provider.py index 57b100a285c90..bc60594b850e2 100644 --- a/localstack/services/sns/provider.py +++ b/localstack/services/sns/provider.py @@ -463,6 +463,15 @@ def publish_batch( raise InvalidParameterException( "Invalid parameter: The MessageGroupId parameter is required for FIFO topics" ) + moto_sns_backend = moto_sns_backends[context.region] + if moto_sns_backend.get_topic(arn=topic_arn).content_based_deduplication == "false": + if not all( + ["MessageDeduplicationId" in entry for entry in publish_batch_request_entries] + ): + raise InvalidParameterException( + "Invalid parameter: The topic should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly", + ) + response = {"Successful": [], "Failed": []} for entry in publish_batch_request_entries: message_id = str(uuid.uuid4()) @@ -499,9 +508,7 @@ def set_subscription_attributes( ) -> None: sub = get_subscription_by_arn(subscription_arn) if not sub: - raise NotFoundException( - f"Unable to find subscription for given ARN: {subscription_arn}" - ) + raise NotFoundException("Subscription does not exist") sub[attribute_name] = attribute_value def confirm_subscription( @@ -663,7 +670,7 @@ def publish( if topic_arn and ".fifo" in topic_arn: if not message_group_id: raise InvalidParameterException( - "The MessageGroupId parameter is required for FIFO topics", + "Invalid parameter: The MessageGroupId parameter is required for FIFO topics", ) moto_sns_backend = moto_sns_backends[context.region] if moto_sns_backend.get_topic(arn=topic_arn).content_based_deduplication == "false": @@ -730,7 +737,7 @@ def subscribe( ) if ".fifo" in endpoint and ".fifo" not in topic_arn: raise InvalidParameterException( - "FIFO SQS Queues can not be subscribed to standard SNS topics" + "Invalid parameter: Invalid parameter: Endpoint Reason: FIFO SQS Queues can not be subscribed to standard SNS topics" ) moto_response = call_moto(context) subscription_arn = moto_response.get("SubscriptionArn") @@ -1167,6 +1174,7 @@ def create_sns_message_body( "Timestamp": timestamp_millis(), "SignatureVersion": "1", # TODO Add a more sophisticated solution with an actual signature + # check KMS for providing real cert and how to serve them # Hardcoded "Signature": "EXAMPLEpH+..", "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-0000000000000000000000.pem", diff --git a/localstack/testing/snapshots/transformer_utility.py b/localstack/testing/snapshots/transformer_utility.py index 77d8892347f59..1aa620d8e5716 100644 --- a/localstack/testing/snapshots/transformer_utility.py +++ b/localstack/testing/snapshots/transformer_utility.py @@ -176,17 +176,42 @@ def sqs_api(): TransformerUtility.key_value("SenderId"), TransformerUtility.jsonpath("$..MessageAttributes.RequestID.StringValue", "request-id"), KeyValueBasedTransformer(_resource_name_transformer, "resource"), - KeyValueBasedTransformer(_signing_cert_url_token_transformer, replacement="token"), + ] + + @staticmethod + def sns_api(): + """ + :return: array with Transformers, for sns api. + """ + return [ + TransformerUtility.key_value("ReceiptHandle"), + TransformerUtility.key_value("SequenceNumber"), # this might need to be in SQS + TransformerUtility.key_value( + "Signature", value_replacement="", reference_replacement=False + ), + # the body of SNS messages contains a timestamp, need to ignore the hash + TransformerUtility.key_value("MD5OfBody", "", reference_replacement=False), + # this can interfere in ARN with the accountID + TransformerUtility.key_value( + "SenderId", value_replacement="", reference_replacement=False + ), KeyValueBasedTransformer( - _sns_pem_file_token_transformer, replacement="signing-cert-file" + _sns_pem_file_token_transformer, + replacement="signing-cert-file", ), - # replaces the domain in "UnsubscribeURL" - TransformerUtility.regex( - re.compile( - r"(?<=UnsubscribeURL[\"|']:\s[\"|'])(https?.*?)(?=/\?Action=Unsubscribe)" - ), + # replaces the domain in "UnsubscribeURL" URL (KeyValue won't work as it replaces reference, and if + # replace_reference is False, then it replaces the whole key + # this will be able to use a KeyValue based once we provide a certificate for message signing in SNS + # a match must be made case-insensitive because the key casing is different from lambda notifications + RegexTransformer( + r"(?<=(?i)UnsubscribeURL[\"|']:\s[\"|'])(https?.*?)(?=/\?Action=Unsubscribe)", replacement="", ), + KeyValueBasedTransformer(_resource_name_transformer, "resource"), + # add a special transformer with 'resource' replacement for SubscriptionARN in UnsubscribeURL + KeyValueBasedTransformer( + _sns_unsubscribe_url_subscription_arn_transformer, replacement="resource" + ), ] @staticmethod @@ -220,15 +245,15 @@ def secretsmanager_secret_id_arn(create_secret_res: CreateSecretResponse, index: def _sns_pem_file_token_transformer(key: str, val: str) -> str: - if isinstance(val, str) and key == "SigningCertURL": - pattern = re.compile(r".*SimpleNotificationService-(.*)?\.pem") + if isinstance(val, str) and key.lower() == "SigningCertURL".lower(): + pattern = re.compile(r".*SimpleNotificationService-(.*\.pem)") match = re.match(pattern, val) if match: return match.groups()[0] -def _signing_cert_url_token_transformer(key: str, val: str) -> str: - if isinstance(val, str) and key == "UnsubscribeURL": +def _sns_unsubscribe_url_subscription_arn_transformer(key: str, val: str) -> str: + if isinstance(val, str) and key.lower() == "UnsubscribeURL".lower(): pattern = re.compile(r".*(?<=\?Action=Unsubscribe&SubscriptionArn=).*:(.*)") match = re.match(pattern, val) if match: diff --git a/tests/integration/s3/test_s3_notifications_sns.py b/tests/integration/s3/test_s3_notifications_sns.py index 3cf3241946449..289644adca9ce 100644 --- a/tests/integration/s3/test_s3_notifications_sns.py +++ b/tests/integration/s3/test_s3_notifications_sns.py @@ -110,14 +110,9 @@ def test_object_created_put( snapshot, ): snapshot.add_transformer(snapshot.transform.sqs_api()) + snapshot.add_transformer(snapshot.transform.sns_api()) snapshot.add_transformer(snapshot.transform.s3_api()) - snapshot.add_transformer( - snapshot.transform.jsonpath( - "$..Signature", - "signature", - reference_replacement=False, - ), - ) + bucket_name = s3_create_bucket() topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() diff --git a/tests/integration/s3/test_s3_notifications_sns.snapshot.json b/tests/integration/s3/test_s3_notifications_sns.snapshot.json index 8850cb6d5a17e..b5128cc183ace 100644 --- a/tests/integration/s3/test_s3_notifications_sns.snapshot.json +++ b/tests/integration/s3/test_s3_notifications_sns.snapshot.json @@ -1,6 +1,6 @@ { "tests/integration/s3/test_s3_notifications_sns.py::TestS3NotificationsToSns::test_object_created_put": { - "recorded-date": "08-08-2022, 17:51:20", + "recorded-date": "09-08-2022, 11:38:05", "recorded-content": { "receive_messages": { "messages": [ @@ -44,14 +44,14 @@ ] }, "MessageId": "", - "Signature": "signature", + "Signature": "", "SignatureVersion": "1", - "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", "Subject": "Amazon S3 Notification", "Timestamp": "date", "TopicArn": "arn:aws:sns::111111111111:", "Type": "Notification", - "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" }, { "Message": { @@ -93,14 +93,14 @@ ] }, "MessageId": "", - "Signature": "signature", + "Signature": "", "SignatureVersion": "1", - "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", "Subject": "Amazon S3 Notification", "Timestamp": "date", "TopicArn": "arn:aws:sns::111111111111:", "Type": "Notification", - "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" } ] } diff --git a/tests/integration/test_sns.py b/tests/integration/test_sns.py index f075f5d5f64a4..1b5b62147d8b1 100644 --- a/tests/integration/test_sns.py +++ b/tests/integration/test_sns.py @@ -2,7 +2,6 @@ import json import queue import random -from base64 import b64encode from operator import itemgetter import pytest @@ -19,7 +18,7 @@ from localstack.testing.aws.util import is_aws_cloud from localstack.utils import testutil from localstack.utils.net import wait_for_port_closed, wait_for_port_open -from localstack.utils.strings import short_uid, to_str +from localstack.utils.strings import short_uid from localstack.utils.sync import poll_condition, retry from localstack.utils.testutil import check_expected_lambda_log_events_length @@ -36,24 +35,29 @@ PUBLICATION_RETRIES = 4 +@pytest.fixture(autouse=True) +def sns_snapshot_transformer(snapshot): + snapshot.add_transformer(snapshot.transform.sns_api()) + + class TestSNSSubscription: @pytest.mark.aws_validated def test_python_lambda_subscribe_sns_topic( self, - create_lambda_function, sns_client, + sns_create_topic, + sns_subscription, + lambda_client, lambda_su_role, - sns_topic, + create_lambda_function, logs_client, - lambda_client, - sqs_client, - sns_subscription, + snapshot, ): function_name = f"{TEST_LAMBDA_FUNCTION_PREFIX}-{short_uid()}" permission_id = f"test-statement-{short_uid()}" subject = "[Subject] Test subject" message = "Hello world." - topic_arn = sns_topic["Attributes"]["TopicArn"] + topic_arn = sns_create_topic()["TopicArn"] lambda_creation_response = create_lambda_function( func_name=function_name, @@ -97,8 +101,7 @@ def check_subscription(): logs_client=logs_client, ) notification = events[0]["Records"][0]["Sns"] - assert "Subject" in notification - assert subject == notification["Subject"] + snapshot.match("notification", notification) class TestSNSProvider: @@ -110,6 +113,7 @@ def test_publish_unicode_chars( sqs_create_queue, sqs_client, sns_create_sqs_subscription, + snapshot, ): topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -122,13 +126,13 @@ def test_publish_unicode_chars( response = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 ) - msg_received = response["Messages"][0] - msg_received = json.loads(to_str(msg_received["Body"])) - msg_received = msg_received["Message"] - assert message == msg_received + + snapshot.match("received-message", response) @pytest.mark.aws_validated - def test_subscribe_with_invalid_protocol(self, sns_client, sns_create_topic, sns_subscription): + def test_subscribe_with_invalid_protocol( + self, sns_client, sns_create_topic, sns_subscription, snapshot + ): topic_arn = sns_create_topic()["TopicArn"] with pytest.raises(ClientError) as e: @@ -136,10 +140,15 @@ def test_subscribe_with_invalid_protocol(self, sns_client, sns_create_topic, sns TopicArn=topic_arn, Protocol="test-protocol", Endpoint="localstack@yopmail.com" ) - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 - assert e.value.response["Error"]["Code"] == "InvalidParameter" + snapshot.match("exception", e.value.response) @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + ] + ) def test_attribute_raw_subscribe( self, sqs_client, @@ -147,7 +156,16 @@ def test_attribute_raw_subscribe( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): + # the hash isn't the same because of the Binary attributes (maybe decoding order?) + snapshot.add_transformer( + snapshot.transform.key_value( + "MD5OfMessageAttributes", + value_replacement="", + reference_replacement=False, + ) + ) topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() subscription = sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) @@ -159,12 +177,10 @@ def test_attribute_raw_subscribe( AttributeValue="true", ) - actual_attributes = sns_client.get_subscription_attributes( + response_attributes = sns_client.get_subscription_attributes( SubscriptionArn=subscription_arn - )["Attributes"] - - # assert the attributes are well set - assert actual_attributes["RawMessageDelivery"] + ) + snapshot.match("subscription-attributes", response_attributes) # publish message to SNS, receive it from SQS, assert that messages are equal and that they are Raw message = "This is a test message" @@ -183,12 +199,16 @@ def test_attribute_raw_subscribe( VisibilityTimeout=0, WaitTimeSeconds=4, ) - msg_received = response["Messages"][0] - assert message == msg_received["Body"] - # MessageAttributes are attached to the message when RawDelivery is true - assert binary_attribute == msg_received["MessageAttributes"]["store"]["BinaryValue"] + snapshot.match("messages-response", response) @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + "$..Attributes.RawMessageDelivery", + ] + ) def test_filter_policy( self, sns_client, @@ -196,7 +216,9 @@ def test_filter_policy( sqs_create_queue, sns_create_topic, sns_create_sqs_subscription, + snapshot, ): + topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() subscription = sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) @@ -209,10 +231,17 @@ def test_filter_policy( AttributeValue=json.dumps(filter_policy), ) - # get number of messages - num_msgs_0 = len( - sqs_client.receive_message(QueueUrl=queue_url, VisibilityTimeout=0).get("Messages", []) + response_attributes = sns_client.get_subscription_attributes( + SubscriptionArn=subscription_arn + ) + snapshot.match("subscription-attributes", response_attributes) + + response_0 = sqs_client.receive_message( + QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=1 ) + snapshot.match("messages-0", response_0) + # get number of messages + num_msgs_0 = len(response_0.get("Messages", [])) # publish message that satisfies the filter policy, assert that message is received message = "This is a test message" @@ -223,10 +252,12 @@ def test_filter_policy( MessageAttributes=message_attributes, ) - msgs_1 = sqs_client.receive_message( + response_1 = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 - )["Messages"] - num_msgs_1 = len(msgs_1) + ) + snapshot.match("messages-1", response_1) + + num_msgs_1 = len(response_1["Messages"]) assert num_msgs_1 == (num_msgs_0 + 1) # publish message that does not satisfy the filter policy, assert that message is not received @@ -237,14 +268,22 @@ def test_filter_policy( MessageAttributes={"attr1": {"DataType": "Number", "StringValue": "111"}}, ) - num_msgs_2 = len( - sqs_client.receive_message(QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4)[ - "Messages" - ] + response_2 = sqs_client.receive_message( + QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 ) + snapshot.match("messages-2", response_2) + num_msgs_2 = len(response_2["Messages"]) assert num_msgs_2 == num_msgs_1 @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + "$..Attributes.RawMessageDelivery", # todo: fix me (not added to response if false) + "$..Attributes.sqs_queue_url", # todo: fix me: added by moto? illegal? + ] + ) def test_exists_filter_policy( self, sns_client, @@ -252,7 +291,9 @@ def test_exists_filter_policy( sqs_create_queue, sns_create_topic, sns_create_sqs_subscription, + snapshot, ): + topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() subscription = sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) @@ -265,13 +306,18 @@ def test_exists_filter_policy( AttributeValue=json.dumps(filter_policy), ) - # get number of messages - num_msgs_0 = len( - sqs_client.receive_message(QueueUrl=queue_url, VisibilityTimeout=0).get("Messages", []) + response_attributes = sns_client.get_subscription_attributes( + SubscriptionArn=subscription_arn ) + snapshot.match("subscription-attributes-policy-1", response_attributes) + + response_0 = sqs_client.receive_message(QueueUrl=queue_url, VisibilityTimeout=0) + snapshot.match("messages-0", response_0) + # get number of messages + num_msgs_0 = len(response_0.get("Messages", [])) # publish message that satisfies the filter policy, assert that message is received - message_1 = f"message-{short_uid()}" + message_1 = "message-1" sns_client.publish( TopicArn=topic_arn, Message=message_1, @@ -280,32 +326,32 @@ def test_exists_filter_policy( "def": {"DataType": "Number", "StringValue": "99"}, }, ) - msgs_1 = sqs_client.receive_message( + response_1 = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 - )["Messages"] - - num_msgs_1 = len(msgs_1) - assert message_1 == json.loads(msgs_1[0]["Body"])["Message"] + ) + snapshot.match("messages-1", response_1) + num_msgs_1 = len(response_1["Messages"]) assert num_msgs_1 == (num_msgs_0 + 1) # publish message that does not satisfy the filter policy, assert that message is not received - message_2 = f"message-{short_uid()}" + message_2 = "message-2" sns_client.publish( TopicArn=topic_arn, Message=message_2, MessageAttributes={"attr1": {"DataType": "Number", "StringValue": "111"}}, ) - msgs_2 = sqs_client.receive_message( + response_2 = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 - )["Messages"] - num_msgs_2 = len(msgs_2) - # assert that it's still the same message that #1 - assert json.loads(msgs_2[0]["Body"])["Message"] == message_1 + ) + snapshot.match("messages-2", response_2) + num_msgs_2 = len(response_2["Messages"]) assert num_msgs_2 == num_msgs_1 # delete first message - sqs_client.delete_message(QueueUrl=queue_url, ReceiptHandle=msgs_1[0]["ReceiptHandle"]) + sqs_client.delete_message( + QueueUrl=queue_url, ReceiptHandle=response_1["Messages"][0]["ReceiptHandle"] + ) # test with exist operator set to false. filter_policy = json.dumps({"store": [{"exists": False}]}) @@ -319,29 +365,32 @@ def get_filter_policy(): subscription_attrs = sns_client.get_subscription_attributes( SubscriptionArn=subscription_arn ) - return subscription_attrs["Attributes"]["FilterPolicy"] == filter_policy + return subscription_attrs["Attributes"]["FilterPolicy"] # wait for the new filter policy to be in effect poll_condition(lambda: get_filter_policy() == filter_policy, timeout=4) + response_attributes_2 = sns_client.get_subscription_attributes( + SubscriptionArn=subscription_arn + ) + snapshot.match("subscription-attributes-policy-2", response_attributes_2) # publish message that satisfies the filter policy, assert that message is received - message_3 = f"message-{short_uid()}" + message_3 = "message-3" sns_client.publish( TopicArn=topic_arn, Message=message_3, MessageAttributes={"def": {"DataType": "Number", "StringValue": "99"}}, ) - msgs_3 = sqs_client.receive_message( + response_3 = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 - )["Messages"] - num_msgs_3 = len(msgs_3) - # assert that it is not the the same message that #1 - assert json.loads(msgs_3[0]["Body"])["Message"] == message_3 + ) + snapshot.match("messages-3", response_3) + num_msgs_3 = len(response_3["Messages"]) assert num_msgs_3 == num_msgs_1 # publish message that does not satisfy the filter policy, assert that message is not received - message_4 = f"message-{short_uid()}" + message_4 = "message-4" sns_client.publish( TopicArn=topic_arn, Message=message_4, @@ -351,15 +400,21 @@ def get_filter_policy(): }, ) - msgs_4 = sqs_client.receive_message( + response_4 = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=4 - )["Messages"] - num_msgs_4 = len(msgs_4) - # assert that it's still the same message that #3 - assert json.loads(msgs_4[0]["Body"])["Message"] == message_3 + ) + snapshot.match("messages-4", response_4) + num_msgs_4 = len(response_4["Messages"]) assert num_msgs_4 == num_msgs_3 @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + "$..Attributes.RawMessageDelivery", + ] + ) def test_subscribe_sqs_queue( self, sns_client, @@ -367,6 +422,7 @@ def test_subscribe_sqs_queue( sqs_create_queue, sns_create_topic, sns_create_sqs_subscription, + snapshot, ): # TODO: check with non default external port @@ -382,6 +438,11 @@ def test_subscribe_sqs_queue( AttributeValue=json.dumps(filter_policy), ) + response_attributes = sns_client.get_subscription_attributes( + SubscriptionArn=subscription["SubscriptionArn"], + ) + snapshot.match("subscription-attributes", response_attributes) + # publish message that satisfies the filter policy message = "This is a test message" sns_client.publish( @@ -397,9 +458,7 @@ def test_subscribe_sqs_queue( MessageAttributeNames=["All"], WaitTimeSeconds=4, ) - message = response["Messages"][0] - message_body = json.loads(message["Body"]) - assert message_body["MessageAttributes"]["attr1"]["Value"] == "99.12" + snapshot.match("messages", response) @pytest.mark.only_localstack def test_subscribe_platform_endpoint( @@ -444,7 +503,7 @@ def check_message(): sns_client.delete_platform_application(PlatformApplicationArn=app_arn) @pytest.mark.aws_validated - def test_unknown_topic_publish(self, sns_client, sns_create_topic): + def test_unknown_topic_publish(self, sns_client, sns_create_topic, snapshot): # create topic to get the basic arn structure # otherwise you get InvalidClientTokenId exception because of account id topic_arn = sns_create_topic()["TopicArn"] @@ -455,9 +514,7 @@ def test_unknown_topic_publish(self, sns_client, sns_create_topic): with pytest.raises(ClientError) as e: sns_client.publish(TopicArn=fake_arn, Message=message) - assert e.value.response["Error"]["Code"] == "NotFound" - assert e.value.response["Error"]["Message"] == "Topic does not exist" - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 404 + snapshot.match("error", e.value.response) @pytest.mark.only_localstack def test_publish_sms(self, sns_client): @@ -465,7 +522,9 @@ def test_publish_sms(self, sns_client): assert "MessageId" in response assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 + @pytest.mark.only_localstack def test_publish_non_existent_target(self, sns_client): + # todo: fix test, the client id in the ARN is wrong so can't test against AWS with pytest.raises(ClientError) as ex: sns_client.publish( TargetArn="arn:aws:sns:us-east-1:000000000000:endpoint/APNS/abcdef/0f7d5971-aa8b-4bd5-b585-0826e9f93a66", @@ -533,8 +592,15 @@ def check_subscription(): retry(check_subscription, retries=PUBLICATION_RETRIES, sleep=PUBLICATION_TIMEOUT) @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Owner", + "$..ConfirmationWasAuthenticated", + "$..RawMessageDelivery", + ] + ) def test_sqs_topic_subscription_confirmation( - self, sns_client, sns_create_topic, sqs_create_queue, sns_create_sqs_subscription + self, sns_client, sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, snapshot ): topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -547,12 +613,25 @@ def check_subscription(): subscription_attrs = sns_client.get_subscription_attributes( SubscriptionArn=subscription_arn )["Attributes"] + else: + snapshot.match("subscription-attrs", subscription_attrs) + return subscription_attrs["PendingConfirmation"] == "false" # SQS subscriptions are auto confirmed if they are from the user and in the same region assert poll_condition(check_subscription, timeout=5) @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Messages..Body.Message.raise_error", # check lambda delivery format into DLQ + "$..Messages..MessageAttributes.ErrorCode.Type", + "$..Messages..MessageAttributes.ErrorMessage.Value", + ] + ) + @pytest.mark.xfail( + reason="Behaviour and format diverges from AWS, see https://github.com/localstack/localstack/issues/6613" + ) def test_sns_topic_as_lambda_dead_letter_queue( self, sns_client, @@ -564,7 +643,16 @@ def test_sns_topic_as_lambda_dead_letter_queue( sqs_create_queue, sns_subscription, sns_create_sqs_subscription, + snapshot, ): + # todo check the skipped paths, as the format is very wrong + snapshot.add_transformer(snapshot.transform.lambda_api()) + snapshot.add_transformer( + snapshot.transform.jsonpath( + "$..Messages..MessageAttributes.RequestID.Value", "request-id" + ) + ) + # create an SNS topic that will be used as a DLQ by the lambda dlq_topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -583,6 +671,10 @@ def test_sns_topic_as_lambda_dead_letter_queue( role=lambda_su_role, DeadLetterConfig={"TargetArn": dlq_topic_arn}, ) + snapshot.match( + "lambda-response-dlq-config", + lambda_creation_response["CreateFunctionResponse"]["DeadLetterConfig"], + ) lambda_arn = lambda_creation_response["CreateFunctionResponse"]["FunctionArn"] # allow the SNS topic to invoke the lambda @@ -612,17 +704,27 @@ def receive_dlq(): QueueUrl=queue_url, MessageAttributeNames=["All"], VisibilityTimeout=0 ) assert len(result["Messages"]) > 0 - msg_body = json.loads(result["Messages"][0]["Body"]) - msg_attrs = msg_body["MessageAttributes"] - assert "RequestID" in msg_attrs - assert "ErrorCode" in msg_attrs - assert "ErrorMessage" in msg_attrs + return result # check that the SQS queue subscribed to the SNS topic used as DLQ received the error from the lambda # on AWS, event retries can be quite delayed, so we have to wait up to 6 minutes here # reduced retries when using localstack to avoid tests flaking retries = 120 if is_aws_cloud() else 3 - retry(receive_dlq, retries=retries, sleep=3) + messages = retry(receive_dlq, retries=retries, sleep=3) + + messages["Messages"][0]["Body"] = json.loads(messages["Messages"][0]["Body"]) + messages["Messages"][0]["Body"]["Message"] = json.loads( + messages["Messages"][0]["Body"]["Message"] + ) + + snapshot.match("messages", messages) + + # as we ignore some fields, still assert manually the presence of certain fields + msg_body = messages["Messages"][0]["Body"] + msg_attrs = msg_body["MessageAttributes"] + assert "RequestID" in msg_attrs + assert "ErrorCode" in msg_attrs + assert "ErrorMessage" in msg_attrs @pytest.mark.only_localstack def test_redrive_policy_http_subscription( @@ -677,7 +779,14 @@ def test_redrive_policy_http_subscription( assert message["Type"] == "Notification" assert json.loads(message["Message"])["message"] == "test_redrive_policy" - @pytest.mark.aws_validated + @pytest.mark.aws_validated # snaphot ok + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Owner", + "$..ConfirmationWasAuthenticated", + "$..RawMessageDelivery", + ] + ) def test_redrive_policy_lambda_subscription( self, sns_client, @@ -690,6 +799,7 @@ def test_redrive_policy_lambda_subscription( sqs_client, sns_subscription, sns_allow_topic_sqs_queue, + snapshot, ): dlq_url = sqs_create_queue() dlq_arn = sqs_queue_arn(dlq_url) @@ -714,27 +824,35 @@ def test_redrive_policy_lambda_subscription( AttributeName="RedrivePolicy", AttributeValue=json.dumps({"deadLetterTargetArn": dlq_arn}), ) + response_attributes = sns_client.get_subscription_attributes( + SubscriptionArn=subscription["SubscriptionArn"] + ) + + snapshot.match("subscription-attributes", response_attributes) + lambda_client.delete_function(FunctionName=lambda_name) sns_client.publish( TopicArn=topic_arn, - Message=json.dumps({"message": "test_redrive_policy"}), + Message="test_redrive_policy", ) response = sqs_client.receive_message(QueueUrl=dlq_url, WaitTimeSeconds=10) + snapshot.match("messages", response) assert ( len(response["Messages"]) == 1 ), f"invalid number of messages in DLQ response {response}" message = json.loads(response["Messages"][0]["Body"]) assert message["Type"] == "Notification" - assert json.loads(message["Message"])["message"] == "test_redrive_policy" + assert message["Message"] == "test_redrive_policy" @pytest.mark.aws_validated - def test_publish_with_empty_subject(self, sns_client, sns_create_topic): + def test_publish_with_empty_subject(self, sns_client, sns_create_topic, snapshot): topic_arn = sns_create_topic()["TopicArn"] # Publish without subject rs = sns_client.publish(TopicArn=topic_arn, Message=json.dumps({"message": "test_publish"})) + snapshot.match("response-without-subject", rs) assert rs["ResponseMetadata"]["HTTPStatusCode"] == 200 with pytest.raises(ClientError) as e: @@ -744,18 +862,21 @@ def test_publish_with_empty_subject(self, sns_client, sns_create_topic): Message=json.dumps({"message": "test_publish"}), ) - assert e.value.response["Error"]["Code"] == "InvalidParameter" - assert e.value.response["Error"]["Message"] == "Invalid parameter: Subject" + snapshot.match("response-with-empty-subject", e.value.response) - # todo test with snapshot to aws validate it - def test_create_topic_test_arn(self, sns_create_topic, sns_client): - topic_name = f"topic-{short_uid()}" + @pytest.mark.aws_validated + def test_create_topic_test_arn(self, sns_create_topic, sns_client, snapshot): + topic_name = "topic-test-create" response = sns_create_topic(Name=topic_name) + snapshot.match("create-topic", response) topic_arn_params = response["TopicArn"].split(":") testutil.response_arn_matches_partition(sns_client, response["TopicArn"]) - assert topic_arn_params[4] == get_aws_account_id() + # we match the response but need to be sure the resource name is the same assert topic_arn_params[5] == topic_name + if not is_aws_cloud(): + assert topic_arn_params[4] == get_aws_account_id() + @pytest.mark.aws_validated def test_publish_message_by_target_arn( self, @@ -764,6 +885,7 @@ def test_publish_message_by_target_arn( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): # using an SQS subscription to test TopicArn/TargetArn as it is easier to check against AWS topic_arn = sns_create_topic()["TopicArn"] @@ -779,11 +901,9 @@ def test_publish_message_by_target_arn( WaitTimeSeconds=4, ) - assert len(response["Messages"]) == 1 - message = response["Messages"][0] - msg_body = json.loads(message["Body"]) - assert msg_body["Message"] == "test-msg-1" + snapshot.match("receive-topic-arn", response) + message = response["Messages"][0] sqs_client.delete_message(QueueUrl=queue_url, ReceiptHandle=message["ReceiptHandle"]) # publish with TargetArn instead of TopicArn @@ -795,10 +915,7 @@ def test_publish_message_by_target_arn( VisibilityTimeout=0, WaitTimeSeconds=4, ) - assert len(response["Messages"]) == 1 - message = response["Messages"][0] - msg_body = json.loads(message["Body"]) - assert msg_body["Message"] == "test-msg-2" + snapshot.match("receive-target-arn", response) @pytest.mark.aws_validated def test_publish_message_before_subscribe_topic( @@ -807,53 +924,43 @@ def test_publish_message_before_subscribe_topic( sns_create_topic, sqs_client, sqs_create_queue, - sns_subscription, sns_create_sqs_subscription, + snapshot, ): topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() rs = sns_client.publish( - TopicArn=topic_arn, Subject="test subject", Message="test_message_1" + TopicArn=topic_arn, Subject="test-subject-before-sub", Message="test_message_before" ) - assert rs["ResponseMetadata"]["HTTPStatusCode"] == 200 + snapshot.match("publish-before-subscribing", rs) sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) - message_subject = "sqs subject" - message_body = "test_message_2" + message_subject = "test-subject-after-sub" + message_body = "test_message_after" rs = sns_client.publish(TopicArn=topic_arn, Subject=message_subject, Message=message_body) - assert rs["ResponseMetadata"]["HTTPStatusCode"] == 200 - message_id = rs["MessageId"] + snapshot.match("publish-after-subscribing", rs) response = sqs_client.receive_message( QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=5 ) # nothing was subscribing to the topic, so the first message is lost - assert len(response["Messages"]) == 1 - message = json.loads(response["Messages"][0]["Body"]) - assert message["MessageId"] == message_id - assert message["Subject"] == message_subject - assert message["Message"] == message_body + snapshot.match("receive-messages", response) @pytest.mark.aws_validated - def test_create_duplicate_topic_with_more_tags(self, sns_client, sns_create_topic): - topic_name = f"test-{short_uid()}" + def test_create_duplicate_topic_with_more_tags(self, sns_client, sns_create_topic, snapshot): + topic_name = "test-duplicated-topic-more-tags" sns_create_topic(Name=topic_name) with pytest.raises(ClientError) as e: sns_client.create_topic(Name=topic_name, Tags=[{"Key": "key1", "Value": "value1"}]) - assert e.value.response["Error"]["Code"] == "InvalidParameter" - assert ( - e.value.response["Error"]["Message"] - == "Invalid parameter: Tags Reason: Topic already exists with different tags" - ) - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 + snapshot.match("exception-duplicate", e.value.response) @pytest.mark.aws_validated - def test_create_duplicate_topic_check_idempotency(self, sns_create_topic): + def test_create_duplicate_topic_check_idempotency(self, sns_create_topic, snapshot): topic_name = f"test-{short_uid()}" tags = [{"Key": "a", "Value": "1"}, {"Key": "b", "Value": "2"}] kwargs = [ @@ -864,12 +971,12 @@ def test_create_duplicate_topic_check_idempotency(self, sns_create_topic): # create topic with two tags response = sns_create_topic(Name=topic_name, Tags=tags) - topic_arn = response["TopicArn"] + snapshot.match("response-created", response) - for arg in kwargs: + for index, arg in enumerate(kwargs): response = sns_create_topic(Name=topic_name, **arg) - # assert TopicArn returned by all the above create_topic calls is the same as the original - assert response["TopicArn"] == topic_arn + # we check in the snapshot that they all have the same tag (original topic) + snapshot.match(f"response-same-arn-{index}", response) @pytest.mark.only_localstack @pytest.mark.skip( @@ -910,8 +1017,9 @@ def test_publish_by_path_parameters( sqs_create_queue, sns_create_sqs_subscription, aws_http_client_factory, + snapshot, ): - message = f"test message {short_uid()}" + message = "test message direct post request" topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) @@ -933,13 +1041,20 @@ def test_publish_by_path_parameters( }, ) + json_response = xmltodict.parse(response.content) + json_response["PublishResponse"].pop("@xmlns") + json_response["PublishResponse"]["ResponseMetadata"][ + "HTTPStatusCode" + ] = response.status_code + json_response["PublishResponse"]["ResponseMetadata"]["HTTPHeaders"] = dict(response.headers) + snapshot.match("post-request", json_response) + assert response.status_code == 200 assert b" The SQS FIFO queue consumer processes the message and deletes it from the queue before the visibility + # > timeout expires. @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + ] + ) def test_publish_batch_exceptions( self, sns_client, @@ -1299,24 +1444,12 @@ def test_publish_batch_exceptions( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): - topic_name = f"topic-{short_uid()}.fifo" - queue_name = f"queue-{short_uid()}.fifo" - - topic_arn = sns_create_topic(Name=topic_name, Attributes={"FifoTopic": "true"})["TopicArn"] - queue_url = sqs_create_queue( - QueueName=queue_name, - Attributes={"FifoQueue": "true"}, - ) - - subscription = sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) - subscription_arn = subscription["SubscriptionArn"] - - sns_client.set_subscription_attributes( - SubscriptionArn=subscription_arn, - AttributeName="RawMessageDelivery", - AttributeValue="true", - ) + fifo_topic_name = f"topic-{short_uid()}.fifo" + topic_arn = sns_create_topic(Name=fifo_topic_name, Attributes={"FifoTopic": "true"})[ + "TopicArn" + ] with pytest.raises(ClientError) as e: sns_client.publish_batch( @@ -1328,12 +1461,7 @@ def test_publish_batch_exceptions( } ], ) - assert e.value.response["Error"]["Code"] == "InvalidParameter" - assert ( - e.value.response["Error"]["Message"] - == "Invalid parameter: The MessageGroupId parameter is required for FIFO topics" - ) - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 + snapshot.match("no-group-id", e.value.response) with pytest.raises(ClientError) as e: sns_client.publish_batch( @@ -1342,12 +1470,7 @@ def test_publish_batch_exceptions( {"Id": f"Id_{i}", "Message": "Too many messages"} for i in range(11) ], ) - assert e.value.response["Error"]["Code"] == "TooManyEntriesInBatchRequest" - assert ( - e.value.response["Error"]["Message"] - == "The batch request contains more entries than permissible." - ) - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 + snapshot.match("too-many-msg", e.value.response) with pytest.raises(ClientError) as e: sns_client.publish_batch( @@ -1356,18 +1479,22 @@ def test_publish_batch_exceptions( {"Id": "1", "Message": "Messages with the same ID"} for i in range(2) ], ) - assert e.value.response["Error"]["Code"] == "BatchEntryIdsNotDistinct" - assert ( - e.value.response["Error"]["Message"] - == "Two or more batch entries in the request have the same Id." - ) - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 - # todo add test and implement behaviour for ContentBasedDeduplication or MessageDeduplicationId + snapshot.match("same-msg-id", e.value.response) - def add_xray_header(self, request, **kwargs): - request.headers[ - "X-Amzn-Trace-Id" - ] = "Root=1-3152b799-8954dae64eda91bc9a23a7e8;Parent=7fa8c0f79203be72;Sampled=1" + with pytest.raises(ClientError) as e: + sns_client.publish_batch( + TopicArn=topic_arn, + PublishBatchRequestEntries=[ + { + "Id": "1", + "Message": "Test message without MessageDeduplicationId", + "MessageGroupId": "msg1", + } + ], + ) + snapshot.match("no-dedup-id", e.value.response) + + # todo add test and implement behaviour for ContentBasedDeduplication or MessageDeduplicationId def test_publish_sqs_from_sns_with_xray_propagation( self, sns_client, sns_create_topic, sqs_client, sqs_create_queue, sns_subscription @@ -1376,7 +1503,12 @@ def test_publish_sqs_from_sns_with_xray_propagation( if SQS_BACKEND_IMPL != "elasticmq": pytest.skip("not using elasticmq as SQS backend") - sns_client.meta.events.register("before-send.sns.Publish", self.add_xray_header) + def add_xray_header(request, **kwargs): + request.headers[ + "X-Amzn-Trace-Id" + ] = "Root=1-3152b799-8954dae64eda91bc9a23a7e8;Parent=7fa8c0f79203be72;Sampled=1" + + sns_client.meta.events.register("before-send.sns.Publish", add_xray_header) topic = sns_create_topic() topic_arn = topic["TopicArn"] @@ -1404,32 +1536,49 @@ def test_publish_sqs_from_sns_with_xray_propagation( ) @pytest.mark.aws_validated - def test_create_topic_after_delete_with_new_tags(self, sns_create_topic, sns_client): + def test_create_topic_after_delete_with_new_tags(self, sns_create_topic, sns_client, snapshot): topic_name = f"test-{short_uid()}" topic = sns_create_topic(Name=topic_name, Tags=[{"Key": "Name", "Value": "pqr"}]) + snapshot.match("topic-0", topic) sns_client.delete_topic(TopicArn=topic["TopicArn"]) topic1 = sns_create_topic(Name=topic_name, Tags=[{"Key": "Name", "Value": "abc"}]) - assert topic["TopicArn"] == topic1["TopicArn"] + snapshot.match("topic-1", topic1) @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + "$..Attributes.RawMessageDelivery", + "$..Subscriptions..Owner", + ] + ) def test_not_found_error_on_set_subscription_attributes( - self, sns_client, sns_create_topic, sqs_create_queue, sqs_queue_arn, sns_subscription + self, + sns_client, + sns_create_topic, + sqs_create_queue, + sqs_queue_arn, + sns_subscription, + snapshot, ): topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() queue_arn = sqs_queue_arn(queue_url) subscription = sns_subscription(TopicArn=topic_arn, Protocol="sqs", Endpoint=queue_arn) + snapshot.match("sub", subscription) subscription_arn = subscription["SubscriptionArn"] - subscription_attributes = sns_client.get_subscription_attributes( - SubscriptionArn=subscription_arn - )["Attributes"] + response = sns_client.get_subscription_attributes(SubscriptionArn=subscription_arn) + subscription_attributes = response["Attributes"] + snapshot.match("sub-attrs", response) assert subscription_attributes["SubscriptionArn"] == subscription_arn subscriptions_by_topic = sns_client.list_subscriptions_by_topic(TopicArn=topic_arn) + snapshot.match("subscriptions-for-topic-before-unsub", subscriptions_by_topic) assert len(subscriptions_by_topic["Subscriptions"]) == 1 sns_client.unsubscribe(SubscriptionArn=subscription_arn) @@ -1446,12 +1595,23 @@ def check_subscription_deleted(): except ClientError as e: assert e.response["Error"]["Code"] == "NotFound" assert e.response["ResponseMetadata"]["HTTPStatusCode"] == 404 + snapshot.match("sub-not-found", e.response) retry(check_subscription_deleted, retries=10, sleep_before=0.2, sleep=3) subscriptions_by_topic = sns_client.list_subscriptions_by_topic(TopicArn=topic_arn) + snapshot.match("subscriptions-for-topic-after-unsub", subscriptions_by_topic) assert len(subscriptions_by_topic["Subscriptions"]) == 0 @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Messages..Body.SignatureVersion", # apparently, messages are not signed in fifo topics + "$..Messages..Body.Signature", + "$..Messages..Body.SigningCertURL", + "$..Messages..Body.SequenceNumber", + "$..Messages..Attributes.SequenceNumber", + ] + ) def test_message_to_fifo_sqs( self, sns_client, @@ -1459,7 +1619,16 @@ def test_message_to_fifo_sqs( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): + # it is the md5 hash of the body containing a timestamp + snapshot.add_transformer( + snapshot.transform.key_value( + "MessageDeduplicationId", + value_replacement="", + reference_replacement=False, + ) + ) topic_name = f"topic-{short_uid()}.fifo" queue_name = f"queue-{short_uid()}.fifo" @@ -1483,12 +1652,17 @@ def test_message_to_fifo_sqs( sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) message = "Test" - sns_client.publish(TopicArn=topic_arn, Message=message, MessageGroupId=short_uid()) + sns_client.publish(TopicArn=topic_arn, Message=message, MessageGroupId="message-group-id-1") - messages = sqs_client.receive_message( - QueueUrl=queue_url, VisibilityTimeout=0, WaitTimeSeconds=10 - )["Messages"] - msg_body = messages[0]["Body"] + response = sqs_client.receive_message( + QueueUrl=queue_url, + VisibilityTimeout=0, + WaitTimeSeconds=10, + AttributeNames=["All"], + ) + snapshot.match("messages", response) + + msg_body = response["Messages"][0]["Body"] assert json.loads(msg_body)["Message"] == message @pytest.mark.aws_validated @@ -1499,6 +1673,7 @@ def test_validations_for_fifo( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): topic_name = f"topic-{short_uid()}" fifo_topic_name = f"topic-{short_uid()}.fifo" @@ -1518,27 +1693,32 @@ def test_validations_for_fifo( sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=fifo_queue_url) assert e.match("standard SNS topic") + snapshot.match("not-fifo-topic", e.value.response) with pytest.raises(ClientError) as e: sns_client.publish(TopicArn=fifo_topic_arn, Message="test") assert e.match("MessageGroupId") + snapshot.match("no-msg-group-id", e.value.response) with pytest.raises(ClientError) as e: sns_client.publish(TopicArn=fifo_topic_arn, Message="test", MessageGroupId=short_uid()) # if ContentBasedDeduplication is not set at the topic level, it needs MessageDeduplicationId for each msg assert e.match("MessageDeduplicationId") assert e.match("ContentBasedDeduplication") + snapshot.match("no-dedup-policy", e.value.response) with pytest.raises(ClientError) as e: sns_client.publish( TopicArn=topic_arn, Message="test", MessageDeduplicationId=short_uid() ) assert e.match("MessageDeduplicationId") + snapshot.match("no-msg-dedup-regular-topic", e.value.response) with pytest.raises(ClientError) as e: sns_client.publish(TopicArn=topic_arn, Message="test", MessageGroupId=short_uid()) assert e.match("MessageGroupId") + snapshot.match("no-msg-group-id-regular-topic", e.value.response) @pytest.mark.aws_validated def test_empty_sns_message( @@ -1548,6 +1728,7 @@ def test_empty_sns_message( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -1555,13 +1736,13 @@ def test_empty_sns_message( with pytest.raises(ClientError) as e: sns_client.publish(Message="", TopicArn=topic_arn) - assert e.match("Empty message") - assert ( - sqs_client.get_queue_attributes( - QueueUrl=queue_url, AttributeNames=["ApproximateNumberOfMessages"] - )["Attributes"]["ApproximateNumberOfMessages"] - == "0" + + snapshot.match("empty-msg-error", e.value.response) + + queue_attrs = sqs_client.get_queue_attributes( + QueueUrl=queue_url, AttributeNames=["ApproximateNumberOfMessages"] ) + snapshot.match("queue-attrs", queue_attrs) @pytest.mark.parametrize("raw_message_delivery", [True, False]) @pytest.mark.aws_validated @@ -1578,19 +1759,14 @@ def test_redrive_policy_sqs_queue_subscription( raw_message_delivery, snapshot, ): - snapshot.add_transformer(snapshot.transform.sqs_api()) - # Need to skip the MD5OfBody/Signature, because it contains a timestamp + # the hash isn't the same because of the Binary attributes (maybe decoding order?) snapshot.add_transformer( snapshot.transform.key_value( - "Signature", - "", + "MD5OfMessageAttributes", + value_replacement="", reference_replacement=False, ) ) - snapshot.add_transformer( - snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) - ) - topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -1637,23 +1813,7 @@ def test_redrive_policy_sqs_queue_subscription( sns_client.publish(TopicArn=topic_arn, Message=message, MessageAttributes=message_attr) response = sqs_client.receive_message(QueueUrl=dlq_url, WaitTimeSeconds=10) - assert ( - len(response["Messages"]) == 1 - ), f"invalid number of messages in DLQ response {response}" - - if raw_message_delivery: - assert response["Messages"][0]["Body"] == message - # MessageAttributes are lost with RawDelivery in AWS - assert "MessageAttributes" not in response["Messages"][0] - snapshot.match("raw_message_delivery", response) - else: - received_message = json.loads(response["Messages"][0]["Body"]) - assert received_message["Type"] == "Notification" - assert received_message["Message"] == message - - # Set the decoded JSON Body to be able to skip keys directly - response["Messages"][0]["Body"] = received_message - snapshot.match("json_encoded_delivery", response) + snapshot.match("messages", response) @pytest.mark.aws_validated def test_message_attributes_not_missing( @@ -1663,13 +1823,20 @@ def test_message_attributes_not_missing( sns_create_sqs_subscription, sns_create_topic, sqs_create_queue, + snapshot, ): - + # the hash isn't the same because of the Binary attributes (maybe decoding order?) + snapshot.add_transformer( + snapshot.transform.key_value( + "MD5OfMessageAttributes", + value_replacement="", + reference_replacement=False, + ) + ) topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() subscription = sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url) - assert subscription["SubscriptionArn"] sns_client.set_subscription_attributes( SubscriptionArn=subscription["SubscriptionArn"], @@ -1686,7 +1853,8 @@ def test_message_attributes_not_missing( Message="text", MessageAttributes=attributes, ) - assert publish_response["MessageId"] + snapshot.match("publish-msg-raw", publish_response) + msg = sqs_client.receive_message( QueueUrl=queue_url, AttributeNames=["All"], @@ -1695,7 +1863,8 @@ def test_message_attributes_not_missing( ) # as SNS piggybacks on SQS MessageAttributes when RawDelivery is true # BinaryValue depends on SQS implementation, and is decoded automatically - assert msg["Messages"][0]["MessageAttributes"] == attributes + snapshot.match("raw-delivery-msg-attrs", msg) + sqs_client.delete_message( QueueUrl=queue_url, ReceiptHandle=msg["Messages"][0]["ReceiptHandle"] ) @@ -1711,23 +1880,17 @@ def test_message_attributes_not_missing( Message="text", MessageAttributes=attributes, ) - assert publish_response["MessageId"] + snapshot.match("publish-msg-json", publish_response) + msg = sqs_client.receive_message( QueueUrl=queue_url, AttributeNames=["All"], MessageAttributeNames=["All"], WaitTimeSeconds=3, ) - assert json.loads(msg["Messages"][0]["Body"])["MessageAttributes"] == { - "an-attribute-key": {"Type": "String", "Value": "an-attribute-value"}, - "binary-attribute": { - # binary payload in base64 encoded by AWS, UTF-8 for JSON - # https://docs.aws.amazon.com/sns/latest/api/API_MessageAttributeValue.html - # need to be decoded manually as it's part of the message Body - "Type": "Binary", - "Value": b64encode(b"\x02\x03\x04").decode("utf-8"), - }, - } + snapshot.match("json-delivery-msg-attrs", msg) + # binary payload in base64 encoded by AWS, UTF-8 for JSON + # https://docs.aws.amazon.com/sns/latest/api/API_MessageAttributeValue.html @pytest.mark.only_localstack @pytest.mark.aws_validated @@ -1909,7 +2072,7 @@ def test_dlq_external_http_endpoint( assert "Messages" not in response @pytest.mark.aws_validated - def test_publish_too_long_message(self, sns_client, sns_create_topic): + def test_publish_too_long_message(self, sns_client, sns_create_topic, snapshot): topic_arn = sns_create_topic()["TopicArn"] # simulate payload over 256kb message = "This is a test message" * 12000 @@ -1917,6 +2080,8 @@ def test_publish_too_long_message(self, sns_client, sns_create_topic): with pytest.raises(ClientError) as e: sns_client.publish(TopicArn=topic_arn, Message=message) + snapshot.match("error", e.value.response) + assert e.value.response["Error"]["Code"] == "InvalidParameter" assert e.value.response["Error"]["Message"] == "Invalid parameter: Message too long" assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 diff --git a/tests/integration/test_sns.snapshot.json b/tests/integration/test_sns.snapshot.json index c0d0bdcbd641f..db2ec6a9a3f63 100644 --- a/tests/integration/test_sns.snapshot.json +++ b/tests/integration/test_sns.snapshot.json @@ -1,14 +1,70 @@ { - "tests/integration/test_sns.py::TestSNSProvider::test_dead_letter_queue_with_deleted_sqs_queue[True]": { - "recorded-date": "29-06-2022, 16:43:14", + "tests/integration/test_sns.py::TestSNSProvider::test_tags": { + "recorded-date": "09-08-2022, 11:30:16", + "recorded-content": { + "duplicate-key-error": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Duplicated keys are not allowed.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "list-created-tags": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Tags": [ + { + "Key": "k1", + "Value": "v1" + }, + { + "Key": "k2", + "Value": "v2" + } + ] + }, + "list-after-delete-tags": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Tags": [ + { + "Key": "k2", + "Value": "v2" + } + ] + }, + "list-after-update-tags": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Tags": [ + { + "Key": "k2", + "Value": "v2b" + } + ] + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_sqs_queue_subscription[True]": { + "recorded-date": "09-08-2022, 11:36:01", "recorded-content": { - "raw_message_delivery": { + "messages": { "Messages": [ { + "Body": "test_dlq_after_sqs_endpoint_deleted", + "MD5OfBody": "", "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "2040fa62e6ff90d21d5fe319d5e65443", - "Body": "test_dlq_after_sqs_endpoint_deleted" + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -18,25 +74,22 @@ } } }, - "tests/integration/test_sns.py::TestSNSProvider::test_dead_letter_queue_with_deleted_sqs_queue[False]": { - "recorded-date": "29-06-2022, 16:43:18", + "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_sqs_queue_subscription[False]": { + "recorded-date": "09-08-2022, 11:36:04", "recorded-content": { - "json_encoded_delivery": { + "messages": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", + "MessageId": "", "TopicArn": "arn:aws:sns::111111111111:", "Message": "test_dlq_after_sqs_endpoint_deleted", "Timestamp": "date", "SignatureVersion": "1", "Signature": "", - "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", - "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", "MessageAttributes": { "attr2": { "Type": "Binary", @@ -47,7 +100,10 @@ "Value": "111" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -57,41 +113,171 @@ } } }, - "tests/integration/test_sns.py::TestSNSProvider::test_tags": { - "recorded-date": "01-08-2022, 17:10:09", + "tests/integration/test_sns.py::TestSNSProvider::test_publish_unicode_chars": { + "recorded-date": "09-08-2022, 11:30:03", "recorded-content": { - "duplicate-key-error": { - "Error": { - "Type": "Sender", - "Code": "InvalidParameter", - "Message": "Invalid parameter: Duplicated keys are not allowed." + "received-message": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "\u00f6\u00a7a1\"_!?,. \u00a3$-", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSSubscription::test_python_lambda_subscribe_sns_topic": { + "recorded-date": "09-08-2022, 11:30:00", + "recorded-content": { + "notification": { + "Message": "Hello world.", + "MessageAttributes": {}, + "MessageId": "", + "Signature": "", + "SignatureVersion": "1", + "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-", + "Subject": "[Subject] Test subject", + "Timestamp": "date", + "TopicArn": "arn:aws:sns::111111111111:", + "Type": "Notification", + "UnsubscribeUrl": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_attribute_raw_subscribe": { + "recorded-date": "09-08-2022, 11:30:06", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, - "HTTPStatusCode": 400 + "HTTPStatusCode": 200 } }, - "list-created-tags": { - "Tags": [ - { - "Key": "k1", - "Value": "v1" - }, + "messages-response": { + "Messages": [ { - "Key": "k2", - "Value": "v2" + "Body": "This is a test message", + "MD5OfBody": "", + "MD5OfMessageAttributes": "", + "MessageAttributes": { + "store": { + "BinaryValue": "b'\\x02\\x03\\x04'", + "DataType": "Binary" + } + }, + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_subscribe_with_invalid_protocol": { + "recorded-date": "09-08-2022, 11:30:03", + "recorded-content": { + "exception": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Amazon SNS does not support this protocol string: test-protocol", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_filter_policy": { + "recorded-date": "09-08-2022, 11:30:09", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "FilterPolicy": { + "attr1": [ + { + "numeric": [ + ">", + 0, + "<=", + 100 + ] + } + ] + }, + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } }, - "list-after-delete-tags": { - "Tags": [ + "messages-0": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-1": { + "Messages": [ { - "Key": "k2", - "Value": "v2" + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "This is a test message", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "99" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -99,11 +285,29 @@ "HTTPStatusCode": 200 } }, - "list-after-update-tags": { - "Tags": [ + "messages-2": { + "Messages": [ { - "Key": "k2", - "Value": "v2b" + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "This is a test message", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "99" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -113,15 +317,64 @@ } } }, - "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_sqs_queue_subscription[True]": { - "recorded-date": "08-08-2022, 17:49:05", + "tests/integration/test_sns.py::TestSNSProvider::test_exists_filter_policy": { + "recorded-date": "09-08-2022, 11:30:12", "recorded-content": { - "raw_message_delivery": { + "subscription-attributes-policy-1": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "FilterPolicy": { + "store": [ + { + "exists": true + } + ] + }, + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-0": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-1": { "Messages": [ { - "Body": "test_dlq_after_sqs_endpoint_deleted", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-1", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + }, + "store": { + "Type": "Number", + "Value": "99" + } + } + }, "MD5OfBody": "", - "MessageId": "", + "MessageId": "", "ReceiptHandle": "" } ], @@ -129,46 +382,1291 @@ "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } - }, - "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_sqs_queue_subscription[False]": { - "recorded-date": "08-08-2022, 17:49:08", - "recorded-content": { - "json_encoded_delivery": { + }, + "messages-2": { "Messages": [ { "Body": { - "Message": "test_dlq_after_sqs_endpoint_deleted", + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-1", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", "MessageAttributes": { - "attr1": { + "def": { "Type": "Number", - "Value": "111" + "Value": "99" }, - "attr2": { - "Type": "Binary", - "Value": "AgME" + "store": { + "Type": "Number", + "Value": "99" } - }, - "MessageId": "", - "Signature": "", - "SignatureVersion": "1", - "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", - "Timestamp": "date", - "TopicArn": "arn:aws:sns::111111111111:", - "Type": "Notification", - "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + } }, "MD5OfBody": "", "MessageId": "", - "ReceiptHandle": "" + "ReceiptHandle": "" } ], "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 } - } - } + }, + "subscription-attributes-policy-2": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "FilterPolicy": { + "store": [ + { + "exists": false + } + ] + }, + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-3": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-3", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-4": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-3", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_subscribe_sqs_queue": { + "recorded-date": "09-08-2022, 11:30:15", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "FilterPolicy": { + "attr1": [ + { + "numeric": [ + ">", + 0, + "<=", + 100 + ] + } + ] + }, + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "This is a test message", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "99.12" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_unknown_topic_publish": { + "recorded-date": "09-08-2022, 11:30:15", + "recorded-content": { + "error": { + "Error": { + "Code": "NotFound", + "Message": "Topic does not exist", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 404 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_sqs_topic_subscription_confirmation": { + "recorded-date": "09-08-2022, 11:30:18", + "recorded-content": { + "subscription-attrs": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_sns_topic_as_lambda_dead_letter_queue": { + "recorded-date": "09-08-2022, 11:34:43", + "recorded-content": { + "lambda-response-dlq-config": { + "TargetArn": "arn:aws:sns::111111111111:" + }, + "messages": { + "Messages": [ + { + "Body": { + "Message": { + "Records": [ + { + "EventSource": "aws:sns", + "EventSubscriptionArn": "arn:aws:sns::111111111111::", + "EventVersion": "1.0", + "Sns": { + "Message": { + "raise_error": 1 + }, + "MessageAttributes": {}, + "MessageId": "", + "Signature": "", + "SignatureVersion": "1", + "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-", + "Subject": null, + "Timestamp": "date", + "TopicArn": "arn:aws:sns::111111111111:", + "Type": "Notification", + "UnsubscribeUrl": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + } + } + ] + }, + "MessageAttributes": { + "ErrorCode": { + "Type": "Number", + "Value": "200" + }, + "ErrorMessage": { + "Type": "String", + "Value": "Test exception (this is intentional)" + }, + "RequestID": { + "Type": "String", + "Value": "" + } + }, + "MessageId": "", + "Signature": "", + "SignatureVersion": "1", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "Timestamp": "date", + "TopicArn": "arn:aws:sns::111111111111:", + "Type": "Notification", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_lambda_subscription": { + "recorded-date": "09-08-2022, 11:34:55", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:lambda::111111111111:function:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "lambda", + "RawMessageDelivery": "false", + "RedrivePolicy": { + "deadLetterTargetArn": "arn:aws:sqs::111111111111:" + }, + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "test_redrive_policy", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_with_empty_subject": { + "recorded-date": "09-08-2022, 11:34:56", + "recorded-content": { + "response-without-subject": { + "MessageId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "response-with-empty-subject": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Subject", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_create_topic_test_arn": { + "recorded-date": "09-08-2022, 11:34:57", + "recorded-content": { + "create-topic": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_message_by_target_arn": { + "recorded-date": "09-08-2022, 11:34:59", + "recorded-content": { + "receive-topic-arn": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "test-msg-1", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "receive-target-arn": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "test-msg-2", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_message_before_subscribe_topic": { + "recorded-date": "09-08-2022, 11:35:08", + "recorded-content": { + "publish-before-subscribing": { + "MessageId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "publish-after-subscribing": { + "MessageId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "receive-messages": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Subject": "test-subject-after-sub", + "Message": "test_message_after", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_create_duplicate_topic_with_more_tags": { + "recorded-date": "09-08-2022, 11:35:08", + "recorded-content": { + "exception-duplicate": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Tags Reason: Topic already exists with different tags", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_create_duplicate_topic_check_idempotency": { + "recorded-date": "09-08-2022, 11:35:09", + "recorded-content": { + "response-created": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + }, + "response-same-arn-0": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + }, + "response-same-arn-1": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + }, + "response-same-arn-2": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_by_path_parameters": { + "recorded-date": "09-08-2022, 11:35:12", + "recorded-content": { + "post-request": { + "PublishResponse": { + "PublishResult": { + "MessageId": "" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + }, + "messages": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "test message direct post request", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_sqs_from_sns": { + "recorded-date": "09-08-2022, 11:35:22", + "recorded-content": { + "sub-attrs-raw-true": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "message-raw-true": { + "Messages": [ + { + "Body": "Test msg", + "MD5OfBody": "", + "MD5OfMessageAttributes": "d302007e9aa062660afc85fd0a482472", + "MessageAttributes": { + "attr1": { + "DataType": "Number", + "StringValue": "99.12" + } + }, + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "sub-attrs-raw-false": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "message-raw-false": { + "Messages": [ + { + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "Test msg", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "100.12" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_batch_messages_from_sns_to_sqs": { + "recorded-date": "09-08-2022, 11:36:59", + "recorded-content": { + "sub-attrs-raw-true": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "publish-batch": { + "Failed": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Successful": [ + { + "Id": "1", + "MessageId": "" + }, + { + "Id": "2", + "MessageId": "" + }, + { + "Id": "3", + "MessageId": "" + }, + { + "Id": "4", + "MessageId": "" + } + ] + }, + "messages": { + "Messages": [ + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "SenderId": "", + "SentTimestamp": "timestamp" + }, + "Body": "Test Message with one attribute", + "MD5OfBody": "", + "MD5OfMessageAttributes": "1f07082409022a373fa2a2601f82b3cb", + "MessageAttributes": { + "attr1": { + "DataType": "Number", + "StringValue": "19.12" + } + }, + "MessageId": "", + "ReceiptHandle": "" + }, + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "SenderId": "", + "SentTimestamp": "timestamp" + }, + "Body": "Test Message with two attributes", + "MD5OfBody": "", + "MD5OfMessageAttributes": "9b720b3af309c1c4b0d395c53b08c502", + "MessageAttributes": { + "attr1": { + "DataType": "Number", + "StringValue": "99.12" + }, + "attr2": { + "DataType": "Number", + "StringValue": "109.12" + } + }, + "MessageId": "", + "ReceiptHandle": "" + }, + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "SenderId": "", + "SentTimestamp": "timestamp" + }, + "Body": "Test Message without attribute", + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + }, + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "SenderId": "", + "SentTimestamp": "timestamp" + }, + "Body": "Test Message without subject", + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ] + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_batch_messages_from_fifo_topic_to_fifo_queue": { + "recorded-date": "09-08-2022, 11:35:46", + "recorded-content": { + "topic-attrs": { + "Attributes": { + "ContentBasedDeduplication": "true", + "DisplayName": "", + "EffectiveDeliveryPolicy": { + "http": { + "defaultHealthyRetryPolicy": { + "minDelayTarget": 20, + "maxDelayTarget": 20, + "numRetries": 3, + "numMaxDelayRetries": 0, + "numNoDelayRetries": 0, + "numMinDelayRetries": 0, + "backoffFunction": "linear" + }, + "disableSubscriptionOverrides": false + } + }, + "FifoTopic": "true", + "Owner": "111111111111", + "Policy": { + "Version": "2008-10-17", + "Id": "__default_policy_ID", + "Statement": [ + { + "Sid": "__default_statement_ID", + "Effect": "Allow", + "Principal": { + "AWS": "*" + }, + "Action": [ + "SNS:GetTopicAttributes", + "SNS:SetTopicAttributes", + "SNS:AddPermission", + "SNS:RemovePermission", + "SNS:DeleteTopic", + "SNS:Subscribe", + "SNS:ListSubscriptionsByTopic", + "SNS:Publish" + ], + "Resource": "arn:aws:sns::111111111111:", + "Condition": { + "StringEquals": { + "AWS:SourceOwner": "111111111111" + } + } + } + ] + }, + "SubscriptionsConfirmed": "0", + "SubscriptionsDeleted": "0", + "SubscriptionsPending": "0", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "sub-attrs-raw-true": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "publish-batch-response-fifo": { + "Failed": [], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Successful": [ + { + "Id": "1", + "MessageId": "", + "SequenceNumber": "" + }, + { + "Id": "2", + "MessageId": "", + "SequenceNumber": "" + }, + { + "Id": "3", + "MessageId": "", + "SequenceNumber": "" + } + ] + }, + "messages": { + "Messages": [ + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "MessageDeduplicationId": "47f6d7e2c477469ad670ae3bfee60ed3e79080bb0105fc01a2805a50f8b21358", + "MessageGroupId": "complexMessageGroupId", + "SenderId": "", + "SentTimestamp": "timestamp", + "SequenceNumber": "" + }, + "Body": "Test Message with two attributes", + "MD5OfBody": "", + "MD5OfMessageAttributes": "9b720b3af309c1c4b0d395c53b08c502", + "MessageAttributes": { + "attr1": { + "DataType": "Number", + "StringValue": "99.12" + }, + "attr2": { + "DataType": "Number", + "StringValue": "109.12" + } + }, + "MessageId": "", + "ReceiptHandle": "" + }, + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "MessageDeduplicationId": "2186166772ec2fb49b7ba9efbda53c6eabf8e785cb00420db531c8eb9287d8e1", + "MessageGroupId": "complexMessageGroupId", + "SenderId": "", + "SentTimestamp": "timestamp", + "SequenceNumber": "" + }, + "Body": "Test Message with one attribute", + "MD5OfBody": "", + "MD5OfMessageAttributes": "1f07082409022a373fa2a2601f82b3cb", + "MessageAttributes": { + "attr1": { + "DataType": "Number", + "StringValue": "19.12" + } + }, + "MessageId": "", + "ReceiptHandle": "" + }, + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "MessageDeduplicationId": "a579d4ebff398c5ad4d5037534a1c1bfab36410100c80d8c7c1029b57c7898fe", + "MessageGroupId": "complexMessageGroupId", + "SenderId": "", + "SentTimestamp": "timestamp", + "SequenceNumber": "" + }, + "Body": "Test Message without attribute", + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ] + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_batch_exceptions": { + "recorded-date": "09-08-2022, 11:35:46", + "recorded-content": { + "no-group-id": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: The MessageGroupId parameter is required for FIFO topics", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "too-many-msg": { + "Error": { + "Code": "TooManyEntriesInBatchRequest", + "Message": "The batch request contains more entries than permissible.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "same-msg-id": { + "Error": { + "Code": "BatchEntryIdsNotDistinct", + "Message": "Two or more batch entries in the request have the same Id.", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "no-dedup-id": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: The topic should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_create_topic_after_delete_with_new_tags": { + "recorded-date": "09-08-2022, 11:35:47", + "recorded-content": { + "topic-0": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + }, + "topic-1": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "TopicArn": "arn:aws:sns::111111111111:" + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_not_found_error_on_set_subscription_attributes": { + "recorded-date": "09-08-2022, 11:35:51", + "recorded-content": { + "sub": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "sub-attrs": { + "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "subscriptions-for-topic-before-unsub": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Subscriptions": [ + { + "Endpoint": "arn:aws:sqs::111111111111:", + "Owner": "111111111111", + "Protocol": "sqs", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" + } + ] + }, + "sub-not-found": { + "Error": { + "Code": "NotFound", + "Message": "Subscription does not exist", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 404 + } + }, + "subscriptions-for-topic-after-unsub": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, + "Subscriptions": [] + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_message_to_fifo_sqs": { + "recorded-date": "09-08-2022, 11:35:53", + "recorded-content": { + "messages": { + "Messages": [ + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "MessageDeduplicationId": "", + "MessageGroupId": "message-group-id-1", + "SenderId": "", + "SentTimestamp": "timestamp", + "SequenceNumber": "" + }, + "Body": { + "Type": "Notification", + "MessageId": "", + "SequenceNumber": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "Test", + "Timestamp": "date", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_validations_for_fifo": { + "recorded-date": "09-08-2022, 11:35:56", + "recorded-content": { + "not-fifo-topic": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Invalid parameter: Endpoint Reason: FIFO SQS Queues can not be subscribed to standard SNS topics", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "no-msg-group-id": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: The MessageGroupId parameter is required for FIFO topics", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "no-dedup-policy": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: The topic should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "no-msg-dedup-regular-topic": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: MessageDeduplicationId Reason: The request includes MessageDeduplicationId parameter that is not valid for this topic type", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "no-msg-group-id-regular-topic": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: MessageGroupId Reason: The request includes MessageGroupId parameter that is not valid for this topic type", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_empty_sns_message": { + "recorded-date": "09-08-2022, 11:35:58", + "recorded-content": { + "empty-msg-error": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Empty message", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + }, + "queue-attrs": { + "Attributes": { + "ApproximateNumberOfMessages": "0" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_message_attributes_not_missing": { + "recorded-date": "09-08-2022, 11:36:07", + "recorded-content": { + "publish-msg-raw": { + "MessageId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "raw-delivery-msg-attrs": { + "Messages": [ + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "SenderId": "", + "SentTimestamp": "timestamp" + }, + "Body": "text", + "MD5OfBody": "", + "MD5OfMessageAttributes": "", + "MessageAttributes": { + "an-attribute-key": { + "DataType": "String", + "StringValue": "an-attribute-value" + }, + "binary-attribute": { + "BinaryValue": "b'\\x02\\x03\\x04'", + "DataType": "Binary" + } + }, + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "publish-msg-json": { + "MessageId": "", + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "json-delivery-msg-attrs": { + "Messages": [ + { + "Attributes": { + "ApproximateFirstReceiveTimestamp": "timestamp", + "ApproximateReceiveCount": "1", + "SenderId": "", + "SentTimestamp": "timestamp" + }, + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "text", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "an-attribute-key": { + "Type": "String", + "Value": "an-attribute-value" + }, + "binary-attribute": { + "Type": "Binary", + "Value": "AgME" + } + } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_too_long_message": { + "recorded-date": "09-08-2022, 11:36:07", + "recorded-content": { + "error": { + "Error": { + "Code": "InvalidParameter", + "Message": "Invalid parameter: Message too long", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_sqs_from_sns_with_xray_propagation": { + "recorded-date": "09-08-2022, 11:35:46", + "recorded-content": {} } }