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

This commit is contained in:
Jenkins
2014-02-04 16:29:47 +00:00
committed by Gerrit Code Review
3 changed files with 152 additions and 13 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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)