|
1 | 1 | from django.test import TestCase
|
| 2 | +import pytest |
2 | 3 | import django
|
| 4 | +from django.db import connection |
3 | 5 | from tests.utils import EnumTypeMixin
|
4 | 6 | from tests.djenum.models import EnumTester, Bug53Tester, NullableStrEnum
|
5 |
| -from tests.djenum.forms import EnumTesterForm |
| 7 | +from tests.djenum.forms import EnumTesterForm, EnumTesterMultipleChoiceForm |
6 | 8 | from django.forms import Form, ModelForm
|
7 |
| -from django_enum.forms import EnumChoiceField |
| 9 | +from django_enum.forms import EnumChoiceField, EnumMultipleChoiceField |
8 | 10 | from django.core.exceptions import ValidationError
|
9 | 11 | from datetime import date, datetime, timedelta, time
|
10 | 12 | from decimal import Decimal
|
@@ -198,6 +200,10 @@ class Meta:
|
198 | 200 | self.assertEqual(form.cleaned_data["required_default"], ExternEnum.ONE)
|
199 | 201 | self.assertIsInstance(form.base_fields["required"], EnumChoiceField)
|
200 | 202 |
|
| 203 | + @pytest.mark.skipif( |
| 204 | + connection.vendor == "oracle", |
| 205 | + reason="Null/blank form behavior on oracle broken", |
| 206 | + ) |
201 | 207 | def test_nullable_blank_tester_form(self):
|
202 | 208 | from tests.djenum.models import NullableBlankFormTester
|
203 | 209 | from tests.djenum.enums import NullableExternEnum
|
@@ -247,6 +253,10 @@ class Meta:
|
247 | 253 | self.assertEqual(form.cleaned_data["required_default"], NullableExternEnum.ONE)
|
248 | 254 | self.assertIsInstance(form.base_fields["required"], EnumChoiceField)
|
249 | 255 |
|
| 256 | + @pytest.mark.skipif( |
| 257 | + connection.vendor == "oracle", |
| 258 | + reason="Null/blank form behavior on oracle broken", |
| 259 | + ) |
250 | 260 | def test_nullable_str_tester_form(self):
|
251 | 261 | from tests.djenum.models import NullableStrFormTester
|
252 | 262 | from tests.djenum.enums import NullableStrEnum
|
@@ -468,3 +478,170 @@ def test_non_strict_field(self):
|
468 | 478 | form["non_strict_int"].field.to_python(form["non_strict_int"].value()),
|
469 | 479 | self.enum_primitive("non_strict_int"),
|
470 | 480 | )
|
| 481 | + |
| 482 | + |
| 483 | +class TestEnumMultipleChoiceFormField(EnumTypeMixin, TestCase): |
| 484 | + MODEL_CLASS = EnumTester |
| 485 | + FORM_CLASS = EnumTesterMultipleChoiceForm |
| 486 | + form_type = None |
| 487 | + |
| 488 | + @property |
| 489 | + def model_params(self): |
| 490 | + return { |
| 491 | + "small_pos_int": [0], |
| 492 | + "small_int": [self.SmallIntEnum.VAL2, self.SmallIntEnum.VALn1], |
| 493 | + "pos_int": [2147483647, self.PosIntEnum.VAL3], |
| 494 | + "int": [self.IntEnum.VALn1], |
| 495 | + "big_pos_int": [2, self.BigPosIntEnum.VAL3], |
| 496 | + "big_int": [self.BigIntEnum.VAL0], |
| 497 | + "constant": [2.71828, self.Constants.GOLDEN_RATIO], |
| 498 | + "text": [self.TextEnum.VALUE3, self.TextEnum.VALUE2], |
| 499 | + "extern": [self.ExternEnum.THREE], |
| 500 | + "date_enum": [self.DateEnum.BRIAN, date(1989, 7, 27)], |
| 501 | + "datetime_enum": [self.DateTimeEnum.ST_HELENS, self.DateTimeEnum.ST_HELENS], |
| 502 | + "duration_enum": [self.DurationEnum.FORTNIGHT], |
| 503 | + "time_enum": [self.TimeEnum.COB, self.TimeEnum.LUNCH], |
| 504 | + "decimal_enum": [self.DecimalEnum.ONE], |
| 505 | + "non_strict_int": [self.SmallPosIntEnum.VAL2], |
| 506 | + "non_strict_text": ["arbitrary", "A" * 13], |
| 507 | + "no_coerce": [self.SmallPosIntEnum.VAL1], |
| 508 | + } |
| 509 | + |
| 510 | + @property |
| 511 | + def bad_values(self): |
| 512 | + return { |
| 513 | + "small_pos_int": [4.1], |
| 514 | + "small_int": ["Value 12"], |
| 515 | + "pos_int": [5.3], |
| 516 | + "int": [10], |
| 517 | + "big_pos_int": ["-12"], |
| 518 | + "big_int": ["-12"], |
| 519 | + "constant": [2.7], |
| 520 | + "text": ["143 emma"], |
| 521 | + "date_enum": ["20159-01-01"], |
| 522 | + "datetime_enum": ["AAAA-01-01 00:00:00"], |
| 523 | + "duration_enum": ["1 elephant"], |
| 524 | + "time_enum": ["2.a"], |
| 525 | + "decimal_enum": ["alpha"], |
| 526 | + "extern": [6], |
| 527 | + "non_strict_int": ["Not an int"], |
| 528 | + "non_strict_text": [], |
| 529 | + "no_coerce": ["Value 0"], |
| 530 | + } |
| 531 | + |
| 532 | + from json import encoder |
| 533 | + |
| 534 | + def verify_field(self, form, field, values): |
| 535 | + # this doesnt work with coerce=False fields |
| 536 | + for idx, value in enumerate(values): |
| 537 | + if self.MODEL_CLASS._meta.get_field(field).strict: |
| 538 | + self.assertEqual( |
| 539 | + form[field].value()[idx], self.enum_type(field)(value).value |
| 540 | + ) |
| 541 | + self.assertIsInstance( |
| 542 | + form[field].field.to_python(form[field].value())[idx], |
| 543 | + self.enum_type(field), |
| 544 | + ) |
| 545 | + |
| 546 | + def test_initial(self): |
| 547 | + form = self.FORM_CLASS(initial=self.model_params) |
| 548 | + for field, values in self.model_params.items(): |
| 549 | + self.verify_field(form, field, values) |
| 550 | + |
| 551 | + def test_data(self): |
| 552 | + form = self.FORM_CLASS(data=self.model_params) |
| 553 | + form.full_clean() |
| 554 | + self.assertTrue(form.is_valid()) |
| 555 | + for field, values in self.model_params.items(): |
| 556 | + self.verify_field(form, field, values) |
| 557 | + |
| 558 | + def test_error(self): |
| 559 | + for field, bad_value in self.bad_values.items(): |
| 560 | + form = self.FORM_CLASS(data={**self.model_params, field: bad_value}) |
| 561 | + form.full_clean() |
| 562 | + self.assertFalse(form.is_valid(), f"{field}={bad_value}: {form.errors}") |
| 563 | + self.assertTrue(field in form.errors) |
| 564 | + |
| 565 | + form = self.FORM_CLASS(data=self.bad_values) |
| 566 | + form.full_clean() |
| 567 | + self.assertFalse(form.is_valid()) |
| 568 | + for field in self.bad_values.keys(): |
| 569 | + self.assertTrue(field in form.errors) |
| 570 | + |
| 571 | + def test_field_validation(self): |
| 572 | + for enum_field, bad_value in [ |
| 573 | + (EnumMultipleChoiceField(self.SmallPosIntEnum), 4.1), |
| 574 | + (EnumMultipleChoiceField(self.SmallIntEnum), 123123123), |
| 575 | + (EnumMultipleChoiceField(self.PosIntEnum), -1), |
| 576 | + (EnumMultipleChoiceField(self.IntEnum), "63"), |
| 577 | + (EnumMultipleChoiceField(self.BigPosIntEnum), None), |
| 578 | + (EnumMultipleChoiceField(self.BigIntEnum), ""), |
| 579 | + (EnumMultipleChoiceField(self.Constants), "y"), |
| 580 | + (EnumMultipleChoiceField(self.TextEnum), 42), |
| 581 | + (EnumMultipleChoiceField(self.DateEnum), "20159-01-01"), |
| 582 | + (EnumMultipleChoiceField(self.DateTimeEnum), "AAAA-01-01 00:00:00"), |
| 583 | + (EnumMultipleChoiceField(self.DurationEnum), "1 elephant"), |
| 584 | + (EnumMultipleChoiceField(self.TimeEnum), "2.a"), |
| 585 | + (EnumMultipleChoiceField(self.DecimalEnum), "alpha"), |
| 586 | + (EnumMultipleChoiceField(self.ExternEnum), 0), |
| 587 | + (EnumMultipleChoiceField(self.DJIntEnum), "5.3"), |
| 588 | + (EnumMultipleChoiceField(self.DJTextEnum), 12), |
| 589 | + (EnumMultipleChoiceField(self.SmallPosIntEnum, strict=False), "not an int"), |
| 590 | + ]: |
| 591 | + self.assertRaises(ValidationError, enum_field.validate, [bad_value]) |
| 592 | + |
| 593 | + for enum_field, bad_value in [ |
| 594 | + (EnumMultipleChoiceField(self.SmallPosIntEnum, strict=False), 4), |
| 595 | + (EnumMultipleChoiceField(self.SmallIntEnum, strict=False), 123123123), |
| 596 | + (EnumMultipleChoiceField(self.PosIntEnum, strict=False), -1), |
| 597 | + (EnumMultipleChoiceField(self.IntEnum, strict=False), "63"), |
| 598 | + (EnumMultipleChoiceField(self.BigPosIntEnum, strict=False), 18), |
| 599 | + (EnumMultipleChoiceField(self.BigIntEnum, strict=False), "-8"), |
| 600 | + (EnumMultipleChoiceField(self.Constants, strict=False), "1.976"), |
| 601 | + (EnumMultipleChoiceField(self.TextEnum, strict=False), 42), |
| 602 | + (EnumMultipleChoiceField(self.ExternEnum, strict=False), 0), |
| 603 | + (EnumMultipleChoiceField(self.DJIntEnum, strict=False), "5"), |
| 604 | + (EnumMultipleChoiceField(self.DJTextEnum, strict=False), 12), |
| 605 | + (EnumMultipleChoiceField(self.SmallPosIntEnum, strict=False), "12"), |
| 606 | + ( |
| 607 | + EnumMultipleChoiceField(self.DateEnum, strict=False), |
| 608 | + date(year=2015, month=1, day=1), |
| 609 | + ), |
| 610 | + ( |
| 611 | + EnumMultipleChoiceField(self.DateTimeEnum, strict=False), |
| 612 | + datetime(year=2014, month=1, day=1, hour=0, minute=0, second=0), |
| 613 | + ), |
| 614 | + ( |
| 615 | + EnumMultipleChoiceField(self.DurationEnum, strict=False), |
| 616 | + timedelta(seconds=15), |
| 617 | + ), |
| 618 | + ( |
| 619 | + EnumMultipleChoiceField(self.TimeEnum, strict=False), |
| 620 | + time(hour=2, minute=0, second=0), |
| 621 | + ), |
| 622 | + (EnumMultipleChoiceField(self.DecimalEnum, strict=False), Decimal("0.5")), |
| 623 | + ]: |
| 624 | + try: |
| 625 | + enum_field.clean([bad_value]) |
| 626 | + except ValidationError: # pragma: no cover |
| 627 | + self.fail( |
| 628 | + f"non-strict choice field for {enum_field.enum} " |
| 629 | + f"raised ValidationError on {bad_value} during clean" |
| 630 | + ) |
| 631 | + |
| 632 | + def test_non_strict_field(self): |
| 633 | + form = self.FORM_CLASS(data={**self.model_params, "non_strict_int": [200, 203]}) |
| 634 | + form.full_clean() |
| 635 | + self.assertTrue(form.is_valid()) |
| 636 | + for idx in range(0, 2): |
| 637 | + self.assertIsInstance( |
| 638 | + form["non_strict_int"].value()[idx], |
| 639 | + self.enum_primitive("non_strict_int"), |
| 640 | + ) |
| 641 | + self.assertIsInstance( |
| 642 | + form["non_strict_int"].field.to_python(form["non_strict_int"].value())[ |
| 643 | + idx |
| 644 | + ], |
| 645 | + self.enum_primitive("non_strict_int"), |
| 646 | + ) |
| 647 | + self.assertEqual(form["non_strict_int"].value(), [200, 203]) |
0 commit comments