From 3318c00571b937f4b227565ac2fc58183a35c1a5 Mon Sep 17 00:00:00 2001 From: Benjamin Simon Date: Tue, 2 Aug 2022 16:05:48 +0200 Subject: [PATCH 1/5] started snapshot tests for SNS --- .../testing/snapshots/transformer_utility.py | 44 +- tests/integration/test_sns.py | 312 ++++++--- tests/integration/test_sns.snapshot.json | 600 ++++++++++++++++++ 3 files changed, 853 insertions(+), 103 deletions(-) diff --git a/localstack/testing/snapshots/transformer_utility.py b/localstack/testing/snapshots/transformer_utility.py index 77d8892347f59..d661478ad0b93 100644 --- a/localstack/testing/snapshots/transformer_utility.py +++ b/localstack/testing/snapshots/transformer_utility.py @@ -176,17 +176,41 @@ 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( + "Signature", value_replacement="", reference_replacement=False + ), + # maybe need check why hashes are different + TransformerUtility.key_value( + "MD5OfMessageAttributes", + value_replacement="", + reference_replacement=False, + ), + KeyValueBasedTransformer(_sns_unsubscribe_url_token_transformer, replacement="token"), KeyValueBasedTransformer( _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="", - ), + ) + # TransformerUtility.regex( + # re.compile( + # r"(?<=(?i)UnsubscribeURL[\"|']:\s[\"|'])(https?.*?)(?=/\?Action=Unsubscribe)" + # ), + # replacement="", + # ), ] @staticmethod @@ -220,15 +244,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": + 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_token_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/test_sns.py b/tests/integration/test_sns.py index f075f5d5f64a4..a857cdc75508f 100644 --- a/tests/integration/test_sns.py +++ b/tests/integration/test_sns.py @@ -36,24 +36,30 @@ PUBLICATION_RETRIES = 4 +@pytest.fixture(autouse=True) +def sns_snapshot_transformer(snapshot): + snapshot.add_transformer(snapshot.transform.sqs_api()) + 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 +103,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 +115,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 +128,18 @@ 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 + # msg_received = response["Messages"][0] + # msg_received = json.loads(to_str(msg_received["Body"])) + # msg_received = msg_received["Message"] + # decoding the json in place to be able to match it + # assert message == msg_received + response["Messages"][0]["Body"] = json.loads(to_str(response["Messages"][0]["Body"])) + 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 +147,17 @@ def test_subscribe_with_invalid_protocol(self, sns_client, sns_create_topic, sns TopicArn=topic_arn, Protocol="test-protocol", Endpoint="localstack@yopmail.com" ) + snapshot.match("exception", e.value.response) assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 assert e.value.response["Error"]["Code"] == "InvalidParameter" - @pytest.mark.aws_validated + @pytest.mark.aws_validated # snapshot ok + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + ] + ) def test_attribute_raw_subscribe( self, sqs_client, @@ -147,6 +165,7 @@ def test_attribute_raw_subscribe( sns_create_topic, sqs_create_queue, sns_create_sqs_subscription, + snapshot, ): topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -159,12 +178,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 +200,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.aws_validated # snapshot ok + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + "$..Attributes.RawMessageDelivery", # todo: fix me (not add to response if false) + ] + ) def test_filter_policy( self, sns_client, @@ -196,7 +217,12 @@ def test_filter_policy( sqs_create_queue, sns_create_topic, sns_create_sqs_subscription, + snapshot, ): + snapshot.add_transformer( + snapshot.transform.key_value("MD5OfBody", "", 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) @@ -209,10 +235,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 +256,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 +272,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.aws_validated # snapshot ok + @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 +295,12 @@ def test_exists_filter_policy( sqs_create_queue, sns_create_topic, sns_create_sqs_subscription, + snapshot, ): + snapshot.add_transformer( + snapshot.transform.key_value("MD5OfBody", "", 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) @@ -265,13 +313,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 +333,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 +372,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 +407,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.aws_validated # snapshot ok + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Attributes.Owner", + "$..Attributes.ConfirmationWasAuthenticated", + "$..Attributes.RawMessageDelivery", # todo: fix me (not added to response if false) + ] + ) def test_subscribe_sqs_queue( self, sns_client, @@ -367,7 +429,11 @@ def test_subscribe_sqs_queue( sqs_create_queue, sns_create_topic, sns_create_sqs_subscription, + snapshot, ): + snapshot.add_transformer( + snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) + ) # TODO: check with non default external port topic_arn = sns_create_topic()["TopicArn"] @@ -382,6 +448,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 +468,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( @@ -443,8 +512,8 @@ def check_message(): sns_client.delete_endpoint(EndpointArn=platform_arn) sns_client.delete_platform_application(PlatformApplicationArn=app_arn) - @pytest.mark.aws_validated - def test_unknown_topic_publish(self, sns_client, sns_create_topic): + @pytest.mark.aws_validated # snapshot ok + 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 +524,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 +532,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", @@ -474,7 +543,7 @@ def test_publish_non_existent_target(self, sns_client): assert ex.value.response["Error"]["Code"] == "InvalidClientTokenId" - @pytest.mark.aws_validated + @pytest.mark.aws_validated # snapshot ok def test_tags(self, sns_client, sns_create_topic, snapshot): topic_arn = sns_create_topic()["TopicArn"] @@ -532,9 +601,16 @@ def check_subscription(): retry(check_subscription, retries=PUBLICATION_RETRIES, sleep=PUBLICATION_TIMEOUT) - @pytest.mark.aws_validated + @pytest.mark.aws_validated # snapshot ok + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Owner", + "$..ConfirmationWasAuthenticated", + "$..RawMessageDelivery", # todo: fix me (not added to response if false) + ] + ) 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 +623,23 @@ 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.aws_validated # snapshot ok + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Messages..Body.Message.raise_error", # todo check lambda delivery format into DLQ + "$..Messages..Body.Message.Records", + "$..Messages..MessageAttributes.ErrorCode.Type", + "$..Messages..MessageAttributes.ErrorMessage.Value", + ] + ) def test_sns_topic_as_lambda_dead_letter_queue( self, sns_client, @@ -564,7 +651,20 @@ def test_sns_topic_as_lambda_dead_letter_queue( sqs_create_queue, sns_subscription, sns_create_sqs_subscription, + snapshot, ): + snapshot.add_transformer(snapshot.transform.lambda_api()) + snapshot.add_transformer( + [ + snapshot.transform.key_value( + "MD5OfBody", "", reference_replacement=False + ), + 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 +683,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 +716,24 @@ 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) + + 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( @@ -678,6 +789,13 @@ def test_redrive_policy_http_subscription( assert json.loads(message["Message"])["message"] == "test_redrive_policy" @pytest.mark.aws_validated + @pytest.mark.skip_snapshot_verify( + paths=[ + "$..Owner", + "$..ConfirmationWasAuthenticated", + "$..RawMessageDelivery", # todo: fix me (not added to response if false) + ] + ) def test_redrive_policy_lambda_subscription( self, sns_client, @@ -690,7 +808,11 @@ def test_redrive_policy_lambda_subscription( sqs_client, sns_subscription, sns_allow_topic_sqs_queue, + snapshot, ): + snapshot.add_transformer( + snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) + ) dlq_url = sqs_create_queue() dlq_arn = sqs_queue_arn(dlq_url) topic_arn = sns_create_topic()["TopicArn"] @@ -714,14 +836,22 @@ def test_redrive_policy_lambda_subscription( AttributeName="RedrivePolicy", AttributeValue=json.dumps({"deadLetterTargetArn": dlq_arn}), ) + response_attributes = sns_client.get_subscription_attributes( + SubscriptionArn=subscription["SubscriptionArn"] + ) + # todo: hacky fix for snapshot resource replacement + + 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}" @@ -1364,6 +1494,10 @@ def test_publish_batch_exceptions( assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 # todo add test and implement behaviour for ContentBasedDeduplication or MessageDeduplicationId + # todo add test and implement behaviour for: + # Invalid parameter: The topic should either have ContentBasedDeduplication enabled + # or MessageDeduplicationId provided explicitly + def add_xray_header(self, request, **kwargs): request.headers[ "X-Amzn-Trace-Id" @@ -1578,15 +1712,7 @@ 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 - snapshot.add_transformer( - snapshot.transform.key_value( - "Signature", - "", - reference_replacement=False, - ) - ) + # Need to skip the MD5OfBody, because it contains a timestamp if raw_message_delivery is False snapshot.add_transformer( snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) ) diff --git a/tests/integration/test_sns.snapshot.json b/tests/integration/test_sns.snapshot.json index c0d0bdcbd641f..5fc30164ee3ce 100644 --- a/tests/integration/test_sns.snapshot.json +++ b/tests/integration/test_sns.snapshot.json @@ -170,5 +170,605 @@ } } } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_publish_unicode_chars": { + "recorded-date": "04-08-2022, 11:31:08", + "recorded-content": { + "received-message": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "84108d8422241e81df0d97db369175ed", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "\u00f6\u00a7a1\"_!?,. \u00a3$-", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSSubscription::test_python_lambda_subscribe_sns_topic": { + "recorded-date": "04-08-2022, 11:32:56", + "recorded-content": { + "notification": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Subject": "[Subject] Test subject", + "Message": "Hello world.", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeUrl": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": {} + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_attribute_raw_subscribe": { + "recorded-date": "04-08-2022, 13:53:03", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "Owner": "111111111111", + "RawMessageDelivery": "true", + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:sqs::111111111111:", + "Protocol": "sqs", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-response": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "fafb00f5732ab283681e124bf8747ed1", + "Body": "This is a test message", + "MD5OfMessageAttributes": "", + "MessageAttributes": { + "store": { + "BinaryValue": "b'\\x02\\x03\\x04'", + "DataType": "Binary" + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_subscribe_with_invalid_protocol": { + "recorded-date": "04-08-2022, 14:02:25", + "recorded-content": { + "exception": { + "Error": { + "Type": "Sender", + "Code": "InvalidParameter", + "Message": "Invalid parameter: Amazon SNS does not support this protocol string: test-protocol" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_filter_policy": { + "recorded-date": "04-08-2022, 14:18:18", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "Owner": "111111111111", + "RawMessageDelivery": "false", + "FilterPolicy": { + "attr1": [ + { + "numeric": [ + ">", + 0, + "<=", + 100 + ] + } + ] + }, + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:sqs::111111111111:", + "Protocol": "sqs", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-0": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-1": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "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-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "99" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-2": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "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-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "99" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_exists_filter_policy": { + "recorded-date": "04-08-2022, 14:53:10", + "recorded-content": { + "subscription-attributes-policy-1": { + "Attributes": { + "Owner": "111111111111", + "RawMessageDelivery": "false", + "FilterPolicy": { + "store": [ + { + "exists": true + } + ] + }, + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:sqs::111111111111:", + "Protocol": "sqs", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-0": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-1": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-1", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + }, + "store": { + "Type": "Number", + "Value": "99" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-2": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-1", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + }, + "store": { + "Type": "Number", + "Value": "99" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "subscription-attributes-policy-2": { + "Attributes": { + "Owner": "111111111111", + "RawMessageDelivery": "false", + "FilterPolicy": { + "store": [ + { + "exists": false + } + ] + }, + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:sqs::111111111111:", + "Protocol": "sqs", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-3": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-3", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages-4": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "message-3", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "def": { + "Type": "Number", + "Value": "99" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_subscribe_sqs_queue": { + "recorded-date": "04-08-2022, 15:04:42", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "Owner": "111111111111", + "RawMessageDelivery": "false", + "FilterPolicy": { + "attr1": [ + { + "numeric": [ + ">", + 0, + "<=", + 100 + ] + } + ] + }, + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:sqs::111111111111:", + "Protocol": "sqs", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "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-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "attr1": { + "Type": "Number", + "Value": "99.12" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_unknown_topic_publish": { + "recorded-date": "04-08-2022, 15:07:58", + "recorded-content": { + "error": { + "Error": { + "Type": "Sender", + "Code": "NotFound", + "Message": "Topic does not exist" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 404 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_sqs_topic_subscription_confirmation": { + "recorded-date": "04-08-2022, 15:37:45", + "recorded-content": { + "subscription-attrs": { + "Owner": "111111111111", + "RawMessageDelivery": "false", + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:sqs::111111111111:", + "Protocol": "sqs", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_sns_topic_as_lambda_dead_letter_queue": { + "recorded-date": "04-08-2022, 16:15:53", + "recorded-content": { + "lambda-response-dlq-config": { + "TargetArn": "arn:aws:sns::111111111111:" + }, + "messages": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": { + "Records": [ + { + "EventSource": "aws:sns", + "EventVersion": "1.0", + "EventSubscriptionArn": "arn:aws:sns::111111111111::", + "Sns": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Subject": null, + "Message": { + "raise_error": 1 + }, + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeUrl": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": {} + } + } + ] + }, + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", + "MessageAttributes": { + "RequestID": { + "Type": "String", + "Value": "" + }, + "ErrorCode": { + "Type": "Number", + "Value": "200" + }, + "ErrorMessage": { + "Type": "String", + "Value": "Test exception (this is intentional)" + } + } + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_lambda_subscription": { + "recorded-date": "04-08-2022, 16:22:25", + "recorded-content": { + "subscription-attributes": { + "Attributes": { + "Owner": "111111111111", + "RawMessageDelivery": "false", + "TopicArn": "arn:aws:sns::111111111111:", + "Endpoint": "arn:aws:lambda::111111111111:function:", + "RedrivePolicy": { + "deadLetterTargetArn": "arn:aws:sqs::111111111111:" + }, + "Protocol": "lambda", + "PendingConfirmation": "false", + "ConfirmationWasAuthenticated": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "messages": { + "Messages": [ + { + "MessageId": "", + "ReceiptHandle": "", + "MD5OfBody": "", + "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", + "Message": "test_redrive_policy", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + } + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } From 41d4935c8736a4834c334b01f98e2f3c4d2fb2b6 Mon Sep 17 00:00:00 2001 From: Benjamin Simon Date: Thu, 4 Aug 2022 17:07:39 +0200 Subject: [PATCH 2/5] fix change --- tests/integration/test_sns.py | 12 +++++++++--- tests/integration/test_sns.snapshot.json | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/integration/test_sns.py b/tests/integration/test_sns.py index a857cdc75508f..3d353b6f0a12f 100644 --- a/tests/integration/test_sns.py +++ b/tests/integration/test_sns.py @@ -42,6 +42,13 @@ def sns_snapshot_transformer(snapshot): snapshot.add_transformer(snapshot.transform.sns_api()) +def _order_dict(response: dict) -> dict: + return { + key: _order_dict(val) if isinstance(val, dict) else val + for key, val in sorted(response.items(), key=itemgetter(0)) + } + + class TestSNSSubscription: @pytest.mark.aws_validated def test_python_lambda_subscribe_sns_topic( @@ -840,8 +847,7 @@ def test_redrive_policy_lambda_subscription( SubscriptionArn=subscription["SubscriptionArn"] ) # todo: hacky fix for snapshot resource replacement - - snapshot.match("subscription-attributes", response_attributes) + snapshot.match("subscription-attributes", _order_dict(response_attributes)) lambda_client.delete_function(FunctionName=lambda_name) @@ -857,7 +863,7 @@ def test_redrive_policy_lambda_subscription( ), 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): diff --git a/tests/integration/test_sns.snapshot.json b/tests/integration/test_sns.snapshot.json index 5fc30164ee3ce..0a486986649fa 100644 --- a/tests/integration/test_sns.snapshot.json +++ b/tests/integration/test_sns.snapshot.json @@ -724,21 +724,21 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_lambda_subscription": { - "recorded-date": "04-08-2022, 16:22:25", + "recorded-date": "04-08-2022, 17:05:46", "recorded-content": { "subscription-attributes": { "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:lambda::111111111111:function:", "Owner": "111111111111", + "PendingConfirmation": "false", + "Protocol": "lambda", "RawMessageDelivery": "false", - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:lambda::111111111111:function:", "RedrivePolicy": { - "deadLetterTargetArn": "arn:aws:sqs::111111111111:" + "deadLetterTargetArn": "arn:aws:sqs::111111111111:" }, - "Protocol": "lambda", - "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -754,13 +754,13 @@ "Body": { "Type": "Notification", "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "test_redrive_policy", "Timestamp": "date", "SignatureVersion": "1", "Signature": "", "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", - "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::" } } ], From 9fe27854af37724335cabb30ffcbc8245d51e7fa Mon Sep 17 00:00:00 2001 From: Benjamin Simon Date: Fri, 5 Aug 2022 17:34:00 +0200 Subject: [PATCH 3/5] add SNS transformer utility, move some SQS into SNS one --- .../testing/snapshots/transformer_utility.py | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/localstack/testing/snapshots/transformer_utility.py b/localstack/testing/snapshots/transformer_utility.py index d661478ad0b93..734a3e01c671e 100644 --- a/localstack/testing/snapshots/transformer_utility.py +++ b/localstack/testing/snapshots/transformer_utility.py @@ -173,7 +173,10 @@ def sqs_api(): """ return [ TransformerUtility.key_value("ReceiptHandle"), - TransformerUtility.key_value("SenderId"), + # account ID + TransformerUtility.key_value( + "SenderId", value_replacement="111111111111", reference_replacement=False + ), TransformerUtility.jsonpath("$..MessageAttributes.RequestID.StringValue", "request-id"), KeyValueBasedTransformer(_resource_name_transformer, "resource"), ] @@ -184,18 +187,15 @@ def sns_api(): :return: array with Transformers, for sns api. """ return [ + TransformerUtility.key_value("SequenceNumber"), # this might need to be in SQS TransformerUtility.key_value( "Signature", value_replacement="", reference_replacement=False ), - # maybe need check why hashes are different - TransformerUtility.key_value( - "MD5OfMessageAttributes", - value_replacement="", - reference_replacement=False, - ), + TransformerUtility.key_value("MD5OfBody", "", reference_replacement=False), KeyValueBasedTransformer(_sns_unsubscribe_url_token_transformer, replacement="token"), KeyValueBasedTransformer( - _sns_pem_file_token_transformer, replacement="signing-cert-file" + _sns_pem_file_token_transformer, + replacement="signing-cert-file", ), # 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 @@ -204,13 +204,7 @@ def sns_api(): RegexTransformer( r"(?<=(?i)UnsubscribeURL[\"|']:\s[\"|'])(https?.*?)(?=/\?Action=Unsubscribe)", replacement="", - ) - # TransformerUtility.regex( - # re.compile( - # r"(?<=(?i)UnsubscribeURL[\"|']:\s[\"|'])(https?.*?)(?=/\?Action=Unsubscribe)" - # ), - # replacement="", - # ), + ), ] @staticmethod @@ -245,7 +239,7 @@ 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.lower() == "SigningCertURL".lower(): - pattern = re.compile(r".*SimpleNotificationService-(.*)?\.pem") + pattern = re.compile(r".*SimpleNotificationService-(.*\.pem)") match = re.match(pattern, val) if match: return match.groups()[0] From 5f6f684629fb816e773130d70f94d48cd0b4e055 Mon Sep 17 00:00:00 2001 From: Benjamin Simon Date: Fri, 5 Aug 2022 17:34:24 +0200 Subject: [PATCH 4/5] fix SNS error messages parity --- localstack/services/sns/provider.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/localstack/services/sns/provider.py b/localstack/services/sns/provider.py index 57b100a285c90..a2511b0a15313 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") From 088352243678d9f261a59c2747ca159d11c61d01 Mon Sep 17 00:00:00 2001 From: Benjamin Simon Date: Fri, 5 Aug 2022 17:36:07 +0200 Subject: [PATCH 5/5] regenerate snapshots with ordering --- localstack/services/sns/provider.py | 1 + .../testing/snapshots/transformer_utility.py | 19 +- .../s3/test_s3_notifications_sns.py | 9 +- .../test_s3_notifications_sns.snapshot.json | 14 +- tests/integration/test_sns.py | 553 ++++--- tests/integration/test_sns.snapshot.json | 1408 ++++++++++++++--- 6 files changed, 1469 insertions(+), 535 deletions(-) diff --git a/localstack/services/sns/provider.py b/localstack/services/sns/provider.py index a2511b0a15313..bc60594b850e2 100644 --- a/localstack/services/sns/provider.py +++ b/localstack/services/sns/provider.py @@ -1174,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 734a3e01c671e..1aa620d8e5716 100644 --- a/localstack/testing/snapshots/transformer_utility.py +++ b/localstack/testing/snapshots/transformer_utility.py @@ -173,10 +173,7 @@ def sqs_api(): """ return [ TransformerUtility.key_value("ReceiptHandle"), - # account ID - TransformerUtility.key_value( - "SenderId", value_replacement="111111111111", reference_replacement=False - ), + TransformerUtility.key_value("SenderId"), TransformerUtility.jsonpath("$..MessageAttributes.RequestID.StringValue", "request-id"), KeyValueBasedTransformer(_resource_name_transformer, "resource"), ] @@ -187,12 +184,17 @@ 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), - KeyValueBasedTransformer(_sns_unsubscribe_url_token_transformer, replacement="token"), + # 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", @@ -205,6 +207,11 @@ def sns_api(): 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 @@ -245,7 +252,7 @@ def _sns_pem_file_token_transformer(key: str, val: str) -> str: return match.groups()[0] -def _sns_unsubscribe_url_token_transformer(key: str, val: str) -> str: +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) 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 3d353b6f0a12f..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 @@ -38,17 +37,9 @@ @pytest.fixture(autouse=True) def sns_snapshot_transformer(snapshot): - snapshot.add_transformer(snapshot.transform.sqs_api()) snapshot.add_transformer(snapshot.transform.sns_api()) -def _order_dict(response: dict) -> dict: - return { - key: _order_dict(val) if isinstance(val, dict) else val - for key, val in sorted(response.items(), key=itemgetter(0)) - } - - class TestSNSSubscription: @pytest.mark.aws_validated def test_python_lambda_subscribe_sns_topic( @@ -135,12 +126,7 @@ 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"] - # decoding the json in place to be able to match it - # assert message == msg_received - response["Messages"][0]["Body"] = json.loads(to_str(response["Messages"][0]["Body"])) + snapshot.match("received-message", response) @pytest.mark.aws_validated @@ -155,10 +141,8 @@ def test_subscribe_with_invalid_protocol( ) snapshot.match("exception", e.value.response) - assert e.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400 - assert e.value.response["Error"]["Code"] == "InvalidParameter" - @pytest.mark.aws_validated # snapshot ok + @pytest.mark.aws_validated @pytest.mark.skip_snapshot_verify( paths=[ "$..Attributes.Owner", @@ -174,6 +158,14 @@ def test_attribute_raw_subscribe( 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) @@ -209,12 +201,12 @@ def test_attribute_raw_subscribe( ) snapshot.match("messages-response", response) - @pytest.mark.aws_validated # snapshot ok + @pytest.mark.aws_validated @pytest.mark.skip_snapshot_verify( paths=[ "$..Attributes.Owner", "$..Attributes.ConfirmationWasAuthenticated", - "$..Attributes.RawMessageDelivery", # todo: fix me (not add to response if false) + "$..Attributes.RawMessageDelivery", ] ) def test_filter_policy( @@ -226,9 +218,6 @@ def test_filter_policy( sns_create_sqs_subscription, snapshot, ): - snapshot.add_transformer( - snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) - ) topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -286,7 +275,7 @@ def test_filter_policy( num_msgs_2 = len(response_2["Messages"]) assert num_msgs_2 == num_msgs_1 - @pytest.mark.aws_validated # snapshot ok + @pytest.mark.aws_validated @pytest.mark.skip_snapshot_verify( paths=[ "$..Attributes.Owner", @@ -304,9 +293,6 @@ def test_exists_filter_policy( sns_create_sqs_subscription, snapshot, ): - snapshot.add_transformer( - snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) - ) topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -421,12 +407,12 @@ def get_filter_policy(): num_msgs_4 = len(response_4["Messages"]) assert num_msgs_4 == num_msgs_3 - @pytest.mark.aws_validated # snapshot ok + @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.RawMessageDelivery", ] ) def test_subscribe_sqs_queue( @@ -438,9 +424,6 @@ def test_subscribe_sqs_queue( sns_create_sqs_subscription, snapshot, ): - snapshot.add_transformer( - snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) - ) # TODO: check with non default external port topic_arn = sns_create_topic()["TopicArn"] @@ -519,7 +502,7 @@ def check_message(): sns_client.delete_endpoint(EndpointArn=platform_arn) sns_client.delete_platform_application(PlatformApplicationArn=app_arn) - @pytest.mark.aws_validated # snapshot ok + @pytest.mark.aws_validated 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 @@ -550,7 +533,7 @@ def test_publish_non_existent_target(self, sns_client): assert ex.value.response["Error"]["Code"] == "InvalidClientTokenId" - @pytest.mark.aws_validated # snapshot ok + @pytest.mark.aws_validated def test_tags(self, sns_client, sns_create_topic, snapshot): topic_arn = sns_create_topic()["TopicArn"] @@ -608,12 +591,12 @@ def check_subscription(): retry(check_subscription, retries=PUBLICATION_RETRIES, sleep=PUBLICATION_TIMEOUT) - @pytest.mark.aws_validated # snapshot ok + @pytest.mark.aws_validated @pytest.mark.skip_snapshot_verify( paths=[ "$..Owner", "$..ConfirmationWasAuthenticated", - "$..RawMessageDelivery", # todo: fix me (not added to response if false) + "$..RawMessageDelivery", ] ) def test_sqs_topic_subscription_confirmation( @@ -638,15 +621,17 @@ def check_subscription(): # 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 # snapshot ok + @pytest.mark.aws_validated @pytest.mark.skip_snapshot_verify( paths=[ - "$..Messages..Body.Message.raise_error", # todo check lambda delivery format into DLQ - "$..Messages..Body.Message.Records", + "$..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, @@ -660,16 +645,12 @@ def test_sns_topic_as_lambda_dead_letter_queue( 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.key_value( - "MD5OfBody", "", reference_replacement=False - ), - snapshot.transform.jsonpath( - "$..Messages..MessageAttributes.RequestID.Value", "request-id" - ), - ] + snapshot.transform.jsonpath( + "$..Messages..MessageAttributes.RequestID.Value", "request-id" + ) ) # create an SNS topic that will be used as a DLQ by the lambda @@ -730,12 +711,15 @@ def receive_dlq(): # reduced retries when using localstack to avoid tests flaking retries = 120 if is_aws_cloud() else 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 @@ -795,12 +779,12 @@ 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", # todo: fix me (not added to response if false) + "$..RawMessageDelivery", ] ) def test_redrive_policy_lambda_subscription( @@ -817,9 +801,6 @@ def test_redrive_policy_lambda_subscription( sns_allow_topic_sqs_queue, snapshot, ): - snapshot.add_transformer( - snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) - ) dlq_url = sqs_create_queue() dlq_arn = sqs_queue_arn(dlq_url) topic_arn = sns_create_topic()["TopicArn"] @@ -846,8 +827,8 @@ def test_redrive_policy_lambda_subscription( response_attributes = sns_client.get_subscription_attributes( SubscriptionArn=subscription["SubscriptionArn"] ) - # todo: hacky fix for snapshot resource replacement - snapshot.match("subscription-attributes", _order_dict(response_attributes)) + + snapshot.match("subscription-attributes", response_attributes) lambda_client.delete_function(FunctionName=lambda_name) @@ -866,11 +847,12 @@ def test_redrive_policy_lambda_subscription( 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: @@ -880,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, @@ -900,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"] @@ -915,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 @@ -931,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( @@ -943,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 = [ @@ -1000,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( @@ -1046,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) @@ -1069,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, @@ -1435,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( @@ -1464,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( @@ -1478,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( @@ -1492,22 +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) - # todo add test and implement behaviour for: - # Invalid parameter: The topic should either have ContentBasedDeduplication enabled - # or MessageDeduplicationId provided explicitly + 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) - def add_xray_header(self, request, **kwargs): - request.headers[ - "X-Amzn-Trace-Id" - ] = "Root=1-3152b799-8954dae64eda91bc9a23a7e8;Parent=7fa8c0f79203be72;Sampled=1" + # 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 @@ -1516,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"] @@ -1544,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) @@ -1586,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, @@ -1599,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" @@ -1623,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 @@ -1639,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" @@ -1658,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( @@ -1688,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() @@ -1695,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 @@ -1718,11 +1759,14 @@ def test_redrive_policy_sqs_queue_subscription( raw_message_delivery, snapshot, ): - # Need to skip the MD5OfBody, because it contains a timestamp if raw_message_delivery is False + # the hash isn't the same because of the Binary attributes (maybe decoding order?) snapshot.add_transformer( - snapshot.transform.key_value("MD5OfBody", "", reference_replacement=False) + snapshot.transform.key_value( + "MD5OfMessageAttributes", + value_replacement="", + reference_replacement=False, + ) ) - topic_arn = sns_create_topic()["TopicArn"] queue_url = sqs_create_queue() @@ -1769,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( @@ -1795,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"], @@ -1818,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"], @@ -1827,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"] ) @@ -1843,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 @@ -2041,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 @@ -2049,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 0a486986649fa..db2ec6a9a3f63 100644 --- a/tests/integration/test_sns.snapshot.json +++ b/tests/integration/test_sns.snapshot.json @@ -1,70 +1,12 @@ { - "tests/integration/test_sns.py::TestSNSProvider::test_dead_letter_queue_with_deleted_sqs_queue[True]": { - "recorded-date": "29-06-2022, 16:43:14", - "recorded-content": { - "raw_message_delivery": { - "Messages": [ - { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "2040fa62e6ff90d21d5fe319d5e65443", - "Body": "test_dlq_after_sqs_endpoint_deleted" - } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } - } - }, - "tests/integration/test_sns.py::TestSNSProvider::test_dead_letter_queue_with_deleted_sqs_queue[False]": { - "recorded-date": "29-06-2022, 16:43:18", - "recorded-content": { - "json_encoded_delivery": { - "Messages": [ - { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", - "Body": { - "Type": "Notification", - "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::", - "MessageAttributes": { - "attr2": { - "Type": "Binary", - "Value": "AgME" - }, - "attr1": { - "Type": "Number", - "Value": "111" - } - } - } - } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } - } - } - }, "tests/integration/test_sns.py::TestSNSProvider::test_tags": { - "recorded-date": "01-08-2022, 17:10:09", + "recorded-date": "09-08-2022, 11:30:16", "recorded-content": { "duplicate-key-error": { "Error": { - "Type": "Sender", "Code": "InvalidParameter", - "Message": "Invalid parameter: Duplicated keys are not allowed." + "Message": "Invalid parameter: Duplicated keys are not allowed.", + "Type": "Sender" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -72,6 +14,10 @@ } }, "list-created-tags": { + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + }, "Tags": [ { "Key": "k1", @@ -81,42 +27,38 @@ "Key": "k2", "Value": "v2" } - ], + ] + }, + "list-after-delete-tags": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 - } - }, - "list-after-delete-tags": { + }, "Tags": [ { "Key": "k2", "Value": "v2" } - ], + ] + }, + "list-after-update-tags": { "ResponseMetadata": { "HTTPHeaders": {}, "HTTPStatusCode": 200 - } - }, - "list-after-update-tags": { + }, "Tags": [ { "Key": "k2", "Value": "v2b" } - ], - "ResponseMetadata": { - "HTTPHeaders": {}, - "HTTPStatusCode": 200 - } + ] } } }, "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_sqs_queue_subscription[True]": { - "recorded-date": "08-08-2022, 17:49:05", + "recorded-date": "09-08-2022, 11:36:01", "recorded-content": { - "raw_message_delivery": { + "messages": { "Messages": [ { "Body": "test_dlq_after_sqs_endpoint_deleted", @@ -133,31 +75,31 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_sqs_queue_subscription[False]": { - "recorded-date": "08-08-2022, 17:49:08", + "recorded-date": "09-08-2022, 11:36:04", "recorded-content": { - "json_encoded_delivery": { + "messages": { "Messages": [ { "Body": { + "Type": "Notification", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "test_dlq_after_sqs_endpoint_deleted", + "Timestamp": "date", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-", + "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", "MessageAttributes": { - "attr1": { - "Type": "Number", - "Value": "111" - }, "attr2": { "Type": "Binary", "Value": "AgME" + }, + "attr1": { + "Type": "Number", + "Value": "111" } - }, - "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": "", @@ -172,25 +114,25 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_publish_unicode_chars": { - "recorded-date": "04-08-2022, 11:31:08", + "recorded-date": "09-08-2022, 11:30:03", "recorded-content": { "received-message": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "84108d8422241e81df0d97db369175ed", "Body": { "Type": "Notification", - "MessageId": "", + "MessageId": "", "TopicArn": "arn:aws:sns::111111111111:", "Message": "\u00f6\u00a7a1\"_!?,. \u00a3$-", "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::" + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -201,36 +143,36 @@ } }, "tests/integration/test_sns.py::TestSNSSubscription::test_python_lambda_subscribe_sns_topic": { - "recorded-date": "04-08-2022, 11:32:56", + "recorded-date": "09-08-2022, 11:30:00", "recorded-content": { "notification": { - "Type": "Notification", + "Message": "Hello world.", + "MessageAttributes": {}, "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "Signature": "", + "SignatureVersion": "1", + "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-", "Subject": "[Subject] Test subject", - "Message": "Hello world.", "Timestamp": "date", - "SignatureVersion": "1", - "Signature": "", - "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-.pem", - "UnsubscribeUrl": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", - "MessageAttributes": {} + "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": "04-08-2022, 13:53:03", + "recorded-date": "09-08-2022, 11:30:06", "recorded-content": { "subscription-attributes": { "Attributes": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", "Owner": "111111111111", - "RawMessageDelivery": "true", - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:sqs::111111111111:", - "Protocol": "sqs", "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "Protocol": "sqs", + "RawMessageDelivery": "true", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -240,17 +182,17 @@ "messages-response": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "fafb00f5732ab283681e124bf8747ed1", "Body": "This is a test message", - "MD5OfMessageAttributes": "", + "MD5OfBody": "", + "MD5OfMessageAttributes": "", "MessageAttributes": { "store": { "BinaryValue": "b'\\x02\\x03\\x04'", "DataType": "Binary" } - } + }, + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -261,13 +203,13 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_subscribe_with_invalid_protocol": { - "recorded-date": "04-08-2022, 14:02:25", + "recorded-date": "09-08-2022, 11:30:03", "recorded-content": { "exception": { "Error": { - "Type": "Sender", "Code": "InvalidParameter", - "Message": "Invalid parameter: Amazon SNS does not support this protocol string: test-protocol" + "Message": "Invalid parameter: Amazon SNS does not support this protocol string: test-protocol", + "Type": "Sender" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -277,12 +219,12 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_filter_policy": { - "recorded-date": "04-08-2022, 14:18:18", + "recorded-date": "09-08-2022, 11:30:09", "recorded-content": { "subscription-attributes": { "Attributes": { - "Owner": "111111111111", - "RawMessageDelivery": "false", + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", "FilterPolicy": { "attr1": [ { @@ -295,12 +237,12 @@ } ] }, - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:sqs::111111111111:", - "Protocol": "sqs", + "Owner": "111111111111", "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -316,26 +258,26 @@ "messages-1": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "This is a test message", "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": { "attr1": { "Type": "Number", "Value": "99" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -346,26 +288,26 @@ "messages-2": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "This is a test message", "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": { "attr1": { "Type": "Number", "Value": "99" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -376,12 +318,12 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_exists_filter_policy": { - "recorded-date": "04-08-2022, 14:53:10", + "recorded-date": "09-08-2022, 11:30:12", "recorded-content": { "subscription-attributes-policy-1": { "Attributes": { - "Owner": "111111111111", - "RawMessageDelivery": "false", + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", "FilterPolicy": { "store": [ { @@ -389,12 +331,12 @@ } ] }, - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:sqs::111111111111:", - "Protocol": "sqs", + "Owner": "111111111111", "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -410,19 +352,16 @@ "messages-1": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "message-1", "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": { "def": { "Type": "Number", @@ -433,7 +372,10 @@ "Value": "99" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -444,19 +386,16 @@ "messages-2": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "message-1", "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": { "def": { "Type": "Number", @@ -467,7 +406,10 @@ "Value": "99" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -477,8 +419,8 @@ }, "subscription-attributes-policy-2": { "Attributes": { - "Owner": "111111111111", - "RawMessageDelivery": "false", + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", "FilterPolicy": { "store": [ { @@ -486,12 +428,12 @@ } ] }, - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:sqs::111111111111:", - "Protocol": "sqs", + "Owner": "111111111111", "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -501,26 +443,26 @@ "messages-3": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "message-3", "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": { "def": { "Type": "Number", "Value": "99" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -531,26 +473,26 @@ "messages-4": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "message-3", "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": { "def": { "Type": "Number", "Value": "99" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -561,12 +503,12 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_subscribe_sqs_queue": { - "recorded-date": "04-08-2022, 15:04:42", + "recorded-date": "09-08-2022, 11:30:15", "recorded-content": { "subscription-attributes": { "Attributes": { - "Owner": "111111111111", - "RawMessageDelivery": "false", + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", "FilterPolicy": { "attr1": [ { @@ -579,12 +521,12 @@ } ] }, - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:sqs::111111111111:", - "Protocol": "sqs", + "Owner": "111111111111", "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "Protocol": "sqs", + "RawMessageDelivery": "false", + "SubscriptionArn": "arn:aws:sns::111111111111::", + "TopicArn": "arn:aws:sns::111111111111:" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -594,26 +536,26 @@ "messages": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", + "MessageId": "", + "TopicArn": "arn:aws:sns::111111111111:", "Message": "This is a test message", "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": { "attr1": { "Type": "Number", "Value": "99.12" } } - } + }, + "MD5OfBody": "", + "MessageId": "", + "ReceiptHandle": "" } ], "ResponseMetadata": { @@ -624,13 +566,13 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_unknown_topic_publish": { - "recorded-date": "04-08-2022, 15:07:58", + "recorded-date": "09-08-2022, 11:30:15", "recorded-content": { "error": { "Error": { - "Type": "Sender", "Code": "NotFound", - "Message": "Topic does not exist" + "Message": "Topic does not exist", + "Type": "Sender" }, "ResponseMetadata": { "HTTPHeaders": {}, @@ -640,22 +582,22 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_sqs_topic_subscription_confirmation": { - "recorded-date": "04-08-2022, 15:37:45", + "recorded-date": "09-08-2022, 11:30:18", "recorded-content": { "subscription-attrs": { + "ConfirmationWasAuthenticated": "true", + "Endpoint": "arn:aws:sqs::111111111111:", "Owner": "111111111111", - "RawMessageDelivery": "false", - "TopicArn": "arn:aws:sns::111111111111:", - "Endpoint": "arn:aws:sqs::111111111111:", - "Protocol": "sqs", "PendingConfirmation": "false", - "ConfirmationWasAuthenticated": "true", - "SubscriptionArn": "arn:aws:sns::111111111111::" + "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": "04-08-2022, 16:15:53", + "recorded-date": "09-08-2022, 11:34:43", "recorded-content": { "lambda-response-dlq-config": { "TargetArn": "arn:aws:sns::111111111111:" @@ -663,47 +605,32 @@ "messages": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { - "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", "Message": { "Records": [ { "EventSource": "aws:sns", - "EventVersion": "1.0", "EventSubscriptionArn": "arn:aws:sns::111111111111::", + "EventVersion": "1.0", "Sns": { - "Type": "Notification", - "MessageId": "", - "TopicArn": "arn:aws:sns::111111111111:", - "Subject": null, "Message": { "raise_error": 1 }, - "Timestamp": "date", - "SignatureVersion": "1", + "MessageAttributes": {}, + "MessageId": "", "Signature": "", - "SigningCertUrl": "https://sns..amazonaws.com/SimpleNotificationService-.pem", - "UnsubscribeUrl": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", - "MessageAttributes": {} + "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::" } } ] }, - "Timestamp": "date", - "SignatureVersion": "1", - "Signature": "", - "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", - "UnsubscribeURL": "/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns::111111111111::", "MessageAttributes": { - "RequestID": { - "Type": "String", - "Value": "" - }, "ErrorCode": { "Type": "Number", "Value": "200" @@ -711,9 +638,24 @@ "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": { @@ -724,7 +666,7 @@ } }, "tests/integration/test_sns.py::TestSNSProvider::test_redrive_policy_lambda_subscription": { - "recorded-date": "04-08-2022, 17:05:46", + "recorded-date": "09-08-2022, 11:34:55", "recorded-content": { "subscription-attributes": { "Attributes": { @@ -748,20 +690,329 @@ "messages": { "Messages": [ { - "MessageId": "", - "ReceiptHandle": "", - "MD5OfBody": "", "Body": { "Type": "Notification", - "MessageId": "", + "MessageId": "", "TopicArn": "arn:aws:sns::111111111111:", "Message": "test_redrive_policy", "Timestamp": "date", "SignatureVersion": "1", "Signature": "", - "SigningCertURL": "https://sns..amazonaws.com/SimpleNotificationService-.pem", + "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": { @@ -770,5 +1021,652 @@ } } } + }, + "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": {} } }