From 162fcc397e8788da41cbb9983bbef44eaf03f139 Mon Sep 17 00:00:00 2001 From: hashlash Date: Mon, 21 Mar 2022 16:00:26 +0700 Subject: [PATCH 1/3] Add retain test data on follow=True --- rest_framework/test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rest_framework/test.py b/rest_framework/test.py index 0212348ee0..07df743c8e 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -288,7 +288,7 @@ def request(self, **kwargs): def get(self, path, data=None, follow=False, **extra): response = super().get(path, data=data, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, **extra) return response def post(self, path, data=None, format=None, content_type=None, @@ -296,7 +296,7 @@ def post(self, path, data=None, format=None, content_type=None, response = super().post( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def put(self, path, data=None, format=None, content_type=None, @@ -304,7 +304,7 @@ def put(self, path, data=None, format=None, content_type=None, response = super().put( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def patch(self, path, data=None, format=None, content_type=None, @@ -312,7 +312,7 @@ def patch(self, path, data=None, format=None, content_type=None, response = super().patch( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def delete(self, path, data=None, format=None, content_type=None, @@ -320,7 +320,7 @@ def delete(self, path, data=None, format=None, content_type=None, response = super().delete( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def options(self, path, data=None, format=None, content_type=None, @@ -328,7 +328,7 @@ def options(self, path, data=None, format=None, content_type=None, response = super().options( path, data=data, format=format, content_type=content_type, **extra) if follow: - response = self._handle_redirects(response, **extra) + response = self._handle_redirects(response, data=data, format=format, content_type=content_type, **extra) return response def logout(self): From fbb29d03e196441c3bff8468f2025588fb3c868d Mon Sep 17 00:00:00 2001 From: hashlash Date: Mon, 21 Mar 2022 17:26:43 +0700 Subject: [PATCH 2/3] Simplify TestAPITestClient.test_follow_redirect Inspired from Django's ClientTest.test_follow_307_and_308_redirect --- tests/test_testing.py | 43 ++++++++----------------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/tests/test_testing.py b/tests/test_testing.py index 5066ee142e..bb569662b2 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -146,41 +146,14 @@ def test_follow_redirect(self): """ Follow redirect by setting follow argument. """ - response = self.client.get('/redirect-view/') - assert response.status_code == 302 - response = self.client.get('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.post('/redirect-view/') - assert response.status_code == 302 - response = self.client.post('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.put('/redirect-view/') - assert response.status_code == 302 - response = self.client.put('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.patch('/redirect-view/') - assert response.status_code == 302 - response = self.client.patch('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.delete('/redirect-view/') - assert response.status_code == 302 - response = self.client.delete('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 - - response = self.client.options('/redirect-view/') - assert response.status_code == 302 - response = self.client.options('/redirect-view/', follow=True) - assert response.redirect_chain is not None - assert response.status_code == 200 + for method in ('get', 'post', 'put', 'patch', 'delete', 'options'): + with self.subTest(method=method): + req_method = getattr(self.client, method) + response = req_method('/redirect-view/') + assert response.status_code == 302 + response = req_method('/redirect-view/', follow=True) + assert response.redirect_chain is not None + assert response.status_code == 200 def test_invalid_multipart_data(self): """ From 149eb33f7a1a111b125b349fa19dbcd2fbc7cbf4 Mon Sep 17 00:00:00 2001 From: hashlash Date: Wed, 23 Mar 2022 13:53:30 +0700 Subject: [PATCH 3/3] Add 307 308 follow redirect test --- tests/test_testing.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_testing.py b/tests/test_testing.py index bb569662b2..b6579e3690 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,7 +1,10 @@ +import itertools from io import BytesIO +from unittest.mock import patch import django from django.contrib.auth.models import User +from django.http import HttpResponseRedirect from django.shortcuts import redirect from django.test import TestCase, override_settings from django.urls import path @@ -14,7 +17,7 @@ ) -@api_view(['GET', 'POST']) +@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) def view(request): return Response({ 'auth': request.META.get('HTTP_AUTHORIZATION', b''), @@ -36,6 +39,11 @@ def redirect_view(request): return redirect('/view/') +@api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) +def redirect_307_308_view(request, code): + return HttpResponseRedirect('/view/', status=code) + + class BasicSerializer(serializers.Serializer): flag = fields.BooleanField(default=lambda: True) @@ -51,6 +59,7 @@ def post_view(request): path('view/', view), path('session-view/', session_view), path('redirect-view/', redirect_view), + path('redirect-view//', redirect_307_308_view), path('post-view/', post_view) ] @@ -155,6 +164,24 @@ def test_follow_redirect(self): assert response.redirect_chain is not None assert response.status_code == 200 + def test_follow_307_308_preserve_kwargs(self, *mocked_methods): + """ + Follow redirect by setting follow argument, and make sure the following + method called with appropriate kwargs. + """ + methods = ('get', 'post', 'put', 'patch', 'delete', 'options') + codes = (307, 308) + for method, code in itertools.product(methods, codes): + subtest_ctx = self.subTest(method=method, code=code) + patch_ctx = patch.object(self.client, method, side_effect=getattr(self.client, method)) + with subtest_ctx, patch_ctx as req_method: + kwargs = {'data': {'example': 'test'}, 'format': 'json'} + response = req_method('/redirect-view/%s/' % code, follow=True, **kwargs) + assert response.redirect_chain is not None + assert response.status_code == 200 + for _, call_args, call_kwargs in req_method.mock_calls: + assert all(call_kwargs[k] == kwargs[k] for k in kwargs if k in call_kwargs) + def test_invalid_multipart_data(self): """ MultiPart encoding cannot support nested data, so raise a helpful