Repository: getmoto/moto

Issue:
boto.SQS.Client.untag_queue() when TagKeys=[] should raise error
In boto, `SQS.Client.untag_queue()` will raise an error if input `TagKeys=[]`:

```
client.untag_queue(QueueUrl=queue_url, TagKeys=[])
---------------------------------------------------------------------------
ClientError                               Traceback (most recent call last)
<ipython-input-12-644dd9d69c3d> in <module>()
----> 1 client.untag_queue(QueueUrl=queue_url, TagKeys=[])

/.../python2.7/site-packages/botocore/client.pyc in _api_call(self, *args, **kwargs)
    310                     "%s() only accepts keyword arguments." % py_operation_name)
    311             # The "self" in this scope is referring to the BaseClient.
--> 312             return self._make_api_call(operation_name, kwargs)
    313
    314         _api_call.__name__ = str(py_operation_name)

/.../python2.7/site-packages/botocore/client.pyc in _make_api_call(self, operation_name, api_params)
    603             error_code = parsed_response.get("Error", {}).get("Code")
    604             error_class = self.exceptions.from_code(error_code)
--> 605             raise error_class(parsed_response, operation_name)
    606         else:
    607             return parsed_response

ClientError: An error occurred (InvalidParameterValue) when calling the UntagQueue operation: Tag keys must be between 1 and 128 characters in length.
```

while in moto, it will silently do nothing.

```
$ python --version
Python 2.7.11
$ python -c "import boto3; print(boto3.__version__)"
1.4.7
$ python -c "import moto; print(moto.__version__)"
1.3.1
```


Discussion:
Thanks for opening. I'll add this as a feature request.

Resolving patch:
diff --git a/moto/sqs/models.py b/moto/sqs/models.py
--- a/moto/sqs/models.py
+++ b/moto/sqs/models.py
@@ -415,7 +415,7 @@ def reset(self):
         self.__dict__ = {}
         self.__init__(region_name)
 
-    def create_queue(self, name, **kwargs):
+    def create_queue(self, name, tags=None, **kwargs):
         queue = self.queues.get(name)
         if queue:
             try:
@@ -454,6 +454,10 @@ def create_queue(self, name, **kwargs):
                 pass
             queue = Queue(name, region=self.region_name, **kwargs)
             self.queues[name] = queue
+
+        if tags:
+            queue.tags = tags
+
         return queue
 
     def list_queues(self, queue_name_prefix):
@@ -654,6 +658,10 @@ def tag_queue(self, queue_name, tags):
 
     def untag_queue(self, queue_name, tag_keys):
         queue = self.get_queue(queue_name)
+
+        if len(tag_keys) == 0:
+            raise RESTError('InvalidParameterValue', 'Tag keys must be between 1 and 128 characters in length.')
+
         for key in tag_keys:
             try:
                 del queue.tags[key]
diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py
--- a/moto/sqs/responses.py
+++ b/moto/sqs/responses.py
@@ -33,6 +33,12 @@ def attribute(self):
             self._attribute = self._get_map_prefix('Attribute', key_end='.Name', value_end='.Value')
         return self._attribute
 
+    @property
+    def tags(self):
+        if not hasattr(self, '_tags'):
+            self._tags = self._get_map_prefix('Tag', key_end='.Key', value_end='.Value')
+        return self._tags
+
     def _get_queue_name(self):
         try:
             queue_name = self.querystring.get('QueueUrl')[0].split("/")[-1]
@@ -73,12 +79,12 @@ def create_queue(self):
         queue_name = self._get_param("QueueName")
 
         try:
-            queue = self.sqs_backend.create_queue(queue_name, **self.attribute)
+            queue = self.sqs_backend.create_queue(queue_name, self.tags, **self.attribute)
         except MessageAttributesInvalid as e:
             return self._error('InvalidParameterValue', e.description)
 
         template = self.response_template(CREATE_QUEUE_RESPONSE)
-        return template.render(queue=queue, request_url=request_url)
+        return template.render(queue_url=queue.url(request_url))
 
     def get_queue_url(self):
         request_url = urlparse(self.uri)
@@ -400,7 +406,11 @@ def untag_queue(self):
         queue_name = self._get_queue_name()
         tag_keys = self._get_multi_param('TagKey')
 
-        self.sqs_backend.untag_queue(queue_name, tag_keys)
+        try:
+            self.sqs_backend.untag_queue(queue_name, tag_keys)
+        except QueueDoesNotExist as e:
+            return self._error('AWS.SimpleQueueService.NonExistentQueue',
+                               e.description)
 
         template = self.response_template(UNTAG_QUEUE_RESPONSE)
         return template.render()
@@ -416,8 +426,7 @@ def list_queue_tags(self):
 
 CREATE_QUEUE_RESPONSE = """<CreateQueueResponse>
     <CreateQueueResult>
-        <QueueUrl>{{ queue.url(request_url) }}</QueueUrl>
-        <VisibilityTimeout>{{ queue.visibility_timeout }}</VisibilityTimeout>
+        <QueueUrl>{{ queue_url }}</QueueUrl>
     </CreateQueueResult>
     <ResponseMetadata>
         <RequestId></RequestId>


Test patch:
diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py
--- a/tests/test_sqs/test_sqs.py
+++ b/tests/test_sqs/test_sqs.py
@@ -140,6 +140,22 @@ def test_create_queue_kms():
     queue.attributes.get('KmsDataKeyReusePeriodSeconds').should.equal('600')
 
 
+@mock_sqs
+def test_create_queue_with_tags():
+    client = boto3.client('sqs', region_name='us-east-1')
+    response = client.create_queue(
+        QueueName = 'test-queue-with-tags',
+        tags = {
+            'tag_key_1': 'tag_value_1'
+        }
+    )
+    queue_url = response['QueueUrl']
+
+    client.list_queue_tags(QueueUrl = queue_url)['Tags'].should.equal({
+        'tag_key_1': 'tag_value_1'
+    })
+
+
 @mock_sqs
 def test_get_nonexistent_queue():
     sqs = boto3.resource('sqs', region_name='us-east-1')
@@ -959,6 +975,48 @@ def test_tags():
     resp['Tags'].should.contain('test1')
     resp['Tags'].should_not.contain('test2')
 
+    # removing a non existing tag should not raise any error
+    client.untag_queue(
+        QueueUrl=queue_url,
+        TagKeys=[
+            'not-existing-tag'
+        ]
+    )
+    client.list_queue_tags(QueueUrl=queue_url)['Tags'].should.equal({
+        'test1': 'value1'
+    })
+
+
+@mock_sqs
+def test_untag_queue_errors():
+    client = boto3.client('sqs', region_name='us-east-1')
+
+    response = client.create_queue(
+        QueueName='test-queue-with-tags',
+        tags={
+            'tag_key_1': 'tag_value_1'
+        }
+    )
+    queue_url = response['QueueUrl']
+
+    client.untag_queue.when.called_with(
+        QueueUrl=queue_url + '-not-existing',
+        TagKeys=[
+            'tag_key_1'
+        ]
+    ).should.throw(
+        ClientError,
+        "The specified queue does not exist for this wsdl version."
+    )
+
+    client.untag_queue.when.called_with(
+        QueueUrl=queue_url,
+        TagKeys=[]
+    ).should.throw(
+        ClientError,
+        'Tag keys must be between 1 and 128 characters in length.'
+    )
+
 
 @mock_sqs
 def test_create_fifo_queue_with_dlq():