Skip to content

Commit 8b48db2

Browse files
committed
add tests for make_middleware_decorator
1 parent e0864c1 commit 8b48db2

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

tests/test_async_utils/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# named like this to not conflict with something from django :/
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
from asgiref.sync import sync_to_async
2+
3+
import pytest
4+
5+
from django.http import HttpResponse
6+
from django.template import engines
7+
from django.template.response import TemplateResponse
8+
from django.test import RequestFactory
9+
10+
from django_async_extensions.middleware.base import AsyncMiddlewareMixin
11+
from django_async_extensions.utils.decorators import decorator_from_middleware
12+
13+
14+
class ProcessViewMiddleware(AsyncMiddlewareMixin):
15+
def __init__(self, get_response):
16+
self.get_response = get_response
17+
18+
async def process_view(self, request, view_func, view_args, view_kwargs):
19+
pass
20+
21+
22+
process_view_dec = decorator_from_middleware(ProcessViewMiddleware)
23+
24+
25+
@process_view_dec
26+
async def async_process_view(request):
27+
return HttpResponse()
28+
29+
30+
@process_view_dec
31+
def process_view(request):
32+
return HttpResponse()
33+
34+
35+
class ClassProcessView:
36+
def __call__(self, request):
37+
return HttpResponse()
38+
39+
40+
class_process_view = process_view_dec(ClassProcessView())
41+
42+
43+
class AsyncClassProcessView:
44+
async def __call__(self, request):
45+
return HttpResponse()
46+
47+
48+
async_class_process_view = process_view_dec(AsyncClassProcessView())
49+
50+
51+
class FullMiddleware(AsyncMiddlewareMixin):
52+
def __init__(self, get_response):
53+
self.get_response = get_response
54+
55+
async def process_request(self, request):
56+
request.process_request_reached = True
57+
58+
async def process_view(self, request, view_func, view_args, view_kwargs):
59+
request.process_view_reached = True
60+
61+
async def process_template_response(self, request, response):
62+
request.process_template_response_reached = True
63+
return response
64+
65+
async def process_response(self, request, response):
66+
# This should never receive unrendered content.
67+
request.process_response_content = response.content
68+
request.process_response_reached = True
69+
return response
70+
71+
72+
full_dec = decorator_from_middleware(FullMiddleware)
73+
74+
75+
class TestDecoratorFromMiddleware:
76+
"""
77+
Tests for view decorators created using
78+
``django.utils.decorators.decorator_from_middleware``.
79+
"""
80+
81+
rf = RequestFactory()
82+
83+
def test_process_view_middleware(self):
84+
"""
85+
Test a middleware that implements process_view.
86+
"""
87+
process_view(self.rf.get("/"))
88+
89+
async def test_process_view_middleware_async(self, async_rf):
90+
await async_process_view(async_rf.get("/"))
91+
92+
async def test_sync_process_view_raises_in_async_context(self):
93+
msg = (
94+
"You cannot use AsyncToSync in the same thread as an async event loop"
95+
" - just await the async function directly."
96+
)
97+
with pytest.raises(RuntimeError, match=msg):
98+
process_view(self.rf.get("/"))
99+
100+
def test_callable_process_view_middleware(self):
101+
"""
102+
Test a middleware that implements process_view, operating on a callable class.
103+
"""
104+
class_process_view(self.rf.get("/"))
105+
106+
async def test_callable_process_view_middleware_async(self, async_rf):
107+
await async_process_view(async_rf.get("/"))
108+
109+
def test_full_dec_normal(self):
110+
"""
111+
All methods of middleware are called for normal HttpResponses
112+
"""
113+
114+
@full_dec
115+
def normal_view(request):
116+
template = engines["django"].from_string("Hello world")
117+
return HttpResponse(template.render())
118+
119+
request = self.rf.get("/")
120+
normal_view(request)
121+
assert getattr(request, "process_request_reached", False)
122+
assert getattr(request, "process_view_reached", False)
123+
# process_template_response must not be called for HttpResponse
124+
assert getattr(request, "process_template_response_reached", False) is False
125+
assert getattr(request, "process_response_reached", False)
126+
127+
async def test_full_dec_normal_async(self, async_rf):
128+
"""
129+
All methods of middleware are called for normal HttpResponses
130+
"""
131+
132+
@full_dec
133+
async def normal_view(request):
134+
template = engines["django"].from_string("Hello world")
135+
return HttpResponse(template.render())
136+
137+
request = async_rf.get("/")
138+
await normal_view(request)
139+
assert getattr(request, "process_request_reached", False)
140+
assert getattr(request, "process_view_reached", False)
141+
# process_template_response must not be called for HttpResponse
142+
assert getattr(request, "process_template_response_reached", False) is False
143+
assert getattr(request, "process_response_reached", False)
144+
145+
def test_full_dec_templateresponse(self):
146+
"""
147+
All methods of middleware are called for TemplateResponses in
148+
the right sequence.
149+
"""
150+
151+
@full_dec
152+
def template_response_view(request):
153+
template = engines["django"].from_string("Hello world")
154+
return TemplateResponse(request, template)
155+
156+
request = self.rf.get("/")
157+
response = template_response_view(request)
158+
assert getattr(request, "process_request_reached", False)
159+
assert getattr(request, "process_view_reached", False)
160+
assert getattr(request, "process_template_response_reached", False)
161+
# response must not be rendered yet.
162+
assert response._is_rendered is False
163+
# process_response must not be called until after response is rendered,
164+
# otherwise some decorators like csrf_protect and gzip_page will not
165+
# work correctly. See #16004
166+
assert getattr(request, "process_response_reached", False) is False
167+
response.render()
168+
assert getattr(request, "process_response_reached", False)
169+
# process_response saw the rendered content
170+
assert request.process_response_content == b"Hello world"
171+
172+
async def test_full_dec_templateresponse_async(self, async_rf):
173+
"""
174+
All methods of middleware are called for TemplateResponses in
175+
the right sequence.
176+
"""
177+
178+
@full_dec
179+
async def template_response_view(request):
180+
template = engines["django"].from_string("Hello world")
181+
return TemplateResponse(request, template)
182+
183+
request = async_rf.get("/")
184+
response = await template_response_view(request)
185+
assert getattr(request, "process_request_reached", False)
186+
assert getattr(request, "process_view_reached", False)
187+
assert getattr(request, "process_template_response_reached", False)
188+
# response must not be rendered yet.
189+
assert response._is_rendered is False
190+
# process_response must not be called until after response is rendered,
191+
# otherwise some decorators like csrf_protect and gzip_page will not
192+
# work correctly. See #16004
193+
assert getattr(request, "process_response_reached", False) is False
194+
await sync_to_async(response.render)()
195+
assert getattr(request, "process_response_reached", False)
196+
# process_response saw the rendered content
197+
assert request.process_response_content == b"Hello world"

0 commit comments

Comments
 (0)