From 971578ca345c3d3bae7fd93b87c41d43483b6f05 Mon Sep 17 00:00:00 2001 From: Andreas Pelme Date: Sun, 2 Mar 2014 12:40:30 +0100 Subject: Support for running the test suite with py.test * Get rid of runtests.py * Moved test code from rest_framework/tests and rest_framework/runtests to tests * Invoke py.test from setup.py * Invoke py.test from Travis * Invoke py.test from tox * Changed setUpClass to be just plain setUp in test_permissions.py * Updated contribution guideline to show how to invoke py.test --- tests/test_validation.py | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/test_validation.py (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py new file mode 100644 index 00000000..124c874d --- /dev/null +++ b/tests/test_validation.py @@ -0,0 +1,104 @@ +from __future__ import unicode_literals +from django.db import models +from django.test import TestCase +from rest_framework import generics, serializers, status +from rest_framework.test import APIRequestFactory + +factory = APIRequestFactory() + + +# Regression for #666 + +class ValidationModel(models.Model): + blank_validated_field = models.CharField(max_length=255) + + +class ValidationModelSerializer(serializers.ModelSerializer): + class Meta: + model = ValidationModel + fields = ('blank_validated_field',) + read_only_fields = ('blank_validated_field',) + + +class UpdateValidationModel(generics.RetrieveUpdateDestroyAPIView): + model = ValidationModel + serializer_class = ValidationModelSerializer + + +class TestPreSaveValidationExclusions(TestCase): + def test_pre_save_validation_exclusions(self): + """ + Somewhat weird test case to ensure that we don't perform model + validation on read only fields. + """ + obj = ValidationModel.objects.create(blank_validated_field='') + request = factory.put('/', {}, format='json') + view = UpdateValidationModel().as_view() + response = view(request, pk=obj.pk).render() + self.assertEqual(response.status_code, status.HTTP_200_OK) + + +# Regression for #653 + +class ShouldValidateModel(models.Model): + should_validate_field = models.CharField(max_length=255) + + +class ShouldValidateModelSerializer(serializers.ModelSerializer): + renamed = serializers.CharField(source='should_validate_field', required=False) + + def validate_renamed(self, attrs, source): + value = attrs[source] + if len(value) < 3: + raise serializers.ValidationError('Minimum 3 characters.') + return attrs + + class Meta: + model = ShouldValidateModel + fields = ('renamed',) + + +class TestPreSaveValidationExclusionsSerializer(TestCase): + def test_renamed_fields_are_model_validated(self): + """ + Ensure fields with 'source' applied do get still get model validation. + """ + # We've set `required=False` on the serializer, but the model + # does not have `blank=True`, so this serializer should not validate. + serializer = ShouldValidateModelSerializer(data={'renamed': ''}) + self.assertEqual(serializer.is_valid(), False) + self.assertIn('renamed', serializer.errors) + self.assertNotIn('should_validate_field', serializer.errors) + + +class TestCustomValidationMethods(TestCase): + def test_custom_validation_method_is_executed(self): + serializer = ShouldValidateModelSerializer(data={'renamed': 'fo'}) + self.assertFalse(serializer.is_valid()) + self.assertIn('renamed', serializer.errors) + + def test_custom_validation_method_passing(self): + serializer = ShouldValidateModelSerializer(data={'renamed': 'foo'}) + self.assertTrue(serializer.is_valid()) + + +class ValidationSerializer(serializers.Serializer): + foo = serializers.CharField() + + def validate_foo(self, attrs, source): + raise serializers.ValidationError("foo invalid") + + def validate(self, attrs): + raise serializers.ValidationError("serializer invalid") + + +class TestAvoidValidation(TestCase): + """ + If serializer was initialized with invalid data (None or non dict-like), it + should avoid validation layer (validate_ and validate methods) + """ + def test_serializer_errors_has_only_invalid_data_error(self): + serializer = ValidationSerializer(data='invalid data') + self.assertFalse(serializer.is_valid()) + self.assertDictEqual(serializer.errors, + {'non_field_errors': ['Invalid data']}) -- cgit v1.2.3 From b3253b42836acd123224e88c0927f1ee6a031d94 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 29 Aug 2014 12:35:53 +0100 Subject: Remove `.model` usage in tests. Remove the shortcut `.model` view attribute usage from test cases. --- tests/test_validation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index e13e4078..f62d9068 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -22,7 +22,7 @@ class ValidationModelSerializer(serializers.ModelSerializer): class UpdateValidationModel(generics.RetrieveUpdateDestroyAPIView): - model = ValidationModel + queryset = ValidationModel.objects.all() serializer_class = ValidationModelSerializer @@ -117,7 +117,7 @@ class ValidationMaxValueValidatorModelSerializer(serializers.ModelSerializer): class UpdateMaxValueValidationModel(generics.RetrieveUpdateDestroyAPIView): - model = ValidationMaxValueValidatorModel + queryset = ValidationMaxValueValidatorModel.objects.all() serializer_class = ValidationMaxValueValidatorModelSerializer -- cgit v1.2.3 From d934824bff21e4a11226af61efba319be227f4f0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Sep 2014 16:29:46 +0100 Subject: Workin on --- tests/test_validation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index f62d9068..fcfc853d 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -48,11 +48,10 @@ class ShouldValidateModel(models.Model): class ShouldValidateModelSerializer(serializers.ModelSerializer): renamed = serializers.CharField(source='should_validate_field', required=False) - def validate_renamed(self, attrs, source): - value = attrs[source] + def validate_renamed(self, value): if len(value) < 3: raise serializers.ValidationError('Minimum 3 characters.') - return attrs + return value class Meta: model = ShouldValidateModel -- cgit v1.2.3 From 613a301a36f9167eec864e80336aee45a916a4e1 Mon Sep 17 00:00:00 2001 From: Christopher Adams Date: Sat, 6 Sep 2014 17:04:35 -0400 Subject: Fixed #1533 - Resolved issue with integer keys on nested choices never validating. - Added unit test for nested `choices` argument. - Added unit test for non-nested `choices` argument. --- tests/test_validation.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index e13e4078..a46e38ac 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 @@ -146,3 +147,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.validate(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.validate(value) + except ValidationError: + self.fail("Value %s does not validate" % str(value)) -- cgit v1.2.3 From 21980b800d04a1d82a6003823abfdf4ab80ae979 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 8 Sep 2014 14:24:05 +0100 Subject: More test sorting --- tests/test_validation.py | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index fcfc853d..40005486 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -26,19 +26,6 @@ class UpdateValidationModel(generics.RetrieveUpdateDestroyAPIView): serializer_class = ValidationModelSerializer -class TestPreSaveValidationExclusions(TestCase): - def test_pre_save_validation_exclusions(self): - """ - Somewhat weird test case to ensure that we don't perform model - validation on read only fields. - """ - obj = ValidationModel.objects.create(blank_validated_field='') - request = factory.put('/', {}, format='json') - view = UpdateValidationModel().as_view() - response = view(request, pk=obj.pk).render() - self.assertEqual(response.status_code, status.HTTP_200_OK) - - # Regression for #653 class ShouldValidateModel(models.Model): -- cgit v1.2.3 From 0d354e8f92c7daaf8dac3b80f0fd64f983f21e0b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 12 Sep 2014 09:49:35 +0100 Subject: to_internal_value() and to_representation() --- tests/test_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index c4506e7e..7543d849 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -158,7 +158,7 @@ class TestChoiceFieldChoicesValidate(TestCase): f = serializers.ChoiceField(choices=self.CHOICES) value = self.CHOICES[0][0] try: - f.to_native(value) + f.to_internal_value(value) except ValidationError: self.fail("Value %s does not validate" % str(value)) -- cgit v1.2.3 From 5e39e159ee6aee90755709cfecec7d22c7ea3049 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 12 Sep 2014 11:38:22 +0100 Subject: UNICODE_JSON and COMPACT_JSON settings --- tests/test_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index 7543d849..ce39714d 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -131,7 +131,7 @@ class TestMaxValueValidatorValidation(TestCase): request = factory.patch('/{0}'.format(obj.pk), {'number_value': 101}, format='json') view = UpdateMaxValueValidationModel().as_view() 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.content, b'{"number_value":["Ensure this value is less than or equal to 100."]}') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) -- cgit v1.2.3 From d9a199ca0ddf92f999aa37b396596d0e3e0a26d9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 10 Oct 2014 14:16:09 +0100 Subject: exceptions.ValidationFailed, not Django's ValidationError --- tests/test_validation.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index ce39714d..849c7e9d 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,9 +1,8 @@ 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 +from rest_framework import exceptions, generics, serializers, status from rest_framework.test import APIRequestFactory factory = APIRequestFactory() @@ -38,7 +37,7 @@ class ShouldValidateModelSerializer(serializers.ModelSerializer): def validate_renamed(self, value): if len(value) < 3: - raise serializers.ValidationError('Minimum 3 characters.') + raise exceptions.ValidationFailed('Minimum 3 characters.') return value class Meta: @@ -74,10 +73,10 @@ class ValidationSerializer(serializers.Serializer): foo = serializers.CharField() def validate_foo(self, attrs, source): - raise serializers.ValidationError("foo invalid") + raise exceptions.ValidationFailed("foo invalid") def validate(self, attrs): - raise serializers.ValidationError("serializer invalid") + raise exceptions.ValidationFailed("serializer invalid") class TestAvoidValidation(TestCase): @@ -159,7 +158,7 @@ class TestChoiceFieldChoicesValidate(TestCase): value = self.CHOICES[0][0] try: f.to_internal_value(value) - except ValidationError: + except exceptions.ValidationFailed: self.fail("Value %s does not validate" % str(value)) # def test_nested_choices(self): -- cgit v1.2.3 From 05cbec9dd7f9f0b6a9b59b29ac6c9272b6ae50d8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 17 Oct 2014 13:23:14 +0100 Subject: Use serializers.ValidationError --- tests/test_validation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index 849c7e9d..4d64e6e1 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.core.validators import MaxValueValidator from django.db import models from django.test import TestCase -from rest_framework import exceptions, generics, serializers, status +from rest_framework import generics, serializers, status from rest_framework.test import APIRequestFactory factory = APIRequestFactory() @@ -37,7 +37,7 @@ class ShouldValidateModelSerializer(serializers.ModelSerializer): def validate_renamed(self, value): if len(value) < 3: - raise exceptions.ValidationFailed('Minimum 3 characters.') + raise serializers.ValidationError('Minimum 3 characters.') return value class Meta: @@ -73,10 +73,10 @@ class ValidationSerializer(serializers.Serializer): foo = serializers.CharField() def validate_foo(self, attrs, source): - raise exceptions.ValidationFailed("foo invalid") + raise serializers.ValidationError("foo invalid") def validate(self, attrs): - raise exceptions.ValidationFailed("serializer invalid") + raise serializers.ValidationError("serializer invalid") class TestAvoidValidation(TestCase): @@ -158,7 +158,7 @@ class TestChoiceFieldChoicesValidate(TestCase): value = self.CHOICES[0][0] try: f.to_internal_value(value) - except exceptions.ValidationFailed: + except serializers.ValidationError: self.fail("Value %s does not validate" % str(value)) # def test_nested_choices(self): -- cgit v1.2.3 From 003c42b0f51f9bfa93964be69fb8cb68b7394280 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 3 Nov 2014 14:01:02 +0000 Subject: Use invalid_data key for error message. Closes #2002. --- tests/test_validation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index 4d64e6e1..1b6aa358 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -87,8 +87,11 @@ class TestAvoidValidation(TestCase): def test_serializer_errors_has_only_invalid_data_error(self): serializer = ValidationSerializer(data='invalid data') self.assertFalse(serializer.is_valid()) - self.assertDictEqual(serializer.errors, - {'non_field_errors': ['Invalid data']}) + self.assertDictEqual(serializer.errors, { + 'non_field_errors': [ + 'Invalid data. Expected a dictionary, but got unicode.' + ] + }) # regression tests for issue: 1493 -- cgit v1.2.3 From 2b535954cdfb050f96588c60d45664b3802dcc25 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 3 Nov 2014 14:14:12 +0000 Subject: Py2/3 compat for validation test --- tests/test_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index 1b6aa358..c278d90f 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -89,7 +89,7 @@ class TestAvoidValidation(TestCase): self.assertFalse(serializer.is_valid()) self.assertDictEqual(serializer.errors, { 'non_field_errors': [ - 'Invalid data. Expected a dictionary, but got unicode.' + 'Invalid data. Expected a dictionary, but got %s.' % type('').__name__ ] }) -- cgit v1.2.3 From 0a5d088287be1bb56f37504cc75cee10fb4e74a0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Nov 2014 10:48:30 +0000 Subject: Fix failing copy of fields when RegexValidator is used. Closes #1954. --- tests/test_validation.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index c278d90f..3db82555 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,9 +1,10 @@ from __future__ import unicode_literals -from django.core.validators import MaxValueValidator +from django.core.validators import RegexValidator, MaxValueValidator from django.db import models from django.test import TestCase from rest_framework import generics, serializers, status from rest_framework.test import APIRequestFactory +import re factory = APIRequestFactory() @@ -174,3 +175,20 @@ class TestChoiceFieldChoicesValidate(TestCase): # f.to_native(value) # except ValidationError: # self.fail("Value %s does not validate" % str(value)) + + +class RegexSerializer(serializers.Serializer): + pin = serializers.CharField( + validators=[RegexValidator(regex=re.compile('^[0-9]{4,6}$'), + message='A PIN is 4-6 digits')]) + +expected_repr = """ +RegexSerializer(): + pin = CharField(validators=[]) +""".strip() + + +class TestRegexSerializer(TestCase): + def test_regex_repr(self): + serializer_repr = repr(RegexSerializer()) + assert serializer_repr == expected_repr -- cgit v1.2.3 From c8764de7881f419c9269913ec3654fc1d904ab2e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 28 Nov 2014 13:04:42 +0000 Subject: Drop defunct tests --- tests/test_validation.py | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'tests/test_validation.py') diff --git a/tests/test_validation.py b/tests/test_validation.py index 3db82555..4234efd3 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -165,17 +165,6 @@ class TestChoiceFieldChoicesValidate(TestCase): except serializers.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)) - class RegexSerializer(serializers.Serializer): pin = serializers.CharField( -- cgit v1.2.3