diff options
| author | Tom Christie | 2012-10-08 17:10:50 +0100 | 
|---|---|---|
| committer | Tom Christie | 2012-10-08 17:10:50 +0100 | 
| commit | 4a21b3557edb3b901b86d3a888c44f772e33b922 (patch) | |
| tree | b54cc491fffd9c7ddd90a414112072aa5562222e /rest_framework | |
| parent | b581ffe323d88b6740abfed0fd552cc436fd2dcc (diff) | |
| download | django-rest-framework-4a21b3557edb3b901b86d3a888c44f772e33b922.tar.bz2 | |
Fix fiddly content-overloading bug
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/request.py | 8 | ||||
| -rw-r--r-- | rest_framework/tests/request.py | 22 | ||||
| -rw-r--r-- | rest_framework/tests/views.py | 211 | 
3 files changed, 99 insertions, 142 deletions
diff --git a/rest_framework/request.py b/rest_framework/request.py index ac15defc..3ce93181 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -227,16 +227,14 @@ class Request(object):              self._method = self._data[self._METHOD_PARAM].upper()              self._data.pop(self._METHOD_PARAM) -        # Content overloading - modify the content type, and re-parse. +        # Content overloading - modify the content type, and force re-parse.          if (self._CONTENT_PARAM and              self._CONTENTTYPE_PARAM and              self._CONTENT_PARAM in self._data and              self._CONTENTTYPE_PARAM in self._data):              self._content_type = self._data[self._CONTENTTYPE_PARAM]              self._stream = StringIO(self._data[self._CONTENT_PARAM]) -            self._data.pop(self._CONTENTTYPE_PARAM) -            self._data.pop(self._CONTENT_PARAM) -            self._data, self._files = self._parse() +            self._data, self._files = (Empty, Empty)      def _parse(self):          """ @@ -250,7 +248,7 @@ class Request(object):          parser = self.negotiator.select_parser(self.parsers, self.content_type)          if not parser: -            raise exceptions.UnsupportedMediaType(self._content_type) +            raise exceptions.UnsupportedMediaType(self.content_type)          parsed = parser.parse(self.stream, meta=self.META,                                upload_handlers=self.upload_handlers) 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):      #     """ diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py index cd1f73c3..61b288c6 100644 --- a/rest_framework/tests/views.py +++ b/rest_framework/tests/views.py @@ -1,128 +1,83 @@ -# from django.core.urlresolvers import reverse -# from django.conf.urls.defaults import patterns, url, include -# from django.http import HttpResponse -# from django.test import TestCase -# from django.utils import simplejson as json - -# from rest_framework.views import View - - -# class MockView(View): -#     """This is a basic mock view""" -#     pass - - -# class MockViewFinal(View): -#     """View with final() override""" - -#     def final(self, request, response, *args, **kwargs): -#         return HttpResponse('{"test": "passed"}', content_type="application/json") - - -# # class ResourceMockView(View): -# #     """This is a resource-based mock view""" - -# #     class MockForm(forms.Form): -# #         foo = forms.BooleanField(required=False) -# #         bar = forms.IntegerField(help_text='Must be an integer.') -# #         baz = forms.CharField(max_length=32) - -# #     form = MockForm - - -# # class MockResource(ModelResource): -# #     """This is a mock model-based resource""" - -# #     class MockResourceModel(models.Model): -# #         foo = models.BooleanField() -# #         bar = models.IntegerField(help_text='Must be an integer.') -# #         baz = models.CharField(max_length=32, help_text='Free text.  Max length 32 chars.') - -# #     model = MockResourceModel -# #     fields = ('foo', 'bar', 'baz') - -# urlpatterns = patterns('', -#     url(r'^mock/$', MockView.as_view()), -#     url(r'^mock/final/$', MockViewFinal.as_view()), -#     # url(r'^resourcemock/$', ResourceMockView.as_view()), -#     # url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)), -#     # url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)), -#     url(r'^restframework/', include('rest_framework.urls', namespace='rest_framework')), -# ) - - -# class BaseViewTests(TestCase): -#     """Test the base view class of rest_framework""" -#     urls = 'rest_framework.tests.views' - -#     def test_view_call_final(self): -#         response = self.client.options('/mock/final/') -#         self.assertEqual(response['Content-Type'].split(';')[0], "application/json") -#         data = json.loads(response.content) -#         self.assertEqual(data['test'], 'passed') - -#     def test_options_method_simple_view(self): -#         response = self.client.options('/mock/') -#         self._verify_options_response(response, -#                                       name='Mock', -#                                       description='This is a basic mock view') - -#     def test_options_method_resource_view(self): -#         response = self.client.options('/resourcemock/') -#         self._verify_options_response(response, -#                                       name='Resource Mock', -#                                       description='This is a resource-based mock view', -#                                       fields={'foo': 'BooleanField', -#                                               'bar': 'IntegerField', -#                                               'baz': 'CharField', -#                                               }) - -#     def test_options_method_model_resource_list_view(self): -#         response = self.client.options('/model/') -#         self._verify_options_response(response, -#                                       name='Mock List', -#                                       description='This is a mock model-based resource', -#                                       fields={'foo': 'BooleanField', -#                                               'bar': 'IntegerField', -#                                               'baz': 'CharField', -#                                               }) - -#     def test_options_method_model_resource_detail_view(self): -#         response = self.client.options('/model/0/') -#         self._verify_options_response(response, -#                                       name='Mock Instance', -#                                       description='This is a mock model-based resource', -#                                       fields={'foo': 'BooleanField', -#                                               'bar': 'IntegerField', -#                                               'baz': 'CharField', -#                                               }) - -#     def _verify_options_response(self, response, name, description, fields=None, status=200, -#                                  mime_type='application/json'): -#         self.assertEqual(response.status_code, status) -#         self.assertEqual(response['Content-Type'].split(';')[0], mime_type) -#         data = json.loads(response.content) -#         self.assertTrue('application/json' in data['renders']) -#         self.assertEqual(name, data['name']) -#         self.assertEqual(description, data['description']) -#         if fields is None: -#             self.assertFalse(hasattr(data, 'fields')) -#         else: -#             self.assertEqual(data['fields'], fields) - - -# class ExtraViewsTests(TestCase): -#     """Test the extra views rest_framework provides""" -#     urls = 'rest_framework.tests.views' - -#     def test_login_view(self): -#         """Ensure the login view exists""" -#         response = self.client.get(reverse('rest_framework:login')) -#         self.assertEqual(response.status_code, 200) -#         self.assertEqual(response['Content-Type'].split(';')[0], 'text/html') - -#     def test_logout_view(self): -#         """Ensure the logout view exists""" -#         response = self.client.get(reverse('rest_framework:logout')) -#         self.assertEqual(response.status_code, 200) -#         self.assertEqual(response['Content-Type'].split(';')[0], 'text/html') +from django.test import TestCase +from django.test.client import RequestFactory +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.response import Response +from rest_framework.settings import api_settings +from rest_framework.views import APIView + +factory = RequestFactory() + + +class BasicView(APIView): +    def get(self, request, *args, **kwargs): +        return Response({'method': 'GET'}) + +    def post(self, request, *args, **kwargs): +        return Response({'method': 'POST', 'data': request.DATA}) + + +@api_view(['GET', 'POST']) +def basic_view(request): +    if request.method == 'GET': +        return {'method': 'GET'} +    elif request.method == 'POST': +        return {'method': 'POST', 'data': request.DATA} + + +class ClassBasedViewIntegrationTests(TestCase): +    def setUp(self): +        self.view = BasicView.as_view() + +    def test_400_parse_error(self): +        request = factory.post('/', 'f00bar', content_type='application/json') +        response = self.view(request) +        expected = { +            'detail': u'JSON parse error - No JSON object could be decoded' +        } +        self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) +        self.assertEquals(response.data, expected) + +    def test_400_parse_error_tunneled_content(self): +        content = 'f00bar' +        content_type = 'application/json' +        form_data = { +            api_settings.FORM_CONTENT_OVERRIDE: content, +            api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type +        } +        request = factory.post('/', form_data) +        response = self.view(request) +        expected = { +            'detail': u'JSON parse error - No JSON object could be decoded' +        } +        self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) +        self.assertEquals(response.data, expected) + + +class FunctionBasedViewIntegrationTests(TestCase): +    def setUp(self): +        self.view = basic_view + +    def test_400_parse_error(self): +        request = factory.post('/', 'f00bar', content_type='application/json') +        response = self.view(request) +        expected = { +            'detail': u'JSON parse error - No JSON object could be decoded' +        } +        self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) +        self.assertEquals(response.data, expected) + +    def test_400_parse_error_tunneled_content(self): +        content = 'f00bar' +        content_type = 'application/json' +        form_data = { +            api_settings.FORM_CONTENT_OVERRIDE: content, +            api_settings.FORM_CONTENTTYPE_OVERRIDE: content_type +        } +        request = factory.post('/', form_data) +        response = self.view(request) +        expected = { +            'detail': u'JSON parse error - No JSON object could be decoded' +        } +        self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST) +        self.assertEquals(response.data, expected)  | 
