Repository: localstack/localstack

Issue:
aws sns publish against non existent topic does not throw exception
With latest localstack image running in Docker, when publishing a message to a non-existent SNS topic no error is thrown.  This differs from AWS.


Discussion:

Resolving patch:
diff --git a/localstack/services/sns/sns_listener.py b/localstack/services/sns/sns_listener.py
--- a/localstack/services/sns/sns_listener.py
+++ b/localstack/services/sns/sns_listener.py
@@ -36,7 +36,6 @@ def forward_request(self, method, path, data, headers):
 
             if topic_arn:
                 topic_arn = topic_arn[0]
-                do_create_topic(topic_arn)
 
             if req_action == 'SetSubscriptionAttributes':
                 sub = get_subscription_by_arn(req_data['SubscriptionArn'][0])
@@ -65,6 +64,8 @@ def forward_request(self, method, path, data, headers):
             elif req_action == 'DeleteTopic':
                 do_delete_topic(topic_arn)
             elif req_action == 'Publish':
+                if topic_arn not in SNS_SUBSCRIPTIONS.keys():
+                    return make_error(code=404, code_string='NotFound', message='Topic does not exist')
                 publish_message(topic_arn, req_data)
                 # return response here because we do not want the request to be forwarded to SNS backend
                 return make_response(req_action)
@@ -89,6 +90,10 @@ def return_response(self, method, path, data, headers, response):
                     sub_arn,
                     attributes
                 )
+            if req_action == 'CreateTopic' and response.status_code < 400:
+                response_data = xmltodict.parse(response.content)
+                topic_arn = response_data['CreateTopicResponse']['CreateTopicResult']['TopicArn']
+                do_create_topic(topic_arn)
 
 
 # instantiate listener
@@ -162,8 +167,7 @@ def do_subscribe(topic_arn, endpoint, protocol, subscription_arn, attributes):
         'SubscriptionArn': subscription_arn,
     }
     subscription.update(attributes)
-
-    SNS_SUBSCRIPTIONS[topic_arn].append(subscription)
+    SNS_SUBSCRIPTIONS.get(topic_arn).append(subscription)
 
 
 def do_unsubscribe(subscription_arn):


Test patch:
diff --git a/tests/integration/test_sns.py b/tests/integration/test_sns.py
--- a/tests/integration/test_sns.py
+++ b/tests/integration/test_sns.py
@@ -3,6 +3,8 @@
 import json
 import unittest
 
+from botocore.exceptions import ClientError
+
 from localstack.utils.aws import aws_stack
 from localstack.utils.common import to_str
 
@@ -72,3 +74,15 @@ def test_attribute_raw_subscribe(self):
         msgs = sqs_client.receive_message(QueueUrl=queue_url)
         msg_received = msgs['Messages'][0]
         self.assertEqual(message, msg_received['Body'])
+
+    def test_unknown_topic_publish(self):
+        sns_client = aws_stack.connect_to_service('sns')
+        fake_arn = 'arn:aws:sns:us-east-1:123456789012:i_dont_exist'
+        message = u'This is a test message'
+        try:
+            sns_client.publish(TopicArn=fake_arn, Message=message)
+            self.fail('This call should not be successful as the topic does not exist')
+        except ClientError as e:
+            self.assertEqual(e.response['Error']['Code'], 'NotFound')
+            self.assertEqual(e.response['Error']['Message'], 'Topic does not exist')
+            self.assertEqual(e.response['ResponseMetadata']['HTTPStatusCode'], 404)