Add TestServerTestCaseMixin
Allows performing mocked HTTP requests. Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import requests
|
||||
|
||||
from django.utils.six import BytesIO
|
||||
|
||||
|
||||
class TestClientAdapter(requests.adapters.BaseAdapter):
|
||||
def __init__(self, test_case):
|
||||
self.test_case = test_case
|
||||
|
||||
def build_response(self, request, django_response):
|
||||
"""
|
||||
Build a requests response from a Django response
|
||||
"""
|
||||
response = requests.Response()
|
||||
|
||||
# Fallback to None if there's no status_code, for whatever reason.
|
||||
response.status_code = getattr(
|
||||
django_response, 'status_code', None
|
||||
)
|
||||
|
||||
# Make headers case-insensitive.
|
||||
response.headers = requests.structures.CaseInsensitiveDict(
|
||||
getattr(django_response, 'headers', {})
|
||||
)
|
||||
|
||||
# Set encoding.
|
||||
response.encoding = requests.utils.get_encoding_from_headers(
|
||||
headers=response.headers
|
||||
)
|
||||
response.raw = BytesIO(django_response.getvalue())
|
||||
|
||||
response.reason = django_response.reason_phrase
|
||||
|
||||
if isinstance(request.url, bytes):
|
||||
response.url = request.url.decode('utf-8')
|
||||
else:
|
||||
response.url = request.url
|
||||
|
||||
# Add new cookies from the server.
|
||||
requests.cookies.extract_cookies_to_jar(
|
||||
jar=response.cookies, request=request,
|
||||
response=django_response
|
||||
)
|
||||
|
||||
# Give the Response some context.
|
||||
response.request = django_response
|
||||
response.connection = self
|
||||
|
||||
return response
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
No connection needs to be closed, but the method must exists
|
||||
or a NotImplementedError is raised.
|
||||
"""
|
||||
|
||||
def send(
|
||||
self, request=None, stream=False, timeout=None, verify=True,
|
||||
cert=None, proxies=None
|
||||
):
|
||||
"""Craft a Django request based on the attribute of
|
||||
requests' request object.
|
||||
"""
|
||||
return self.build_response(
|
||||
request=request, django_response=self.test_case.generic(
|
||||
data=request.body,
|
||||
headers=request.headers,
|
||||
method=request.method,
|
||||
viewname=self.test_case.test_view_name,
|
||||
)
|
||||
)
|
||||
@@ -43,3 +43,6 @@ TEST_TAR_GZ_FILE_PATH = os.path.join(
|
||||
TEST_ZIP_FILE_PATH = os.path.join(
|
||||
settings.BASE_DIR, 'apps', 'common', 'tests', 'contrib', TEST_ZIP_FILE
|
||||
)
|
||||
|
||||
TEST_SERVER_HOST = 'testserver'
|
||||
TEST_SERVER_SCHEME = 'http'
|
||||
|
||||
@@ -23,7 +23,9 @@ from django.utils.six import PY3
|
||||
|
||||
from mayan.apps.storage.settings import setting_temporary_directory
|
||||
|
||||
from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
|
||||
from .literals import (
|
||||
TEST_SERVER_HOST, TEST_SERVER_SCHEME, TEST_VIEW_NAME, TEST_VIEW_URL
|
||||
)
|
||||
|
||||
|
||||
if getattr(settings, 'COMMON_TEST_FILE_HANDLES', False):
|
||||
@@ -35,6 +37,7 @@ class ClientMethodsTestCaseMixin(object):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
query = kwargs.pop('query', {})
|
||||
headers = kwargs.pop('headers', {})
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
@@ -42,7 +45,9 @@ class ClientMethodsTestCaseMixin(object):
|
||||
path = furl(url=path)
|
||||
path.args.update(query)
|
||||
|
||||
return {'follow': follow, 'data': data, 'path': path.tostr()}
|
||||
result = {'follow': follow, 'data': data, 'path': path.tostr()}
|
||||
result.update(headers)
|
||||
return result
|
||||
|
||||
def delete(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.delete(
|
||||
@@ -51,6 +56,13 @@ class ClientMethodsTestCaseMixin(object):
|
||||
)
|
||||
)
|
||||
|
||||
def generic(self, method, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.generic(
|
||||
method=method, **self._build_verb_kwargs(
|
||||
path=path, viewname=viewname, *args, **kwargs
|
||||
)
|
||||
)
|
||||
|
||||
def get(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.get(
|
||||
**self._build_verb_kwargs(
|
||||
@@ -381,8 +393,43 @@ class TestModelTestMixin(object):
|
||||
self.test_object = TestModel.objects.create(**kwargs)
|
||||
|
||||
|
||||
class TestServerTestCaseMixin(object):
|
||||
def setUp(self):
|
||||
super(TestServerTestCaseMixin, self).setUp()
|
||||
self.testserver_prefix = self.get_testserver_prefix()
|
||||
self.testserver_url = self.get_testserver_url()
|
||||
self.test_view_request = None
|
||||
|
||||
def _test_view_factory(self, test_object=None):
|
||||
def test_view(request):
|
||||
self.test_view_request = request
|
||||
return HttpResponse()
|
||||
|
||||
return test_view
|
||||
|
||||
def get_testserver_prefix(self):
|
||||
return furl(
|
||||
scheme=TEST_SERVER_SCHEME, host=TEST_SERVER_HOST,
|
||||
).tostr()
|
||||
|
||||
def get_testserver_url(self):
|
||||
return furl(
|
||||
scheme=TEST_SERVER_SCHEME, host=TEST_SERVER_HOST,
|
||||
path=self.test_view_url
|
||||
).tostr()
|
||||
|
||||
|
||||
class TestViewTestCaseMixin(object):
|
||||
auto_add_test_view = False
|
||||
has_test_view = False
|
||||
test_view_object = None
|
||||
test_view_name = TEST_VIEW_NAME
|
||||
test_view_url = TEST_VIEW_URL
|
||||
|
||||
def setUp(self):
|
||||
super(TestViewTestCaseMixin, self).setUp()
|
||||
if self.auto_add_test_view:
|
||||
self.add_test_view(test_object=self.test_view_object)
|
||||
|
||||
def tearDown(self):
|
||||
urlconf = importlib.import_module(settings.ROOT_URLCONF)
|
||||
@@ -392,9 +439,7 @@ class TestViewTestCaseMixin(object):
|
||||
urlconf.urlpatterns.pop(0)
|
||||
super(TestViewTestCaseMixin, self).tearDown()
|
||||
|
||||
def add_test_view(self, test_object):
|
||||
urlconf = importlib.import_module(settings.ROOT_URLCONF)
|
||||
|
||||
def _test_view_factory(self, test_object=None):
|
||||
def test_view(request):
|
||||
template = Template('{{ object }}')
|
||||
context = Context(
|
||||
@@ -402,12 +447,23 @@ class TestViewTestCaseMixin(object):
|
||||
)
|
||||
return HttpResponse(template.render(context=context))
|
||||
|
||||
urlconf.urlpatterns.insert(0, url(TEST_VIEW_URL, test_view, name=TEST_VIEW_NAME))
|
||||
return test_view
|
||||
|
||||
def add_test_view(self, test_object=None):
|
||||
urlconf = importlib.import_module(settings.ROOT_URLCONF)
|
||||
|
||||
urlconf.urlpatterns.insert(
|
||||
0, url(
|
||||
regex=self.test_view_url, view=self._test_view_factory(
|
||||
test_object=test_object
|
||||
), name=self.test_view_name
|
||||
)
|
||||
)
|
||||
clear_url_caches()
|
||||
self.has_test_view = True
|
||||
|
||||
def get_test_view(self):
|
||||
response = self.get(TEST_VIEW_NAME)
|
||||
response = self.get(viewname=self.test_view_name)
|
||||
if isinstance(response.context, ContextList):
|
||||
# template widget rendering causes test client response to be
|
||||
# ContextList rather than RequestContext. Typecast to dictionary
|
||||
@@ -416,5 +472,6 @@ class TestViewTestCaseMixin(object):
|
||||
result.update({'request': response.wsgi_request})
|
||||
return Context(result)
|
||||
else:
|
||||
response.context.update({'request': response.wsgi_request})
|
||||
return Context(response.context)
|
||||
result = response.context or {}
|
||||
result.update({'request': response.wsgi_request})
|
||||
return Context(result)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import requests
|
||||
|
||||
from .http_adapters import TestClientAdapter
|
||||
|
||||
|
||||
def request_method_factory(test_case):
|
||||
def request(method, url, **kwargs):
|
||||
with requests.sessions.Session() as session:
|
||||
session.mount(
|
||||
prefix=test_case.testserver_prefix,
|
||||
adapter=TestClientAdapter(test_case=test_case)
|
||||
)
|
||||
return session.request(method=method, url=url, **kwargs)
|
||||
|
||||
return request
|
||||
Reference in New Issue
Block a user