Add TestServerTestCaseMixin

Allows performing mocked HTTP requests.

Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-11-08 00:14:39 -04:00
parent 1615e584ce
commit e91232f722
4 changed files with 159 additions and 9 deletions
+73
View File
@@ -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,
)
)
+3
View File
@@ -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'
+66 -9
View File
@@ -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)
+17
View File
@@ -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