Repository: getmoto/moto

Issue:
SNS.create_topic shouldn't accept invalid input  
Per the [AWS SNS documentation](http://docs.aws.amazon.com/sns/latest/api/API_CreateTopic.html) the CreateTopic shouldn't accept topic names that don't match the constraint: _Topic names must be made up of only uppercase and lowercase ASCII letters, numbers, underscores, and hyphens, and must be between 1 and 256 characters long._

Expected:
- create_topic should raise an InvalidParameterValue when an invalid topic is passed to create_topic

Results:
- no error is raised



Discussion:
Forgot to sync my fork; ignore previous commit.. One sec

Resolving patch:
diff --git a/moto/sns/exceptions.py b/moto/sns/exceptions.py
--- a/moto/sns/exceptions.py
+++ b/moto/sns/exceptions.py
@@ -32,3 +32,11 @@ class SNSInvalidParameter(RESTError):
     def __init__(self, message):
         super(SNSInvalidParameter, self).__init__(
             "InvalidParameter", message)
+
+
+class InvalidParameterValue(RESTError):
+    code = 400
+
+    def __init__(self, message):
+        super(InvalidParameterValue, self).__init__(
+            "InvalidParameterValue", message)
diff --git a/moto/sns/models.py b/moto/sns/models.py
--- a/moto/sns/models.py
+++ b/moto/sns/models.py
@@ -7,6 +7,7 @@
 import boto.sns
 import requests
 import six
+import re
 
 from moto.compat import OrderedDict
 from moto.core import BaseBackend, BaseModel
@@ -15,7 +16,8 @@
 from moto.awslambda import lambda_backends
 
 from .exceptions import (
-    SNSNotFoundError, DuplicateSnsEndpointError, SnsEndpointDisabled, SNSInvalidParameter
+    SNSNotFoundError, DuplicateSnsEndpointError, SnsEndpointDisabled, SNSInvalidParameter,
+    InvalidParameterValue
 )
 from .utils import make_arn_for_topic, make_arn_for_subscription
 
@@ -193,6 +195,9 @@ def update_sms_attributes(self, attrs):
         self.sms_attributes.update(attrs)
 
     def create_topic(self, name):
+        fails_constraints = not re.match(r'^[a-zA-Z0-9](?:[A-Za-z0-9_-]{0,253}[a-zA-Z0-9])?$', name)
+        if fails_constraints:
+            raise InvalidParameterValue("Topic names must be made up of only uppercase and lowercase ASCII letters, numbers, underscores, and hyphens, and must be between 1 and 256 characters long.")
         candidate_topic = Topic(name, self)
         if candidate_topic.arn in self.topics:
             return self.topics[candidate_topic.arn]


Test patch:
diff --git a/tests/test_sns/test_server.py b/tests/test_sns/test_server.py
--- a/tests/test_sns/test_server.py
+++ b/tests/test_sns/test_server.py
@@ -13,12 +13,12 @@ def test_sns_server_get():
     backend = server.create_backend_app("sns")
     test_client = backend.test_client()
 
-    topic_data = test_client.action_data("CreateTopic", Name="test topic")
+    topic_data = test_client.action_data("CreateTopic", Name="testtopic")
     topic_data.should.contain("CreateTopicResult")
     topic_data.should.contain(
-        "<TopicArn>arn:aws:sns:us-east-1:123456789012:test topic</TopicArn>")
+        "<TopicArn>arn:aws:sns:us-east-1:123456789012:testtopic</TopicArn>")
 
     topics_data = test_client.action_data("ListTopics")
     topics_data.should.contain("ListTopicsResult")
     topic_data.should.contain(
-        "<TopicArn>arn:aws:sns:us-east-1:123456789012:test topic</TopicArn>")
+        "<TopicArn>arn:aws:sns:us-east-1:123456789012:testtopic</TopicArn>")
diff --git a/tests/test_sns/test_topics_boto3.py b/tests/test_sns/test_topics_boto3.py
--- a/tests/test_sns/test_topics_boto3.py
+++ b/tests/test_sns/test_topics_boto3.py
@@ -50,17 +50,35 @@ def test_create_topic_should_be_indempodent():
     topic_display_name = conn.get_topic_attributes(
         TopicArn=topic_arn
     )['Attributes']['DisplayName']
-    
     topic_display_name.should.be.equal("should_be_set")
 
-
-
 @mock_sns
 def test_get_missing_topic():
     conn = boto3.client("sns", region_name="us-east-1")
     conn.get_topic_attributes.when.called_with(
         TopicArn="a-fake-arn").should.throw(ClientError)
 
+@mock_sns
+def test_create_topic_must_meet_constraints():
+    conn = boto3.client("sns", region_name="us-east-1")
+    common_random_chars = [':', ";", "!", "@", "|", "^", "%"]
+    for char in common_random_chars:
+        conn.create_topic.when.called_with(
+            Name="no%s_invalidchar" % char).should.throw(ClientError)
+    conn.create_topic.when.called_with(
+            Name="no spaces allowed").should.throw(ClientError)
+
+
+@mock_sns
+def test_create_topic_should_be_of_certain_length():
+    conn = boto3.client("sns", region_name="us-east-1")
+    too_short = ""
+    conn.create_topic.when.called_with(
+            Name=too_short).should.throw(ClientError)
+    too_long = "x" * 257
+    conn.create_topic.when.called_with(
+            Name=too_long).should.throw(ClientError)
+
 
 @mock_sns
 def test_create_topic_in_multiple_regions():