Merge "test(functional): Use direct WSGI requests in lieu of a wsgiref server"
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
This app should be used by external WSGI
|
This app should be used by external WSGI
|
||||||
containers. For example:
|
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
|
NOTE: As for external containers, it is necessary
|
||||||
to put config files in the standard paths. There's
|
to put config files in the standard paths. There's
|
||||||
|
|||||||
@@ -17,20 +17,28 @@
|
|||||||
import abc
|
import abc
|
||||||
import jsonschema
|
import jsonschema
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
|
||||||
from marconi.openstack.common import timeutils
|
from marconi.openstack.common import timeutils
|
||||||
from marconi.queues import bootstrap
|
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
|
# wsgi configs and won't be permanent. It'll be
|
||||||
# refactored as part of the work for this blueprint
|
# refactored as part of the work for this blueprint
|
||||||
from marconi.queues.transport import validation
|
from marconi.queues.transport import validation
|
||||||
from marconi.queues.transport import wsgi # noqa
|
from marconi.queues.transport import wsgi # noqa
|
||||||
|
from marconi.queues.transport.wsgi import app
|
||||||
from marconi import tests as testing
|
from marconi import tests as testing
|
||||||
from marconi.tests.functional import config
|
from marconi.tests.functional import config
|
||||||
from marconi.tests.functional import helpers
|
from marconi.tests.functional import helpers
|
||||||
from marconi.tests.functional import http
|
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):
|
class FunctionalTestBase(testing.TestBase):
|
||||||
|
|
||||||
@@ -51,19 +59,24 @@ class FunctionalTestBase(testing.TestBase):
|
|||||||
|
|
||||||
self.mconf = self.load_conf(self.cfg.marconi.config)
|
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)
|
validator = validation.Validator(self.mconf)
|
||||||
self.limits = validator._limits_conf
|
self.limits = validator._limits_conf
|
||||||
|
|
||||||
# NOTE(flaper87): Create client
|
if _TEST_INTEGRATION:
|
||||||
# for this test unit.
|
# TODO(kgriffs): This code should be replaced to use
|
||||||
self.client = http.Client()
|
# 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)
|
self.headers = helpers.create_marconi_headers(self.cfg)
|
||||||
|
|
||||||
if self.cfg.auth.auth_on:
|
if self.cfg.auth.auth_on:
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from falcon import testing as ftest
|
||||||
import requests
|
import requests
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
def _build_url(method):
|
def _build_url(method):
|
||||||
@@ -38,6 +40,7 @@ def _build_url(method):
|
|||||||
class Client(object):
|
class Client(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
# NOTE(kgriffs): used by @_build_url
|
||||||
self.base_url = None
|
self.base_url = None
|
||||||
self.session = requests.session()
|
self.session = requests.session()
|
||||||
|
|
||||||
@@ -86,3 +89,126 @@ class Client(object):
|
|||||||
if "data" in kwargs:
|
if "data" in kwargs:
|
||||||
kwargs['data'] = json.dumps(kwargs["data"])
|
kwargs['data'] = json.dumps(kwargs["data"])
|
||||||
return self.session.patch(url, **kwargs)
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user