summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Day <eday@oddments.org>2010-08-17 23:46:16 -0700
committerEric Day <eday@oddments.org>2010-08-17 23:46:16 -0700
commit67ea462eadcc02ca2f8244062c786bd98871e9e8 (patch)
tree4fa3dd4bb2f73c234c955eaa6d9b703b86812d27
parent1e403e56dc1147ce3feea1b8931948bc35f23a44 (diff)
downloadnova-67ea462eadcc02ca2f8244062c786bd98871e9e8.tar.gz
nova-67ea462eadcc02ca2f8244062c786bd98871e9e8.tar.xz
nova-67ea462eadcc02ca2f8244062c786bd98871e9e8.zip
Added unittests for wsgi and api.
-rw-r--r--nova/api/__init__.py5
-rw-r--r--nova/api/test.py70
-rw-r--r--nova/wsgi.py17
-rw-r--r--nova/wsgi_test.py133
-rw-r--r--pylintrc14
5 files changed, 224 insertions, 15 deletions
diff --git a/nova/api/__init__.py b/nova/api/__init__.py
index a6bb93348..b9b9e3988 100644
--- a/nova/api/__init__.py
+++ b/nova/api/__init__.py
@@ -32,7 +32,6 @@ class API(wsgi.Router):
def __init__(self):
mapper = routes.Mapper()
- mapper.connect(None, "/v1.0/{path_info:.*}",
- controller=rackspace.API())
- mapper.connect(None, "/ec2/{path_info:.*}", controller=ec2.API())
+ mapper.connect("/v1.0/{path_info:.*}", controller=rackspace.API())
+ mapper.connect("/ec2/{path_info:.*}", controller=ec2.API())
super(API, self).__init__(mapper)
diff --git a/nova/api/test.py b/nova/api/test.py
new file mode 100644
index 000000000..09f79c02e
--- /dev/null
+++ b/nova/api/test.py
@@ -0,0 +1,70 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Test for the root WSGI middleware for all API controllers.
+"""
+
+import unittest
+
+import stubout
+
+from nova import api
+from nova import wsgi_test
+
+
+class Test(unittest.TestCase):
+
+ def setUp(self): # pylint: disable-msg=C0103
+ self.called = False
+ self.stubs = stubout.StubOutForTesting()
+
+ def tearDown(self): # pylint: disable-msg=C0103
+ self.stubs.UnsetAll()
+
+ def test_rackspace(self):
+ self.stubs.Set(api.rackspace, 'API', get_api_stub(self))
+ api.API()(wsgi_test.get_environ({'PATH_INFO': '/v1.0/cloud'}),
+ wsgi_test.start_response)
+ self.assertTrue(self.called)
+
+ def test_ec2(self):
+ self.stubs.Set(api.ec2, 'API', get_api_stub(self))
+ api.API()(wsgi_test.get_environ({'PATH_INFO': '/ec2/cloud'}),
+ wsgi_test.start_response)
+ self.assertTrue(self.called)
+
+ def test_not_found(self):
+ self.stubs.Set(api.ec2, 'API', get_api_stub(self))
+ self.stubs.Set(api.rackspace, 'API', get_api_stub(self))
+ api.API()(wsgi_test.get_environ({'PATH_INFO': '/'}),
+ wsgi_test.start_response)
+ self.assertFalse(self.called)
+
+
+def get_api_stub(test_object):
+ """Get a stub class that verifies next part of the request."""
+
+ class APIStub(object):
+ """Class to verify request and mark it was called."""
+ test = test_object
+
+ def __call__(self, environ, start_response):
+ self.test.assertEqual(environ['PATH_INFO'], '/cloud')
+ self.test.called = True
+
+ return APIStub
diff --git a/nova/wsgi.py b/nova/wsgi.py
index a0a175dc7..baf6cccd9 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -83,7 +83,7 @@ class Application(object):
raise NotImplementedError("You must implement __call__")
-class Middleware(Application): # pylint: disable=W0223
+class Middleware(Application):
"""
Base WSGI middleware wrapper. These classes require an application to be
initialized that will be called next. By default the middleware will
@@ -91,11 +91,11 @@ class Middleware(Application): # pylint: disable=W0223
behavior.
"""
- def __init__(self, application): # pylint: disable=W0231
+ def __init__(self, application): # pylint: disable-msg=W0231
self.application = application
@webob.dec.wsgify
- def __call__(self, req):
+ def __call__(self, req): # pylint: disable-msg=W0221
"""Override to implement middleware behavior."""
return self.application
@@ -113,7 +113,7 @@ class Debug(Middleware):
resp = req.get_response(self.application)
print ("*" * 40) + " RESPONSE HEADERS"
- for (key, value) in resp.headers:
+ for (key, value) in resp.headers.iteritems():
print key, "=", value
print
@@ -127,7 +127,7 @@ class Debug(Middleware):
Iterator that prints the contents of a wrapper string iterator
when iterated.
"""
- print ("*" * 40) + "BODY"
+ print ("*" * 40) + " BODY"
for part in app_iter:
sys.stdout.write(part)
sys.stdout.flush()
@@ -176,8 +176,9 @@ class Router(object):
"""
return self._router
+ @staticmethod
@webob.dec.wsgify
- def _dispatch(self, req):
+ def _dispatch(req):
"""
Called by self._router after matching the incoming request to a route
and putting the information into req.environ. Either returns 404
@@ -197,6 +198,7 @@ class Controller(object):
must, in addition to their normal parameters, accept a 'req' argument
which is the incoming webob.Request.
"""
+
@webob.dec.wsgify
def __call__(self, req):
"""
@@ -249,6 +251,7 @@ class Serializer(object):
return repr(data)
def _to_xml_node(self, doc, metadata, nodename, data):
+ """Recursive method to convert data members to XML nodes."""
result = doc.createElement(nodename)
if type(data) is list:
singular = metadata.get('plurals', {}).get(nodename, None)
@@ -262,7 +265,7 @@ class Serializer(object):
result.appendChild(node)
elif type(data) is dict:
attrs = metadata.get('attributes', {}).get(nodename, {})
- for k,v in data.items():
+ for k, v in data.items():
if k in attrs:
result.setAttribute(k, str(v))
else:
diff --git a/nova/wsgi_test.py b/nova/wsgi_test.py
new file mode 100644
index 000000000..02bf067d6
--- /dev/null
+++ b/nova/wsgi_test.py
@@ -0,0 +1,133 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2010 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Test WSGI basics and provide some helper functions for other WSGI tests.
+"""
+
+import unittest
+
+import routes
+
+from nova import wsgi
+
+
+class Test(unittest.TestCase):
+
+ def setUp(self): # pylint: disable-msg=C0103
+ self.called = False
+
+ def test_debug(self):
+
+ class Application(wsgi.Application):
+ """Dummy application to test debug."""
+ test = self
+
+ def __call__(self, environ, test_start_response):
+ test_start_response("200", [("X-Test", "checking")])
+ self.test.called = True
+ return ['Test response']
+
+ app = wsgi.Debug(Application())(get_environ(), start_response)
+ self.assertTrue(self.called)
+ for _ in app:
+ pass
+
+ def test_router(self):
+
+ class Application(wsgi.Application):
+ """Test application to call from router."""
+ test = self
+
+ def __call__(self, environ, test_start_response):
+ test_start_response("200", [])
+ self.test.called = True
+ return []
+
+ class Router(wsgi.Router):
+ """Test router."""
+
+ def __init__(self):
+ mapper = routes.Mapper()
+ mapper.connect("/test", controller=Application())
+ super(Router, self).__init__(mapper)
+
+ Router()(get_environ({'PATH_INFO': '/test'}), start_response)
+ self.assertTrue(self.called)
+ self.called = False
+ Router()(get_environ({'PATH_INFO': '/bad'}), start_response)
+ self.assertFalse(self.called)
+
+ def test_controller(self):
+
+ class Controller(wsgi.Controller):
+ """Test controller to call from router."""
+ test = self
+
+ def show(self, **kwargs):
+ """Mark that this has been called."""
+ self.test.called = True
+ self.test.assertEqual(kwargs['id'], '123')
+ return "Test"
+
+ class Router(wsgi.Router):
+ """Test router."""
+
+ def __init__(self):
+ mapper = routes.Mapper()
+ mapper.resource("test", "tests", controller=Controller())
+ super(Router, self).__init__(mapper)
+
+ Router()(get_environ({'PATH_INFO': '/tests/123'}), start_response)
+ self.assertTrue(self.called)
+ self.called = False
+ Router()(get_environ({'PATH_INFO': '/test/123'}), start_response)
+ self.assertFalse(self.called)
+
+ def test_serializer(self):
+ # TODO(eday): Placeholder for serializer testing.
+ pass
+
+
+def get_environ(overwrite={}): # pylint: disable-msg=W0102
+ """Get a WSGI environment, overwriting any entries given."""
+ environ = {'SERVER_PROTOCOL': 'HTTP/1.1',
+ 'GATEWAY_INTERFACE': 'CGI/1.1',
+ 'wsgi.version': (1, 0),
+ 'SERVER_PORT': '443',
+ 'SERVER_NAME': '127.0.0.1',
+ 'REMOTE_ADDR': '127.0.0.1',
+ 'wsgi.run_once': False,
+ 'wsgi.errors': None,
+ 'wsgi.multiprocess': False,
+ 'SCRIPT_NAME': '',
+ 'wsgi.url_scheme': 'https',
+ 'wsgi.input': None,
+ 'REQUEST_METHOD': 'GET',
+ 'PATH_INFO': '/',
+ 'CONTENT_TYPE': 'text/plain',
+ 'wsgi.multithread': True,
+ 'QUERY_STRING': '',
+ 'eventlet.input': None}
+ return dict(environ, **overwrite)
+
+
+def start_response(_status, _headers):
+ """Dummy start_response to use with WSGI tests."""
+ pass
diff --git a/pylintrc b/pylintrc
index 6c799c7ea..36cc337e5 100644
--- a/pylintrc
+++ b/pylintrc
@@ -1,9 +1,7 @@
[Messages Control]
-disable=C0103
-# TODOs in code comments are fine...
-disable=W0511
-# *args and **kwargs are fine
-disable=W0142
+# W0511: TODOs in code comments are fine.
+# W0142: *args and **kwargs are fine.
+disable-msg=W0511,W0142
[Basic]
# Variables can be 1 to 31 characters long, with
@@ -14,6 +12,12 @@ variable-rgx=[a-z_][a-z0-9_]{0,30}$
# and be lowecased with underscores
method-rgx=[a-z_][a-z0-9_]{2,50}$
+# Module names matching nova-* are ok (files in bin/)
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(nova-[a-z0-9_]+))$
+
+# Don't require docstrings on tests.
+no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
+
[Design]
max-public-methods=100
min-public-methods=0