From d6572ee4b05b61d8c199b95a831f35a49c775f7e Mon Sep 17 00:00:00 2001 From: Marc Gibbons <1726961+marcgibbons@users.noreply.github.com> Date: Sun, 21 Feb 2021 15:12:41 -0500 Subject: [PATCH] Fixed #32470 -- Fixed ResolverMatch instance on test clients when request.urlconf is set. --- django/test/client.py | 10 ++++-- tests/test_client/tests.py | 32 +++++++++++++++++++- tests/test_client/urls_middleware_urlconf.py | 11 +++++++ 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 tests/test_client/urls_middleware_urlconf.py diff --git a/django/test/client.py b/django/test/client.py index 2d501e0da62..00c949ce58e 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -725,7 +725,10 @@ class Client(ClientMixin, RequestFactory): response.context = data.get('context') response.json = partial(self._parse_json, response) # Attach the ResolverMatch instance to the response. - response.resolver_match = SimpleLazyObject(lambda: resolve(request['PATH_INFO'])) + urlconf = getattr(response.wsgi_request, 'urlconf', None) + response.resolver_match = SimpleLazyObject( + lambda: resolve(request['PATH_INFO'], urlconf=urlconf), + ) # Flatten a single context. Not really necessary anymore thanks to the # __getattr__ flattening in ContextList, but has some edge case # backwards compatibility implications. @@ -914,7 +917,10 @@ class AsyncClient(ClientMixin, AsyncRequestFactory): response.context = data.get('context') response.json = partial(self._parse_json, response) # Attach the ResolverMatch instance to the response. - response.resolver_match = SimpleLazyObject(lambda: resolve(request['path'])) + urlconf = getattr(response.asgi_request, 'urlconf', None) + response.resolver_match = SimpleLazyObject( + lambda: resolve(request['path'], urlconf=urlconf), + ) # Flatten a single context. Not really necessary anymore thanks to the # __getattr__ flattening in ContextList, but has some edge case # backwards compatibility implications. diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index ef8312d1c0e..6156a1ddbc7 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -28,13 +28,31 @@ from django.core import mail from django.http import HttpResponse, HttpResponseNotAllowed from django.test import ( AsyncRequestFactory, Client, RequestFactory, SimpleTestCase, TestCase, - override_settings, + modify_settings, override_settings, ) from django.urls import reverse_lazy +from django.utils.decorators import async_only_middleware from .views import TwoArgException, get_view, post_view, trace_view +def middleware_urlconf(get_response): + def middleware(request): + request.urlconf = 'tests.test_client.urls_middleware_urlconf' + return get_response(request) + + return middleware + + +@async_only_middleware +def async_middleware_urlconf(get_response): + async def middleware(request): + request.urlconf = 'tests.test_client.urls_middleware_urlconf' + return await get_response(request) + + return middleware + + @override_settings(ROOT_URLCONF='test_client.urls') class ClientTest(TestCase): @@ -195,6 +213,11 @@ class ClientTest(TestCase): response = self.client.get('/get_view/') self.assertEqual(response.resolver_match.url_name, 'get_view') + @modify_settings(MIDDLEWARE={'prepend': 'test_client.tests.middleware_urlconf'}) + def test_response_resolver_match_middleware_urlconf(self): + response = self.client.get('/middleware_urlconf_view/') + self.assertEqual(response.resolver_match.url_name, 'middleware_urlconf_view') + def test_raw_post(self): "POST raw data (with a content type) to a view" test_doc = """ @@ -952,6 +975,13 @@ class AsyncClientTest(TestCase): self.assertTrue(hasattr(response, 'resolver_match')) self.assertEqual(response.resolver_match.url_name, 'async_get_view') + @modify_settings( + MIDDLEWARE={'prepend': 'test_client.tests.async_middleware_urlconf'}, + ) + async def test_response_resolver_match_middleware_urlconf(self): + response = await self.async_client.get('/middleware_urlconf_view/') + self.assertEqual(response.resolver_match.url_name, 'middleware_urlconf_view') + async def test_follow_parameter_not_implemented(self): msg = 'AsyncClient request methods do not accept the follow parameter.' tests = ( diff --git a/tests/test_client/urls_middleware_urlconf.py b/tests/test_client/urls_middleware_urlconf.py new file mode 100644 index 00000000000..59893699f9d --- /dev/null +++ b/tests/test_client/urls_middleware_urlconf.py @@ -0,0 +1,11 @@ +from django.http import HttpResponse +from django.urls import path + + +def empty_response(request): + return HttpResponse() + + +urlpatterns = [ + path('middleware_urlconf_view/', empty_response, name='middleware_urlconf_view'), +]