From 368182414ed1bb0958cbd4aed4b8b20f0c17c2ce Mon Sep 17 00:00:00 2001 From: Daksh Bhayana Date: Sun, 9 Feb 2025 19:59:19 +0530 Subject: [PATCH 1/4] Duplicate error msg fix for IP address field --- rest_framework/utils/field_mapping.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index fc63f96fe0..e04199a5d7 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -207,8 +207,10 @@ def get_field_kwargs(field_name, model_field): if isinstance(model_field, models.GenericIPAddressField): validator_kwarg = [ validator for validator in validator_kwarg - if validator is not validators.validate_ipv46_address + if validator not in [validators.validate_ipv46_address, validators.validate_ipv6_address, validators.validate_ipv4_address] ] + kwargs['protocol'] = getattr(model_field, 'protocol', 'both') + # Our decimal validation is handled in the field code, not validator code. if isinstance(model_field, models.DecimalField): validator_kwarg = [ From 08aba54700b113fc91dbaebb6215703da8d50fa4 Mon Sep 17 00:00:00 2001 From: Daksh Bhayana Date: Mon, 10 Feb 2025 23:48:01 +0530 Subject: [PATCH 2/4] WIP --- tests/test_ipaddress_field_serializer.py | 97 ++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/test_ipaddress_field_serializer.py diff --git a/tests/test_ipaddress_field_serializer.py b/tests/test_ipaddress_field_serializer.py new file mode 100644 index 0000000000..8cc0268453 --- /dev/null +++ b/tests/test_ipaddress_field_serializer.py @@ -0,0 +1,97 @@ +from django.db import models +from django.test import TestCase + +from rest_framework import serializers +from rest_framework.exceptions import ValidationError + + +# Define the model +class TestModel(models.Model): + address = models.GenericIPAddressField(protocol="both") + + class Meta: + app_label = "main" + + +class TestSerializer(serializers.ModelSerializer): + + class Meta: + model = TestModel + fields = "__all__" + + +# Define the serializer in setUp +class TestSerializerTestCase(TestCase): + def setUp(self): + """Initialize serializer class.""" + self.serializer_class = TestSerializer + + def test_invalid_ipv4_for_ipv4_field(self): + """Test that an invalid IPv4 raises only an IPv4-related error.""" + TestModel._meta.get_field("address").protocol = "IPv4" # Set field to IPv4 only + invalid_data = {"address": "invalid-ip"} + serializer = self.serializer_class(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv4 address." + ) + + def test_invalid_ipv6_for_ipv6_field(self): + """Test that an invalid IPv6 raises only an IPv6-related error.""" + TestModel._meta.get_field("address").protocol = "IPv6" # Set field to IPv6 only + invalid_data = {"address": "invalid-ip"} + serializer = self.serializer_class(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv6 address." + ) + + def test_invalid_both_protocol(self): + """Test that an invalid IP raises a combined error message when protocol is both.""" + TestModel._meta.get_field("address").protocol = "both" # Allow both IPv4 & IPv6 + invalid_data = {"address": "invalid-ip"} + serializer = self.serializer_class(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv4 or IPv6 address." + ) + + def test_valid_ipv4(self): + """Test that a valid IPv4 passes validation.""" + TestModel._meta.get_field("address").protocol = "IPv4" + valid_data = {"address": "192.168.1.1"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + + def test_valid_ipv6(self): + """Test that a valid IPv6 passes validation.""" + TestModel._meta.get_field("address").protocol = "IPv6" + valid_data = {"address": "2001:db8::ff00:42:8329"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + + def test_valid_ipv4_for_both_protocol(self): + """Test that a valid IPv4 is accepted when protocol is 'both'.""" + TestModel._meta.get_field("address").protocol = "both" + valid_data = {"address": "192.168.1.1"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + + def test_valid_ipv6_for_both_protocol(self): + """Test that a valid IPv6 is accepted when protocol is 'both'.""" + TestModel._meta.get_field("address").protocol = "both" + valid_data = {"address": "2001:db8::ff00:42:8329"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) From ec927c9083fddccb58825d9523c3b5d71946434e Mon Sep 17 00:00:00 2001 From: Daksh Bhayana Date: Tue, 11 Feb 2025 22:24:43 +0530 Subject: [PATCH 3/4] Refactored Test cases for GenericIPAddress Field --- tests/test_ipaddress_field_serializer.py | 97 ------------------------ tests/test_model_serializer.py | 80 ++++++++++++++++++- 2 files changed, 78 insertions(+), 99 deletions(-) delete mode 100644 tests/test_ipaddress_field_serializer.py diff --git a/tests/test_ipaddress_field_serializer.py b/tests/test_ipaddress_field_serializer.py deleted file mode 100644 index 8cc0268453..0000000000 --- a/tests/test_ipaddress_field_serializer.py +++ /dev/null @@ -1,97 +0,0 @@ -from django.db import models -from django.test import TestCase - -from rest_framework import serializers -from rest_framework.exceptions import ValidationError - - -# Define the model -class TestModel(models.Model): - address = models.GenericIPAddressField(protocol="both") - - class Meta: - app_label = "main" - - -class TestSerializer(serializers.ModelSerializer): - - class Meta: - model = TestModel - fields = "__all__" - - -# Define the serializer in setUp -class TestSerializerTestCase(TestCase): - def setUp(self): - """Initialize serializer class.""" - self.serializer_class = TestSerializer - - def test_invalid_ipv4_for_ipv4_field(self): - """Test that an invalid IPv4 raises only an IPv4-related error.""" - TestModel._meta.get_field("address").protocol = "IPv4" # Set field to IPv4 only - invalid_data = {"address": "invalid-ip"} - serializer = self.serializer_class(data=invalid_data) - - with self.assertRaises(ValidationError) as context: - serializer.is_valid(raise_exception=True) - - self.assertEqual( - str(context.exception.detail["address"][0]), - "Enter a valid IPv4 address." - ) - - def test_invalid_ipv6_for_ipv6_field(self): - """Test that an invalid IPv6 raises only an IPv6-related error.""" - TestModel._meta.get_field("address").protocol = "IPv6" # Set field to IPv6 only - invalid_data = {"address": "invalid-ip"} - serializer = self.serializer_class(data=invalid_data) - - with self.assertRaises(ValidationError) as context: - serializer.is_valid(raise_exception=True) - - self.assertEqual( - str(context.exception.detail["address"][0]), - "Enter a valid IPv6 address." - ) - - def test_invalid_both_protocol(self): - """Test that an invalid IP raises a combined error message when protocol is both.""" - TestModel._meta.get_field("address").protocol = "both" # Allow both IPv4 & IPv6 - invalid_data = {"address": "invalid-ip"} - serializer = self.serializer_class(data=invalid_data) - - with self.assertRaises(ValidationError) as context: - serializer.is_valid(raise_exception=True) - - self.assertEqual( - str(context.exception.detail["address"][0]), - "Enter a valid IPv4 or IPv6 address." - ) - - def test_valid_ipv4(self): - """Test that a valid IPv4 passes validation.""" - TestModel._meta.get_field("address").protocol = "IPv4" - valid_data = {"address": "192.168.1.1"} - serializer = self.serializer_class(data=valid_data) - self.assertTrue(serializer.is_valid()) - - def test_valid_ipv6(self): - """Test that a valid IPv6 passes validation.""" - TestModel._meta.get_field("address").protocol = "IPv6" - valid_data = {"address": "2001:db8::ff00:42:8329"} - serializer = self.serializer_class(data=valid_data) - self.assertTrue(serializer.is_valid()) - - def test_valid_ipv4_for_both_protocol(self): - """Test that a valid IPv4 is accepted when protocol is 'both'.""" - TestModel._meta.get_field("address").protocol = "both" - valid_data = {"address": "192.168.1.1"} - serializer = self.serializer_class(data=valid_data) - self.assertTrue(serializer.is_valid()) - - def test_valid_ipv6_for_both_protocol(self): - """Test that a valid IPv6 is accepted when protocol is 'both'.""" - TestModel._meta.get_field("address").protocol = "both" - valid_data = {"address": "2001:db8::ff00:42:8329"} - serializer = self.serializer_class(data=valid_data) - self.assertTrue(serializer.is_valid()) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index ae1a2b0fa1..95459196c3 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -25,6 +25,7 @@ from rest_framework import serializers from rest_framework.compat import postgres_fields +from rest_framework.exceptions import ValidationError from .models import NestedForeignKeySource @@ -409,7 +410,8 @@ class Meta: class TestGenericIPAddressFieldValidation(TestCase): - def test_ip_address_validation(self): + + def setUp(self): class IPAddressFieldModel(models.Model): address = models.GenericIPAddressField() @@ -418,12 +420,86 @@ class Meta: model = IPAddressFieldModel fields = '__all__' - s = TestSerializer(data={'address': 'not an ip address'}) + self.serializer_class = TestSerializer + self.model = IPAddressFieldModel + + def test_ip_address_validation(self): + s = self.serializer_class(data={'address': 'not an ip address'}) self.assertFalse(s.is_valid()) self.assertEqual(1, len(s.errors['address']), 'Unexpected number of validation errors: ' '{}'.format(s.errors)) + def test_invalid_ipv4_for_ipv4_field(self): + """Test that an invalid IPv4 raises only an IPv4-related error.""" + self.model._meta.get_field("address").protocol = "IPv4" # Set field to IPv4 only + invalid_data = {"address": "invalid-ip"} + serializer = self.serializer_class(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv4 address." + ) + + def test_invalid_ipv6_for_ipv6_field(self): + """Test that an invalid IPv6 raises only an IPv6-related error.""" + self.model._meta.get_field("address").protocol = "IPv6" # Set field to IPv6 only + invalid_data = {"address": "invalid-ip"} + serializer = self.serializer_class(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv6 address." + ) + + def test_invalid_both_protocol(self): + """Test that an invalid IP raises a combined error message when protocol is both.""" + self.model._meta.get_field("address").protocol = "both" # Allow both IPv4 & IPv6 + invalid_data = {"address": "invalid-ip"} + serializer = self.serializer_class(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv4 or IPv6 address." + ) + + def test_valid_ipv4(self): + """Test that a valid IPv4 passes validation.""" + self.model._meta.get_field("address").protocol = "IPv4" + valid_data = {"address": "192.168.1.1"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + + def test_valid_ipv6(self): + """Test that a valid IPv6 passes validation.""" + self.model._meta.get_field("address").protocol = "IPv6" + valid_data = {"address": "2001:db8::ff00:42:8329"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + + def test_valid_ipv4_for_both_protocol(self): + """Test that a valid IPv4 is accepted when protocol is 'both'.""" + self.model._meta.get_field("address").protocol = "both" + valid_data = {"address": "192.168.1.1"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + + def test_valid_ipv6_for_both_protocol(self): + """Test that a valid IPv6 is accepted when protocol is 'both'.""" + self.model._meta.get_field("address").protocol = "both" + valid_data = {"address": "2001:db8::ff00:42:8329"} + serializer = self.serializer_class(data=valid_data) + self.assertTrue(serializer.is_valid()) + @pytest.mark.skipif('not postgres_fields') class TestPosgresFieldsMapping(TestCase): From 6128db6c1d11e2cc707d307e1ce5858468ab02a0 Mon Sep 17 00:00:00 2001 From: Daksh Bhayana Date: Fri, 14 Feb 2025 23:35:28 +0530 Subject: [PATCH 4/4] Refactored Test Cases with additional models and Serializers --- tests/test_model_serializer.py | 81 +++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 95459196c3..31723704d3 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -412,19 +412,39 @@ class Meta: class TestGenericIPAddressFieldValidation(TestCase): def setUp(self): - class IPAddressFieldModel(models.Model): - address = models.GenericIPAddressField() + class IPv4Model(models.Model): + address = models.GenericIPAddressField(protocol="IPv4") - class TestSerializer(serializers.ModelSerializer): + class IPv4TestSerializer(serializers.ModelSerializer): + class Meta: + model = IPv4Model + fields = '__all__' + + class IPv6Model(models.Model): + address = models.GenericIPAddressField(protocol="IPv6") + + class IPv6TestSerializer(serializers.ModelSerializer): + class Meta: + model = IPv6Model + fields = '__all__' + + class BothProtocolsModel(models.Model): + address = models.GenericIPAddressField(protocol="both") + + class BothProtocolsTestSerializer(serializers.ModelSerializer): class Meta: - model = IPAddressFieldModel + model = BothProtocolsModel fields = '__all__' - self.serializer_class = TestSerializer - self.model = IPAddressFieldModel + self.ipv4_serializer = IPv4TestSerializer + self.ipv4_model = IPv4Model + self.ipv6_serializer = IPv6TestSerializer + self.ipv6_model = IPv6Model + self.both_protocols_serializer = BothProtocolsTestSerializer + self.both_protocols_model = BothProtocolsModel def test_ip_address_validation(self): - s = self.serializer_class(data={'address': 'not an ip address'}) + s = self.both_protocols_serializer(data={'address': 'not an ip address'}) self.assertFalse(s.is_valid()) self.assertEqual(1, len(s.errors['address']), 'Unexpected number of validation errors: ' @@ -432,9 +452,8 @@ def test_ip_address_validation(self): def test_invalid_ipv4_for_ipv4_field(self): """Test that an invalid IPv4 raises only an IPv4-related error.""" - self.model._meta.get_field("address").protocol = "IPv4" # Set field to IPv4 only invalid_data = {"address": "invalid-ip"} - serializer = self.serializer_class(data=invalid_data) + serializer = self.ipv4_serializer(data=invalid_data) with self.assertRaises(ValidationError) as context: serializer.is_valid(raise_exception=True) @@ -446,9 +465,34 @@ def test_invalid_ipv4_for_ipv4_field(self): def test_invalid_ipv6_for_ipv6_field(self): """Test that an invalid IPv6 raises only an IPv6-related error.""" - self.model._meta.get_field("address").protocol = "IPv6" # Set field to IPv6 only invalid_data = {"address": "invalid-ip"} - serializer = self.serializer_class(data=invalid_data) + serializer = self.ipv6_serializer(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv6 address." + ) + + def test_invalid_ipv6_message_v1(self): + """Test that an invalid IPv6 raises error message when data contains ':' in it.""" + invalid_data = {"address": "invalid : data"} + serializer = self.ipv6_serializer(data=invalid_data) + + with self.assertRaises(ValidationError) as context: + serializer.is_valid(raise_exception=True) + + self.assertEqual( + str(context.exception.detail["address"][0]), + "Enter a valid IPv4 or IPv6 address." + ) + + def test_invalid_ipv6_message_v2(self): + """Test that an invalid IPv6 raises error message when data doesn't contains ':' in it.""" + invalid_data = {"address": "invalid-ip"} + serializer = self.ipv6_serializer(data=invalid_data) with self.assertRaises(ValidationError) as context: serializer.is_valid(raise_exception=True) @@ -460,9 +504,8 @@ def test_invalid_ipv6_for_ipv6_field(self): def test_invalid_both_protocol(self): """Test that an invalid IP raises a combined error message when protocol is both.""" - self.model._meta.get_field("address").protocol = "both" # Allow both IPv4 & IPv6 invalid_data = {"address": "invalid-ip"} - serializer = self.serializer_class(data=invalid_data) + serializer = self.both_protocols_serializer(data=invalid_data) with self.assertRaises(ValidationError) as context: serializer.is_valid(raise_exception=True) @@ -474,30 +517,26 @@ def test_invalid_both_protocol(self): def test_valid_ipv4(self): """Test that a valid IPv4 passes validation.""" - self.model._meta.get_field("address").protocol = "IPv4" valid_data = {"address": "192.168.1.1"} - serializer = self.serializer_class(data=valid_data) + serializer = self.ipv4_serializer(data=valid_data) self.assertTrue(serializer.is_valid()) def test_valid_ipv6(self): """Test that a valid IPv6 passes validation.""" - self.model._meta.get_field("address").protocol = "IPv6" valid_data = {"address": "2001:db8::ff00:42:8329"} - serializer = self.serializer_class(data=valid_data) + serializer = self.ipv6_serializer(data=valid_data) self.assertTrue(serializer.is_valid()) def test_valid_ipv4_for_both_protocol(self): """Test that a valid IPv4 is accepted when protocol is 'both'.""" - self.model._meta.get_field("address").protocol = "both" valid_data = {"address": "192.168.1.1"} - serializer = self.serializer_class(data=valid_data) + serializer = self.both_protocols_serializer(data=valid_data) self.assertTrue(serializer.is_valid()) def test_valid_ipv6_for_both_protocol(self): """Test that a valid IPv6 is accepted when protocol is 'both'.""" - self.model._meta.get_field("address").protocol = "both" valid_data = {"address": "2001:db8::ff00:42:8329"} - serializer = self.serializer_class(data=valid_data) + serializer = self.both_protocols_serializer(data=valid_data) self.assertTrue(serializer.is_valid()) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy