818 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			818 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import json
 | |
| 
 | |
| import boto3
 | |
| from botocore.exceptions import ClientError
 | |
| import pytest
 | |
| 
 | |
| from moto import mock_sns, mock_sqs
 | |
| from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 | |
| from moto.sns.models import (
 | |
|     DEFAULT_PAGE_SIZE,
 | |
|     DEFAULT_EFFECTIVE_DELIVERY_POLICY,
 | |
| )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_subscribe_sms():
 | |
|     client = boto3.client("sns", region_name="us-east-1")
 | |
|     client.create_topic(Name="some-topic")
 | |
|     resp = client.create_topic(Name="some-topic")
 | |
|     arn = resp["TopicArn"]
 | |
| 
 | |
|     resp = client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="+15551234567")
 | |
|     assert "SubscriptionArn" in resp
 | |
| 
 | |
|     resp = client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="+15/55-123.4567")
 | |
|     assert "SubscriptionArn" in resp
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_double_subscription():
 | |
|     client = boto3.client("sns", region_name="us-east-1")
 | |
|     client.create_topic(Name="some-topic")
 | |
|     resp = client.create_topic(Name="some-topic")
 | |
|     arn = resp["TopicArn"]
 | |
| 
 | |
|     resp1 = client.subscribe(
 | |
|         TopicArn=arn, Protocol="sqs", Endpoint="arn:aws:sqs:elasticmq:000000000000:foo"
 | |
|     )
 | |
|     resp2 = client.subscribe(
 | |
|         TopicArn=arn, Protocol="sqs", Endpoint="arn:aws:sqs:elasticmq:000000000000:foo"
 | |
|     )
 | |
| 
 | |
|     assert resp1["SubscriptionArn"] == resp2["SubscriptionArn"]
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_subscribe_bad_sms():
 | |
|     client = boto3.client("sns", region_name="us-east-1")
 | |
|     client.create_topic(Name="some-topic")
 | |
|     resp = client.create_topic(Name="some-topic")
 | |
|     arn = resp["TopicArn"]
 | |
| 
 | |
|     try:
 | |
|         # Test invalid number
 | |
|         client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="NAA+15551234567")
 | |
|     except ClientError as err:
 | |
|         assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
| 
 | |
|     with pytest.raises(ClientError) as client_err:
 | |
|         client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="+15--551234567")
 | |
|     assert (
 | |
|         client_err.value.response["Error"]["Message"]
 | |
|         == "Invalid SMS endpoint: +15--551234567"
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as client_err:
 | |
|         client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="+15551234567.")
 | |
|     assert (
 | |
|         client_err.value.response["Error"]["Message"]
 | |
|         == "Invalid SMS endpoint: +15551234567."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as client_err:
 | |
|         assert client.subscribe(TopicArn=arn, Protocol="sms", Endpoint="/+15551234567")
 | |
| 
 | |
|     assert (
 | |
|         client_err.value.response["Error"]["Message"]
 | |
|         == "Invalid SMS endpoint: /+15551234567"
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_creating_subscription():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     conn.create_topic(Name="some-topic")
 | |
|     response = conn.list_topics()
 | |
|     topic_arn = response["Topics"][0]["TopicArn"]
 | |
| 
 | |
|     conn.subscribe(TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/")
 | |
| 
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 1
 | |
|     subscription = subscriptions[0]
 | |
|     assert subscription["TopicArn"] == topic_arn
 | |
|     assert subscription["Protocol"] == "http"
 | |
|     assert topic_arn in subscription["SubscriptionArn"]
 | |
|     assert subscription["Endpoint"] == "http://example.com/"
 | |
| 
 | |
|     # Now unsubscribe the subscription
 | |
|     conn.unsubscribe(SubscriptionArn=subscription["SubscriptionArn"])
 | |
| 
 | |
|     # And there should be zero subscriptions left
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 0
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_unsubscribe_from_deleted_topic():
 | |
|     client = boto3.client("sns", region_name="us-east-1")
 | |
|     client.create_topic(Name="some-topic")
 | |
|     response = client.list_topics()
 | |
|     topic_arn = response["Topics"][0]["TopicArn"]
 | |
| 
 | |
|     client.subscribe(
 | |
|         TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/"
 | |
|     )
 | |
| 
 | |
|     subscriptions = client.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 1
 | |
|     subscription = subscriptions[0]
 | |
|     subscription_arn = subscription["SubscriptionArn"]
 | |
|     assert subscription["TopicArn"] == topic_arn
 | |
|     assert subscription["Protocol"] == "http"
 | |
|     assert topic_arn in subscription_arn
 | |
|     assert subscription["Endpoint"] == "http://example.com/"
 | |
| 
 | |
|     # Now delete the topic
 | |
|     client.delete_topic(TopicArn=topic_arn)
 | |
| 
 | |
|     # And there should now be 0 topics
 | |
|     topics_json = client.list_topics()
 | |
|     topics = topics_json["Topics"]
 | |
|     assert len(topics) == 0
 | |
| 
 | |
|     # as per the documentation deleting a topic deletes all the subscriptions
 | |
|     subscriptions = client.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 0
 | |
| 
 | |
|     # Now delete hanging subscription
 | |
|     client.unsubscribe(SubscriptionArn=subscription_arn)
 | |
| 
 | |
|     subscriptions = client.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 0
 | |
| 
 | |
|     # Deleting it again should not result in any error
 | |
|     client.unsubscribe(SubscriptionArn=subscription_arn)
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_getting_subscriptions_by_topic():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     conn.create_topic(Name="topic1")
 | |
|     conn.create_topic(Name="topic2")
 | |
| 
 | |
|     response = conn.list_topics()
 | |
|     topics = response["Topics"]
 | |
|     topic1_arn = topics[0]["TopicArn"]
 | |
|     topic2_arn = topics[1]["TopicArn"]
 | |
| 
 | |
|     conn.subscribe(
 | |
|         TopicArn=topic1_arn, Protocol="http", Endpoint="http://example1.com/"
 | |
|     )
 | |
|     conn.subscribe(
 | |
|         TopicArn=topic2_arn, Protocol="http", Endpoint="http://example2.com/"
 | |
|     )
 | |
| 
 | |
|     topic1_subscriptions = conn.list_subscriptions_by_topic(TopicArn=topic1_arn)[
 | |
|         "Subscriptions"
 | |
|     ]
 | |
|     assert len(topic1_subscriptions) == 1
 | |
|     assert topic1_subscriptions[0]["Endpoint"] == "http://example1.com/"
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_subscription_paging():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     conn.create_topic(Name="topic1")
 | |
| 
 | |
|     response = conn.list_topics()
 | |
|     topics = response["Topics"]
 | |
|     topic1_arn = topics[0]["TopicArn"]
 | |
| 
 | |
|     for index in range(DEFAULT_PAGE_SIZE + int(DEFAULT_PAGE_SIZE / 3)):
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic1_arn,
 | |
|             Protocol="email",
 | |
|             Endpoint="email_" + str(index) + "@test.com",
 | |
|         )
 | |
| 
 | |
|     all_subscriptions = conn.list_subscriptions()
 | |
|     assert len(all_subscriptions["Subscriptions"]) == DEFAULT_PAGE_SIZE
 | |
|     next_token = all_subscriptions["NextToken"]
 | |
|     assert next_token == str(DEFAULT_PAGE_SIZE)
 | |
| 
 | |
|     all_subscriptions = conn.list_subscriptions(NextToken=next_token)
 | |
|     assert len(all_subscriptions["Subscriptions"]) == int(DEFAULT_PAGE_SIZE / 3)
 | |
|     assert "NextToken" not in all_subscriptions
 | |
| 
 | |
|     topic1_subscriptions = conn.list_subscriptions_by_topic(TopicArn=topic1_arn)
 | |
|     assert len(topic1_subscriptions["Subscriptions"]) == DEFAULT_PAGE_SIZE
 | |
|     next_token = topic1_subscriptions["NextToken"]
 | |
|     assert next_token == str(DEFAULT_PAGE_SIZE)
 | |
| 
 | |
|     topic1_subscriptions = conn.list_subscriptions_by_topic(
 | |
|         TopicArn=topic1_arn, NextToken=next_token
 | |
|     )
 | |
|     assert len(topic1_subscriptions["Subscriptions"]) == int(DEFAULT_PAGE_SIZE / 3)
 | |
|     assert "NextToken" not in topic1_subscriptions
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_subscribe_attributes():
 | |
|     client = boto3.client("sns", region_name="us-east-1")
 | |
|     client.create_topic(Name="some-topic")
 | |
|     resp = client.create_topic(Name="some-topic")
 | |
|     arn = resp["TopicArn"]
 | |
| 
 | |
|     resp = client.subscribe(TopicArn=arn, Protocol="http", Endpoint="http://test.com")
 | |
| 
 | |
|     response = client.get_subscription_attributes(
 | |
|         SubscriptionArn=resp["SubscriptionArn"]
 | |
|     )
 | |
| 
 | |
|     assert "Attributes" in response
 | |
|     attributes = response["Attributes"]
 | |
|     assert attributes["PendingConfirmation"] == "false"
 | |
|     assert attributes["ConfirmationWasAuthenticated"] == "true"
 | |
|     assert attributes["Endpoint"] == "http://test.com"
 | |
|     assert attributes["TopicArn"] == arn
 | |
|     assert attributes["Protocol"] == "http"
 | |
|     assert attributes["SubscriptionArn"] == resp["SubscriptionArn"]
 | |
|     assert attributes["Owner"] == str(ACCOUNT_ID)
 | |
|     assert attributes["RawMessageDelivery"] == "false"
 | |
|     assert json.loads(attributes["EffectiveDeliveryPolicy"]) == (
 | |
|         DEFAULT_EFFECTIVE_DELIVERY_POLICY
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_creating_subscription_with_attributes():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     conn.create_topic(Name="some-topic")
 | |
|     response = conn.list_topics()
 | |
|     topic_arn = response["Topics"][0]["TopicArn"]
 | |
| 
 | |
|     delivery_policy = json.dumps(
 | |
|         {
 | |
|             "healthyRetryPolicy": {
 | |
|                 "numRetries": 10,
 | |
|                 "minDelayTarget": 1,
 | |
|                 "maxDelayTarget": 2,
 | |
|             }
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     filter_policy = json.dumps(
 | |
|         {
 | |
|             "store": ["example_corp"],
 | |
|             "encrypted": [False],
 | |
|             "customer_interests": ["basketball", "baseball"],
 | |
|             "price": [100, 100.12],
 | |
|             "error": [None],
 | |
|         }
 | |
|     )
 | |
| 
 | |
|     subscription_role_arn = "arn:aws:iam:000000000:role/test-role"
 | |
| 
 | |
|     conn.subscribe(
 | |
|         TopicArn=topic_arn,
 | |
|         Protocol="http",
 | |
|         Endpoint="http://example.com/",
 | |
|         Attributes={
 | |
|             "RawMessageDelivery": "true",
 | |
|             "DeliveryPolicy": delivery_policy,
 | |
|             "FilterPolicy": filter_policy,
 | |
|             "SubscriptionRoleArn": subscription_role_arn,
 | |
|         },
 | |
|     )
 | |
| 
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 1
 | |
|     subscription = subscriptions[0]
 | |
|     assert subscription["TopicArn"] == topic_arn
 | |
|     assert subscription["Protocol"] == "http"
 | |
|     assert topic_arn in subscription["SubscriptionArn"]
 | |
|     assert subscription["Endpoint"] == "http://example.com/"
 | |
| 
 | |
|     # Test the subscription attributes have been set
 | |
|     subscription_arn = subscription["SubscriptionArn"]
 | |
|     attrs = conn.get_subscription_attributes(SubscriptionArn=subscription_arn)
 | |
| 
 | |
|     assert attrs["Attributes"]["RawMessageDelivery"] == "true"
 | |
|     assert attrs["Attributes"]["DeliveryPolicy"] == delivery_policy
 | |
|     assert attrs["Attributes"]["FilterPolicy"] == filter_policy
 | |
|     assert attrs["Attributes"]["SubscriptionRoleArn"] == subscription_role_arn
 | |
| 
 | |
|     # Now unsubscribe the subscription
 | |
|     conn.unsubscribe(SubscriptionArn=subscription["SubscriptionArn"])
 | |
| 
 | |
|     # And there should be zero subscriptions left
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 0
 | |
| 
 | |
|     # invalid attr name
 | |
|     with pytest.raises(ClientError):
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"InvalidName": "true"},
 | |
|         )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| @mock_sqs
 | |
| def test_delete_subscriptions_on_delete_topic():
 | |
|     sqs = boto3.client("sqs", region_name="us-east-1")
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
| 
 | |
|     queue = sqs.create_queue(QueueName="test-queue")
 | |
|     topic = conn.create_topic(Name="some-topic")
 | |
| 
 | |
|     conn.subscribe(
 | |
|         TopicArn=topic.get("TopicArn"), Protocol="sqs", Endpoint=queue.get("QueueUrl")
 | |
|     )
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
| 
 | |
|     assert len(subscriptions) == 1
 | |
| 
 | |
|     conn.delete_topic(TopicArn=topic.get("TopicArn"))
 | |
| 
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 0
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_set_subscription_attributes():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     conn.create_topic(Name="some-topic")
 | |
|     response = conn.list_topics()
 | |
|     topic_arn = response["Topics"][0]["TopicArn"]
 | |
| 
 | |
|     conn.subscribe(TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/")
 | |
| 
 | |
|     subscriptions = conn.list_subscriptions()["Subscriptions"]
 | |
|     assert len(subscriptions) == 1
 | |
|     subscription = subscriptions[0]
 | |
|     assert subscription["TopicArn"] == topic_arn
 | |
|     assert subscription["Protocol"] == "http"
 | |
|     assert topic_arn in subscription["SubscriptionArn"]
 | |
|     assert subscription["Endpoint"] == "http://example.com/"
 | |
| 
 | |
|     subscription_arn = subscription["SubscriptionArn"]
 | |
|     attrs = conn.get_subscription_attributes(SubscriptionArn=subscription_arn)
 | |
|     assert "Attributes" in attrs
 | |
|     conn.set_subscription_attributes(
 | |
|         SubscriptionArn=subscription_arn,
 | |
|         AttributeName="RawMessageDelivery",
 | |
|         AttributeValue="true",
 | |
|     )
 | |
|     delivery_policy = json.dumps(
 | |
|         {
 | |
|             "healthyRetryPolicy": {
 | |
|                 "numRetries": 10,
 | |
|                 "minDelayTarget": 1,
 | |
|                 "maxDelayTarget": 2,
 | |
|             }
 | |
|         }
 | |
|     )
 | |
|     conn.set_subscription_attributes(
 | |
|         SubscriptionArn=subscription_arn,
 | |
|         AttributeName="DeliveryPolicy",
 | |
|         AttributeValue=delivery_policy,
 | |
|     )
 | |
| 
 | |
|     filter_policy = json.dumps(
 | |
|         {
 | |
|             "store": ["example_corp"],
 | |
|             "encrypted": [False],
 | |
|             "customer_interests": ["basketball", "baseball"],
 | |
|             "price": [100, 100.12],
 | |
|             "error": [None],
 | |
|         }
 | |
|     )
 | |
|     conn.set_subscription_attributes(
 | |
|         SubscriptionArn=subscription_arn,
 | |
|         AttributeName="FilterPolicy",
 | |
|         AttributeValue=filter_policy,
 | |
|     )
 | |
| 
 | |
|     attrs = conn.get_subscription_attributes(SubscriptionArn=subscription_arn)
 | |
| 
 | |
|     assert attrs["Attributes"]["RawMessageDelivery"] == "true"
 | |
|     assert attrs["Attributes"]["DeliveryPolicy"] == delivery_policy
 | |
|     assert attrs["Attributes"]["FilterPolicy"] == filter_policy
 | |
| 
 | |
|     filter_policy_scope = "MessageBody"
 | |
|     conn.set_subscription_attributes(
 | |
|         SubscriptionArn=subscription_arn,
 | |
|         AttributeName="FilterPolicyScope",
 | |
|         AttributeValue=filter_policy_scope,
 | |
|     )
 | |
| 
 | |
|     attrs = conn.get_subscription_attributes(SubscriptionArn=subscription_arn)
 | |
| 
 | |
|     assert attrs["Attributes"]["FilterPolicyScope"] == filter_policy_scope
 | |
| 
 | |
|     # not existing subscription
 | |
|     with pytest.raises(ClientError):
 | |
|         conn.set_subscription_attributes(
 | |
|             SubscriptionArn="invalid",
 | |
|             AttributeName="RawMessageDelivery",
 | |
|             AttributeValue="true",
 | |
|         )
 | |
|     with pytest.raises(ClientError):
 | |
|         attrs = conn.get_subscription_attributes(SubscriptionArn="invalid")
 | |
| 
 | |
|     # invalid attr name
 | |
|     with pytest.raises(ClientError):
 | |
|         conn.set_subscription_attributes(
 | |
|             SubscriptionArn=subscription_arn,
 | |
|             AttributeName="InvalidName",
 | |
|             AttributeValue="true",
 | |
|         )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_subscribe_invalid_filter_policy():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     conn.create_topic(Name="some-topic")
 | |
|     response = conn.list_topics()
 | |
|     topic_arn = response["Topics"][0]["TopicArn"]
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"store": [str(i) for i in range(151)]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: FilterPolicy: Filter policy is too complex"
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"store": [["example_corp"]]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: FilterPolicy: Match value must be String, number, true, false, or null"
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"store": [{"exists": None}]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: FilterPolicy: exists match pattern must be either true or false."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"store": [{"error": True}]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: FilterPolicy: Unrecognized match type error"
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"store": [1000000001]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InternalFailure"
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"price": [{"numeric": ["<", "100"]}]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Value of < must be numeric\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps(
 | |
|                     {"price": [{"numeric": [">", 50, "<=", "100"]}]}
 | |
|                 )
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Value of <= must be numeric\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"price": [{"numeric": []}]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: "
 | |
|         "Invalid member in numeric match: ]\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"price": [{"numeric": [50, "<=", "100"]}]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Invalid "
 | |
|         "member in numeric match: 50\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"price": [{"numeric": ["<"]}]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Value of < must be numeric\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps({"price": [{"numeric": ["0"]}]})},
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: "
 | |
|         "Unrecognized numeric range operator: 0\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"price": [{"numeric": ["<", 20, ">", 1]}]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Too many "
 | |
|         "elements in numeric expression\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"price": [{"numeric": [">", 20, ">", 1]}]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Bad numeric range operator: >\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"price": [{"numeric": [">", 20, "<", 1]}]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Bottom must be less than top\n at ..."
 | |
|     )
 | |
| 
 | |
|     with pytest.raises(ClientError) as err_info:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"price": [{"numeric": [">", 20, "<"]}]})
 | |
|             },
 | |
|         )
 | |
| 
 | |
|     err = err_info.value
 | |
|     assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|     assert err.response["Error"]["Message"] == (
 | |
|         "Invalid parameter: Attributes Reason: FilterPolicy: Value of < must be numeric\n at ..."
 | |
|     )
 | |
| 
 | |
|     try:
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicy": json.dumps({"store": {"key": [{"exists": None}]}})
 | |
|             },
 | |
|         )
 | |
|     except ClientError as err:
 | |
|         assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|         assert err.response["Error"]["Message"] == (
 | |
|             "Invalid parameter: Filter policy scope MessageAttributes does "
 | |
|             "not support nested filter policy"
 | |
|         )
 | |
| 
 | |
|     try:
 | |
|         filter_policy = {
 | |
|             "key_a": ["value_one"],
 | |
|             "key_b": ["value_two"],
 | |
|             "key_c": ["value_three"],
 | |
|             "key_d": ["value_four"],
 | |
|             "key_e": ["value_five"],
 | |
|             "key_f": ["value_six"],
 | |
|         }
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={"FilterPolicy": json.dumps(filter_policy)},
 | |
|         )
 | |
|     except ClientError as err:
 | |
|         assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|         assert err.response["Error"]["Message"] == (
 | |
|             "Invalid parameter: FilterPolicy: Filter policy can not have more than 5 keys"
 | |
|         )
 | |
| 
 | |
|     try:
 | |
|         nested_filter_policy = {
 | |
|             "key_a": {
 | |
|                 "key_b": {
 | |
|                     "key_c": ["value_one", "value_two", "value_three", "value_four"]
 | |
|                 },
 | |
|             },
 | |
|             "key_d": {"key_e": ["value_one", "value_two", "value_three"]},
 | |
|             "key_f": ["value_one", "value_two", "value_three"],
 | |
|         }
 | |
|         # The first array has four values in a three-level nested key,
 | |
|         # and the second has three values in a two-level nested key. The
 | |
|         # total combination is calculated as follows:
 | |
|         # 3 x 4 x 2 x 3 x 1 x 3 = 216
 | |
|         conn.subscribe(
 | |
|             TopicArn=topic_arn,
 | |
|             Protocol="http",
 | |
|             Endpoint="http://example.com/",
 | |
|             Attributes={
 | |
|                 "FilterPolicyScope": "MessageBody",
 | |
|                 "FilterPolicy": json.dumps(nested_filter_policy),
 | |
|             },
 | |
|         )
 | |
|     except ClientError as err:
 | |
|         assert err.response["Error"]["Code"] == "InvalidParameter"
 | |
|         assert err.response["Error"]["Message"] == (
 | |
|             "Invalid parameter: FilterPolicy: Filter policy is too complex"
 | |
|         )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_check_not_opted_out():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     response = conn.check_if_phone_number_is_opted_out(phoneNumber="+447428545375")
 | |
| 
 | |
|     assert "isOptedOut" in response
 | |
|     assert response["isOptedOut"] is False
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_check_opted_out():
 | |
|     # Phone number ends in 99 so is hardcoded in the endpoint to return opted
 | |
|     # out status
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     response = conn.check_if_phone_number_is_opted_out(phoneNumber="+447428545399")
 | |
| 
 | |
|     assert "isOptedOut" in response
 | |
|     assert response["isOptedOut"] is True
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_check_opted_out_invalid():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
| 
 | |
|     # Invalid phone number
 | |
|     with pytest.raises(ClientError):
 | |
|         conn.check_if_phone_number_is_opted_out(phoneNumber="+44742LALALA")
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_list_opted_out():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     response = conn.list_phone_numbers_opted_out()
 | |
| 
 | |
|     assert "phoneNumbers" in response
 | |
|     assert len(response["phoneNumbers"]) > 0
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_opt_in():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     response = conn.list_phone_numbers_opted_out()
 | |
|     current_len = len(response["phoneNumbers"])
 | |
|     assert current_len > 0
 | |
| 
 | |
|     conn.opt_in_phone_number(phoneNumber=response["phoneNumbers"][0])
 | |
| 
 | |
|     response = conn.list_phone_numbers_opted_out()
 | |
