diff --git a/moto/__init__.py b/moto/__init__.py
index 24dc1456c..e9a5815ed 100644
--- a/moto/__init__.py
+++ b/moto/__init__.py
@@ -4,3 +4,4 @@ logging.getLogger('boto').setLevel(logging.CRITICAL)
from .dynamodb import mock_dynamodb
from .ec2 import mock_ec2
from .s3 import mock_s3
+from .sqs import mock_sqs
diff --git a/moto/core/utils.py b/moto/core/utils.py
new file mode 100644
index 000000000..e49670573
--- /dev/null
+++ b/moto/core/utils.py
@@ -0,0 +1,43 @@
+import inspect
+from urlparse import parse_qs
+
+
+def headers_to_dict(headers):
+ result = {}
+ for index, header in enumerate(headers.split("\r\n")):
+ if not header:
+ continue
+ if index:
+ # Parsing headers
+ key, value = header.split(":", 1)
+ result[key.strip()] = value.strip()
+ else:
+ # Parsing method and path
+ path_and_querystring = header.split(" /")[1]
+ if '?' in path_and_querystring:
+ querystring = path_and_querystring.split("?")[1]
+ else:
+ querystring = path_and_querystring
+ queryset_dict = parse_qs(querystring)
+ result.update(queryset_dict)
+ return result
+
+
+def camelcase_to_underscores(argument):
+ ''' Converts a camelcase param like theNewAttribute to the equivalent
+ python underscore variable like the_new_attribute'''
+ result = ''
+ prev_char_title = True
+ for char in argument:
+ if char.istitle() and not prev_char_title:
+ # Only add underscore if char is capital, not first letter, and prev
+ # char wasn't capital
+ result += "_"
+ prev_char_title = char.istitle()
+ if not char.isspace(): # Only add non-whitespace
+ result += char.lower()
+ return result
+
+
+def method_names_from_class(clazz):
+ return [x[0] for x in inspect.getmembers(clazz, predicate=inspect.ismethod)]
diff --git a/moto/ec2/responses/__init__.py b/moto/ec2/responses/__init__.py
index 8203a1c36..ce27693c6 100644
--- a/moto/ec2/responses/__init__.py
+++ b/moto/ec2/responses/__init__.py
@@ -1,6 +1,6 @@
from urlparse import parse_qs
-from moto.ec2.utils import camelcase_to_underscores, method_namess_from_class
+from moto.core.utils import camelcase_to_underscores, method_names_from_class
from .amazon_dev_pay import AmazonDevPay
from .amis import AmisResponse
@@ -76,7 +76,7 @@ class EC2Response(object):
action = camelcase_to_underscores(action)
for sub_response in self.sub_responses:
- method_names = method_namess_from_class(sub_response)
+ method_names = method_names_from_class(sub_response)
if action in method_names:
response = sub_response(querystring)
method = getattr(response, action)
diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py
index 758b89a38..798b84137 100644
--- a/moto/ec2/responses/instances.py
+++ b/moto/ec2/responses/instances.py
@@ -1,7 +1,8 @@
from jinja2 import Template
+from moto.core.utils import camelcase_to_underscores
from moto.ec2.models import ec2_backend
-from moto.ec2.utils import instance_ids_from_querystring, camelcase_to_underscores
+from moto.ec2.utils import instance_ids_from_querystring
class InstanceResponse(object):
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index 08b07b58c..d95ad3bbe 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -1,4 +1,3 @@
-import inspect
import random
@@ -53,23 +52,3 @@ def resource_ids_from_querystring(querystring_dict):
response_values[value[0]] = (tag_key, tag_value)
return response_values
-
-
-def camelcase_to_underscores(argument):
- ''' Converts a camelcase param like theNewAttribute to the equivalent
- python underscore variable like the_new_attribute'''
- result = ''
- prev_char_title = True
- for char in argument:
- if char.istitle() and not prev_char_title:
- # Only add underscore if char is capital, not first letter, and prev
- # char wasn't capital
- result += "_"
- prev_char_title = char.istitle()
- if not char.isspace(): # Only add non-whitespace
- result += char.lower()
- return result
-
-
-def method_namess_from_class(clazz):
- return [x[0] for x in inspect.getmembers(clazz, predicate=inspect.ismethod)]
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index 74f80ec81..f830701a5 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -1,7 +1,8 @@
from jinja2 import Template
from .models import s3_backend
-from .utils import bucket_name_from_hostname, headers_to_dict
+from moto.core.utils import headers_to_dict
+from .utils import bucket_name_from_hostname
def all_buckets(uri, body, method):
diff --git a/moto/s3/utils.py b/moto/s3/utils.py
index 40cbf99f0..4f1325aae 100644
--- a/moto/s3/utils.py
+++ b/moto/s3/utils.py
@@ -6,13 +6,3 @@ bucket_name_regex = re.compile("(.+).s3.amazonaws.com")
def bucket_name_from_hostname(hostname):
bucket_result = bucket_name_regex.search(hostname)
return bucket_result.groups()[0]
-
-
-def headers_to_dict(headers):
- result = {}
- for header in headers.split("\r\n"):
- if ':' in header:
- key, value = header.split(":", 1)
- result[key.strip()] = value.strip()
-
- return result
diff --git a/moto/sqs/__init__.py b/moto/sqs/__init__.py
new file mode 100644
index 000000000..e04391a39
--- /dev/null
+++ b/moto/sqs/__init__.py
@@ -0,0 +1,2 @@
+from .models import sqs_backend
+mock_sqs = sqs_backend.decorator
diff --git a/moto/sqs/models.py b/moto/sqs/models.py
new file mode 100644
index 000000000..f90aee579
--- /dev/null
+++ b/moto/sqs/models.py
@@ -0,0 +1,42 @@
+from moto.core import BaseBackend
+from moto.core.utils import camelcase_to_underscores
+
+
+class Queue(object):
+ camelcase_attributes = ['VisibilityTimeout']
+
+ def __init__(self, name, visibility_timeout):
+ self.name = name
+ self.visibility_timeout = visibility_timeout
+
+ @property
+ def attributes(self):
+ result = {}
+ for attribute in self.camelcase_attributes:
+ result[attribute] = getattr(self, camelcase_to_underscores(attribute))
+ return result
+
+class SQSBackend(BaseBackend):
+
+ def __init__(self):
+ self.queues = {}
+ super(SQSBackend, self).__init__()
+
+ def create_queue(self, name, visibility_timeout):
+ queue = Queue(name, visibility_timeout)
+ self.queues[name] = queue
+ return queue
+
+ def list_queues(self):
+ return self.queues.values()
+
+ def get_queue(self, queue_name):
+ return self.queues[queue_name]
+
+ def delete_queue(self, queue_name):
+ if queue_name in self.queues:
+ return self.queues.pop(queue_name)
+ return False
+
+
+sqs_backend = SQSBackend()
diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py
new file mode 100644
index 000000000..a4f4c735b
--- /dev/null
+++ b/moto/sqs/responses.py
@@ -0,0 +1,110 @@
+from urlparse import parse_qs
+
+from jinja2 import Template
+
+from moto.core.utils import headers_to_dict, camelcase_to_underscores, method_names_from_class
+from .models import sqs_backend
+
+
+class BaseResponse(object):
+ def dispatch(self, uri, body, headers):
+ if body:
+ querystring = parse_qs(body)
+ else:
+ querystring = headers_to_dict(headers)
+
+ self.path = uri.path
+ self.querystring = querystring
+
+ action = querystring['Action'][0]
+ action = camelcase_to_underscores(action)
+
+ method_names = method_names_from_class(self.__class__)
+ if action in method_names:
+ method = getattr(self, action)
+ return method()
+ raise NotImplementedError("The {} action has not been implemented".format(action))
+
+
+class QueuesResponse(BaseResponse):
+
+ def create_queue(self):
+ visibility_timeout = None
+ if 'Attribute.1.Name' in self.querystring and self.querystring.get('Attribute.1.Name')[0] == 'VisibilityTimeout':
+ visibility_timeout = self.querystring.get("Attribute.1.Value")[0]
+
+ queue_name = self.querystring.get("QueueName")[0]
+ queue = sqs_backend.create_queue(queue_name, visibility_timeout=visibility_timeout)
+ template = Template(CREATE_QUEUE_RESPONSE)
+ return template.render(queue=queue)
+
+ def list_queues(self):
+ queues = sqs_backend.list_queues()
+ template = Template(LIST_QUEUES_RESPONSE)
+ return template.render(queues=queues)
+
+
+class QueueResponse(BaseResponse):
+ def get_queue_attributes(self):
+ queue_name = self.path.split("/")[-1]
+ queue = sqs_backend.get_queue(queue_name)
+ template = Template(GET_QUEUE_ATTRIBUTES_RESPONSE)
+ return template.render(queue=queue)
+
+ def delete_queue(self):
+ queue_name = self.path.split("/")[-1]
+ queue = sqs_backend.delete_queue(queue_name)
+ if not queue:
+ return "A queue with name {} does not exist".format(queue_name), dict(status=404)
+ template = Template(DELETE_QUEUE_RESPONSE)
+ return template.render(queue=queue)
+
+
+
+CREATE_QUEUE_RESPONSE = """
+
+ http://sqs.us-east-1.amazonaws.com/123456789012/{{ queue.name }}
+ {{ queue.visibility_timeout }}
+
+
+
+ 7a62c49f-347e-4fc4-9331-6e8e7a96aa73
+
+
+"""
+
+LIST_QUEUES_RESPONSE = """
+
+ {% for queue in queues %}
+ http://sqs.us-east-1.amazonaws.com/123456789012/{{ queue.name }}
+ {{ queue.visibility_timeout }}
+ {% endfor %}
+
+
+
+ 725275ae-0b9b-4762-b238-436d7c65a1ac
+
+
+"""
+
+DELETE_QUEUE_RESPONSE = """
+
+
+ 6fde8d1e-52cd-4581-8cd9-c512f4c64223
+
+
+"""
+
+GET_QUEUE_ATTRIBUTES_RESPONSE = """
+
+ {% for key, value in queue.attributes.items() %}
+
+ {{ key }}
+ {{ value }}
+
+ {% endfor %}
+
+
+ 1ea71be5-b5a2-4f9d-b85a-945d8d08cd0b
+
+"""
\ No newline at end of file
diff --git a/moto/sqs/urls.py b/moto/sqs/urls.py
new file mode 100644
index 000000000..acf8c4a32
--- /dev/null
+++ b/moto/sqs/urls.py
@@ -0,0 +1,10 @@
+from .responses import QueueResponse, QueuesResponse
+
+base_url = "https://(.*).amazonaws.com"
+#base_url2 = "https://sqs.us-east-1.amazonaws.com"
+
+
+urls = {
+ '{0}/$'.format(base_url): QueuesResponse().dispatch,
+ '{0}/(\d+)/(.*)$'.format(base_url): QueueResponse().dispatch,
+}
diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py
new file mode 100644
index 000000000..c3dce18f5
--- /dev/null
+++ b/tests/test_sqs/test_sqs.py
@@ -0,0 +1,30 @@
+import boto
+from boto.exception import SQSError
+
+from sure import expect
+
+from moto import mock_sqs
+
+
+@mock_sqs
+def test_create_queue():
+ conn = boto.connect_sqs('the_key', 'the_secret')
+ conn.create_queue("test-queue", visibility_timeout=60)
+
+ all_queues = conn.get_all_queues()
+ all_queues[0].name.should.equal("test-queue")
+
+ all_queues[0].get_timeout().should.equal(60)
+
+
+@mock_sqs
+def test_delete_queue():
+ conn = boto.connect_sqs('the_key', 'the_secret')
+ queue = conn.create_queue("test-queue", visibility_timeout=60)
+
+ conn.get_all_queues().should.have.length_of(1)
+
+ queue.delete()
+ conn.get_all_queues().should.have.length_of(0)
+
+ queue.delete.when.called_with().should.throw(SQSError)