mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +03:00
Added tests
This commit is contained in:
parent
650111dc8c
commit
8a12f89aaa
13
README.txt
Normal file
13
README.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
# To install django-rest-framework...
|
||||
#
|
||||
# Requirements:
|
||||
# python2.6
|
||||
# virtualenv
|
||||
|
||||
hg clone https://tomchristie@bitbucket.org/tomchristie/django-rest-framework
|
||||
cd django-rest-framework/
|
||||
virtualenv --no-site-packages --distribute --python=python2.6 env
|
||||
source ./env/bin/activate
|
||||
pip install -r ./requirements.txt
|
||||
python ./src/manage.py test
|
||||
|
|
@ -4,6 +4,14 @@ from rest import emitters, parsers
|
|||
|
||||
class Resource(object):
|
||||
|
||||
class HTTPException(Exception):
|
||||
def __init__(self, status, content, headers):
|
||||
self.status = status
|
||||
self.content = content
|
||||
self.headers = headers
|
||||
|
||||
allowed_methods = ('GET',)
|
||||
|
||||
callmap = { 'GET': 'read', 'POST': 'create',
|
||||
'PUT': 'update', 'DELETE': 'delete' }
|
||||
|
||||
|
@ -17,6 +25,7 @@ class Resource(object):
|
|||
'application/xml': parsers.XMLParser,
|
||||
'application/x-www-form-urlencoded': parsers.FormParser }
|
||||
|
||||
|
||||
def __new__(cls, request, *args, **kwargs):
|
||||
self = object.__new__(cls)
|
||||
self.__init__()
|
||||
|
@ -28,7 +37,6 @@ class Resource(object):
|
|||
def _determine_parser(self, request):
|
||||
"""Return the appropriate parser for the input, given the client's 'Content-Type' header,
|
||||
and the content types that this Resource knows how to parse."""
|
||||
print request.META
|
||||
return self.parsers.values()[0]
|
||||
|
||||
# TODO: Raise 415 Unsupported media type
|
||||
|
@ -79,25 +87,40 @@ class Resource(object):
|
|||
(accept_mimetype == mimetype)):
|
||||
return (mimetype, emitter)
|
||||
|
||||
# TODO: Raise 406, Not Acceptable
|
||||
raise self.HTTPException(406, {'status': 'Not Acceptable',
|
||||
'accepts': ','.join(item[0] for item in self.emitters)}, {})
|
||||
|
||||
|
||||
def _handle_request(self, request, *args, **kwargs):
|
||||
meth = request.method
|
||||
method = request.method
|
||||
|
||||
try:
|
||||
if not method in self.allowed_methods:
|
||||
raise self.HTTPException(405, {'status': 'Method Not Allowed'}, {})
|
||||
|
||||
# Parse the HTTP Request content
|
||||
if meth in ('PUT', 'POST'):
|
||||
func = getattr(self, self.callmap.get(method, ''))
|
||||
|
||||
if method in ('PUT', 'POST'):
|
||||
parser = self._determine_parser(request)
|
||||
data = parser(self, request).parse(request.raw_post_data)
|
||||
(status, ret, headers) = func(data, request.META, *args, **kwargs)
|
||||
|
||||
if meth == "POST":
|
||||
(status, ret, headers) = self.handle_post(data, request.META, *args, **kwargs)
|
||||
else:
|
||||
(status, ret, headers) = self.handle_get(request.META, *args, **kwargs)
|
||||
(status, ret, headers) = func(request.META, *args, **kwargs)
|
||||
except self.HTTPException, exc:
|
||||
(status, ret, headers) = (exc.status, exc.content, exc.headers)
|
||||
|
||||
headers['Allow'] = ', '.join(self.allowed_methods)
|
||||
|
||||
# Serialize the HTTP Response content
|
||||
try:
|
||||
mimetype, emitter = self._determine_emitter(request)
|
||||
except self.HTTPException, exc:
|
||||
(status, ret, headers) = (exc.status, exc.content, exc.headers)
|
||||
mimetype, emitter = self.emitters[0]
|
||||
|
||||
content = emitter(self, status, headers).emit(ret)
|
||||
print mimetype, emitter, content
|
||||
|
||||
# Build the HTTP Response
|
||||
resp = HttpResponse(content, mimetype=mimetype, status=status)
|
||||
|
@ -106,8 +129,19 @@ class Resource(object):
|
|||
|
||||
return resp
|
||||
|
||||
def handle_get(self):
|
||||
raise NotImplementedError(self.handle_get)
|
||||
def _not_implemented(self, operation):
|
||||
resource_name = self.__class__.__name__
|
||||
return (500, {'status': 'Internal Server Error',
|
||||
'detail': '%s %s operation is permitted but has not been implemented' % (resource_name, operation)}, {})
|
||||
|
||||
def handle_post(self):
|
||||
raise NotImplementedError(self.handle_post)
|
||||
def read(self, headers={}, *args, **kwargs):
|
||||
return self._not_implemented('read')
|
||||
|
||||
def create(self, data=None, headers={}, *args, **kwargs):
|
||||
return self._not_implemented('create')
|
||||
|
||||
def update(self, data=None, headers={}, *args, **kwargs):
|
||||
return self._not_implemented('update')
|
||||
|
||||
def delete(self, headers={}, *args, **kwargs):
|
||||
return self._not_implemented('delete')
|
||||
|
|
|
@ -75,7 +75,7 @@ MIDDLEWARE_CLASSES = (
|
|||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'src.urls'
|
||||
ROOT_URLCONF = 'urls'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
|
@ -93,5 +93,6 @@ INSTALLED_APPS = (
|
|||
'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
'testarchive',
|
||||
'testapp',
|
||||
'rest',
|
||||
)
|
||||
|
|
54
src/testapp/tests.py
Normal file
54
src/testapp/tests.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from testapp.views import ReadOnlyResource, MirroringWriteResource
|
||||
|
||||
class AcceptHeaderTests(TestCase):
|
||||
def assert_accept_mimetype(self, mimetype, expect=None, expect_match=True):
|
||||
"""
|
||||
Assert that a request with given mimetype in the accept header,
|
||||
gives a response with the appropriate content-type.
|
||||
"""
|
||||
if expect is None:
|
||||
expect = mimetype
|
||||
|
||||
resp = self.client.get(reverse(ReadOnlyResource), HTTP_ACCEPT=mimetype)
|
||||
|
||||
if expect_match:
|
||||
self.assertEquals(resp['content-type'], expect)
|
||||
else:
|
||||
self.assertNotEquals(resp['content-type'], expect)
|
||||
|
||||
def test_accept_xml(self):
|
||||
self.assert_accept_mimetype('application/xml')
|
||||
|
||||
def test_accept_json(self):
|
||||
self.assert_accept_mimetype('application/json')
|
||||
|
||||
def test_accept_xml_prefered_to_json(self):
|
||||
self.assert_accept_mimetype('application/xml,q=0.9;application/json,q=0.1', expect='application/xml')
|
||||
|
||||
def test_accept_json_prefered_to_xml(self):
|
||||
self.assert_accept_mimetype('application/json,q=0.9;application/xml,q=0.1', expect='application/json')
|
||||
|
||||
def test_dont_accept_invalid(self):
|
||||
self.assert_accept_mimetype('application/invalid', expect_match=False)
|
||||
|
||||
def test_invalid_accept_header_returns_406(self):
|
||||
resp = self.client.get(reverse(ReadOnlyResource), HTTP_ACCEPT='invalid/invalid')
|
||||
self.assertEquals(resp.status_code, 406)
|
||||
|
||||
class AllowedMethodsTests(TestCase):
|
||||
def test_write_on_read_only_resource_returns_405(self):
|
||||
resp = self.client.put(reverse(ReadOnlyResource), {})
|
||||
self.assertEquals(resp.status_code, 405)
|
||||
|
||||
def test_read_on_write_only_resource_returns_405(self):
|
||||
resp = self.client.get(reverse(MirroringWriteResource))
|
||||
self.assertEquals(resp.status_code, 405)
|
8
src/testapp/urls.py
Normal file
8
src/testapp/urls.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.conf.urls.defaults import patterns
|
||||
from testapp.views import ReadOnlyResource, MirroringWriteResource
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^read-only$', ReadOnlyResource),
|
||||
(r'^mirroring-write$', MirroringWriteResource),
|
||||
)
|
21
src/testapp/views.py
Normal file
21
src/testapp/views.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from decimal import Decimal
|
||||
from rest.resource import Resource
|
||||
|
||||
class ReadOnlyResource(Resource):
|
||||
"""This is my docstring
|
||||
"""
|
||||
allowed_methods = ('GET',)
|
||||
|
||||
def read(self, headers={}, *args, **kwargs):
|
||||
return (200, {'ExampleString': 'Example',
|
||||
'ExampleInt': 1,
|
||||
'ExampleDecimal': 1.0}, {})
|
||||
|
||||
|
||||
class MirroringWriteResource(Resource):
|
||||
"""This is my docstring
|
||||
"""
|
||||
allowed_methods = ('PUT',)
|
||||
|
||||
def create(self, data, headers={}, *args, **kwargs):
|
||||
return (200, data, {})
|
|
@ -1,23 +0,0 @@
|
|||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 3)
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
from django.conf.urls.defaults import patterns
|
||||
from testarchive.views import RootResource
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', RootResource),
|
||||
)
|
|
@ -1,8 +0,0 @@
|
|||
from rest.resource import Resource
|
||||
|
||||
class RootResource(Resource):
|
||||
"""This is my docstring
|
||||
"""
|
||||
|
||||
def handle_get(self, headers={}, *args, **kwargs):
|
||||
return (200, {'Name': 'Test', 'Value': 1}, {'Location': 'BLAH'})
|
|
@ -5,7 +5,7 @@ admin.autodiscover()
|
|||
|
||||
urlpatterns = patterns('',
|
||||
# Example:
|
||||
(r'^testarchive/', include('testarchive.urls')),
|
||||
(r'^testapp/', include('testapp.urls')),
|
||||
|
||||
# Uncomment the admin/doc line below to enable admin documentation:
|
||||
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
|
|
Loading…
Reference in New Issue
Block a user