diff options
| author | Tom Christie | 2014-12-12 15:37:43 +0000 | 
|---|---|---|
| committer | Tom Christie | 2014-12-12 15:37:43 +0000 | 
| commit | baaa356489dd51d7c68161db40e99cd59b1124c3 (patch) | |
| tree | 23dc5c4cbe1065580ff88ddd1bfa6dcda956ac68 /tests | |
| parent | 5e6052811716a494e995a84c497579867ee6acaa (diff) | |
| parent | fd473aa905337908b41c9a1087967a19f0558f89 (diff) | |
| download | django-rest-framework-baaa356489dd51d7c68161db40e99cd59b1124c3.tar.bz2 | |
Merge master
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/models.py | 111 | ||||
| -rw-r--r-- | tests/test_authentication.py | 2 | ||||
| -rw-r--r-- | tests/test_description.py | 7 | ||||
| -rw-r--r-- | tests/test_fields.py | 15 | ||||
| -rw-r--r-- | tests/test_generics.py | 56 | ||||
| -rw-r--r-- | tests/test_model_serializer.py | 74 | ||||
| -rw-r--r-- | tests/test_multitable_inheritance.py | 2 | ||||
| -rw-r--r-- | tests/test_parsers.py | 32 | ||||
| -rw-r--r-- | tests/test_relations_generic.py | 2 | ||||
| -rw-r--r-- | tests/test_relations_hyperlink.py | 18 | ||||
| -rw-r--r-- | tests/test_relations_pk.py | 12 | ||||
| -rw-r--r-- | tests/test_relations_slug.py | 15 | ||||
| -rw-r--r-- | tests/test_renderers.py | 10 | ||||
| -rw-r--r-- | tests/test_request.py | 2 | ||||
| -rw-r--r-- | tests/test_serializer_lists.py | 16 | ||||
| -rw-r--r-- | tests/test_testing.py | 20 | ||||
| -rw-r--r-- | tests/test_validators.py | 22 | ||||
| -rw-r--r-- | tests/test_viewsets.py | 35 | 
18 files changed, 299 insertions, 152 deletions
| diff --git a/tests/models.py b/tests/models.py index 06ec5a22..456b0a0b 100644 --- a/tests/models.py +++ b/tests/models.py @@ -3,62 +3,20 @@ from django.db import models  from django.utils.translation import ugettext_lazy as _ -def foobar(): -    return 'foobar' - - -class CustomField(models.CharField): - -    def __init__(self, *args, **kwargs): -        kwargs['max_length'] = 12 -        super(CustomField, self).__init__(*args, **kwargs) - -  class RESTFrameworkModel(models.Model):      """      Base for test models that sets app_label, so they play nicely.      """ +      class Meta:          app_label = 'tests'          abstract = True -class HasPositiveIntegerAsChoice(RESTFrameworkModel): -    some_choices = ((1, 'A'), (2, 'B'), (3, 'C')) -    some_integer = models.PositiveIntegerField(choices=some_choices) - - -class Anchor(RESTFrameworkModel): -    text = models.CharField(max_length=100, default='anchor') - -  class BasicModel(RESTFrameworkModel):      text = models.CharField(max_length=100, verbose_name=_("Text comes here"), help_text=_("Text description.")) -class SlugBasedModel(RESTFrameworkModel): -    text = models.CharField(max_length=100) -    slug = models.SlugField(max_length=32) - - -class DefaultValueModel(RESTFrameworkModel): -    text = models.CharField(default='foobar', max_length=100) -    extra = models.CharField(blank=True, null=True, max_length=100) - - -class CallableDefaultValueModel(RESTFrameworkModel): -    text = models.CharField(default=foobar, max_length=100) - - -class ManyToManyModel(RESTFrameworkModel): -    rel = models.ManyToManyField(Anchor, help_text='Some help text.') - - -class ReadOnlyManyToManyModel(RESTFrameworkModel): -    text = models.CharField(max_length=100, default='anchor') -    rel = models.ManyToManyField(Anchor) - -  class BaseFilterableItem(RESTFrameworkModel):      text = models.CharField(max_length=100) @@ -71,73 +29,6 @@ class FilterableItem(BaseFilterableItem):      date = models.DateField() -# Model for regression test for #285 - -class Comment(RESTFrameworkModel): -    email = models.EmailField() -    content = models.CharField(max_length=200) -    created = models.DateTimeField(auto_now_add=True) - - -class ActionItem(RESTFrameworkModel): -    title = models.CharField(max_length=200) -    started = models.NullBooleanField(default=False) -    done = models.BooleanField(default=False) -    info = CustomField(default='---', max_length=12) - - -# Models for reverse relations -class Person(RESTFrameworkModel): -    name = models.CharField(max_length=10) -    age = models.IntegerField(null=True, blank=True) - -    @property -    def info(self): -        return { -            'name': self.name, -            'age': self.age, -        } - - -class BlogPost(RESTFrameworkModel): -    title = models.CharField(max_length=100) -    writer = models.ForeignKey(Person, null=True, blank=True) - -    def get_first_comment(self): -        return self.blogpostcomment_set.all()[0] - - -class BlogPostComment(RESTFrameworkModel): -    text = models.TextField() -    blog_post = models.ForeignKey(BlogPost) - - -class Album(RESTFrameworkModel): -    title = models.CharField(max_length=100, unique=True) -    ref = models.CharField(max_length=10, unique=True, null=True, blank=True) - - -class Photo(RESTFrameworkModel): -    description = models.TextField() -    album = models.ForeignKey(Album) - - -# Model for issue #324 -class BlankFieldModel(RESTFrameworkModel): -    title = models.CharField(max_length=100, blank=True, null=False, -                             default="title") - - -# Model for issue #380 -class OptionalRelationModel(RESTFrameworkModel): -    other = models.ForeignKey('OptionalRelationModel', blank=True, null=True) - - -# Model for RegexField -class Book(RESTFrameworkModel): -    isbn = models.CharField(max_length=13) - -  # Models for relations tests  # ManyToMany  class ManyToManyTarget(RESTFrameworkModel): diff --git a/tests/test_authentication.py b/tests/test_authentication.py index bf0fd6fb..04c5782e 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -108,7 +108,7 @@ class SessionAuthTests(TestCase):          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>') +        self.assertContains(response, '<label for="id_username">Username:</label>')      def test_post_form_session_auth_failing_csrf(self):          """ diff --git a/tests/test_description.py b/tests/test_description.py index 0675d209..78ce2350 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -2,7 +2,8 @@  from __future__ import unicode_literals  from django.test import TestCase -from rest_framework.compat import apply_markdown, smart_text +from django.utils.encoding import python_2_unicode_compatible, smart_text +from rest_framework.compat import apply_markdown  from rest_framework.views import APIView  from .description import ViewWithNonASCIICharactersInDocstring  from .description import UTF8_TEST_DOCSTRING @@ -107,6 +108,7 @@ class TestViewNamesAndDescriptions(TestCase):          """          # 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 +        @python_2_unicode_compatible          class MockLazyStr(object):              def __init__(self, string):                  self.s = string @@ -114,9 +116,6 @@ class TestViewNamesAndDescriptions(TestCase):              def __str__(self):                  return self.s -            def __unicode__(self): -                return self.s -          class MockView(APIView):              __doc__ = MockLazyStr("a gettext string") diff --git a/tests/test_fields.py b/tests/test_fields.py index 13525632..3f4e65f2 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -804,6 +804,21 @@ class TestChoiceField(FieldValues):          ]      ) +    def test_allow_blank(self): +        """ +        If `allow_blank=True` then '' is a valid input. +        """ +        field = serializers.ChoiceField( +            allow_blank=True, +            choices=[ +                ('poor', 'Poor quality'), +                ('medium', 'Medium quality'), +                ('good', 'Good quality'), +            ] +        ) +        output = field.run_validation('') +        assert output is '' +  class TestChoiceFieldWithType(FieldValues):      """ diff --git a/tests/test_generics.py b/tests/test_generics.py index 2690fb47..94023c30 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -6,12 +6,26 @@ from django.test import TestCase  from django.utils import six  from rest_framework import generics, renderers, serializers, status  from rest_framework.test import APIRequestFactory -from tests.models import BasicModel, Comment, SlugBasedModel +from tests.models import BasicModel, RESTFrameworkModel  from tests.models import ForeignKeySource, ForeignKeyTarget  factory = APIRequestFactory() +# Models +class SlugBasedModel(RESTFrameworkModel): +    text = models.CharField(max_length=100) +    slug = models.SlugField(max_length=32) + + +# Model for regression test for #285 +class Comment(RESTFrameworkModel): +    email = models.EmailField() +    content = models.CharField(max_length=200) +    created = models.DateTimeField(auto_now_add=True) + + +# Serializers  class BasicSerializer(serializers.ModelSerializer):      class Meta:          model = BasicModel @@ -22,6 +36,15 @@ class ForeignKeySerializer(serializers.ModelSerializer):          model = ForeignKeySource +class SlugSerializer(serializers.ModelSerializer): +    slug = serializers.ReadOnlyField() + +    class Meta: +        model = SlugBasedModel +        fields = ('text', 'slug') + + +# Views  class RootView(generics.ListCreateAPIView):      queryset = BasicModel.objects.all()      serializer_class = BasicSerializer @@ -37,14 +60,6 @@ class FKInstanceView(generics.RetrieveUpdateDestroyAPIView):      serializer_class = ForeignKeySerializer -class SlugSerializer(serializers.ModelSerializer): -    slug = serializers.ReadOnlyField() - -    class Meta: -        model = SlugBasedModel -        fields = ('text', 'slug') - -  class SlugBasedInstanceView(InstanceView):      """      A model with a slug-field. @@ -54,6 +69,7 @@ class SlugBasedInstanceView(InstanceView):      lookup_field = 'slug' +# Tests  class TestRootView(TestCase):      def setUp(self):          """ @@ -127,13 +143,13 @@ class TestRootView(TestCase):          self.assertEqual(created.text, 'foobar') -EXPECTED_QUERYS_FOR_PUT = 3 if django.VERSION < (1, 6) else 2 +EXPECTED_QUERIES_FOR_PUT = 3 if django.VERSION < (1, 6) else 2  class TestInstanceView(TestCase):      def setUp(self):          """ -        Create 3 BasicModel intances. +        Create 3 BasicModel instances.          """          items = ['foo', 'bar', 'baz', 'filtered out']          for item in items: @@ -173,7 +189,7 @@ class TestInstanceView(TestCase):          """          data = {'text': 'foobar'}          request = factory.put('/1', data, format='json') -        with self.assertNumQueries(EXPECTED_QUERYS_FOR_PUT): +        with self.assertNumQueries(EXPECTED_QUERIES_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'}) @@ -187,7 +203,7 @@ class TestInstanceView(TestCase):          data = {'text': 'foobar'}          request = factory.patch('/1', data, format='json') -        with self.assertNumQueries(EXPECTED_QUERYS_FOR_PUT): +        with self.assertNumQueries(EXPECTED_QUERIES_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'}) @@ -222,7 +238,7 @@ class TestInstanceView(TestCase):          """          data = {'id': 999, 'text': 'foobar'}          request = factory.put('/1', data, format='json') -        with self.assertNumQueries(EXPECTED_QUERYS_FOR_PUT): +        with self.assertNumQueries(EXPECTED_QUERIES_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'}) @@ -288,9 +304,10 @@ class TestOverriddenGetObject(TestCase):      Test cases for a RetrieveUpdateDestroyAPIView that does NOT use the      queryset/model mechanism but instead overrides get_object()      """ +      def setUp(self):          """ -        Create 3 BasicModel intances. +        Create 3 BasicModel instances.          """          items = ['foo', 'bar', 'baz']          for item in items: @@ -363,11 +380,11 @@ class ClassB(models.Model):  class ClassA(models.Model):      name = models.CharField(max_length=255) -    childs = models.ManyToManyField(ClassB, blank=True, null=True) +    children = models.ManyToManyField(ClassB, blank=True, null=True)  class ClassASerializer(serializers.ModelSerializer): -    childs = serializers.PrimaryKeyRelatedField( +    children = serializers.PrimaryKeyRelatedField(          many=True, queryset=ClassB.objects.all()      ) @@ -380,8 +397,8 @@ class ExampleView(generics.ListCreateAPIView):      queryset = ClassA.objects.all() -class TestM2MBrowseableAPI(TestCase): -    def test_m2m_in_browseable_api(self): +class TestM2MBrowsableAPI(TestCase): +    def test_m2m_in_browsable_api(self):          """          Test for particularly ugly regression with m2m in browsable API          """ @@ -424,7 +441,6 @@ class DynamicSerializerView(generics.ListCreateAPIView):  class TestFilterBackendAppliedToViews(TestCase): -      def setUp(self):          """          Create 3 BasicModel instances to filter on. diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 3aec0da0..da79164a 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -26,6 +26,10 @@ class CustomField(models.Field):      pass +class OneFieldModel(models.Model): +    char_field = models.CharField(max_length=100) + +  class RegularFieldsModel(models.Model):      """      A model class for testing regular flat fields. @@ -68,6 +72,26 @@ class FieldOptionsModel(models.Model):      choices_field = models.CharField(max_length=100, choices=COLOR_CHOICES) +class TestModelSerializer(TestCase): +    def test_create_method(self): +        class TestSerializer(serializers.ModelSerializer): +            non_model_field = serializers.CharField() + +            class Meta: +                model = OneFieldModel +                fields = ('char_field', 'non_model_field') + +        serializer = TestSerializer(data={ +            'char_field': 'foo', +            'non_model_field': 'bar', +        }) +        serializer.is_valid() +        with self.assertRaises(TypeError) as excinfo: +            serializer.save() +        msginitial = 'Got a `TypeError` when calling `OneFieldModel.objects.create()`.' +        assert str(excinfo.exception).startswith(msginitial) + +  class TestRegularFieldMappings(TestCase):      def test_regular_fields(self):          """ @@ -535,3 +559,53 @@ class TestBulkCreate(TestCase):          # Serializer returns correct data.          assert serializer.data == data + + +class TestMetaClassModel(models.Model): +    text = models.CharField(max_length=100) + + +class TestSerializerMetaClass(TestCase): +    def test_meta_class_fields_option(self): +        class ExampleSerializer(serializers.ModelSerializer): +            class Meta: +                model = TestMetaClassModel +                fields = 'text' + +        with self.assertRaises(TypeError) as result: +            ExampleSerializer().fields + +        exception = result.exception +        assert str(exception).startswith( +            "The `fields` option must be a list or tuple" +        ) + +    def test_meta_class_exclude_option(self): +        class ExampleSerializer(serializers.ModelSerializer): +            class Meta: +                model = TestMetaClassModel +                exclude = 'text' + +        with self.assertRaises(TypeError) as result: +            ExampleSerializer().fields + +        exception = result.exception +        assert str(exception).startswith( +            "The `exclude` option must be a list or tuple" +        ) + +    def test_meta_class_fields_and_exclude_options(self): +        class ExampleSerializer(serializers.ModelSerializer): +            class Meta: +                model = TestMetaClassModel +                fields = ('text',) +                exclude = ('text',) + +        with self.assertRaises(AssertionError) as result: +            ExampleSerializer().fields + +        exception = result.exception +        self.assertEqual( +            str(exception), +            "Cannot set both 'fields' and 'exclude'." +        ) diff --git a/tests/test_multitable_inheritance.py b/tests/test_multitable_inheritance.py index ce1bf3ea..e1b40cc7 100644 --- a/tests/test_multitable_inheritance.py +++ b/tests/test_multitable_inheritance.py @@ -31,7 +31,7 @@ class AssociatedModelSerializer(serializers.ModelSerializer):  # Tests -class IneritedModelSerializationTests(TestCase): +class InheritedModelSerializationTests(TestCase):      def test_multitable_inherited_model_fields_as_expected(self):          """ diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 32fb0595..54455cf6 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -4,7 +4,8 @@ from __future__ import unicode_literals  from django import forms  from django.core.files.uploadhandler import MemoryFileUploadHandler  from django.test import TestCase -from rest_framework.compat import StringIO +from django.utils.six.moves import StringIO +from rest_framework.exceptions import ParseError  from rest_framework.parsers import FormParser, FileUploadParser @@ -44,13 +45,40 @@ class TestFileUploadParser(TestCase):          self.parser_context = {'request': request, 'kwargs': {}}      def test_parse(self): -        """ Make sure the `QueryDict` works OK """ +        """ +        Parse raw file upload. +        """          parser = FileUploadParser()          self.stream.seek(0)          data_and_files = parser.parse(self.stream, None, self.parser_context)          file_obj = data_and_files.files['file']          self.assertEqual(file_obj._size, 14) +    def test_parse_missing_filename(self): +        """ +        Parse raw file upload when filename is missing. +        """ +        parser = FileUploadParser() +        self.stream.seek(0) +        self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = '' +        with self.assertRaises(ParseError): +            parser.parse(self.stream, None, self.parser_context) + +    def test_parse_missing_filename_multiple_upload_handlers(self): +        """ +        Parse raw file upload with multiple handlers when filename is missing. +        Regression test for #2109. +        """ +        parser = FileUploadParser() +        self.stream.seek(0) +        self.parser_context['request'].upload_handlers = ( +            MemoryFileUploadHandler(), +            MemoryFileUploadHandler() +        ) +        self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = '' +        with self.assertRaises(ParseError): +            parser.parse(self.stream, None, self.parser_context) +      def test_get_filename(self):          parser = FileUploadParser()          filename = parser.get_filename(self.stream, None, self.parser_context) diff --git a/tests/test_relations_generic.py b/tests/test_relations_generic.py index 380ad91d..b600b333 100644 --- a/tests/test_relations_generic.py +++ b/tests/test_relations_generic.py @@ -3,8 +3,8 @@ from django.contrib.contenttypes.models import ContentType  from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey  from django.db import models  from django.test import TestCase +from django.utils.encoding import python_2_unicode_compatible  from rest_framework import serializers -from rest_framework.compat import python_2_unicode_compatible  @python_2_unicode_compatible diff --git a/tests/test_relations_hyperlink.py b/tests/test_relations_hyperlink.py index b938e385..f1b882ed 100644 --- a/tests/test_relations_hyperlink.py +++ b/tests/test_relations_hyperlink.py @@ -89,7 +89,14 @@ class HyperlinkedManyToManyTests(TestCase):              {'url': 'http://testserver/manytomanysource/2/', 'name': 'source-2', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/']},              {'url': 'http://testserver/manytomanysource/3/', 'name': 'source-3', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']}          ] -        self.assertEqual(serializer.data, expected) +        with self.assertNumQueries(4): +            self.assertEqual(serializer.data, expected) + +    def test_many_to_many_retrieve_prefetch_related(self): +        queryset = ManyToManySource.objects.all().prefetch_related('targets') +        serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request}) +        with self.assertNumQueries(2): +            serializer.data      def test_reverse_many_to_many_retrieve(self):          queryset = ManyToManyTarget.objects.all() @@ -99,7 +106,8 @@ class HyperlinkedManyToManyTests(TestCase):              {'url': 'http://testserver/manytomanytarget/2/', 'name': 'target-2', 'sources': ['http://testserver/manytomanysource/2/', 'http://testserver/manytomanysource/3/']},              {'url': 'http://testserver/manytomanytarget/3/', 'name': 'target-3', 'sources': ['http://testserver/manytomanysource/3/']}          ] -        self.assertEqual(serializer.data, expected) +        with self.assertNumQueries(4): +            self.assertEqual(serializer.data, expected)      def test_many_to_many_update(self):          data = {'url': 'http://testserver/manytomanysource/1/', 'name': 'source-1', 'targets': ['http://testserver/manytomanytarget/1/', 'http://testserver/manytomanytarget/2/', 'http://testserver/manytomanytarget/3/']} @@ -197,7 +205,8 @@ class HyperlinkedForeignKeyTests(TestCase):              {'url': 'http://testserver/foreignkeysource/2/', 'name': 'source-2', 'target': 'http://testserver/foreignkeytarget/1/'},              {'url': 'http://testserver/foreignkeysource/3/', 'name': 'source-3', 'target': 'http://testserver/foreignkeytarget/1/'}          ] -        self.assertEqual(serializer.data, expected) +        with self.assertNumQueries(1): +            self.assertEqual(serializer.data, expected)      def test_reverse_foreign_key_retrieve(self):          queryset = ForeignKeyTarget.objects.all() @@ -206,7 +215,8 @@ class HyperlinkedForeignKeyTests(TestCase):              {'url': 'http://testserver/foreignkeytarget/1/', 'name': 'target-1', 'sources': ['http://testserver/foreignkeysource/1/', 'http://testserver/foreignkeysource/2/', 'http://testserver/foreignkeysource/3/']},              {'url': 'http://testserver/foreignkeytarget/2/', 'name': 'target-2', 'sources': []},          ] -        self.assertEqual(serializer.data, expected) +        with self.assertNumQueries(3): +            self.assertEqual(serializer.data, expected)      def test_foreign_key_update(self):          data = {'url': 'http://testserver/foreignkeysource/1/', 'name': 'source-1', 'target': 'http://testserver/foreignkeytarget/2/'} diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index e95a877e..f872a8dc 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -71,6 +71,12 @@ class PKManyToManyTests(TestCase):          with self.assertNumQueries(4):              self.assertEqual(serializer.data, expected) +    def test_many_to_many_retrieve_prefetch_related(self): +        queryset = ManyToManySource.objects.all().prefetch_related('targets') +        serializer = ManyToManySourceSerializer(queryset, many=True) +        with self.assertNumQueries(2): +            serializer.data +      def test_reverse_many_to_many_retrieve(self):          queryset = ManyToManyTarget.objects.all()          serializer = ManyToManyTargetSerializer(queryset, many=True) @@ -188,6 +194,12 @@ class PKForeignKeyTests(TestCase):          with self.assertNumQueries(3):              self.assertEqual(serializer.data, expected) +    def test_reverse_foreign_key_retrieve_prefetch_related(self): +        queryset = ForeignKeyTarget.objects.all().prefetch_related('sources') +        serializer = ForeignKeyTargetSerializer(queryset, many=True) +        with self.assertNumQueries(2): +            serializer.data +      def test_foreign_key_update(self):          data = {'id': 1, 'name': 'source-1', 'target': 2}          instance = ForeignKeySource.objects.get(pk=1) diff --git a/tests/test_relations_slug.py b/tests/test_relations_slug.py index 7bac9046..cd2cb1ed 100644 --- a/tests/test_relations_slug.py +++ b/tests/test_relations_slug.py @@ -54,7 +54,14 @@ class SlugForeignKeyTests(TestCase):              {'id': 2, 'name': 'source-2', 'target': 'target-1'},              {'id': 3, 'name': 'source-3', 'target': 'target-1'}          ] -        self.assertEqual(serializer.data, expected) +        with self.assertNumQueries(4): +            self.assertEqual(serializer.data, expected) + +    def test_foreign_key_retrieve_select_related(self): +        queryset = ForeignKeySource.objects.all().select_related('target') +        serializer = ForeignKeySourceSerializer(queryset, many=True) +        with self.assertNumQueries(1): +            serializer.data      def test_reverse_foreign_key_retrieve(self):          queryset = ForeignKeyTarget.objects.all() @@ -65,6 +72,12 @@ class SlugForeignKeyTests(TestCase):          ]          self.assertEqual(serializer.data, expected) +    def test_reverse_foreign_key_retrieve_prefetch_related(self): +        queryset = ForeignKeyTarget.objects.all().prefetch_related('sources') +        serializer = ForeignKeyTargetSerializer(queryset, many=True) +        with self.assertNumQueries(2): +            serializer.data +      def test_foreign_key_update(self):          data = {'id': 1, 'name': 'source-1', 'target': 'target-2'}          instance = ForeignKeySource.objects.get(pk=1) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 2c8da2dc..7b78f7ba 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -1,7 +1,6 @@  # -*- coding: utf-8 -*-  from __future__ import unicode_literals -from decimal import Decimal  from django.conf.urls import patterns, url, include  from django.core.cache import cache  from django.db import models @@ -378,6 +377,15 @@ class UnicodeJSONRendererTests(TestCase):          content = renderer.render(obj, 'application/json')          self.assertEqual(content, '{"countries":["United Kingdom","France","EspaƱa"]}'.encode('utf-8')) +    def test_u2028_u2029(self): +        # The \u2028 and \u2029 characters should be escaped, +        # even when the non-escaping unicode representation is used. +        # Regression test for #2169 +        obj = {'should_escape': '\u2028\u2029'} +        renderer = JSONRenderer() +        content = renderer.render(obj, 'application/json') +        self.assertEqual(content, '{"should_escape":"\\u2028\\u2029"}'.encode('utf-8')) +  class AsciiJSONRendererTests(TestCase):      """ diff --git a/tests/test_request.py b/tests/test_request.py index 44afd243..7cf8c327 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -187,7 +187,7 @@ class MockView(APIView):          if request.POST.get('example') is not None:              return Response(status=status.HTTP_200_OK) -        return Response(status=status.INTERNAL_SERVER_ERROR) +        return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)  urlpatterns = patterns(      '', diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py index 640067e3..35b68ae7 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -272,3 +272,19 @@ class TestNestedListOfListsSerializer:          serializer = self.Serializer(data=input_data)          assert serializer.is_valid()          assert serializer.validated_data == expected_output + + +class TestListSerializerClass: +    """Tests for a custom list_serializer_class.""" +    def test_list_serializer_class_validate(self): +        class CustomListSerializer(serializers.ListSerializer): +            def validate(self, attrs): +                raise serializers.ValidationError('Non field error') + +        class TestSerializer(serializers.Serializer): +            class Meta: +                list_serializer_class = CustomListSerializer + +        serializer = TestSerializer(data=[], many=True) +        assert not serializer.is_valid() +        assert serializer.errors == {'non_field_errors': ['Non field error']} diff --git a/tests/test_testing.py b/tests/test_testing.py index 9fd5966e..87d2b61f 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,15 +1,13 @@ -# -- coding: utf-8 -- - +# encoding: utf-8  from __future__ import unicode_literals  from django.conf.urls import patterns, url -from io import BytesIO -  from django.contrib.auth.models import User  from django.shortcuts import redirect  from django.test import TestCase  from rest_framework.decorators import api_view  from rest_framework.response import Response  from rest_framework.test import APIClient, APIRequestFactory, force_authenticate +from io import BytesIO  @api_view(['GET', 'POST']) @@ -109,7 +107,7 @@ class TestAPITestClient(TestCase):      def test_can_logout(self):          """ -        `logout()` reset stored credentials +        `logout()` resets stored credentials          """          self.client.credentials(HTTP_AUTHORIZATION='example')          response = self.client.get('/view/') @@ -118,6 +116,18 @@ class TestAPITestClient(TestCase):          response = self.client.get('/view/')          self.assertEqual(response.data['auth'], b'') +    def test_logout_resets_force_authenticate(self): +        """ +        `logout()` resets any `force_authenticate` +        """ +        user = User.objects.create_user('example', 'example@example.com', 'password') +        self.client.force_authenticate(user) +        response = self.client.get('/view/') +        self.assertEqual(response.data['user'], 'example') +        self.client.logout() +        response = self.client.get('/view/') +        self.assertEqual(response.data['user'], '') +      def test_follow_redirect(self):          """          Follow redirect by setting follow argument. diff --git a/tests/test_validators.py b/tests/test_validators.py index 1df0641c..072cec36 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -20,6 +20,15 @@ class UniquenessSerializer(serializers.ModelSerializer):          model = UniquenessModel +class AnotherUniquenessModel(models.Model): +    code = models.IntegerField(unique=True) + + +class AnotherUniquenessSerializer(serializers.ModelSerializer): +    class Meta: +        model = AnotherUniquenessModel + +  class TestUniquenessValidation(TestCase):      def setUp(self):          self.instance = UniquenessModel.objects.create(username='existing') @@ -51,6 +60,17 @@ class TestUniquenessValidation(TestCase):          assert serializer.is_valid()          assert serializer.validated_data == {'username': 'existing'} +    def test_doesnt_pollute_model(self): +        instance = AnotherUniquenessModel.objects.create(code='100') +        serializer = AnotherUniquenessSerializer(instance) +        self.assertEqual( +            AnotherUniquenessModel._meta.get_field('code').validators, []) + +        # Accessing data shouldn't effect validators on the model +        serializer.data +        self.assertEqual( +            AnotherUniquenessModel._meta.get_field('code').validators, []) +  # Tests for `UniqueTogetherValidator`  # ----------------------------------- @@ -148,7 +168,7 @@ class TestUniquenessTogetherValidation(TestCase):      def test_ignore_excluded_fields(self):          """          When model fields are not included in a serializer, then uniqueness -        validtors should not be added for that field. +        validators should not be added for that field.          """          class ExcludedFieldSerializer(serializers.ModelSerializer):              class Meta: diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py new file mode 100644 index 00000000..4d18a955 --- /dev/null +++ b/tests/test_viewsets.py @@ -0,0 +1,35 @@ +from django.test import TestCase +from rest_framework import status +from rest_framework.response import Response +from rest_framework.test import APIRequestFactory +from rest_framework.viewsets import GenericViewSet + + +factory = APIRequestFactory() + + +class BasicViewSet(GenericViewSet): +    def list(self, request, *args, **kwargs): +        return Response({'ACTION': 'LIST'}) + + +class InitializeViewSetsTestCase(TestCase): +    def test_initialize_view_set_with_actions(self): +        request = factory.get('/', '', content_type='application/json') +        my_view = BasicViewSet.as_view(actions={ +            'get': 'list', +        }) + +        response = my_view(request) +        self.assertEqual(response.status_code, status.HTTP_200_OK) +        self.assertEqual(response.data, {'ACTION': 'LIST'}) + +    def test_initialize_view_set_with_empty_actions(self): +        try: +            BasicViewSet.as_view() +        except TypeError as e: +            self.assertEqual(str(e), "The `actions` argument must be provided " +                                     "when calling `.as_view()` on a ViewSet. " +                                     "For example `.as_view({'get': 'list'})`") +        else: +            self.fail("actions must not be empty.") | 
