Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

test(functional): Use direct WSGI requests in lieu of a wsgiref server

The wsgiref server is very flaky when running in the gate, so let's just
pass WSGI requests directly to the app for now, at look into adding a
devstack job to the gate that will use a standalone server.

Change-Id: Ifb6cd0c9080ded7ab93cced6e0d40a776eb2cd50
  • Loading branch information...
commit 626609f4cf23785b41960b800c1a9f5bc877b343 1 parent d99e975
@kgriffs kgriffs authored
View
2  marconi/queues/transport/wsgi/app.py
@@ -18,7 +18,7 @@
This app should be used by external WSGI
containers. For example:
- $ gunicorn marconi.queues.transport.wsgi.public.app:app
+ $ gunicorn marconi.queues.transport.wsgi.app:app
NOTE: As for external containers, it is necessary
to put config files in the standard paths. There's
View
37 marconi/tests/functional/base.py
@@ -17,20 +17,28 @@
import abc
import jsonschema
import multiprocessing
-
+import os
from marconi.openstack.common import timeutils
from marconi.queues import bootstrap
-# NOTE(flaper87): This is necessary to register,
+# TODO(flaper87): This is necessary to register,
# wsgi configs and won't be permanent. It'll be
# refactored as part of the work for this blueprint
from marconi.queues.transport import validation
from marconi.queues.transport import wsgi # noqa
+from marconi.queues.transport.wsgi import app
from marconi import tests as testing
from marconi.tests.functional import config
from marconi.tests.functional import helpers
from marconi.tests.functional import http
+# TODO(kgriffs): Run functional tests to a devstack gate job and
+# set this using an environment variable or something.
+#
+# TODO(kgriffs): Find a more general way to do this; we seem to be
+# using this environ flag pattern over and over againg.
+_TEST_INTEGRATION = os.environ.get('MARCONI_TEST_INTEGRATION') is not None
+
class FunctionalTestBase(testing.TestBase):
@@ -51,19 +59,24 @@ def setUp(self):
self.mconf = self.load_conf(self.cfg.marconi.config)
- # NOTE(flaper87): Use running instances.
- if self.cfg.marconi.run_server:
- if not (self.server and self.server.is_alive()):
- # pylint: disable=not-callable
- self.server = self.server_class()
- self.server.start(self.mconf)
-
validator = validation.Validator(self.mconf)
self.limits = validator._limits_conf
- # NOTE(flaper87): Create client
- # for this test unit.
- self.client = http.Client()
+ if _TEST_INTEGRATION:
+ # TODO(kgriffs): This code should be replaced to use
+ # an external wsgi server instance.
+
+ # NOTE(flaper87): Use running instances.
+ if self.cfg.marconi.run_server:
+ if not (self.server and self.server.is_alive()):
+ # pylint: disable=not-callable
+ self.server = self.server_class()
+ self.server.start(self.mconf)
+
+ self.client = http.Client()
+ else:
+ self.client = http.WSGIClient(app.app)
+
self.headers = helpers.create_marconi_headers(self.cfg)
if self.cfg.auth.auth_on:
View
126 marconi/tests/functional/http.py
@@ -16,7 +16,9 @@
import functools
import json
+from falcon import testing as ftest
import requests
+import six
def _build_url(method):
@@ -38,6 +40,7 @@ def wrapper(self, url='', **kwargs):
class Client(object):
def __init__(self):
+ # NOTE(kgriffs): used by @_build_url
self.base_url = None
self.session = requests.session()
@@ -86,3 +89,126 @@ def patch(self, url=None, **kwargs):
if "data" in kwargs:
kwargs['data'] = json.dumps(kwargs["data"])
return self.session.patch(url, **kwargs)
+
+
+class ResponseMock(object):
+ """Mocks part of the Requests library's Response object."""
+
+ def __init__(self, srmock, wsgi_result):
+ self.status_code = int(srmock.status.partition(' ')[0])
+ self._body = wsgi_result[0] if wsgi_result else ''
+ self.headers = srmock.headers_dict
+
+ def json(self):
+ return json.loads(self._body, encoding='utf-8')
+
+
+class WSGIClient(object):
+ """Same inteface as Client, but speaks directly to a WSGI callable."""
+
+ def __init__(self, app):
+ # NOTE(kgriffs): used by @_build_url
+ self.base_url = None
+
+ self.app = app
+ self.headers = {}
+
+ @staticmethod
+ def _sanitize_headers(headers):
+ # NOTE(kgriffs): Workaround for a little create_environ bug
+ return dict([(key, '' if value is None else value)
+ for key, value in headers.items()])
+
+ def _simulate_request(self, url, method='GET', data=None,
+ headers=None, params=None):
+ """Simulate a request.
+
+ Simulates a WSGI request to the API for testing.
+
+ :param url: Request path for the desired resource
+ :param method: (Default 'GET') The HTTP method to send
+ :param data: (Default None) A dict that will be serialized
+ to JSON and submitted as the body of the request. May
+ also be a pre-serialized string.
+ :param headers: (Default None) A dict containing
+ extra HTTP headers to send.
+ :param params: (Default None) A dict of parameters
+ to use in the query string for the request.
+
+ :returns: a requests response instance
+ """
+
+ if headers is None:
+ headers = self.headers
+
+ headers = self._sanitize_headers(headers)
+
+ if data is None:
+ body = ''
+ elif isinstance(data, str) or isinstance(data, six.text_type):
+ body = data
+ else:
+ body = json.dumps(data, ensure_ascii=False)
+
+ parsed_url = six.moves.urllib_parse.urlparse(url)
+
+ query = parsed_url.query
+
+ if params is not None:
+ extra = '&'.join([key + '=' + str(value)
+ for key, value in params.items()])
+
+ query += '&' + extra
+
+ environ = ftest.create_environ(method=method,
+ path=parsed_url.path,
+ query_string=query,
+ headers=headers,
+ body=body)
+
+ srmock = ftest.StartResponseMock()
+ wsgi_result = self.app(environ, srmock)
+
+ return ResponseMock(srmock, wsgi_result)
+
+ def set_base_url(self, base_url):
+ self.base_url = base_url
+
+ def set_headers(self, headers):
+ self.headers.update(headers)
+
+ @_build_url
+ def get(self, url=None, **kwargs):
+ """Simulate a GET request."""
+ kwargs['method'] = 'GET'
+ return self._simulate_request(url=url, **kwargs)
+
+ @_build_url
+ def head(self, url=None, **kwargs):
+ """Simulate a HEAD request."""
+ kwargs['method'] = 'HEAD'
+ return self._simulate_request(url=url, **kwargs)
+
+ @_build_url
+ def post(self, url=None, **kwargs):
+ """Simulate a POST request."""
+ kwargs['method'] = 'POST'
+ return self._simulate_request(url=url, **kwargs)
+
+ @_build_url
+ def put(self, url=None, **kwargs):
+ """Simulate a PUT request."""
+ kwargs['method'] = 'PUT'
+ return self._simulate_request(url=url, **kwargs)
+
+ @_build_url
+ def delete(self, url=None, **kwargs):
+ """Simulate a DELETE request."""
+ kwargs['method'] = 'DELETE'
+ return self._simulate_request(url=url, **kwargs)
+
+ @_build_url
+ def patch(self, url=None, **kwargs):
+ """Simulate a PATCH request."""
+ kwargs['method'] = 'PATCH'
+ return self._simulate_request(url=url, **kwargs)
Please sign in to comment.
Something went wrong with that request. Please try again.