aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework/tests
diff options
context:
space:
mode:
Diffstat (limited to 'djangorestframework/tests')
-rw-r--r--djangorestframework/tests/accept.py19
-rw-r--r--djangorestframework/tests/authentication.py18
-rw-r--r--djangorestframework/tests/breadcrumbs.py26
-rw-r--r--djangorestframework/tests/content.py198
-rw-r--r--djangorestframework/tests/description.py62
-rw-r--r--djangorestframework/tests/emitters.py75
-rw-r--r--djangorestframework/tests/files.py19
-rw-r--r--djangorestframework/tests/methods.py79
-rw-r--r--djangorestframework/tests/parsers.py261
-rw-r--r--djangorestframework/tests/renderers.py108
-rw-r--r--djangorestframework/tests/resources.py31
-rw-r--r--djangorestframework/tests/reverse.py18
-rw-r--r--djangorestframework/tests/throttling.py38
-rw-r--r--djangorestframework/tests/validators.py221
-rw-r--r--djangorestframework/tests/views.py2
15 files changed, 625 insertions, 550 deletions
diff --git a/djangorestframework/tests/accept.py b/djangorestframework/tests/accept.py
index f2a21277..f26de633 100644
--- a/djangorestframework/tests/accept.py
+++ b/djangorestframework/tests/accept.py
@@ -1,6 +1,6 @@
from django.test import TestCase
from djangorestframework.compat import RequestFactory
-from djangorestframework.resource import Resource
+from djangorestframework.views import View
# See: http://www.useragentstring.com/
@@ -18,13 +18,16 @@ class UserAgentMungingTest(TestCase):
http://www.gethifi.com/blog/browser-rest-http-accept-headers"""
def setUp(self):
- class MockResource(Resource):
- anon_allowed_methods = allowed_methods = ('GET',)
- def get(self, request, auth):
+
+ class MockView(View):
+ permissions = ()
+
+ def get(self, request):
return {'a':1, 'b':2, 'c':3}
+
self.req = RequestFactory()
- self.MockResource = MockResource
- self.view = MockResource.as_view()
+ self.MockView = MockView
+ self.view = MockView.as_view()
def test_munge_msie_accept_header(self):
"""Send MSIE user agent strings and ensure that we get an HTML response,
@@ -37,9 +40,9 @@ class UserAgentMungingTest(TestCase):
self.assertEqual(resp['Content-Type'], 'text/html')
def test_dont_rewrite_msie_accept_header(self):
- """Turn off REWRITE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
+ """Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
that we get a JSON response if we set a */* accept header."""
- view = self.MockResource.as_view(REWRITE_IE_ACCEPT_HEADER=False)
+ view = self.MockView.as_view(_IGNORE_IE_ACCEPT_HEADER=False)
for user_agent in (MSIE_9_USER_AGENT,
MSIE_8_USER_AGENT,
diff --git a/djangorestframework/tests/authentication.py b/djangorestframework/tests/authentication.py
index 246ad4a0..8254403c 100644
--- a/djangorestframework/tests/authentication.py
+++ b/djangorestframework/tests/authentication.py
@@ -1,23 +1,24 @@
from django.conf.urls.defaults import patterns
+from django.contrib.auth.models import User
+from django.contrib.auth import login
from django.test import Client, TestCase
+
from django.utils import simplejson as json
from djangorestframework.compat import RequestFactory
-from djangorestframework.resource import Resource
-from django.contrib.auth.models import User
-from django.contrib.auth import login
+from djangorestframework.views import View
+from djangorestframework import permissions
import base64
-class MockResource(Resource):
- allowed_methods = ('POST',)
-
- def post(self, request, auth, content):
+class MockView(View):
+ permissions = ( permissions.IsAuthenticated, )
+ def post(self, request):
return {'a':1, 'b':2, 'c':3}
urlpatterns = patterns('',
- (r'^$', MockResource.as_view()),
+ (r'^$', MockView.as_view()),
)
@@ -86,3 +87,4 @@ class SessionAuthTests(TestCase):
"""Ensure POSTing form over session authentication without logged in user fails."""
response = self.csrf_client.post('/', {'example': 'example'})
self.assertEqual(response.status_code, 403)
+
diff --git a/djangorestframework/tests/breadcrumbs.py b/djangorestframework/tests/breadcrumbs.py
index cc0d283d..dc1a02bf 100644
--- a/djangorestframework/tests/breadcrumbs.py
+++ b/djangorestframework/tests/breadcrumbs.py
@@ -1,34 +1,34 @@
from django.conf.urls.defaults import patterns, url
from django.test import TestCase
-from djangorestframework.breadcrumbs import get_breadcrumbs
-from djangorestframework.resource import Resource
+from djangorestframework.utils.breadcrumbs import get_breadcrumbs
+from djangorestframework.views import View
-class Root(Resource):
+class Root(View):
pass
-class ResourceRoot(Resource):
+class ResourceRoot(View):
pass
-class ResourceInstance(Resource):
+class ResourceInstance(View):
pass
-class NestedResourceRoot(Resource):
+class NestedResourceRoot(View):
pass
-class NestedResourceInstance(Resource):
+class NestedResourceInstance(View):
pass
urlpatterns = patterns('',
- url(r'^$', Root),
- url(r'^resource/$', ResourceRoot),
- url(r'^resource/(?P<key>[0-9]+)$', ResourceInstance),
- url(r'^resource/(?P<key>[0-9]+)/$', NestedResourceRoot),
- url(r'^resource/(?P<key>[0-9]+)/(?P<other>[A-Za-z]+)$', NestedResourceInstance),
+ url(r'^$', Root.as_view()),
+ url(r'^resource/$', ResourceRoot.as_view()),
+ url(r'^resource/(?P<key>[0-9]+)$', ResourceInstance.as_view()),
+ url(r'^resource/(?P<key>[0-9]+)/$', NestedResourceRoot.as_view()),
+ url(r'^resource/(?P<key>[0-9]+)/(?P<other>[A-Za-z]+)$', NestedResourceInstance.as_view()),
)
class BreadcrumbTests(TestCase):
- """Tests the breadcrumb functionality used by the HTML emitter."""
+ """Tests the breadcrumb functionality used by the HTML renderer."""
urls = 'djangorestframework.tests.breadcrumbs'
diff --git a/djangorestframework/tests/content.py b/djangorestframework/tests/content.py
index c5eae2f9..ee3597a4 100644
--- a/djangorestframework/tests/content.py
+++ b/djangorestframework/tests/content.py
@@ -1,122 +1,78 @@
-# TODO: refactor these tests
-#from django.test import TestCase
-#from djangorestframework.compat import RequestFactory
-#from djangorestframework.content import ContentMixin, StandardContentMixin, OverloadedContentMixin
-#
-#
-#class TestContentMixins(TestCase):
-# def setUp(self):
-# self.req = RequestFactory()
-#
-# # Interface tests
-#
-# def test_content_mixin_interface(self):
-# """Ensure the ContentMixin interface is as expected."""
-# self.assertRaises(NotImplementedError, ContentMixin().determine_content, None)
-#
-# def test_standard_content_mixin_interface(self):
-# """Ensure the OverloadedContentMixin interface is as expected."""
-# self.assertTrue(issubclass(StandardContentMixin, ContentMixin))
-# getattr(StandardContentMixin, 'determine_content')
-#
-# def test_overloaded_content_mixin_interface(self):
-# """Ensure the OverloadedContentMixin interface is as expected."""
-# self.assertTrue(issubclass(OverloadedContentMixin, ContentMixin))
-# getattr(OverloadedContentMixin, 'CONTENT_PARAM')
-# getattr(OverloadedContentMixin, 'CONTENTTYPE_PARAM')
-# getattr(OverloadedContentMixin, 'determine_content')
-#
-#
-# # Common functionality to test with both StandardContentMixin and OverloadedContentMixin
-#
-# def ensure_determines_no_content_GET(self, mixin):
-# """Ensure determine_content(request) returns None for GET request with no content."""
-# request = self.req.get('/')
-# self.assertEqual(mixin.determine_content(request), None)
-#
-# def ensure_determines_form_content_POST(self, mixin):
-# """Ensure determine_content(request) returns content for POST request with content."""
-# form_data = {'qwerty': 'uiop'}
-# request = self.req.post('/', data=form_data)
-# self.assertEqual(mixin.determine_content(request), (request.META['CONTENT_TYPE'], request.raw_post_data))
-#
-# def ensure_determines_non_form_content_POST(self, mixin):
-# """Ensure determine_content(request) returns (content type, content) for POST request with content."""
-# content = 'qwerty'
-# content_type = 'text/plain'
-# request = self.req.post('/', content, content_type=content_type)
-# self.assertEqual(mixin.determine_content(request), (content_type, content))
-#
-# def ensure_determines_form_content_PUT(self, mixin):
-# """Ensure determine_content(request) returns content for PUT request with content."""
-# form_data = {'qwerty': 'uiop'}
-# request = self.req.put('/', data=form_data)
-# self.assertEqual(mixin.determine_content(request), (request.META['CONTENT_TYPE'], request.raw_post_data))
-#
-# def ensure_determines_non_form_content_PUT(self, mixin):
-# """Ensure determine_content(request) returns (content type, content) for PUT request with content."""
-# content = 'qwerty'
-# content_type = 'text/plain'
-# request = self.req.put('/', content, content_type=content_type)
-# self.assertEqual(mixin.determine_content(request), (content_type, content))
-#
-# # StandardContentMixin behavioural tests
-#
-# def test_standard_behaviour_determines_no_content_GET(self):
-# """Ensure StandardContentMixin.determine_content(request) returns None for GET request with no content."""
-# self.ensure_determines_no_content_GET(StandardContentMixin())
-#
-# def test_standard_behaviour_determines_form_content_POST(self):
-# """Ensure StandardContentMixin.determine_content(request) returns content for POST request with content."""
-# self.ensure_determines_form_content_POST(StandardContentMixin())
-#
-# def test_standard_behaviour_determines_non_form_content_POST(self):
-# """Ensure StandardContentMixin.determine_content(request) returns (content type, content) for POST request with content."""
-# self.ensure_determines_non_form_content_POST(StandardContentMixin())
-#
-# def test_standard_behaviour_determines_form_content_PUT(self):
-# """Ensure StandardContentMixin.determine_content(request) returns content for PUT request with content."""
-# self.ensure_determines_form_content_PUT(StandardContentMixin())
-#
-# def test_standard_behaviour_determines_non_form_content_PUT(self):
-# """Ensure StandardContentMixin.determine_content(request) returns (content type, content) for PUT request with content."""
-# self.ensure_determines_non_form_content_PUT(StandardContentMixin())
-#
-# # OverloadedContentMixin behavioural tests
-#
-# def test_overloaded_behaviour_determines_no_content_GET(self):
-# """Ensure StandardContentMixin.determine_content(request) returns None for GET request with no content."""
-# self.ensure_determines_no_content_GET(OverloadedContentMixin())
-#
-# def test_overloaded_behaviour_determines_form_content_POST(self):
-# """Ensure StandardContentMixin.determine_content(request) returns content for POST request with content."""
-# self.ensure_determines_form_content_POST(OverloadedContentMixin())
-#
-# def test_overloaded_behaviour_determines_non_form_content_POST(self):
-# """Ensure StandardContentMixin.determine_content(request) returns (content type, content) for POST request with content."""
-# self.ensure_determines_non_form_content_POST(OverloadedContentMixin())
-#
-# def test_overloaded_behaviour_determines_form_content_PUT(self):
-# """Ensure StandardContentMixin.determine_content(request) returns content for PUT request with content."""
-# self.ensure_determines_form_content_PUT(OverloadedContentMixin())
-#
-# def test_overloaded_behaviour_determines_non_form_content_PUT(self):
-# """Ensure StandardContentMixin.determine_content(request) returns (content type, content) for PUT request with content."""
-# self.ensure_determines_non_form_content_PUT(OverloadedContentMixin())
-#
-# def test_overloaded_behaviour_allows_content_tunnelling(self):
-# """Ensure determine_content(request) returns (content type, content) for overloaded POST request"""
-# content = 'qwerty'
-# content_type = 'text/plain'
-# form_data = {OverloadedContentMixin.CONTENT_PARAM: content,
-# OverloadedContentMixin.CONTENTTYPE_PARAM: content_type}
-# request = self.req.post('/', form_data)
-# self.assertEqual(OverloadedContentMixin().determine_content(request), (content_type, content))
-# self.assertEqual(request.META['CONTENT_TYPE'], content_type)
-#
-# def test_overloaded_behaviour_allows_content_tunnelling_content_type_not_set(self):
-# """Ensure determine_content(request) returns (None, content) for overloaded POST request with content type not set"""
-# content = 'qwerty'
-# request = self.req.post('/', {OverloadedContentMixin.CONTENT_PARAM: content})
-# self.assertEqual(OverloadedContentMixin().determine_content(request), (None, content))
+"""
+Tests for content parsing, and form-overloaded content parsing.
+"""
+from django.test import TestCase
+from djangorestframework.compat import RequestFactory
+from djangorestframework.mixins import RequestMixin
+from djangorestframework.parsers import FormParser, MultiPartParser, PlainTextParser
+
+class TestContentParsing(TestCase):
+ def setUp(self):
+ self.req = RequestFactory()
+
+ def ensure_determines_no_content_GET(self, view):
+ """Ensure view.DATA returns None for GET request with no content."""
+ view.request = self.req.get('/')
+ self.assertEqual(view.DATA, None)
+
+ def ensure_determines_form_content_POST(self, view):
+ """Ensure view.DATA returns content for POST request with form content."""
+ form_data = {'qwerty': 'uiop'}
+ view.parsers = (FormParser, MultiPartParser)
+ view.request = self.req.post('/', data=form_data)
+ self.assertEqual(view.DATA.items(), form_data.items())
+
+ def ensure_determines_non_form_content_POST(self, view):
+ """Ensure view.RAW_CONTENT returns content for POST request with non-form content."""
+ content = 'qwerty'
+ content_type = 'text/plain'
+ view.parsers = (PlainTextParser,)
+ view.request = self.req.post('/', content, content_type=content_type)
+ self.assertEqual(view.DATA, content)
+
+ def ensure_determines_form_content_PUT(self, view):
+ """Ensure view.RAW_CONTENT returns content for PUT request with form content."""
+ form_data = {'qwerty': 'uiop'}
+ view.parsers = (FormParser, MultiPartParser)
+ view.request = self.req.put('/', data=form_data)
+ self.assertEqual(view.DATA.items(), form_data.items())
+
+ def ensure_determines_non_form_content_PUT(self, view):
+ """Ensure view.RAW_CONTENT returns content for PUT request with non-form content."""
+ content = 'qwerty'
+ content_type = 'text/plain'
+ view.parsers = (PlainTextParser,)
+ view.request = self.req.post('/', content, content_type=content_type)
+ self.assertEqual(view.DATA, content)
+
+ def test_standard_behaviour_determines_no_content_GET(self):
+ """Ensure view.DATA returns None for GET request with no content."""
+ self.ensure_determines_no_content_GET(RequestMixin())
+
+ def test_standard_behaviour_determines_form_content_POST(self):
+ """Ensure view.DATA returns content for POST request with form content."""
+ self.ensure_determines_form_content_POST(RequestMixin())
+
+ def test_standard_behaviour_determines_non_form_content_POST(self):
+ """Ensure view.DATA returns content for POST request with non-form content."""
+ self.ensure_determines_non_form_content_POST(RequestMixin())
+
+ def test_standard_behaviour_determines_form_content_PUT(self):
+ """Ensure view.DATA returns content for PUT request with form content."""
+ self.ensure_determines_form_content_PUT(RequestMixin())
+
+ def test_standard_behaviour_determines_non_form_content_PUT(self):
+ """Ensure view.DATA returns content for PUT request with non-form content."""
+ self.ensure_determines_non_form_content_PUT(RequestMixin())
+
+ def test_overloaded_behaviour_allows_content_tunnelling(self):
+ """Ensure request.DATA returns content for overloaded POST request"""
+ content = 'qwerty'
+ content_type = 'text/plain'
+ view = RequestMixin()
+ form_data = {view._CONTENT_PARAM: content,
+ view._CONTENTTYPE_PARAM: content_type}
+ view.request = self.req.post('/', form_data)
+ view.parsers = (PlainTextParser,)
+ self.assertEqual(view.DATA, content)
diff --git a/djangorestframework/tests/description.py b/djangorestframework/tests/description.py
index 3e3f7b21..1ce29112 100644
--- a/djangorestframework/tests/description.py
+++ b/djangorestframework/tests/description.py
@@ -1,7 +1,7 @@
from django.test import TestCase
-from djangorestframework.resource import Resource
-from djangorestframework.markdownwrapper import apply_markdown
-from djangorestframework.description import get_name, get_description
+from djangorestframework.views import View
+from djangorestframework.compat import apply_markdown
+from djangorestframework.utils.description import get_name, get_description
# We check that docstrings get nicely un-indented.
DESCRIPTION = """an example docstring
@@ -32,23 +32,24 @@ MARKED_DOWN = """<h2>an example docstring</h2>
<h2 id="hash_style_header">hash style header</h2>"""
-class TestResourceNamesAndDescriptions(TestCase):
+class TestViewNamesAndDescriptions(TestCase):
def test_resource_name_uses_classname_by_default(self):
"""Ensure Resource names are based on the classname by default."""
- class MockResource(Resource):
+ class MockView(View):
pass
- self.assertEquals(get_name(MockResource()), 'Mock Resource')
+ self.assertEquals(get_name(MockView()), 'Mock')
- def test_resource_name_can_be_set_explicitly(self):
- """Ensure Resource names can be set using the 'name' class attribute."""
- example = 'Some Other Name'
- class MockResource(Resource):
- name = example
- self.assertEquals(get_name(MockResource()), example)
+ # This has been turned off now.
+ #def test_resource_name_can_be_set_explicitly(self):
+ # """Ensure Resource names can be set using the 'name' class attribute."""
+ # example = 'Some Other Name'
+ # class MockView(View):
+ # name = example
+ # self.assertEquals(get_name(MockView()), example)
def test_resource_description_uses_docstring_by_default(self):
"""Ensure Resource names are based on the docstring by default."""
- class MockResource(Resource):
+ class MockView(View):
"""an example docstring
====================
@@ -64,28 +65,29 @@ class TestResourceNamesAndDescriptions(TestCase):
# hash style header #"""
- self.assertEquals(get_description(MockResource()), DESCRIPTION)
-
- def test_resource_description_can_be_set_explicitly(self):
- """Ensure Resource descriptions can be set using the 'description' class attribute."""
- example = 'Some other description'
- class MockResource(Resource):
- """docstring"""
- description = example
- self.assertEquals(get_description(MockResource()), example)
+ self.assertEquals(get_description(MockView()), DESCRIPTION)
+
+ # This has been turned off now
+ #def test_resource_description_can_be_set_explicitly(self):
+ # """Ensure Resource descriptions can be set using the 'description' class attribute."""
+ # example = 'Some other description'
+ # class MockView(View):
+ # """docstring"""
+ # description = example
+ # self.assertEquals(get_description(MockView()), example)
- def test_resource_description_does_not_require_docstring(self):
- """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'description' class attribute."""
- example = 'Some other description'
- class MockResource(Resource):
- description = example
- self.assertEquals(get_description(MockResource()), example)
+ #def test_resource_description_does_not_require_docstring(self):
+ # """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'description' class attribute."""
+ # example = 'Some other description'
+ # class MockView(View):
+ # description = example
+ # self.assertEquals(get_description(MockView()), example)
def test_resource_description_can_be_empty(self):
"""Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string"""
- class MockResource(Resource):
+ class MockView(View):
pass
- self.assertEquals(get_description(MockResource()), '')
+ self.assertEquals(get_description(MockView()), '')
def test_markdown(self):
"""Ensure markdown to HTML works as expected"""
diff --git a/djangorestframework/tests/emitters.py b/djangorestframework/tests/emitters.py
deleted file mode 100644
index 7d024ccf..00000000
--- a/djangorestframework/tests/emitters.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from django.conf.urls.defaults import patterns, url
-from django import http
-from django.test import TestCase
-from djangorestframework.compat import View
-from djangorestframework.emitters import EmitterMixin, BaseEmitter
-from djangorestframework.response import Response
-
-DUMMYSTATUS = 200
-DUMMYCONTENT = 'dummycontent'
-
-EMITTER_A_SERIALIZER = lambda x: 'Emitter A: %s' % x
-EMITTER_B_SERIALIZER = lambda x: 'Emitter B: %s' % x
-
-class MockView(EmitterMixin, View):
- def get(self, request):
- response = Response(DUMMYSTATUS, DUMMYCONTENT)
- return self.emit(response)
-
-class EmitterA(BaseEmitter):
- media_type = 'mock/emittera'
-
- def emit(self, output, verbose=False):
- return EMITTER_A_SERIALIZER(output)
-
-class EmitterB(BaseEmitter):
- media_type = 'mock/emitterb'
-
- def emit(self, output, verbose=False):
- return EMITTER_B_SERIALIZER(output)
-
-
-urlpatterns = patterns('',
- url(r'^$', MockView.as_view(emitters=[EmitterA, EmitterB])),
-)
-
-
-class EmitterIntegrationTests(TestCase):
- """End-to-end testing of emitters using an EmitterMixin on a generic view."""
-
- urls = 'djangorestframework.tests.emitters'
-
- def test_default_emitter_serializes_content(self):
- """If the Accept header is not set the default emitter should serialize the response."""
- resp = self.client.get('/')
- self.assertEquals(resp['Content-Type'], EmitterA.media_type)
- self.assertEquals(resp.content, EMITTER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_default_emitter_serializes_content_on_accept_any(self):
- """If the Accept header is set to */* the default emitter should serialize the response."""
- resp = self.client.get('/', HTTP_ACCEPT='*/*')
- self.assertEquals(resp['Content-Type'], EmitterA.media_type)
- self.assertEquals(resp.content, EMITTER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_specified_emitter_serializes_content_default_case(self):
- """If the Accept header is set the specified emitter should serialize the response.
- (In this case we check that works for the default emitter)"""
- resp = self.client.get('/', HTTP_ACCEPT=EmitterA.media_type)
- self.assertEquals(resp['Content-Type'], EmitterA.media_type)
- self.assertEquals(resp.content, EMITTER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_specified_emitter_serializes_content_non_default_case(self):
- """If the Accept header is set the specified emitter should serialize the response.
- (In this case we check that works for a non-default emitter)"""
- resp = self.client.get('/', HTTP_ACCEPT=EmitterB.media_type)
- self.assertEquals(resp['Content-Type'], EmitterB.media_type)
- self.assertEquals(resp.content, EMITTER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
- """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
- resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
- self.assertEquals(resp.status_code, 406) \ No newline at end of file
diff --git a/djangorestframework/tests/files.py b/djangorestframework/tests/files.py
index e155f181..25aad9b4 100644
--- a/djangorestframework/tests/files.py
+++ b/djangorestframework/tests/files.py
@@ -1,7 +1,8 @@
from django.test import TestCase
from django import forms
from djangorestframework.compat import RequestFactory
-from djangorestframework.resource import Resource
+from djangorestframework.views import View
+from djangorestframework.resources import FormResource
import StringIO
class UploadFilesTests(TestCase):
@@ -15,19 +16,21 @@ class UploadFilesTests(TestCase):
class FileForm(forms.Form):
file = forms.FileField
- class MockResource(Resource):
- allowed_methods = anon_allowed_methods = ('POST',)
+ class MockResource(FormResource):
form = FileForm
- def post(self, request, auth, content, *args, **kwargs):
- #self.uploaded = content.file
- return {'FILE_NAME': content['file'].name,
- 'FILE_CONTENT': content['file'].read()}
+ class MockView(View):
+ permissions = ()
+ resource = MockResource
+
+ def post(self, request, *args, **kwargs):
+ return {'FILE_NAME': self.CONTENT['file'][0].name,
+ 'FILE_CONTENT': self.CONTENT['file'][0].read()}
file = StringIO.StringIO('stuff')
file.name = 'stuff.txt'
request = self.factory.post('/', {'file': file})
- view = MockResource.as_view()
+ view = MockView.as_view()
response = view(request)
self.assertEquals(response.content, '{"FILE_CONTENT": "stuff", "FILE_NAME": "stuff.txt"}')
diff --git a/djangorestframework/tests/methods.py b/djangorestframework/tests/methods.py
index f19bb3e5..d8f0d919 100644
--- a/djangorestframework/tests/methods.py
+++ b/djangorestframework/tests/methods.py
@@ -1,53 +1,26 @@
-# TODO: Refactor these tests
-#from django.test import TestCase
-#from djangorestframework.compat import RequestFactory
-#from djangorestframework.methods import MethodMixin, StandardMethodMixin, OverloadedPOSTMethodMixin
-#
-#
-#class TestMethodMixins(TestCase):
-# def setUp(self):
-# self.req = RequestFactory()
-#
-# # Interface tests
-#
-# def test_method_mixin_interface(self):
-# """Ensure the base ContentMixin interface is as expected."""
-# self.assertRaises(NotImplementedError, MethodMixin().determine_method, None)
-#
-# def test_standard_method_mixin_interface(self):
-# """Ensure the StandardMethodMixin interface is as expected."""
-# self.assertTrue(issubclass(StandardMethodMixin, MethodMixin))
-# getattr(StandardMethodMixin, 'determine_method')
-#
-# def test_overloaded_method_mixin_interface(self):
-# """Ensure the OverloadedPOSTMethodMixin interface is as expected."""
-# self.assertTrue(issubclass(OverloadedPOSTMethodMixin, MethodMixin))
-# getattr(OverloadedPOSTMethodMixin, 'METHOD_PARAM')
-# getattr(OverloadedPOSTMethodMixin, 'determine_method')
-#
-# # Behavioural tests
-#
-# def test_standard_behaviour_determines_GET(self):
-# """GET requests identified as GET method with StandardMethodMixin"""
-# request = self.req.get('/')
-# self.assertEqual(StandardMethodMixin().determine_method(request), 'GET')
-#
-# def test_standard_behaviour_determines_POST(self):
-# """POST requests identified as POST method with StandardMethodMixin"""
-# request = self.req.post('/')
-# self.assertEqual(StandardMethodMixin().determine_method(request), 'POST')
-#
-# def test_overloaded_POST_behaviour_determines_GET(self):
-# """GET requests identified as GET method with OverloadedPOSTMethodMixin"""
-# request = self.req.get('/')
-# self.assertEqual(OverloadedPOSTMethodMixin().determine_method(request), 'GET')
-#
-# def test_overloaded_POST_behaviour_determines_POST(self):
-# """POST requests identified as POST method with OverloadedPOSTMethodMixin"""
-# request = self.req.post('/')
-# self.assertEqual(OverloadedPOSTMethodMixin().determine_method(request), 'POST')
-#
-# def test_overloaded_POST_behaviour_determines_overloaded_method(self):
-# """POST requests can be overloaded to another method by setting a reserved form field with OverloadedPOSTMethodMixin"""
-# request = self.req.post('/', {OverloadedPOSTMethodMixin.METHOD_PARAM: 'DELETE'})
-# self.assertEqual(OverloadedPOSTMethodMixin().determine_method(request), 'DELETE')
+from django.test import TestCase
+from djangorestframework.compat import RequestFactory
+from djangorestframework.mixins import RequestMixin
+
+
+class TestMethodOverloading(TestCase):
+ def setUp(self):
+ self.req = RequestFactory()
+
+ def test_standard_behaviour_determines_GET(self):
+ """GET requests identified"""
+ view = RequestMixin()
+ view.request = self.req.get('/')
+ self.assertEqual(view.method, 'GET')
+
+ def test_standard_behaviour_determines_POST(self):
+ """POST requests identified"""
+ view = RequestMixin()
+ view.request = self.req.post('/')
+ self.assertEqual(view.method, 'POST')
+
+ def test_overloaded_POST_behaviour_determines_overloaded_method(self):
+ """POST requests can be overloaded to another method by setting a reserved form field"""
+ view = RequestMixin()
+ view.request = self.req.post('/', {view._METHOD_PARAM: 'DELETE'})
+ self.assertEqual(view.method, 'DELETE')
diff --git a/djangorestframework/tests/parsers.py b/djangorestframework/tests/parsers.py
index 4753f6f3..3ab1a61c 100644
--- a/djangorestframework/tests/parsers.py
+++ b/djangorestframework/tests/parsers.py
@@ -1,130 +1,133 @@
-"""
-..
- >>> from djangorestframework.parsers import FormParser
- >>> from djangorestframework.compat import RequestFactory
- >>> from djangorestframework.resource import Resource
- >>> from StringIO import StringIO
- >>> from urllib import urlencode
- >>> req = RequestFactory().get('/')
- >>> some_resource = Resource()
- >>> some_resource.request = req # Make as if this request had been dispatched
-
-FormParser
-============
-
-Data flatening
-----------------
-
-Here is some example data, which would eventually be sent along with a post request :
-
- >>> inpt = urlencode([
- ... ('key1', 'bla1'),
- ... ('key2', 'blo1'), ('key2', 'blo2'),
- ... ])
-
-Default behaviour for :class:`parsers.FormParser`, is to return a single value for each parameter :
-
- >>> FormParser(some_resource).parse(StringIO(inpt)) == {'key1': 'bla1', 'key2': 'blo1'}
- True
-
-However, you can customize this behaviour by subclassing :class:`parsers.FormParser`, and overriding :meth:`parsers.FormParser.is_a_list` :
-
- >>> class MyFormParser(FormParser):
- ...
- ... def is_a_list(self, key, val_list):
- ... return len(val_list) > 1
-
-This new parser only flattens the lists of parameters that contain a single value.
-
- >>> MyFormParser(some_resource).parse(StringIO(inpt)) == {'key1': 'bla1', 'key2': ['blo1', 'blo2']}
- True
-
-.. note:: The same functionality is available for :class:`parsers.MultipartParser`.
-
-Submitting an empty list
---------------------------
-
-When submitting an empty select multiple, like this one ::
-
- <select multiple="multiple" name="key2"></select>
-
-The browsers usually strip the parameter completely. A hack to avoid this, and therefore being able to submit an empty select multiple, is to submit a value that tells the server that the list is empty ::
-
- <select multiple="multiple" name="key2"><option value="_empty"></select>
-
-:class:`parsers.FormParser` provides the server-side implementation for this hack. Considering the following posted data :
-
- >>> inpt = urlencode([
- ... ('key1', 'blo1'), ('key1', '_empty'),
- ... ('key2', '_empty'),
- ... ])
-
-:class:`parsers.FormParser` strips the values ``_empty`` from all the lists.
-
- >>> MyFormParser(some_resource).parse(StringIO(inpt)) == {'key1': 'blo1'}
- True
-
-Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a list, so the parser just stripped it.
-
- >>> class MyFormParser(FormParser):
- ...
- ... def is_a_list(self, key, val_list):
- ... return key == 'key2'
- ...
- >>> MyFormParser(some_resource).parse(StringIO(inpt)) == {'key1': 'blo1', 'key2': []}
- True
-
-Better like that. Note that you can configure something else than ``_empty`` for the empty value by setting :attr:`parsers.FormParser.EMPTY_VALUE`.
-"""
-import httplib, mimetypes
-from tempfile import TemporaryFile
-from django.test import TestCase
-from djangorestframework.compat import RequestFactory
-from djangorestframework.parsers import MultipartParser
-from djangorestframework.resource import Resource
-from djangorestframework.mediatypes import MediaType
-from StringIO import StringIO
-
-def encode_multipart_formdata(fields, files):
- """For testing multipart parser.
- fields is a sequence of (name, value) elements for regular form fields.
- files is a sequence of (name, filename, value) elements for data to be uploaded as files
- Return (content_type, body)."""
- BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
- CRLF = '\r\n'
- L = []
- for (key, value) in fields:
- L.append('--' + BOUNDARY)
- L.append('Content-Disposition: form-data; name="%s"' % key)
- L.append('')
- L.append(value)
- for (key, filename, value) in files:
- L.append('--' + BOUNDARY)
- L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
- L.append('Content-Type: %s' % get_content_type(filename))
- L.append('')
- L.append(value)
- L.append('--' + BOUNDARY + '--')
- L.append('')
- body = CRLF.join(L)
- content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
- return content_type, body
-
-def get_content_type(filename):
- return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-
-class TestMultipartParser(TestCase):
- def setUp(self):
- self.req = RequestFactory()
- self.content_type, self.body = encode_multipart_formdata([('key1', 'val1'), ('key1', 'val2')],
- [('file1', 'pic.jpg', 'blablabla'), ('file1', 't.txt', 'blobloblo')])
-
- def test_multipartparser(self):
- """Ensure that MultipartParser can parse multipart/form-data that contains a mix of several files and parameters."""
- post_req = RequestFactory().post('/', self.body, content_type=self.content_type)
- resource = Resource()
- resource.request = post_req
- parsed = MultipartParser(resource).parse(StringIO(self.body))
- self.assertEqual(parsed['key1'], 'val1')
- self.assertEqual(parsed.FILES['file1'].read(), 'blablabla')
+# """
+# ..
+# >>> from djangorestframework.parsers import FormParser
+# >>> from djangorestframework.compat import RequestFactory
+# >>> from djangorestframework.views import View
+# >>> from StringIO import StringIO
+# >>> from urllib import urlencode
+# >>> req = RequestFactory().get('/')
+# >>> some_view = View()
+# >>> some_view.request = req # Make as if this request had been dispatched
+#
+# FormParser
+# ============
+#
+# Data flatening
+# ----------------
+#
+# Here is some example data, which would eventually be sent along with a post request :
+#
+# >>> inpt = urlencode([
+# ... ('key1', 'bla1'),
+# ... ('key2', 'blo1'), ('key2', 'blo2'),
+# ... ])
+#
+# Default behaviour for :class:`parsers.FormParser`, is to return a single value for each parameter :
+#
+# >>> (data, files) = FormParser(some_view).parse(StringIO(inpt))
+# >>> data == {'key1': 'bla1', 'key2': 'blo1'}
+# True
+#
+# However, you can customize this behaviour by subclassing :class:`parsers.FormParser`, and overriding :meth:`parsers.FormParser.is_a_list` :
+#
+# >>> class MyFormParser(FormParser):
+# ...
+# ... def is_a_list(self, key, val_list):
+# ... return len(val_list) > 1
+#
+# This new parser only flattens the lists of parameters that contain a single value.
+#
+# >>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
+# >>> data == {'key1': 'bla1', 'key2': ['blo1', 'blo2']}
+# True
+#
+# .. note:: The same functionality is available for :class:`parsers.MultiPartParser`.
+#
+# Submitting an empty list
+# --------------------------
+#
+# When submitting an empty select multiple, like this one ::
+#
+# <select multiple="multiple" name="key2"></select>
+#
+# The browsers usually strip the parameter completely. A hack to avoid this, and therefore being able to submit an empty select multiple, is to submit a value that tells the server that the list is empty ::
+#
+# <select multiple="multiple" name="key2"><option value="_empty"></select>
+#
+# :class:`parsers.FormParser` provides the server-side implementation for this hack. Considering the following posted data :
+#
+# >>> inpt = urlencode([
+# ... ('key1', 'blo1'), ('key1', '_empty'),
+# ... ('key2', '_empty'),
+# ... ])
+#
+# :class:`parsers.FormParser` strips the values ``_empty`` from all the lists.
+#
+# >>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
+# >>> data == {'key1': 'blo1'}
+# True
+#
+# Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a list, so the parser just stripped it.
+#
+# >>> class MyFormParser(FormParser):
+# ...
+# ... def is_a_list(self, key, val_list):
+# ... return key == 'key2'
+# ...
+# >>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
+# >>> data == {'key1': 'blo1', 'key2': []}
+# True
+#
+# Better like that. Note that you can configure something else than ``_empty`` for the empty value by setting :attr:`parsers.FormParser.EMPTY_VALUE`.
+# """
+# import httplib, mimetypes
+# from tempfile import TemporaryFile
+# from django.test import TestCase
+# from djangorestframework.compat import RequestFactory
+# from djangorestframework.parsers import MultiPartParser
+# from djangorestframework.views import View
+# from StringIO import StringIO
+#
+# def encode_multipart_formdata(fields, files):
+# """For testing multipart parser.
+# fields is a sequence of (name, value) elements for regular form fields.
+# files is a sequence of (name, filename, value) elements for data to be uploaded as files
+# Return (content_type, body)."""
+# BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
+# CRLF = '\r\n'
+# L = []
+# for (key, value) in fields:
+# L.append('--' + BOUNDARY)
+# L.append('Content-Disposition: form-data; name="%s"' % key)
+# L.append('')
+# L.append(value)
+# for (key, filename, value) in files:
+# L.append('--' + BOUNDARY)
+# L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
+# L.append('Content-Type: %s' % get_content_type(filename))
+# L.append('')
+# L.append(value)
+# L.append('--' + BOUNDARY + '--')
+# L.append('')
+# body = CRLF.join(L)
+# content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
+# return content_type, body
+#
+# def get_content_type(filename):
+# return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+#
+#class TestMultiPartParser(TestCase):
+# def setUp(self):
+# self.req = RequestFactory()
+# self.content_type, self.body = encode_multipart_formdata([('key1', 'val1'), ('key1', 'val2')],
+# [('file1', 'pic.jpg', 'blablabla'), ('file1', 't.txt', 'blobloblo')])
+#
+# def test_multipartparser(self):
+# """Ensure that MultiPartParser can parse multipart/form-data that contains a mix of several files and parameters."""
+# post_req = RequestFactory().post('/', self.body, content_type=self.content_type)
+# view = View()
+# view.request = post_req
+# (data, files) = MultiPartParser(view).parse(StringIO(self.body))
+# self.assertEqual(data['key1'], 'val1')
+# self.assertEqual(files['file1'].read(), 'blablabla')
diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py
new file mode 100644
index 00000000..54276993
--- /dev/null
+++ b/djangorestframework/tests/renderers.py
@@ -0,0 +1,108 @@
+from django.conf.urls.defaults import patterns, url
+from django import http
+from django.test import TestCase
+from djangorestframework.compat import View as DjangoView
+from djangorestframework.renderers import BaseRenderer, JSONRenderer
+from djangorestframework.mixins import ResponseMixin
+from djangorestframework.response import Response
+from djangorestframework.utils.mediatypes import add_media_type_param
+
+DUMMYSTATUS = 200
+DUMMYCONTENT = 'dummycontent'
+
+RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x
+RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x
+
+class RendererA(BaseRenderer):
+ media_type = 'mock/renderera'
+
+ def render(self, obj=None, media_type=None):
+ return RENDERER_A_SERIALIZER(obj)
+
+class RendererB(BaseRenderer):
+ media_type = 'mock/rendererb'
+
+ def render(self, obj=None, media_type=None):
+ return RENDERER_B_SERIALIZER(obj)
+
+class MockView(ResponseMixin, DjangoView):
+ renderers = (RendererA, RendererB)
+
+ def get(self, request):
+ response = Response(DUMMYSTATUS, DUMMYCONTENT)
+ return self.render(response)
+
+urlpatterns = patterns('',
+ url(r'^$', MockView.as_view(renderers=[RendererA, RendererB])),
+)
+
+
+class RendererIntegrationTests(TestCase):
+ """
+ End-to-end testing of renderers using an RendererMixin on a generic view.
+ """
+
+ urls = 'djangorestframework.tests.renderers'
+
+ def test_default_renderer_serializes_content(self):
+ """If the Accept header is not set the default renderer should serialize the response."""
+ resp = self.client.get('/')
+ self.assertEquals(resp['Content-Type'], RendererA.media_type)
+ self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
+ self.assertEquals(resp.status_code, DUMMYSTATUS)
+
+ def test_default_renderer_serializes_content_on_accept_any(self):
+ """If the Accept header is set to */* the default renderer should serialize the response."""
+ resp = self.client.get('/', HTTP_ACCEPT='*/*')
+ self.assertEquals(resp['Content-Type'], RendererA.media_type)
+ self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
+ self.assertEquals(resp.status_code, DUMMYSTATUS)
+
+ def test_specified_renderer_serializes_content_default_case(self):
+ """If the Accept header is set the specified renderer should serialize the response.
+ (In this case we check that works for the default renderer)"""
+ resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type)
+ self.assertEquals(resp['Content-Type'], RendererA.media_type)
+ self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
+ self.assertEquals(resp.status_code, DUMMYSTATUS)
+
+ def test_specified_renderer_serializes_content_non_default_case(self):
+ """If the Accept header is set the specified renderer should serialize the response.
+ (In this case we check that works for a non-default renderer)"""
+ resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type)
+ self.assertEquals(resp['Content-Type'], RendererB.media_type)
+ self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
+ self.assertEquals(resp.status_code, DUMMYSTATUS)
+
+ def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
+ """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
+ resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
+ self.assertEquals(resp.status_code, 406)
+
+
+
+_flat_repr = '{"foo": ["bar", "baz"]}'
+
+_indented_repr = """{
+ "foo": [
+ "bar",
+ "baz"
+ ]
+}"""
+
+
+class JSONRendererTests(TestCase):
+ """
+ Tests specific to the JSON Renderer
+ """
+ def test_without_content_type_args(self):
+ obj = {'foo':['bar','baz']}
+ renderer = JSONRenderer(None)
+ content = renderer.render(obj, 'application/json')
+ self.assertEquals(content, _flat_repr)
+
+ def test_with_content_type_args(self):
+ obj = {'foo':['bar','baz']}
+ renderer = JSONRenderer(None)
+ content = renderer.render(obj, 'application/json; indent=2')
+ self.assertEquals(content, _indented_repr)
diff --git a/djangorestframework/tests/resources.py b/djangorestframework/tests/resources.py
new file mode 100644
index 00000000..fd1226be
--- /dev/null
+++ b/djangorestframework/tests/resources.py
@@ -0,0 +1,31 @@
+"""Tests for the resource module"""
+from django.test import TestCase
+from djangorestframework.resources import _object_to_data
+
+import datetime
+import decimal
+
+class TestObjectToData(TestCase):
+ """Tests for the _object_to_data function"""
+
+ def test_decimal(self):
+ """Decimals need to be converted to a string representation."""
+ self.assertEquals(_object_to_data(decimal.Decimal('1.5')), '1.5')
+
+ def test_function(self):
+ """Functions with no arguments should be called."""
+ def foo():
+ return 1
+ self.assertEquals(_object_to_data(foo), 1)
+
+ def test_method(self):
+ """Methods with only a ``self`` argument should be called."""
+ class Foo(object):
+ def foo(self):
+ return 1
+ self.assertEquals(_object_to_data(Foo().foo), 1)
+
+ def test_datetime(self):
+ """datetime objects are left as-is."""
+ now = datetime.datetime.now()
+ self.assertEquals(_object_to_data(now), now) \ No newline at end of file
diff --git a/djangorestframework/tests/reverse.py b/djangorestframework/tests/reverse.py
index 2718ebca..b4b0a793 100644
--- a/djangorestframework/tests/reverse.py
+++ b/djangorestframework/tests/reverse.py
@@ -3,19 +3,19 @@ from django.core.urlresolvers import reverse
from django.test import TestCase
from django.utils import simplejson as json
-from djangorestframework.resource import Resource
+from djangorestframework.views import View
-class MockResource(Resource):
+class MockView(View):
"""Mock resource which simply returns a URL, so that we can ensure that reversed URLs are fully qualified"""
- anon_allowed_methods = ('GET',)
+ permissions = ()
- def get(self, request, auth):
+ def get(self, request):
return reverse('another')
urlpatterns = patterns('',
- url(r'^$', MockResource.as_view()),
- url(r'^another$', MockResource.as_view(), name='another'),
+ url(r'^$', MockView.as_view()),
+ url(r'^another$', MockView.as_view(), name='another'),
)
@@ -24,5 +24,9 @@ class ReverseTests(TestCase):
urls = 'djangorestframework.tests.reverse'
def test_reversed_urls_are_fully_qualified(self):
- response = self.client.get('/')
+ try:
+ response = self.client.get('/')
+ except:
+ import traceback
+ traceback.print_exc()
self.assertEqual(json.loads(response.content), 'http://testserver/another')
diff --git a/djangorestframework/tests/throttling.py b/djangorestframework/tests/throttling.py
new file mode 100644
index 00000000..a8f08b18
--- /dev/null
+++ b/djangorestframework/tests/throttling.py
@@ -0,0 +1,38 @@
+from django.conf.urls.defaults import patterns
+from django.test import TestCase
+from django.utils import simplejson as json
+
+from djangorestframework.compat import RequestFactory
+from djangorestframework.views import View
+from djangorestframework.permissions import PerUserThrottling
+
+
+class MockView(View):
+ permissions = ( PerUserThrottling, )
+ throttle = (3, 1) # 3 requests per second
+
+ def get(self, request):
+ return 'foo'
+
+urlpatterns = patterns('',
+ (r'^$', MockView.as_view()),
+)
+
+
+#class ThrottlingTests(TestCase):
+# """Basic authentication"""
+# urls = 'djangorestframework.tests.throttling'
+#
+# def test_requests_are_throttled(self):
+# """Ensure request rate is limited"""
+# for dummy in range(3):
+# response = self.client.get('/')
+# response = self.client.get('/')
+#
+# def test_request_throttling_is_per_user(self):
+# """Ensure request rate is only limited per user, not globally"""
+# pass
+#
+# def test_request_throttling_expires(self):
+# """Ensure request rate is limited for a limited duration only"""
+# pass
diff --git a/djangorestframework/tests/validators.py b/djangorestframework/tests/validators.py
index b5d2d566..a1e5d2d7 100644
--- a/djangorestframework/tests/validators.py
+++ b/djangorestframework/tests/validators.py
@@ -2,68 +2,62 @@ from django import forms
from django.db import models
from django.test import TestCase
from djangorestframework.compat import RequestFactory
-from djangorestframework.validators import ValidatorMixin, FormValidatorMixin, ModelFormValidatorMixin
-from djangorestframework.response import ResponseException
+from djangorestframework.resources import Resource, FormResource, ModelResource
+from djangorestframework.response import ErrorResponse
+from djangorestframework.views import View
+from djangorestframework.resources import Resource
-class TestValidatorMixinInterfaces(TestCase):
- """Basic tests to ensure that the ValidatorMixin classes expose the expected interfaces"""
-
- def test_validator_mixin_interface(self):
- """Ensure the ValidatorMixin base class interface is as expected."""
- self.assertRaises(NotImplementedError, ValidatorMixin().validate, None)
-
- def test_form_validator_mixin_interface(self):
- """Ensure the FormValidatorMixin interface is as expected."""
- self.assertTrue(issubclass(FormValidatorMixin, ValidatorMixin))
- getattr(FormValidatorMixin, 'form')
- getattr(FormValidatorMixin, 'validate')
-
- def test_model_form_validator_mixin_interface(self):
- """Ensure the ModelFormValidatorMixin interface is as expected."""
- self.assertTrue(issubclass(ModelFormValidatorMixin, FormValidatorMixin))
- getattr(ModelFormValidatorMixin, 'model')
- getattr(ModelFormValidatorMixin, 'form')
- getattr(ModelFormValidatorMixin, 'fields')
- getattr(ModelFormValidatorMixin, 'exclude_fields')
- getattr(ModelFormValidatorMixin, 'validate')
-
class TestDisabledValidations(TestCase):
- """Tests on Validator Mixins with validation disabled by setting form to None"""
+ """Tests on FormValidator with validation disabled by setting form to None"""
def test_disabled_form_validator_returns_content_unchanged(self):
- """If the form attribute is None on FormValidatorMixin then validate(content) should just return the content unmodified."""
- class DisabledFormValidator(FormValidatorMixin):
+ """If the view's form attribute is None then FormValidator(view).validate_request(content, None)
+ should just return the content unmodified."""
+ class DisabledFormResource(FormResource):
form = None
+ class MockView(View):
+ resource = DisabledFormResource
+
+ view = MockView()
content = {'qwerty':'uiop'}
- self.assertEqual(DisabledFormValidator().validate(content), content)
+ self.assertEqual(FormResource(view).validate_request(content, None), content)
def test_disabled_form_validator_get_bound_form_returns_none(self):
- """If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
- class DisabledFormValidator(FormValidatorMixin):
+ """If the view's form attribute is None on then
+ FormValidator(view).get_bound_form(content) should just return None."""
+ class DisabledFormResource(FormResource):
form = None
- content = {'qwerty':'uiop'}
- self.assertEqual(DisabledFormValidator().get_bound_form(content), None)
+ class MockView(View):
+ resource = DisabledFormResource
+
+ view = MockView()
+ content = {'qwerty':'uiop'}
+ self.assertEqual(FormResource(view).get_bound_form(content), None)
+
def test_disabled_model_form_validator_returns_content_unchanged(self):
- """If the form attribute is None on FormValidatorMixin then validate(content) should just return the content unmodified."""
- class DisabledModelFormValidator(ModelFormValidatorMixin):
- form = None
+ """If the view's form is None and does not have a Resource with a model set then
+ ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
+ class DisabledModelFormView(View):
+ resource = ModelResource
+
+ view = DisabledModelFormView()
content = {'qwerty':'uiop'}
- self.assertEqual(DisabledModelFormValidator().validate(content), content)
+ self.assertEqual(ModelResource(view).get_bound_form(content), None)#
def test_disabled_model_form_validator_get_bound_form_returns_none(self):
"""If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
- class DisabledModelFormValidator(ModelFormValidatorMixin):
- form = None
-
- content = {'qwerty':'uiop'}
- self.assertEqual(DisabledModelFormValidator().get_bound_form(content), None)
-
+ class DisabledModelFormView(View):
+ resource = ModelResource
+
+ view = DisabledModelFormView()
+ content = {'qwerty':'uiop'}
+ self.assertEqual(ModelResource(view).get_bound_form(content), None)
class TestNonFieldErrors(TestCase):
"""Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
@@ -80,69 +74,81 @@ class TestNonFieldErrors(TestCase):
raise forms.ValidationError(self.ERROR_TEXT)
return self.cleaned_data #pragma: no cover
- class MockValidator(FormValidatorMixin):
+ class MockResource(FormResource):
form = MockForm
+ class MockView(View):
+ pass
+
+ view = MockView()
content = {'field1': 'example1', 'field2': 'example2'}
try:
- MockValidator().validate(content)
- except ResponseException, exc:
+ MockResource(view).validate_request(content, None)
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
else:
- self.fail('ResourceException was not raised') #pragma: no cover
+ self.fail('ErrorResponse was not raised') #pragma: no cover
class TestFormValidation(TestCase):
"""Tests which check basic form validation.
Also includes the same set of tests with a ModelFormValidator for which the form has been explicitly set.
- (ModelFormValidatorMixin should behave as FormValidatorMixin if form is set rather than relying on the default ModelForm)"""
+ (ModelFormValidator should behave as FormValidator if a form is set rather than relying on the default ModelForm)"""
def setUp(self):
class MockForm(forms.Form):
qwerty = forms.CharField(required=True)
- class MockFormValidator(FormValidatorMixin):
- form = MockForm
-
- class MockModelFormValidator(ModelFormValidatorMixin):
+ class MockFormResource(FormResource):
form = MockForm
- self.MockFormValidator = MockFormValidator
- self.MockModelFormValidator = MockModelFormValidator
+ class MockModelResource(ModelResource):
+ form = MockForm
+
+ class MockFormView(View):
+ resource = MockFormResource
+
+ class MockModelFormView(View):
+ resource = MockModelResource
+
+ self.MockFormResource = MockFormResource
+ self.MockModelResource = MockModelResource
+ self.MockFormView = MockFormView
+ self.MockModelFormView = MockModelFormView
def validation_returns_content_unchanged_if_already_valid_and_clean(self, validator):
"""If the content is already valid and clean then validate(content) should just return the content unmodified."""
content = {'qwerty':'uiop'}
- self.assertEqual(validator.validate(content), content)
+ self.assertEqual(validator.validate_request(content, None), content)
def validation_failure_raises_response_exception(self, validator):
"""If form validation fails a ResourceException 400 (Bad Request) should be raised."""
- content = {}
- self.assertRaises(ResponseException, validator.validate, content)
+ content = {}
+ self.assertRaises(ErrorResponse, validator.validate_request, content, None)
def validation_does_not_allow_extra_fields_by_default(self, validator):
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
content = {'qwerty': 'uiop', 'extra': 'extra'}
- self.assertRaises(ResponseException, validator.validate, content)
+ self.assertRaises(ErrorResponse, validator.validate_request, content, None)
def validation_allows_extra_fields_if_explicitly_set(self, validator):
"""If we include an allowed_extra_fields paramater on _validate, then allow fields with those names."""
content = {'qwerty': 'uiop', 'extra': 'extra'}
- validator._validate(content, allowed_extra_fields=('extra',))
+ validator._validate(content, None, allowed_extra_fields=('extra',))
def validation_does_not_require_extra_fields_if_explicitly_set(self, validator):
"""If we include an allowed_extra_fields paramater on _validate, then do not fail if we do not have fields with those names."""
content = {'qwerty': 'uiop'}
- self.assertEqual(validator._validate(content, allowed_extra_fields=('extra',)), content)
+ self.assertEqual(validator._validate(content, None, allowed_extra_fields=('extra',)), content)
def validation_failed_due_to_no_content_returns_appropriate_message(self, validator):
"""If validation fails due to no content, ensure the response contains a single non-field error"""
content = {}
try:
- validator.validate(content)
- except ResponseException, exc:
+ validator.validate_request(content, None)
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -151,8 +157,8 @@ class TestFormValidation(TestCase):
"""If validation fails due to a field error, ensure the response contains a single field error"""
content = {'qwerty': ''}
try:
- validator.validate(content)
- except ResponseException, exc:
+ validator.validate_request(content, None)
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -161,8 +167,8 @@ class TestFormValidation(TestCase):
"""If validation fails due to an invalid field, ensure the response contains a single field error"""
content = {'qwerty': 'uiop', 'extra': 'extra'}
try:
- validator.validate(content)
- except ResponseException, exc:
+ validator.validate_request(content, None)
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'extra': ['This field does not exist.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
@@ -171,70 +177,88 @@ class TestFormValidation(TestCase):
"""If validation for multiple reasons, ensure the response contains each error"""
content = {'qwerty': '', 'extra': 'extra'}
try:
- validator.validate(content)
- except ResponseException, exc:
+ validator.validate_request(content, None)
+ except ErrorResponse, exc:
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.'],
'extra': ['This field does not exist.']}})
else:
self.fail('ResourceException was not raised') #pragma: no cover
- # Tests on FormValidtionMixin
+ # Tests on FormResource
def test_form_validation_returns_content_unchanged_if_already_valid_and_clean(self):
- self.validation_returns_content_unchanged_if_already_valid_and_clean(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
def test_form_validation_failure_raises_response_exception(self):
- self.validation_failure_raises_response_exception(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_failure_raises_response_exception(validator)
def test_validation_does_not_allow_extra_fields_by_default(self):
- self.validation_does_not_allow_extra_fields_by_default(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_does_not_allow_extra_fields_by_default(validator)
def test_validation_allows_extra_fields_if_explicitly_set(self):
- self.validation_allows_extra_fields_if_explicitly_set(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_allows_extra_fields_if_explicitly_set(validator)
def test_validation_does_not_require_extra_fields_if_explicitly_set(self):
- self.validation_does_not_require_extra_fields_if_explicitly_set(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
def test_validation_failed_due_to_no_content_returns_appropriate_message(self):
- self.validation_failed_due_to_no_content_returns_appropriate_message(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
def test_validation_failed_due_to_field_error_returns_appropriate_message(self):
- self.validation_failed_due_to_field_error_returns_appropriate_message(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
def test_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
- self.validation_failed_due_to_invalid_field_returns_appropriate_message(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
def test_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
- self.validation_failed_due_to_multiple_errors_returns_appropriate_message(self.MockFormValidator())
+ validator = self.MockFormResource(self.MockFormView())
+ self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
- # Same tests on ModelFormValidtionMixin
+ # Same tests on ModelResource
def test_modelform_validation_returns_content_unchanged_if_already_valid_and_clean(self):
- self.validation_returns_content_unchanged_if_already_valid_and_clean(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
def test_modelform_validation_failure_raises_response_exception(self):
- self.validation_failure_raises_response_exception(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_failure_raises_response_exception(validator)
def test_modelform_validation_does_not_allow_extra_fields_by_default(self):
- self.validation_does_not_allow_extra_fields_by_default(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_does_not_allow_extra_fields_by_default(validator)
def test_modelform_validation_allows_extra_fields_if_explicitly_set(self):
- self.validation_allows_extra_fields_if_explicitly_set(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_allows_extra_fields_if_explicitly_set(validator)
def test_modelform_validation_does_not_require_extra_fields_if_explicitly_set(self):
- self.validation_does_not_require_extra_fields_if_explicitly_set(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
def test_modelform_validation_failed_due_to_no_content_returns_appropriate_message(self):
- self.validation_failed_due_to_no_content_returns_appropriate_message(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
def test_modelform_validation_failed_due_to_field_error_returns_appropriate_message(self):
- self.validation_failed_due_to_field_error_returns_appropriate_message(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
def test_modelform_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
- self.validation_failed_due_to_invalid_field_returns_appropriate_message(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
def test_modelform_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
- self.validation_failed_due_to_multiple_errors_returns_appropriate_message(self.MockModelFormValidator())
+ validator = self.MockModelResource(self.MockModelFormView())
+ self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
class TestModelFormValidator(TestCase):
@@ -249,43 +273,46 @@ class TestModelFormValidator(TestCase):
@property
def readonly(self):
return 'read only'
-
- class MockValidator(ModelFormValidatorMixin):
+
+ class MockResource(ModelResource):
model = MockModel
+
+ class MockView(View):
+ resource = MockResource
- self.MockValidator = MockValidator
+ self.validator = MockResource(MockView)
def test_property_fields_are_allowed_on_model_forms(self):
"""Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
content = {'qwerty':'example', 'uiop': 'example', 'readonly': 'read only'}
- self.assertEqual(self.MockValidator().validate(content), content)
+ self.assertEqual(self.validator.validate_request(content, None), content)
def test_property_fields_are_not_required_on_model_forms(self):
"""Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
content = {'qwerty':'example', 'uiop': 'example'}
- self.assertEqual(self.MockValidator().validate(content), content)
+ self.assertEqual(self.validator.validate_request(content, None), content)
def test_extra_fields_not_allowed_on_model_forms(self):
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
- content = {'qwerty': 'example', 'uiop':'example', 'readonly': 'read only', 'extra': 'extra'}
- self.assertRaises(ResponseException, self.MockValidator().validate, content)
+ content = {'qwerty': 'example', 'uiop':'example', 'readonly': 'read only', 'extra': 'extra'}
+ self.assertRaises(ErrorResponse, self.validator.validate_request, content, None)
def test_validate_requires_fields_on_model_forms(self):
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
content = {'readonly': 'read only'}
- self.assertRaises(ResponseException, self.MockValidator().validate, content)
+ self.assertRaises(ErrorResponse, self.validator.validate_request, content, None)
def test_validate_does_not_require_blankable_fields_on_model_forms(self):
"""Test standard ModelForm validation behaviour - fields with blank=True are not required."""
content = {'qwerty':'example', 'readonly': 'read only'}
- self.MockValidator().validate(content)
+ self.validator.validate_request(content, None)
def test_model_form_validator_uses_model_forms(self):
- self.assertTrue(isinstance(self.MockValidator().get_bound_form(), forms.ModelForm))
+ self.assertTrue(isinstance(self.validator.get_bound_form(), forms.ModelForm))
diff --git a/djangorestframework/tests/views.py b/djangorestframework/tests/views.py
index 9e2e893f..598712d2 100644
--- a/djangorestframework/tests/views.py
+++ b/djangorestframework/tests/views.py
@@ -3,7 +3,7 @@ from django.test import TestCase
from django.test import Client
-urlpatterns = patterns('djangorestframework.views',
+urlpatterns = patterns('djangorestframework.utils.staticviews',
url(r'^robots.txt$', 'deny_robots'),
url(r'^favicon.ico$', 'favicon'),
url(r'^accounts/login$', 'api_login'),