Repository: getmoto/moto

Issue:
Support missing for SNS Publish "Subject" optional parameter
The API (and boto3) allow for an optional parameter [``Subject`` to the ``publish``](http://docs.aws.amazon.com/sns/latest/api/API_Publish.html) method on an sns client. There are some constraints on the value of that field, (<100 characters is one) which do not appear to be checked in the current ``moto``.

A simplification of the method I'd like to test with ``moto`` is at the end. If I test using a message of
```python
    msg = "nope " * 21  # > 100 characters
```
It passes with ``moto``, but throws an exception on AWS: ``An error occurred (InvalidParameter) when calling the Publish operation: Invalid parameter:
Subject``

```python
    def send_sns(self, msg):
        subject = msg.split('\n')[0]
        client = boto3.client("sns")
        topic_arn = os.environ.get('SNSARN', "")
        response = client.publish(Message=msg, Subject=subject,
                                  TopicArn=topic_arn)
```

Here are my current library versions for python2.7:
```
Package                      Version     
---------------------------- ----------- 
boto                         2.48.0      
boto3                        1.4.7       
botocore                     1.7.29      
moto                         1.1.22      
```


Discussion:
@hwine Thanks for finding and reporting this! Would you mind writing a (failing) test that reproduces this bug? If you get stuck feel free to make a comment here and somebody will jump in to help out. I recommend looking at `tests/test_sns/test_sns.py` to start.

Resolving patch:
diff --git a/moto/sns/models.py b/moto/sns/models.py
--- a/moto/sns/models.py
+++ b/moto/sns/models.py
@@ -256,7 +256,10 @@ def list_subscriptions(self, topic_arn=None, next_token=None):
         else:
             return self._get_values_nexttoken(self.subscriptions, next_token)
 
-    def publish(self, arn, message):
+    def publish(self, arn, message, subject=None):
+        if subject is not None and len(subject) >= 100:
+            raise ValueError('Subject must be less than 100 characters')
+
         try:
             topic = self.get_topic(arn)
             message_id = topic.publish(message)
diff --git a/moto/sns/responses.py b/moto/sns/responses.py
--- a/moto/sns/responses.py
+++ b/moto/sns/responses.py
@@ -239,6 +239,8 @@ def publish(self):
         target_arn = self._get_param('TargetArn')
         topic_arn = self._get_param('TopicArn')
         phone_number = self._get_param('PhoneNumber')
+        subject = self._get_param('Subject')
+
         if phone_number is not None:
             # Check phone is correct syntax (e164)
             if not is_e164(phone_number):
@@ -261,7 +263,12 @@ def publish(self):
             arn = topic_arn
 
         message = self._get_param('Message')
-        message_id = self.backend.publish(arn, message)
+
+        try:
+            message_id = self.backend.publish(arn, message, subject=subject)
+        except ValueError as err:
+            error_response = self._error('InvalidParameter', str(err))
+            return error_response, dict(status=400)
 
         if self.request_json:
             return json.dumps({


Test patch:
diff --git a/tests/test_sns/test_publishing_boto3.py b/tests/test_sns/test_publishing_boto3.py
--- a/tests/test_sns/test_publishing_boto3.py
+++ b/tests/test_sns/test_publishing_boto3.py
@@ -177,3 +177,33 @@ def callback(request):
     response = conn.publish(
         TopicArn=topic_arn, Message="my message", Subject="my subject")
     message_id = response['MessageId']
+
+
+@mock_sqs
+@mock_sns
+def test_publish_subject():
+    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']
+
+    sqs_conn = boto3.resource('sqs', region_name='us-east-1')
+    sqs_conn.create_queue(QueueName="test-queue")
+
+    conn.subscribe(TopicArn=topic_arn,
+                   Protocol="sqs",
+                   Endpoint="arn:aws:sqs:us-east-1:123456789012:test-queue")
+    message = 'my message'
+    subject1 = 'test subject'
+    subject2 = 'test subject' * 20
+    with freeze_time("2015-01-01 12:00:00"):
+        conn.publish(TopicArn=topic_arn, Message=message, Subject=subject1)
+
+    # Just that it doesnt error is a pass
+    try:
+        with freeze_time("2015-01-01 12:00:00"):
+            conn.publish(TopicArn=topic_arn, Message=message, Subject=subject2)
+    except ClientError as err:
+        err.response['Error']['Code'].should.equal('InvalidParameter')
+    else:
+        raise RuntimeError('Should have raised an InvalidParameter exception')