Repository: getmoto/moto
Pull request #6349 (2023-05-30 09:41:54)

SNS: Fix error message when topic is not found
This PR includes a small fix for the error message when topic is not found. It is changed to match AWS (checked for following operations):
- list-subscriptions-by-topic
- add-permission
- remove-permission
- publish
- subscribe
- delete-topic
- get-topic-attributes

The delete-topic is fixed not to throw an error for nonexistent topics because it is [idempotent](https://docs.aws.amazon.com/sns/latest/api/API_DeleteTopic.html)

Also updated is the documented list of implemented SNS operations.


Patch:
diff --git a/docs/docs/services/sns.rst b/docs/docs/services/sns.rst
index 6dd2cfaf4..352176938 100644
--- a/docs/docs/services/sns.rst
+++ b/docs/docs/services/sns.rst
@@ -28,8 +28,8 @@ sns
 |start-h3| Implemented features for this service |end-h3|
 
 - [X] add_permission
-- [ ] check_if_phone_number_is_opted_out
-- [ ] confirm_subscription
+- [x] check_if_phone_number_is_opted_out
+- [x] confirm_subscription
 - [X] create_platform_application
 - [X] create_platform_endpoint
 - [ ] create_sms_sandbox_phone_number
@@ -39,22 +39,22 @@ sns
 - [ ] delete_sms_sandbox_phone_number
 - [X] delete_topic
 - [ ] get_data_protection_policy
-- [ ] get_endpoint_attributes
-- [ ] get_platform_application_attributes
-- [ ] get_sms_attributes
+- [x] get_endpoint_attributes
+- [x] get_platform_application_attributes
+- [x] get_sms_attributes
 - [ ] get_sms_sandbox_account_status
 - [X] get_subscription_attributes
-- [ ] get_topic_attributes
+- [x] get_topic_attributes
 - [X] list_endpoints_by_platform_application
 - [ ] list_origination_numbers
-- [ ] list_phone_numbers_opted_out
+- [x] list_phone_numbers_opted_out
 - [X] list_platform_applications
 - [ ] list_sms_sandbox_phone_numbers
 - [X] list_subscriptions
-- [ ] list_subscriptions_by_topic
+- [x] list_subscriptions_by_topic
 - [X] list_tags_for_resource
 - [X] list_topics
-- [ ] opt_in_phone_number
+- [x] opt_in_phone_number
 - [X] publish
 - [X] publish_batch
   
@@ -64,8 +64,8 @@ sns
 - [ ] put_data_protection_policy
 - [X] remove_permission
 - [X] set_endpoint_attributes
-- [ ] set_platform_application_attributes
-- [ ] set_sms_attributes
+- [x] set_platform_application_attributes
+- [x] set_sms_attributes
 - [X] set_subscription_attributes
 - [ ] set_topic_attributes
 - [X] subscribe
diff --git a/moto/sns/models.py b/moto/sns/models.py
index 4b87649aa..a4a0ff719 100644
--- a/moto/sns/models.py
+++ b/moto/sns/models.py
@@ -1,3 +1,4 @@
+import contextlib
 import datetime
 import json
 import requests
@@ -483,20 +484,18 @@ class SNSBackend(BaseBackend):
                 self.subscriptions.pop(key)
 
     def delete_topic(self, arn: str) -> None:
-        try:
+        with contextlib.suppress(TopicNotFound):
             topic = self.get_topic(arn)
             self.delete_topic_subscriptions(topic)
             parsed_arn = parse_arn(arn)
             sns_backends[parsed_arn.account][parsed_arn.region].topics.pop(arn, None)
-        except KeyError:
-            raise SNSNotFoundError(f"Topic with arn {arn} not found")
 
     def get_topic(self, arn: str) -> Topic:
         parsed_arn = parse_arn(arn)
         try:
             return sns_backends[parsed_arn.account][parsed_arn.region].topics[arn]
         except KeyError:
-            raise SNSNotFoundError(f"Topic with arn {arn} not found")
+            raise TopicNotFound
 
     def set_topic_attribute(
         self, topic_arn: str, attribute_name: str, attribute_value: str
@@ -1006,10 +1005,7 @@ class SNSBackend(BaseBackend):
         """
         The MessageStructure and MessageDeduplicationId-parameters have not yet been implemented.
         """
-        try:
-            topic = self.get_topic(topic_arn)
-        except SNSNotFoundError:
-            raise TopicNotFound
+        topic = self.get_topic(topic_arn)
 
         if len(publish_batch_request_entries) > 10:
             raise TooManyEntriesInBatchRequest


Test patch:
diff --git a/tests/test_sns/test_topics_boto3.py b/tests/test_sns/test_topics_boto3.py
index c0bcade9b..922909882 100644
--- a/tests/test_sns/test_topics_boto3.py
+++ b/tests/test_sns/test_topics_boto3.py
@@ -25,6 +25,9 @@ def test_create_and_delete_topic():
         # Delete the topic
         conn.delete_topic(TopicArn=topics[0]["TopicArn"])
 
+        # Ensure DeleteTopic is idempotent
+        conn.delete_topic(TopicArn=topics[0]["TopicArn"])
+
         # And there should now be 0 topics
         topics_json = conn.list_topics()
         topics = topics_json["Topics"]
@@ -35,9 +38,10 @@ def test_create_and_delete_topic():
 def test_delete_non_existent_topic():
     conn = boto3.client("sns", region_name="us-east-1")
 
-    conn.delete_topic.when.called_with(
-        TopicArn="arn:aws:sns:us-east-1:123456789012:fake-topic"
-    ).should.throw(conn.exceptions.NotFoundException)
+    # Ensure DeleteTopic does not throw an error for non-existent topics
+    conn.delete_topic(
+        TopicArn="arn:aws:sns:us-east-1:123456789012:this-topic-does-not-exist"
+    )
 
 
 @mock_sns
@@ -362,7 +366,7 @@ def test_add_permission_errors():
         ActionName=["AddPermission"],
     ).should.throw(
         ClientError,
-        f"An error occurred (NotFound) when calling the AddPermission operation: Topic with arn {topic_arn + '-not-existing'} not found",
+        "An error occurred (NotFound) when calling the AddPermission operation: Topic does not exist",
     )
 
     client.add_permission.when.called_with(
@@ -388,7 +392,7 @@ def test_remove_permission_errors():
         TopicArn=topic_arn + "-not-existing", Label="test"
     ).should.throw(
         ClientError,
-        f"An error occurred (NotFound) when calling the RemovePermission operation: Topic with arn {topic_arn + '-not-existing'} not found",
+        "An error occurred (NotFound) when calling the RemovePermission operation: Topic does not exist",
     )