diff options
| author | Tom Christie | 2014-09-11 13:20:44 +0100 |
|---|---|---|
| committer | Tom Christie | 2014-09-11 13:20:44 +0100 |
| commit | de301f3b6647e1c79a506405a88071ef977418d1 (patch) | |
| tree | 407f3497b422f334b47088b0bb35d39a8a3a520a /tests | |
| parent | 80ba0473473501968154c5cc5dd5922e53d96a70 (diff) | |
| parent | 015a8122c7738dd8913939b42d3f0ec932d88711 (diff) | |
| download | django-rest-framework-de301f3b6647e1c79a506405a88071ef977418d1.tar.bz2 | |
Merge master
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/browsable_api/__init__.py | 0 | ||||
| -rw-r--r-- | tests/browsable_api/auth_urls.py | 10 | ||||
| -rw-r--r-- | tests/browsable_api/no_auth_urls.py | 9 | ||||
| -rw-r--r-- | tests/browsable_api/test_browsable_api.py | 65 | ||||
| -rw-r--r-- | tests/browsable_api/views.py | 15 | ||||
| -rw-r--r-- | tests/conftest.py | 1 | ||||
| -rw-r--r-- | tests/test_authentication.py | 12 | ||||
| -rw-r--r-- | tests/test_description.py | 24 | ||||
| -rw-r--r-- | tests/test_fields.py | 20 | ||||
| -rw-r--r-- | tests/test_filters.py | 75 | ||||
| -rw-r--r-- | tests/test_generics.py | 10 | ||||
| -rw-r--r-- | tests/test_model_field_mappings.py | 28 | ||||
| -rw-r--r-- | tests/test_pagination.py | 19 | ||||
| -rw-r--r-- | tests/test_parsers.py | 24 | ||||
| -rw-r--r-- | tests/test_validation.py | 40 |
15 files changed, 323 insertions, 29 deletions
diff --git a/tests/browsable_api/__init__.py b/tests/browsable_api/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/browsable_api/__init__.py diff --git a/tests/browsable_api/auth_urls.py b/tests/browsable_api/auth_urls.py new file mode 100644 index 00000000..bce7dcf9 --- /dev/null +++ b/tests/browsable_api/auth_urls.py @@ -0,0 +1,10 @@ +from __future__ import unicode_literals +from django.conf.urls import patterns, url, include + +from .views import MockView + +urlpatterns = patterns( + '', + (r'^$', MockView.as_view()), + url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), +) diff --git a/tests/browsable_api/no_auth_urls.py b/tests/browsable_api/no_auth_urls.py new file mode 100644 index 00000000..5e3604a6 --- /dev/null +++ b/tests/browsable_api/no_auth_urls.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals +from django.conf.urls import patterns + +from .views import MockView + +urlpatterns = patterns( + '', + (r'^$', MockView.as_view()), +) diff --git a/tests/browsable_api/test_browsable_api.py b/tests/browsable_api/test_browsable_api.py new file mode 100644 index 00000000..5f264783 --- /dev/null +++ b/tests/browsable_api/test_browsable_api.py @@ -0,0 +1,65 @@ +from __future__ import unicode_literals +from django.contrib.auth.models import User +from django.test import TestCase + +from rest_framework.test import APIClient + + +class DropdownWithAuthTests(TestCase): + """Tests correct dropdown behaviour with Auth views enabled.""" + + urls = 'tests.browsable_api.auth_urls' + + def setUp(self): + self.client = APIClient(enforce_csrf_checks=True) + self.username = 'john' + self.email = 'lennon@thebeatles.com' + self.password = 'password' + self.user = User.objects.create_user(self.username, self.email, self.password) + + def tearDown(self): + self.client.logout() + + def test_name_shown_when_logged_in(self): + self.client.login(username=self.username, password=self.password) + response = self.client.get('/') + self.assertContains(response, 'john') + + def test_logout_shown_when_logged_in(self): + self.client.login(username=self.username, password=self.password) + response = self.client.get('/') + self.assertContains(response, '>Log out<') + + def test_login_shown_when_logged_out(self): + response = self.client.get('/') + self.assertContains(response, '>Log in<') + + +class NoDropdownWithoutAuthTests(TestCase): + """Tests correct dropdown behaviour with Auth views NOT enabled.""" + + urls = 'tests.browsable_api.no_auth_urls' + + def setUp(self): + self.client = APIClient(enforce_csrf_checks=True) + self.username = 'john' + self.email = 'lennon@thebeatles.com' + self.password = 'password' + self.user = User.objects.create_user(self.username, self.email, self.password) + + def tearDown(self): + self.client.logout() + + def test_name_shown_when_logged_in(self): + self.client.login(username=self.username, password=self.password) + response = self.client.get('/') + self.assertContains(response, 'john') + + def test_dropdown_not_shown_when_logged_in(self): + self.client.login(username=self.username, password=self.password) + response = self.client.get('/') + self.assertNotContains(response, '<li class="dropdown">') + + def test_dropdown_not_shown_when_logged_out(self): + response = self.client.get('/') + self.assertNotContains(response, '<li class="dropdown">') diff --git a/tests/browsable_api/views.py b/tests/browsable_api/views.py new file mode 100644 index 00000000..000f4e80 --- /dev/null +++ b/tests/browsable_api/views.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals + +from rest_framework.views import APIView +from rest_framework import authentication +from rest_framework import renderers +from rest_framework.response import Response + + +class MockView(APIView): + + authentication_classes = (authentication.SessionAuthentication,) + renderer_classes = (renderers.BrowsableAPIRenderer,) + + def get(self, request): + return Response({'a': 1, 'b': 2, 'c': 3}) diff --git a/tests/conftest.py b/tests/conftest.py index f3723aea..4b33e19c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ def pytest_configure(): DEBUG_PROPAGATE_EXCEPTIONS=True, DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}}, + SITE_ID=1, SECRET_KEY='not very secret in tests', USE_I18N=True, USE_L10N=True, diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 2b9d73e4..32041f9c 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -57,7 +57,8 @@ urlpatterns = patterns( authentication_classes=[OAuthAuthentication], permission_classes=[permissions.TokenHasReadWriteScope] ) - ) + ), + url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')) ) @@ -134,6 +135,15 @@ class SessionAuthTests(TestCase): def tearDown(self): self.csrf_client.logout() + def test_login_view_renders_on_get(self): + """ + Ensure the login template renders for a basic GET. + + cf. [#1810](https://github.com/tomchristie/django-rest-framework/pull/1810) + """ + response = self.csrf_client.get('/auth/login/') + self.assertContains(response, '<Label class="span4">Username:</label>') + def test_post_form_session_auth_failing_csrf(self): """ Ensure POSTing form over session authentication without CSRF token fails. diff --git a/tests/test_description.py b/tests/test_description.py index 1e481f06..0675d209 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -98,6 +98,30 @@ class TestViewNamesAndDescriptions(TestCase): pass self.assertEqual(MockView().get_view_description(), '') + def test_view_description_can_be_promise(self): + """ + Ensure a view may have a docstring that is actually a lazily evaluated + class that can be converted to a string. + + See: https://github.com/tomchristie/django-rest-framework/issues/1708 + """ + # use a mock object instead of gettext_lazy to ensure that we can't end + # up with a test case string in our l10n catalog + class MockLazyStr(object): + def __init__(self, string): + self.s = string + + def __str__(self): + return self.s + + def __unicode__(self): + return self.s + + class MockView(APIView): + __doc__ = MockLazyStr("a gettext string") + + self.assertEqual(MockView().get_view_description(), 'a gettext string') + def test_markdown(self): """ Ensure markdown to HTML works as expected. diff --git a/tests/test_fields.py b/tests/test_fields.py index 53c60538..a92fafbc 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1012,6 +1012,26 @@ # def get_my_test(self, obj): # return obj.my_test[0:5] +# class ModelCharField(TestCase): +# """ +# Tests for CharField +# """ +# def test_none_serializing(self): +# class CharFieldSerializer(serializers.Serializer): +# char = serializers.CharField(allow_none=True, required=False) +# serializer = CharFieldSerializer(data={'char': None}) +# self.assertTrue(serializer.is_valid()) +# self.assertIsNone(serializer.object['char']) + + +# class SerializerMethodFieldTest(TestCase): +# """ +# Tests for the SerializerMethodField field_to_native() behavior +# """ +# class SerializerTest(serializers.Serializer): +# def get_my_test(self, obj): +# return obj.my_test[0:5] + # class Example(): # my_test = 'Hey, this is a test !' diff --git a/tests/test_filters.py b/tests/test_filters.py index 6f24b1ab..300e47e4 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -422,19 +422,70 @@ class SearchFilterTests(TestCase): ) -class OrdringFilterModel(models.Model): +class OrderingFilterModel(models.Model): title = models.CharField(max_length=20) text = models.CharField(max_length=100) class OrderingFilterRelatedModel(models.Model): - related_object = models.ForeignKey(OrdringFilterModel, + related_object = models.ForeignKey(OrderingFilterModel, related_name="relateds") class OrderingFilterSerializer(serializers.ModelSerializer): class Meta: - model = OrdringFilterModel + model = OrderingFilterModel + + +class DjangoFilterOrderingModel(models.Model): + date = models.DateField() + text = models.CharField(max_length=10) + + class Meta: + ordering = ['-date'] + + +class DjangoFilterOrderingSerializer(serializers.ModelSerializer): + class Meta: + model = DjangoFilterOrderingModel + + +class DjangoFilterOrderingTests(TestCase): + def setUp(self): + data = [{ + 'date': datetime.date(2012, 10, 8), + 'text': 'abc' + }, { + 'date': datetime.date(2013, 10, 8), + 'text': 'bcd' + }, { + 'date': datetime.date(2014, 10, 8), + 'text': 'cde' + }] + + for d in data: + DjangoFilterOrderingModel.objects.create(**d) + + def test_default_ordering(self): + class DjangoFilterOrderingView(generics.ListAPIView): + serializer_class = DjangoFilterOrderingSerializer + queryset = DjangoFilterOrderingModel.objects.all() + filter_backends = (filters.DjangoFilterBackend,) + filter_fields = ['text'] + ordering = ('-date',) + + view = DjangoFilterOrderingView.as_view() + request = factory.get('/') + response = view(request) + + self.assertEqual( + response.data, + [ + {'id': 3, 'date': datetime.date(2014, 10, 8), 'text': 'cde'}, + {'id': 2, 'date': datetime.date(2013, 10, 8), 'text': 'bcd'}, + {'id': 1, 'date': datetime.date(2012, 10, 8), 'text': 'abc'} + ] + ) class OrderingFilterTests(TestCase): @@ -455,11 +506,11 @@ class OrderingFilterTests(TestCase): chr(idx + ord('b')) + chr(idx + ord('c')) ) - OrdringFilterModel(title=title, text=text).save() + OrderingFilterModel(title=title, text=text).save() def test_ordering(self): class OrderingListView(generics.ListAPIView): - queryset = OrdringFilterModel.objects.all() + queryset = OrderingFilterModel.objects.all() serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = ('title',) @@ -479,7 +530,7 @@ class OrderingFilterTests(TestCase): def test_reverse_ordering(self): class OrderingListView(generics.ListAPIView): - queryset = OrdringFilterModel.objects.all() + queryset = OrderingFilterModel.objects.all() serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = ('title',) @@ -499,7 +550,7 @@ class OrderingFilterTests(TestCase): def test_incorrectfield_ordering(self): class OrderingListView(generics.ListAPIView): - queryset = OrdringFilterModel.objects.all() + queryset = OrderingFilterModel.objects.all() serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = ('title',) @@ -519,7 +570,7 @@ class OrderingFilterTests(TestCase): def test_default_ordering(self): class OrderingListView(generics.ListAPIView): - queryset = OrdringFilterModel.objects.all() + queryset = OrderingFilterModel.objects.all() serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = ('title',) @@ -539,7 +590,7 @@ class OrderingFilterTests(TestCase): def test_default_ordering_using_string(self): class OrderingListView(generics.ListAPIView): - queryset = OrdringFilterModel.objects.all() + queryset = OrderingFilterModel.objects.all() serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = 'title' @@ -560,7 +611,7 @@ class OrderingFilterTests(TestCase): def test_ordering_by_aggregate_field(self): # create some related models to aggregate order by num_objs = [2, 5, 3] - for obj, num_relateds in zip(OrdringFilterModel.objects.all(), + for obj, num_relateds in zip(OrderingFilterModel.objects.all(), num_objs): for _ in range(num_relateds): new_related = OrderingFilterRelatedModel( @@ -573,7 +624,7 @@ class OrderingFilterTests(TestCase): filter_backends = (filters.OrderingFilter,) ordering = 'title' ordering_fields = '__all__' - queryset = OrdringFilterModel.objects.all().annotate( + queryset = OrderingFilterModel.objects.all().annotate( models.Count("relateds")) view = OrderingListView.as_view() @@ -591,7 +642,7 @@ class OrderingFilterTests(TestCase): def test_ordering_with_nonstandard_ordering_param(self): with temporary_setting('ORDERING_PARAM', 'order', filters): class OrderingListView(generics.ListAPIView): - queryset = OrdringFilterModel.objects.all() + queryset = OrderingFilterModel.objects.all() serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = ('title',) diff --git a/tests/test_generics.py b/tests/test_generics.py index 1b00c351..17bfca2f 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +import django from django.db import models from django.shortcuts import get_object_or_404 from django.test import TestCase @@ -176,6 +177,9 @@ class TestRootView(TestCase): self.assertEqual(created.text, 'foobar') +EXPECTED_QUERYS_FOR_PUT = 3 if django.VERSION < (1, 6) else 2 + + class TestInstanceView(TestCase): def setUp(self): """ @@ -219,7 +223,7 @@ class TestInstanceView(TestCase): """ data = {'text': 'foobar'} request = factory.put('/1', data, format='json') - with self.assertNumQueries(3): + with self.assertNumQueries(EXPECTED_QUERYS_FOR_PUT): response = self.view(request, pk='1').render() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(dict(response.data), {'id': 1, 'text': 'foobar'}) @@ -233,7 +237,7 @@ class TestInstanceView(TestCase): data = {'text': 'foobar'} request = factory.patch('/1', data, format='json') - with self.assertNumQueries(3): + with self.assertNumQueries(EXPECTED_QUERYS_FOR_PUT): response = self.view(request, pk=1).render() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) @@ -351,7 +355,7 @@ class TestInstanceView(TestCase): """ data = {'id': 999, 'text': 'foobar'} request = factory.put('/1', data, format='json') - with self.assertNumQueries(3): + with self.assertNumQueries(EXPECTED_QUERYS_FOR_PUT): response = self.view(request, pk=1).render() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data, {'id': 1, 'text': 'foobar'}) diff --git a/tests/test_model_field_mappings.py b/tests/test_model_field_mappings.py index dc254da4..57c97cb7 100644 --- a/tests/test_model_field_mappings.py +++ b/tests/test_model_field_mappings.py @@ -15,7 +15,7 @@ from rest_framework import serializers class RegularFieldsModel(models.Model): auto_field = models.AutoField(primary_key=True) big_integer_field = models.BigIntegerField() - boolean_field = models.BooleanField() + boolean_field = models.BooleanField(default=False) char_field = models.CharField(max_length=100) comma_seperated_integer_field = models.CommaSeparatedIntegerField(max_length=100) date_field = models.DateField() @@ -60,22 +60,22 @@ TestSerializer(): # Model for testing relational field mapping -class ForeignKeyTarget(models.Model): - char_field = models.CharField(max_length=100) +class ForeignKeyTargetModel(models.Model): + name = models.CharField(max_length=100) -class ManyToManyTarget(models.Model): - char_field = models.CharField(max_length=100) +class ManyToManyTargetModel(models.Model): + name = models.CharField(max_length=100) -class OneToOneTarget(models.Model): - char_field = models.CharField(max_length=100) +class OneToOneTargetModel(models.Model): + name = models.CharField(max_length=100) class RelationalModel(models.Model): - foreign_key = models.ForeignKey(ForeignKeyTarget) - many_to_many = models.ManyToManyField(ManyToManyTarget) - one_to_one = models.OneToOneField(OneToOneTarget) + foreign_key = models.ForeignKey(ForeignKeyTargetModel) + many_to_many = models.ManyToManyField(ManyToManyTargetModel) + one_to_one = models.OneToOneField(OneToOneTargetModel) RELATIONAL_FLAT_REPR = """ @@ -105,9 +105,9 @@ TestSerializer(): HYPERLINKED_FLAT_REPR = """ TestSerializer(): url = HyperlinkedIdentityField(view_name='relationalmodel-detail') - foreign_key = HyperlinkedRelatedField(label='foreign key', queryset=<django.db.models.manager.Manager object>, view_name='foreignkeytarget-detail') - one_to_one = HyperlinkedRelatedField(label='one to one', queryset=<django.db.models.manager.Manager object>, view_name='onetoonetarget-detail') - many_to_many = HyperlinkedRelatedField(label='many to many', many=True, queryset=<django.db.models.manager.Manager object>, view_name='manytomanytarget-detail') + foreign_key = HyperlinkedRelatedField(label='foreign key', queryset=<django.db.models.manager.Manager object>, view_name='foreignkeytargetmodel-detail') + one_to_one = HyperlinkedRelatedField(label='one to one', queryset=<django.db.models.manager.Manager object>, view_name='onetoonetargetmodel-detail') + many_to_many = HyperlinkedRelatedField(label='many to many', many=True, queryset=<django.db.models.manager.Manager object>, view_name='manytomanytargetmodel-detail') """.strip() @@ -127,6 +127,8 @@ TestSerializer(): class TestSerializerMappings(TestCase): + maxDiff = 10000 + def test_regular_fields(self): class TestSerializer(serializers.ModelSerializer): class Meta: diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 2e56d970..68983ba2 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -428,6 +428,15 @@ class CustomPaginationSerializer(pagination.BasePaginationSerializer): results_field = 'objects' +class CustomFooSerializer(serializers.Serializer): + foo = serializers.CharField() + + +class CustomFooPaginationSerializer(pagination.PaginationSerializer): + class Meta: + object_serializer_class = CustomFooSerializer + + class TestCustomPaginationSerializer(TestCase): def setUp(self): objects = ['john', 'paul', 'george', 'ringo'] @@ -450,6 +459,16 @@ class TestCustomPaginationSerializer(TestCase): } self.assertEqual(serializer.data, expected) + def test_custom_pagination_serializer_with_custom_object_serializer(self): + objects = [ + {'foo': 'bar'}, + {'foo': 'spam'} + ] + paginator = Paginator(objects, 1) + page = paginator.page(1) + serializer = CustomFooPaginationSerializer(page) + serializer.data + class NonIntegerPage(object): diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 8af90677..3f2672df 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from __future__ import unicode_literals from rest_framework.compat import StringIO from django import forms @@ -113,3 +115,25 @@ class TestFileUploadParser(TestCase): parser = FileUploadParser() filename = parser.get_filename(self.stream, None, self.parser_context) self.assertEqual(filename, 'file.txt') + + def test_get_encoded_filename(self): + parser = FileUploadParser() + + self.__replace_content_disposition('inline; filename*=utf-8\'\'ÀĥƦ.txt') + filename = parser.get_filename(self.stream, None, self.parser_context) + self.assertEqual(filename, 'ÀĥƦ.txt') + + self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8\'\'ÀĥƦ.txt') + filename = parser.get_filename(self.stream, None, self.parser_context) + self.assertEqual(filename, 'ÀĥƦ.txt') + + self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8\'en-us\'ÀĥƦ.txt') + filename = parser.get_filename(self.stream, None, self.parser_context) + self.assertEqual(filename, 'ÀĥƦ.txt') + + self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8--ÀĥƦ.txt') + filename = parser.get_filename(self.stream, None, self.parser_context) + self.assertEqual(filename, 'fallback.txt') + + def __replace_content_disposition(self, disposition): + self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition diff --git a/tests/test_validation.py b/tests/test_validation.py index 40005486..c4506e7e 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals from django.core.validators import MaxValueValidator +from django.core.exceptions import ValidationError from django.db import models from django.test import TestCase from rest_framework import generics, serializers, status @@ -132,3 +133,42 @@ class TestMaxValueValidatorValidation(TestCase): response = view(request, pk=obj.pk).render() self.assertEqual(response.content, b'{"number_value": ["Ensure this value is less than or equal to 100."]}') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + +class TestChoiceFieldChoicesValidate(TestCase): + CHOICES = [ + (0, 'Small'), + (1, 'Medium'), + (2, 'Large'), + ] + + CHOICES_NESTED = [ + ('Category', ( + (1, 'First'), + (2, 'Second'), + (3, 'Third'), + )), + (4, 'Fourth'), + ] + + def test_choices(self): + """ + Make sure a value for choices works as expected. + """ + f = serializers.ChoiceField(choices=self.CHOICES) + value = self.CHOICES[0][0] + try: + f.to_native(value) + except ValidationError: + self.fail("Value %s does not validate" % str(value)) + + # def test_nested_choices(self): + # """ + # Make sure a nested value for choices works as expected. + # """ + # f = serializers.ChoiceField(choices=self.CHOICES_NESTED) + # value = self.CHOICES_NESTED[0][1][0][0] + # try: + # f.to_native(value) + # except ValidationError: + # self.fail("Value %s does not validate" % str(value)) |
