aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortom christie tom@tomchristie.com2010-12-30 01:02:11 +0000
committertom christie tom@tomchristie.com2010-12-30 01:02:11 +0000
commit8a12f89aaacfc0839d6ab1e62b4b5046930517ba (patch)
tree3f0b4fd095d93c5b003832b5e19ecf7f670924ab
parent650111dc8c0800e5b7d4c878c1d454657b68efca (diff)
downloaddjango-rest-framework-8a12f89aaacfc0839d6ab1e62b4b5046930517ba.tar.bz2
Added tests
-rw-r--r--.hgignore2
-rw-r--r--README.txt13
-rw-r--r--src/rest/resource.py70
-rw-r--r--src/rest/templates/emitter.html (renamed from src/testarchive/templates/emitter.html)0
-rw-r--r--src/rest/templates/emitter.txt (renamed from src/testarchive/templates/emitter.txt)0
-rw-r--r--src/rest/templates/emitter.xhtml (renamed from src/testarchive/templates/emitter.xhtml)0
-rw-r--r--src/settings.py5
-rw-r--r--src/testapp/__init__.py (renamed from src/testarchive/__init__.py)0
-rw-r--r--src/testapp/models.py (renamed from src/testarchive/models.py)0
-rw-r--r--src/testapp/tests.py54
-rw-r--r--src/testapp/urls.py8
-rw-r--r--src/testapp/views.py21
-rw-r--r--src/testarchive/tests.py23
-rw-r--r--src/testarchive/urls.py7
-rw-r--r--src/testarchive/views.py8
-rw-r--r--src/urls.py2
16 files changed, 154 insertions, 59 deletions
diff --git a/.hgignore b/.hgignore
index f72e6586..48c9bb0d 100644
--- a/.hgignore
+++ b/.hgignore
@@ -3,3 +3,5 @@ syntax: glob
*.pyc
*.db
env
+.project
+.pydevproject
diff --git a/README.txt b/README.txt
new file mode 100644
index 00000000..00c5e8b8
--- /dev/null
+++ b/README.txt
@@ -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
+
diff --git a/src/rest/resource.py b/src/rest/resource.py
index 7e4a6cd5..18421a19 100644
--- a/src/rest/resource.py
+++ b/src/rest/resource.py
@@ -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
-
- # Parse the HTTP Request content
- if meth in ('PUT', 'POST'):
- parser = self._determine_parser(request)
- data = parser(self, request).parse(request.raw_post_data)
+ method = request.method
- 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)
+ try:
+ if not method in self.allowed_methods:
+ raise self.HTTPException(405, {'status': 'Method Not Allowed'}, {})
+
+ # Parse the HTTP Request content
+ 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)
+
+ else:
+ (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
- mimetype, emitter = self._determine_emitter(request)
+ 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 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 handle_post(self):
- raise NotImplementedError(self.handle_post) \ No newline at end of file
+ def delete(self, headers={}, *args, **kwargs):
+ return self._not_implemented('delete')
diff --git a/src/testarchive/templates/emitter.html b/src/rest/templates/emitter.html
index 4c843aa3..4c843aa3 100644
--- a/src/testarchive/templates/emitter.html
+++ b/src/rest/templates/emitter.html
diff --git a/src/testarchive/templates/emitter.txt b/src/rest/templates/emitter.txt
index 3bf094c6..3bf094c6 100644
--- a/src/testarchive/templates/emitter.txt
+++ b/src/rest/templates/emitter.txt
diff --git a/src/testarchive/templates/emitter.xhtml b/src/rest/templates/emitter.xhtml
index d9fb3ce9..d9fb3ce9 100644
--- a/src/testarchive/templates/emitter.xhtml
+++ b/src/rest/templates/emitter.xhtml
diff --git a/src/settings.py b/src/settings.py
index 2a55c76a..fa3476db 100644
--- a/src/settings.py
+++ b/src/settings.py
@@ -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',
)
diff --git a/src/testarchive/__init__.py b/src/testapp/__init__.py
index e69de29b..e69de29b 100644
--- a/src/testarchive/__init__.py
+++ b/src/testapp/__init__.py
diff --git a/src/testarchive/models.py b/src/testapp/models.py
index 71a83623..71a83623 100644
--- a/src/testarchive/models.py
+++ b/src/testapp/models.py
diff --git a/src/testapp/tests.py b/src/testapp/tests.py
new file mode 100644
index 00000000..5543cd96
--- /dev/null
+++ b/src/testapp/tests.py
@@ -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)
diff --git a/src/testapp/urls.py b/src/testapp/urls.py
new file mode 100644
index 00000000..a41c156b
--- /dev/null
+++ b/src/testapp/urls.py
@@ -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),
+)
diff --git a/src/testapp/views.py b/src/testapp/views.py
new file mode 100644
index 00000000..f0174414
--- /dev/null
+++ b/src/testapp/views.py
@@ -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, {})
diff --git a/src/testarchive/tests.py b/src/testarchive/tests.py
deleted file mode 100644
index 2aaf1bab..00000000
--- a/src/testarchive/tests.py
+++ /dev/null
@@ -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
-"""}
-
diff --git a/src/testarchive/urls.py b/src/testarchive/urls.py
deleted file mode 100644
index 81aa0fa8..00000000
--- a/src/testarchive/urls.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.conf.urls.defaults import patterns
-from testarchive.views import RootResource
-
-
-urlpatterns = patterns('',
- (r'^$', RootResource),
-)
diff --git a/src/testarchive/views.py b/src/testarchive/views.py
deleted file mode 100644
index a7812dc3..00000000
--- a/src/testarchive/views.py
+++ /dev/null
@@ -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'})
diff --git a/src/urls.py b/src/urls.py
index f6c598ce..f95e9afa 100644
--- a/src/urls.py
+++ b/src/urls.py
@@ -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')),