From 4b691c402707775c3048a90531024f3bc5be6f91 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:06:27 +0100 Subject: Change package name: djangorestframework -> rest_framework --- rest_framework/tests/request.py | 252 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 rest_framework/tests/request.py (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py new file mode 100644 index 00000000..805f6efc --- /dev/null +++ b/rest_framework/tests/request.py @@ -0,0 +1,252 @@ +""" +Tests for content parsing, and form-overloaded content parsing. +""" +from django.conf.urls.defaults import patterns +from django.contrib.auth.models import User +from django.test import TestCase, Client + +from rest_framework import status +from rest_framework.authentication import SessionAuthentication +from rest_framework.compat import RequestFactory +from rest_framework.parsers import ( + FormParser, + MultiPartParser, + PlainTextParser, +) +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.views import APIView + + +factory = RequestFactory() + + +class TestMethodOverloading(TestCase): + def test_method(self): + """ + Request methods should be same as underlying request. + """ + request = Request(factory.get('/')) + self.assertEqual(request.method, 'GET') + request = Request(factory.post('/')) + self.assertEqual(request.method, 'POST') + + def test_overloaded_method(self): + """ + POST requests can be overloaded to another method by setting a + reserved form field + """ + request = Request(factory.post('/', {Request._METHOD_PARAM: 'DELETE'})) + self.assertEqual(request.method, 'DELETE') + + +class TestContentParsing(TestCase): + def test_standard_behaviour_determines_no_content_GET(self): + """ + Ensure request.DATA returns None for GET request with no content. + """ + request = Request(factory.get('/')) + self.assertEqual(request.DATA, None) + + def test_standard_behaviour_determines_no_content_HEAD(self): + """ + Ensure request.DATA returns None for HEAD request. + """ + request = Request(factory.head('/')) + self.assertEqual(request.DATA, None) + + def test_standard_behaviour_determines_form_content_POST(self): + """ + Ensure request.DATA returns content for POST request with form content. + """ + data = {'qwerty': 'uiop'} + request = Request(factory.post('/', data)) + request.parser_classes = (FormParser, MultiPartParser) + self.assertEqual(request.DATA.items(), data.items()) + + def test_standard_behaviour_determines_non_form_content_POST(self): + """ + Ensure request.DATA returns content for POST request with + non-form content. + """ + content = 'qwerty' + content_type = 'text/plain' + request = Request(factory.post('/', content, content_type=content_type)) + request.parser_classes = (PlainTextParser,) + self.assertEqual(request.DATA, content) + + def test_standard_behaviour_determines_form_content_PUT(self): + """ + Ensure request.DATA returns content for PUT request with form content. + """ + data = {'qwerty': 'uiop'} + + from django import VERSION + + if VERSION >= (1, 5): + from django.test.client import MULTIPART_CONTENT, BOUNDARY, encode_multipart + request = Request(factory.put('/', encode_multipart(BOUNDARY, data), + content_type=MULTIPART_CONTENT)) + else: + request = Request(factory.put('/', data)) + + request.parser_classes = (FormParser, MultiPartParser) + self.assertEqual(request.DATA.items(), data.items()) + + def test_standard_behaviour_determines_non_form_content_PUT(self): + """ + Ensure request.DATA returns content for PUT request with + non-form content. + """ + content = 'qwerty' + content_type = 'text/plain' + request = Request(factory.put('/', content, content_type=content_type)) + request.parser_classes = (PlainTextParser, ) + self.assertEqual(request.DATA, content) + + def test_overloaded_behaviour_allows_content_tunnelling(self): + """ + Ensure request.DATA returns content for overloaded POST request. + """ + content = 'qwerty' + content_type = 'text/plain' + data = { + Request._CONTENT_PARAM: content, + Request._CONTENTTYPE_PARAM: content_type + } + request = Request(factory.post('/', data)) + request.parser_classes = (PlainTextParser, ) + self.assertEqual(request.DATA, content) + + # def test_accessing_post_after_data_form(self): + # """ + # Ensures request.POST can be accessed after request.DATA in + # form request. + # """ + # data = {'qwerty': 'uiop'} + # request = factory.post('/', data=data) + # self.assertEqual(request.DATA.items(), data.items()) + # self.assertEqual(request.POST.items(), data.items()) + + # def test_accessing_post_after_data_for_json(self): + # """ + # Ensures request.POST can be accessed after request.DATA in + # json request. + # """ + # data = {'qwerty': 'uiop'} + # content = json.dumps(data) + # content_type = 'application/json' + # parsers = (JSONParser, ) + + # request = factory.post('/', content, content_type=content_type, + # parsers=parsers) + # self.assertEqual(request.DATA.items(), data.items()) + # self.assertEqual(request.POST.items(), []) + + # def test_accessing_post_after_data_for_overloaded_json(self): + # """ + # Ensures request.POST can be accessed after request.DATA in overloaded + # json request. + # """ + # data = {'qwerty': 'uiop'} + # content = json.dumps(data) + # content_type = 'application/json' + # parsers = (JSONParser, ) + # form_data = {Request._CONTENT_PARAM: content, + # Request._CONTENTTYPE_PARAM: content_type} + + # request = factory.post('/', form_data, parsers=parsers) + # self.assertEqual(request.DATA.items(), data.items()) + # self.assertEqual(request.POST.items(), form_data.items()) + + # def test_accessing_data_after_post_form(self): + # """ + # Ensures request.DATA can be accessed after request.POST in + # form request. + # """ + # data = {'qwerty': 'uiop'} + # parsers = (FormParser, MultiPartParser) + # request = factory.post('/', data, parsers=parsers) + + # self.assertEqual(request.POST.items(), data.items()) + # self.assertEqual(request.DATA.items(), data.items()) + + # def test_accessing_data_after_post_for_json(self): + # """ + # Ensures request.DATA can be accessed after request.POST in + # json request. + # """ + # data = {'qwerty': 'uiop'} + # content = json.dumps(data) + # content_type = 'application/json' + # parsers = (JSONParser, ) + # request = factory.post('/', content, content_type=content_type, + # parsers=parsers) + # self.assertEqual(request.POST.items(), []) + # self.assertEqual(request.DATA.items(), data.items()) + + # def test_accessing_data_after_post_for_overloaded_json(self): + # """ + # Ensures request.DATA can be accessed after request.POST in overloaded + # json request + # """ + # data = {'qwerty': 'uiop'} + # content = json.dumps(data) + # content_type = 'application/json' + # parsers = (JSONParser, ) + # form_data = {Request._CONTENT_PARAM: content, + # Request._CONTENTTYPE_PARAM: content_type} + + # request = factory.post('/', form_data, parsers=parsers) + # self.assertEqual(request.POST.items(), form_data.items()) + # self.assertEqual(request.DATA.items(), data.items()) + + +class MockView(APIView): + authentication_classes = (SessionAuthentication,) + + def post(self, request): + if request.POST.get('example') is not None: + return Response(status=status.HTTP_200_OK) + + return Response(status=status.INTERNAL_SERVER_ERROR) + +urlpatterns = patterns('', + (r'^$', MockView.as_view()), +) + + +class TestContentParsingWithAuthentication(TestCase): + urls = 'rest_framework.tests.request' + + def setUp(self): + self.csrf_client = Client(enforce_csrf_checks=True) + self.username = 'john' + self.email = 'lennon@thebeatles.com' + self.password = 'password' + self.user = User.objects.create_user(self.username, self.email, self.password) + + def test_user_logged_in_authentication_has_POST_when_not_logged_in(self): + """ + Ensures request.POST exists after SessionAuthentication when user + doesn't log in. + """ + content = {'example': 'example'} + + response = self.client.post('/', content) + self.assertEqual(status.HTTP_200_OK, response.status_code) + + response = self.csrf_client.post('/', content) + self.assertEqual(status.HTTP_200_OK, response.status_code) + + # def test_user_logged_in_authentication_has_post_when_logged_in(self): + # """Ensures request.POST exists after UserLoggedInAuthentication when user does log in""" + # self.client.login(username='john', password='password') + # self.csrf_client.login(username='john', password='password') + # content = {'example': 'example'} + + # response = self.client.post('/', content) + # self.assertEqual(status.OK, response.status_code, "POST data is malformed") + + # response = self.csrf_client.post('/', content) + # self.assertEqual(status.OK, response.status_code, "POST data is malformed") -- cgit v1.2.3 From fe666a14ee0bcce05972671c08cdf91f3b85ebac Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 27 Sep 2012 12:50:18 +0100 Subject: Add test for request.POST --- rest_framework/tests/request.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index 805f6efc..402f9d1b 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -55,7 +55,7 @@ class TestContentParsing(TestCase): request = Request(factory.head('/')) self.assertEqual(request.DATA, None) - def test_standard_behaviour_determines_form_content_POST(self): + def test_request_DATA_with_form_content(self): """ Ensure request.DATA returns content for POST request with form content. """ @@ -64,7 +64,7 @@ class TestContentParsing(TestCase): request.parser_classes = (FormParser, MultiPartParser) self.assertEqual(request.DATA.items(), data.items()) - def test_standard_behaviour_determines_non_form_content_POST(self): + def test_request_DATA_with_text_content(self): """ Ensure request.DATA returns content for POST request with non-form content. @@ -75,6 +75,15 @@ class TestContentParsing(TestCase): request.parser_classes = (PlainTextParser,) self.assertEqual(request.DATA, content) + def test_request_POST_with_form_content(self): + """ + Ensure request.POST returns content for POST request with form content. + """ + data = {'qwerty': 'uiop'} + request = Request(factory.post('/', data)) + request.parser_classes = (FormParser, MultiPartParser) + self.assertEqual(request.POST.items(), data.items()) + def test_standard_behaviour_determines_form_content_PUT(self): """ Ensure request.DATA returns content for PUT request with form content. -- cgit v1.2.3 From 91b3fb0b05778edeee48ea2a8cd8c784afa1a744 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 27 Sep 2012 13:06:04 +0100 Subject: Remove RequestFactory from compat (Now 1.2 is not supported) --- rest_framework/tests/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index 402f9d1b..42274fcd 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -7,7 +7,7 @@ from django.test import TestCase, Client from rest_framework import status from rest_framework.authentication import SessionAuthentication -from rest_framework.compat import RequestFactory +from django.test.client import RequestFactory from rest_framework.parsers import ( FormParser, MultiPartParser, -- cgit v1.2.3 From 9d8bce8f5b0915223f57d9fe3d4b63029cfc64c2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 14:48:33 +0100 Subject: Remove Parser.can_handle_request() --- rest_framework/tests/request.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index 42274fcd..f5c63f11 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -61,7 +61,7 @@ class TestContentParsing(TestCase): """ data = {'qwerty': 'uiop'} request = Request(factory.post('/', data)) - request.parser_classes = (FormParser, MultiPartParser) + request.parsers = (FormParser(), MultiPartParser()) self.assertEqual(request.DATA.items(), data.items()) def test_request_DATA_with_text_content(self): @@ -72,7 +72,7 @@ class TestContentParsing(TestCase): content = 'qwerty' content_type = 'text/plain' request = Request(factory.post('/', content, content_type=content_type)) - request.parser_classes = (PlainTextParser,) + request.parsers = (PlainTextParser(),) self.assertEqual(request.DATA, content) def test_request_POST_with_form_content(self): @@ -81,7 +81,7 @@ class TestContentParsing(TestCase): """ data = {'qwerty': 'uiop'} request = Request(factory.post('/', data)) - request.parser_classes = (FormParser, MultiPartParser) + request.parsers = (FormParser(), MultiPartParser()) self.assertEqual(request.POST.items(), data.items()) def test_standard_behaviour_determines_form_content_PUT(self): @@ -99,7 +99,7 @@ class TestContentParsing(TestCase): else: request = Request(factory.put('/', data)) - request.parser_classes = (FormParser, MultiPartParser) + request.parsers = (FormParser(), MultiPartParser()) self.assertEqual(request.DATA.items(), data.items()) def test_standard_behaviour_determines_non_form_content_PUT(self): @@ -110,7 +110,7 @@ class TestContentParsing(TestCase): content = 'qwerty' content_type = 'text/plain' request = Request(factory.put('/', content, content_type=content_type)) - request.parser_classes = (PlainTextParser, ) + request.parsers = (PlainTextParser(), ) self.assertEqual(request.DATA, content) def test_overloaded_behaviour_allows_content_tunnelling(self): @@ -124,7 +124,7 @@ class TestContentParsing(TestCase): Request._CONTENTTYPE_PARAM: content_type } request = Request(factory.post('/', data)) - request.parser_classes = (PlainTextParser, ) + request.parsers = (PlainTextParser(), ) self.assertEqual(request.DATA, content) # def test_accessing_post_after_data_form(self): -- cgit v1.2.3 From 4a21b3557edb3b901b86d3a888c44f772e33b922 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 8 Oct 2012 17:10:50 +0100 Subject: Fix fiddly content-overloading bug --- rest_framework/tests/request.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index f5c63f11..7b24b036 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -4,6 +4,7 @@ Tests for content parsing, and form-overloaded content parsing. from django.conf.urls.defaults import patterns from django.contrib.auth.models import User from django.test import TestCase, Client +from django.utils import simplejson as json from rest_framework import status from rest_framework.authentication import SessionAuthentication @@ -12,9 +13,11 @@ from rest_framework.parsers import ( FormParser, MultiPartParser, PlainTextParser, + JSONParser ) from rest_framework.request import Request from rest_framework.response import Response +from rest_framework.settings import api_settings from rest_framework.views import APIView @@ -36,7 +39,7 @@ class TestMethodOverloading(TestCase): POST requests can be overloaded to another method by setting a reserved form field """ - request = Request(factory.post('/', {Request._METHOD_PARAM: 'DELETE'})) + request = Request(factory.post('/', {api_settings.FORM_METHOD_OVERRIDE: 'DELETE'})) self.assertEqual(request.method, 'DELETE') @@ -117,15 +120,16 @@ class TestContentParsing(TestCase): """ Ensure request.DATA returns content for overloaded POST request. """ - content = 'qwerty' - content_type = 'text/plain' - data = { - Request._CONTENT_PARAM: content, - Request._CONTENTTYPE_PARAM: content_type + json_data = {'foobar': 'qwerty'} + content = json.dumps(json_data) + content_type = 'application/json' + form_data = { + api_settings.FORM_CONTENT_OVERRIDE: content, + api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type } - request = Request(factory.post('/', data)) - request.parsers = (PlainTextParser(), ) - self.assertEqual(request.DATA, content) + request = Request(factory.post('/', form_data)) + request.parsers = (JSONParser(), ) + self.assertEqual(request.DATA, json_data) # def test_accessing_post_after_data_form(self): # """ -- cgit v1.2.3 From 551c86c43a71f7dee5cce68c5142714301f6196f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 14 Oct 2012 22:43:07 +0100 Subject: Documentation for parsers --- rest_framework/tests/request.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index 7b24b036..f00ee85f 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -10,9 +10,9 @@ from rest_framework import status from rest_framework.authentication import SessionAuthentication from django.test.client import RequestFactory from rest_framework.parsers import ( + BaseParser, FormParser, MultiPartParser, - PlainTextParser, JSONParser ) from rest_framework.request import Request @@ -24,6 +24,19 @@ from rest_framework.views import APIView factory = RequestFactory() +class PlainTextParser(BaseParser): + media_type = 'text/plain' + + def parse_stream(self, stream, **opts): + """ + Returns a 2-tuple of `(data, files)`. + + `data` will simply be a string representing the body of the request. + `files` will always be `None`. + """ + return stream.read() + + class TestMethodOverloading(TestCase): def test_method(self): """ -- cgit v1.2.3 From 9c1fba3483b7e81da0744464dcf23a5f12711de2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 15 Oct 2012 13:27:50 +0100 Subject: Tweak parsers to take parser_context --- rest_framework/tests/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index f00ee85f..f90bebf4 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -27,7 +27,7 @@ factory = RequestFactory() class PlainTextParser(BaseParser): media_type = 'text/plain' - def parse_stream(self, stream, **opts): + def parse_stream(self, stream, parser_context=None): """ Returns a 2-tuple of `(data, files)`. -- cgit v1.2.3 From 99d48f90030d174ef80498b48f56af6489865f0d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 22:07:56 +0100 Subject: Drop .parse_string_or_stream() - keep API minimal. --- rest_framework/tests/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index f90bebf4..f698e845 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -27,7 +27,7 @@ factory = RequestFactory() class PlainTextParser(BaseParser): media_type = 'text/plain' - def parse_stream(self, stream, parser_context=None): + def parse(self, stream, parser_context=None): """ Returns a 2-tuple of `(data, files)`. -- cgit v1.2.3 From fb56f215ae50da0aebe99e05036ece259fd3e6f1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 22:39:07 +0100 Subject: Added `media_type` to `.parse()` - Consistency with renderer API. --- rest_framework/tests/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework/tests/request.py') diff --git a/rest_framework/tests/request.py b/rest_framework/tests/request.py index f698e845..ff48f3fa 100644 --- a/rest_framework/tests/request.py +++ b/rest_framework/tests/request.py @@ -27,7 +27,7 @@ factory = RequestFactory() class PlainTextParser(BaseParser): media_type = 'text/plain' - def parse(self, stream, parser_context=None): + def parse(self, stream, media_type=None, parser_context=None): """ Returns a 2-tuple of `(data, files)`. -- cgit v1.2.3