|     assert len(response["phoneNumbers"]) > 0
 | |
|     assert len(response["phoneNumbers"]) < current_len
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_confirm_subscription():
 | |
|     conn = boto3.client("sns", region_name="us-east-1")
 | |
|     response = conn.create_topic(Name="testconfirm")
 | |
| 
 | |
|     conn.confirm_subscription(
 | |
|         TopicArn=response["TopicArn"],
 | |
|         Token=(
 | |
|             "2336412f37fb687f5d51e6e241d59b68c4e583a5cee0be6f95bbf97ab8d"
 | |
|             "2441cf47b99e848408adaadf4c197e65f03473d53c4ba398f6abbf38ce2"
 | |
|             "e8ebf7b4ceceb2cd817959bcde1357e58a2861b05288c535822eb88cac3"
 | |
|             "db04f592285249971efc6484194fc4a4586147f16916692"
 | |
|         ),
 | |
|         AuthenticateOnUnsubscribe="true",
 | |
|     )
 | |
| 
 | |
| 
 | |
| @mock_sns
 | |
| def test_get_subscription_attributes_error_not_exists():
 | |
|     # given
 | |
|     client = boto3.client("sns", region_name="us-east-1")
 | |
|     sub_arn = (
 | |
|         f"arn:aws:sqs:us-east-1:{ACCOUNT_ID}:test-queue"
 | |
|         ":66d97e76-31e5-444f-8fa7-b60b680d0d39"
 | |
|     )
 | |
| 
 | |
|     # when
 | |
|     with pytest.raises(ClientError) as exc:
 | |
|         client.get_subscription_attributes(SubscriptionArn=sub_arn)
 | |
| 
 | |
|     # then
 | |
|     exc_value = exc.value
 | |
|     assert exc_value.operation_name == "GetSubscriptionAttributes"
 | |
|     assert exc_value.response["ResponseMetadata"]["HTTPStatusCode"] == 404
 | |
|     assert "NotFound" in exc_value.response["Error"]["Code"]
 | |
|     assert exc_value.response["Error"]["Message"] == "Subscription does not exist"
 |