Skip to content

Commit df92e57

Browse files
authored
Added test client support for HTTP 307 and 308 redirects (#8419)
* Add retain test data on follow=True * Simplify TestAPITestClient.test_follow_redirect Inspired from Django's ClientTest.test_follow_307_and_308_redirect * Add 307 308 follow redirect test
1 parent df4d16d commit df92e57

File tree

2 files changed

+42
-42
lines changed

2 files changed

+42
-42
lines changed

rest_framework/test.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,47 +288,47 @@ def request(self, **kwargs):
288288
def get(self, path, data=None, follow=False, **extra):
289289
response = super().get(path, data=data, **extra)
290290
if follow:
291-
response = self._handle_redirects(response, **extra)
291+
response = self._handle_redirects(response, data=data, **extra)
292292
return response
293293

294294
def post(self, path, data=None, format=None, content_type=None,
295295
follow=False, **extra):
296296
response = super().post(
297297
path, data=data, format=format, content_type=content_type, **extra)
298298
if follow:
299-
response = self._handle_redirects(response, **extra)
299+
response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra)
300300
return response
301301

302302
def put(self, path, data=None, format=None, content_type=None,
303303
follow=False, **extra):
304304
response = super().put(
305305
path, data=data, format=format, content_type=content_type, **extra)
306306
if follow:
307-
response = self._handle_redirects(response, **extra)
307+
response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra)
308308
return response
309309

310310
def patch(self, path, data=None, format=None, content_type=None,
311311
follow=False, **extra):
312312
response = super().patch(
313313
path, data=data, format=format, content_type=content_type, **extra)
314314
if follow:
315-
response = self._handle_redirects(response, **extra)
315+
response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra)
316316
return response
317317

318318
def delete(self, path, data=None, format=None, content_type=None,
319319
follow=False, **extra):
320320
response = super().delete(
321321
path, data=data, format=format, content_type=content_type, **extra)
322322
if follow:
323-
response = self._handle_redirects(response, **extra)
323+
response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra)
324324
return response
325325

326326
def options(self, path, data=None, format=None, content_type=None,
327327
follow=False, **extra):
328328
response = super().options(
329329
path, data=data, format=format, content_type=content_type, **extra)
330330
if follow:
331-
response = self._handle_redirects(response, **extra)
331+
response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra)
332332
return response
333333

334334
def logout(self):

tests/test_testing.py

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import itertools
12
from io import BytesIO
3+
from unittest.mock import patch
24

35
import django
46
from django.contrib.auth.models import User
7+
from django.http import HttpResponseRedirect
58
from django.shortcuts import redirect
69
from django.test import TestCase, override_settings
710
from django.urls import path
@@ -14,7 +17,7 @@
1417
)
1518

1619

17-
@api_view(['GET', 'POST'])
20+
@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'])
1821
def view(request):
1922
return Response({
2023
'auth': request.META.get('HTTP_AUTHORIZATION', b''),
@@ -36,6 +39,11 @@ def redirect_view(request):
3639
return redirect('/view/')
3740

3841

42+
@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'])
43+
def redirect_307_308_view(request, code):
44+
return HttpResponseRedirect('/view/', status=code)
45+
46+
3947
class BasicSerializer(serializers.Serializer):
4048
flag = fields.BooleanField(default=lambda: True)
4149

@@ -51,6 +59,7 @@ def post_view(request):
5159
path('view/', view),
5260
path('session-view/', session_view),
5361
path('redirect-view/', redirect_view),
62+
path('redirect-view/<int:code>/', redirect_307_308_view),
5463
path('post-view/', post_view)
5564
]
5665

@@ -146,41 +155,32 @@ def test_follow_redirect(self):
146155
"""
147156
Follow redirect by setting follow argument.
148157
"""
149-
response = self.client.get('/redirect-view/')
150-
assert response.status_code == 302
151-
response = self.client.get('/redirect-view/', follow=True)
152-
assert response.redirect_chain is not None
153-
assert response.status_code == 200
154-
155-
response = self.client.post('/redirect-view/')
156-
assert response.status_code == 302
157-
response = self.client.post('/redirect-view/', follow=True)
158-
assert response.redirect_chain is not None
159-
assert response.status_code == 200
160-
161-
response = self.client.put('/redirect-view/')
162-
assert response.status_code == 302
163-
response = self.client.put('/redirect-view/', follow=True)
164-
assert response.redirect_chain is not None
165-
assert response.status_code == 200
166-
167-
response = self.client.patch('/redirect-view/')
168-
assert response.status_code == 302
169-
response = self.client.patch('/redirect-view/', follow=True)
170-
assert response.redirect_chain is not None
171-
assert response.status_code == 200
172-
173-
response = self.client.delete('/redirect-view/')
174-
assert response.status_code == 302
175-
response = self.client.delete('/redirect-view/', follow=True)
176-
assert response.redirect_chain is not None
177-
assert response.status_code == 200
178-
179-
response = self.client.options('/redirect-view/')
180-
assert response.status_code == 302
181-
response = self.client.options('/redirect-view/', follow=True)
182-
assert response.redirect_chain is not None
183-
assert response.status_code == 200
158+
for method in ('get', 'post', 'put', 'patch', 'delete', 'options'):
159+
with self.subTest(method=method):
160+
req_method = getattr(self.client, method)
161+
response = req_method('/redirect-view/')
162+
assert response.status_code == 302
163+
response = req_method('/redirect-view/', follow=True)
164+
assert response.redirect_chain is not None
165+
assert response.status_code == 200
166+
167+
def test_follow_307_308_preserve_kwargs(self, *mocked_methods):
168+
"""
169+
Follow redirect by setting follow argument, and make sure the following
170+
method called with appropriate kwargs.
171+
"""
172+
methods = ('get', 'post', 'put', 'patch', 'delete', 'options')
173+
codes = (307, 308)
174+
for method, code in itertools.product(methods, codes):
175+
subtest_ctx = self.subTest(method=method, code=code)
176+
patch_ctx = patch.object(self.client, method, side_effect=getattr(self.client, method))
177+
with subtest_ctx, patch_ctx as req_method:
178+
kwargs = {'data': {'example': 'test'}, 'format': 'json'}
179+
response = req_method('/redirect-view/%s/' % code, follow=True, **kwargs)
180+
assert response.redirect_chain is not None
181+
assert response.status_code == 200
182+
for _, call_args, call_kwargs in req_method.mock_calls:
183+
assert all(call_kwargs[k] == kwargs[k] for k in kwargs if k in call_kwargs)
184184

185185
def test_invalid_multipart_data(self):
186186
"""

0 commit comments

Comments
 (0)