|
25 | 25 | from django_enum.tests.djenum.models import EnumTester
|
26 | 26 | from django_test_migrations.constants import MIGRATION_TEST_MARKER
|
27 | 27 | from django_test_migrations.contrib.unittest_case import MigratorTestCase
|
| 28 | +from django.http import QueryDict |
| 29 | + |
| 30 | +try: |
| 31 | + import django_filters |
| 32 | + DJANGO_FILTERS_INSTALLED = True |
| 33 | +except (ImportError, ModuleNotFoundError): # pragma: no cover |
| 34 | + DJANGO_FILTERS_INSTALLED = False |
| 35 | + |
| 36 | +try: |
| 37 | + import enum_properties |
| 38 | + ENUM_PROPERTIES_INSTALLED = True |
| 39 | +except (ImportError, ModuleNotFoundError): # pragma: no cover |
| 40 | + ENUM_PROPERTIES_INSTALLED = False |
28 | 41 |
|
29 | 42 |
|
30 | 43 | def set_models(version):
|
@@ -368,8 +381,9 @@ def test_django_filters_missing(self):
|
368 | 381 | with patch.dict(sys.modules, {'django_filters': None}):
|
369 | 382 | reload(sys.modules['django_enum.filters'])
|
370 | 383 | self.do_django_filters_missing()
|
| 384 | + reload(sys.modules['django_enum.filters']) |
371 | 385 | else:
|
372 |
| - self.do_django_filters_missing() |
| 386 | + self.do_django_filters_missing() # pragma: no cover |
373 | 387 |
|
374 | 388 | def do_enum_properties_missing(self):
|
375 | 389 | import enum
|
@@ -427,8 +441,9 @@ def test_enum_properties_missing(self):
|
427 | 441 | from django_enum import choices
|
428 | 442 | reload(sys.modules['django_enum.choices'])
|
429 | 443 | self.do_enum_properties_missing()
|
| 444 | + reload(sys.modules['django_enum.choices']) |
430 | 445 | else:
|
431 |
| - self.do_enum_properties_missing() |
| 446 | + self.do_enum_properties_missing() # pragma: no cover |
432 | 447 |
|
433 | 448 |
|
434 | 449 | class TestFieldTypeResolution(EnumTypeMixin, TestCase):
|
@@ -535,20 +550,11 @@ class TestRequests(EnumTypeMixin, TestCase):
|
535 | 550 | NAMESPACE = 'django_enum_tests_djenum'
|
536 | 551 |
|
537 | 552 | objects = []
|
538 |
| - values = { |
539 |
| - val: {} for val in [ |
540 |
| - 'small_pos_int', |
541 |
| - 'small_int', |
542 |
| - 'pos_int', |
543 |
| - 'int', |
544 |
| - 'big_pos_int', |
545 |
| - 'big_int', |
546 |
| - 'constant', |
547 |
| - 'text' |
548 |
| - ] |
549 |
| - } |
| 553 | + values = {} |
550 | 554 |
|
551 | 555 | def setUp(self):
|
| 556 | + self.values = {val: {} for val in self.compared_attributes} |
| 557 | + self.objects = [] |
552 | 558 | self.MODEL_CLASS.objects.all().delete()
|
553 | 559 | self.objects.append(
|
554 | 560 | self.MODEL_CLASS.objects.create()
|
@@ -597,6 +603,9 @@ def setUp(self):
|
597 | 603 | self.values[attr].setdefault(getattr(obj, attr), [])
|
598 | 604 | self.values[attr][getattr(obj, attr)].append(obj.pk)
|
599 | 605 |
|
| 606 | + def tearDown(self): |
| 607 | + self.MODEL_CLASS.objects.all().delete() |
| 608 | + |
600 | 609 | @property
|
601 | 610 | def post_params(self):
|
602 | 611 | return {
|
@@ -674,6 +683,8 @@ def test_post(self):
|
674 | 683 |
|
675 | 684 | @staticmethod
|
676 | 685 | def get_enum_val(enum, value):
|
| 686 | + if value is None or value == '': |
| 687 | + return None |
677 | 688 | if int in enum.__mro__:
|
678 | 689 | return enum(int(value))
|
679 | 690 | if float in enum.__mro__:
|
@@ -765,6 +776,91 @@ def test_update_form(self):
|
765 | 776 | if not field.null and not field.blank:
|
766 | 777 | self.assertFalse(null_opt, "An unexpected null option is present") # pragma: no cover
|
767 | 778 |
|
| 779 | + @property |
| 780 | + def field_filter_properties(self): |
| 781 | + return { |
| 782 | + 'small_pos_int': ['value'], |
| 783 | + 'small_int': ['value'], |
| 784 | + 'pos_int': ['value'], |
| 785 | + 'int': ['value'], |
| 786 | + 'big_pos_int': ['value'], |
| 787 | + 'big_int': ['value'], |
| 788 | + 'constant': ['value'], |
| 789 | + 'text': ['value'], |
| 790 | + 'dj_int_enum': ['value'], |
| 791 | + 'dj_text_enum': ['value'], |
| 792 | + 'non_strict_int': ['value'] |
| 793 | + } |
| 794 | + |
| 795 | + @property |
| 796 | + def compared_attributes(self): |
| 797 | + return [ |
| 798 | + 'small_pos_int', |
| 799 | + 'small_int', |
| 800 | + 'pos_int', |
| 801 | + 'int', |
| 802 | + 'big_pos_int', |
| 803 | + 'big_int', |
| 804 | + 'constant', |
| 805 | + 'text', |
| 806 | + 'dj_int_enum', |
| 807 | + 'dj_text_enum', |
| 808 | + 'non_strict_int' |
| 809 | + ] |
| 810 | + |
| 811 | + def list_to_objects(self, resp_content): |
| 812 | + objects = {} |
| 813 | + for obj_div in resp_content.find('body').find_all(f'div', class_='enum'): |
| 814 | + objects[int(obj_div['data-obj-id'])] = { |
| 815 | + attr: self.get_enum_val( |
| 816 | + self.MODEL_CLASS._meta.get_field(attr).enum, |
| 817 | + obj_div.find(f'p', {'class': attr}).find( |
| 818 | + 'span', class_='value' |
| 819 | + ).text |
| 820 | + ) |
| 821 | + for attr in self.compared_attributes |
| 822 | + } |
| 823 | + return objects |
| 824 | + |
| 825 | + if DJANGO_FILTERS_INSTALLED: |
| 826 | + def test_django_filter(self): |
| 827 | + self.do_test_django_filter( |
| 828 | + reverse(f'{self.NAMESPACE}:enum-filter') |
| 829 | + ) |
| 830 | + |
| 831 | + def do_test_django_filter(self, url): |
| 832 | + """ |
| 833 | + Exhaustively test query parameter permutations based on data |
| 834 | + created in setUp |
| 835 | + """ |
| 836 | + client = Client() |
| 837 | + for attr, val_map in self.values.items(): |
| 838 | + for val, objs in val_map.items(): |
| 839 | + if val is None: |
| 840 | + continue # todo how to query None? |
| 841 | + for prop in self.field_filter_properties[attr]: |
| 842 | + qry = QueryDict(mutable=True) |
| 843 | + prop_vals = getattr(val, prop) |
| 844 | + if not isinstance(prop_vals, (set, list)): |
| 845 | + prop_vals = [prop_vals] |
| 846 | + for prop_val in prop_vals: |
| 847 | + qry[attr] = prop_val |
| 848 | + objects = { |
| 849 | + obj.pk: { |
| 850 | + attr: getattr(obj, attr) |
| 851 | + for attr in self.compared_attributes |
| 852 | + } for obj in |
| 853 | + self.MODEL_CLASS.objects.filter(id__in=objs) |
| 854 | + } |
| 855 | + self.assertEqual(len(objects), len(objs)) |
| 856 | + response = client.get(f'{url}?{qry.urlencode()}') |
| 857 | + resp_objects = self.list_to_objects( |
| 858 | + Soup(response.content, features='html.parser') |
| 859 | + ) |
| 860 | + self.assertEqual(objects, resp_objects) |
| 861 | + else: |
| 862 | + pass # pragma: no cover |
| 863 | + |
768 | 864 |
|
769 | 865 | class TestBulkOperations(EnumTypeMixin, TestCase):
|
770 | 866 | """Tests bulk insertions and updates"""
|
@@ -838,10 +934,9 @@ def test_bulk_update(self):
|
838 | 934 | )
|
839 | 935 |
|
840 | 936 |
|
841 |
| -try: |
| 937 | +if ENUM_PROPERTIES_INSTALLED: |
842 | 938 |
|
843 | 939 | from enum_properties import s
|
844 |
| - from django_enum import filters |
845 | 940 | from django_enum.tests.enum_prop.enums import (
|
846 | 941 | BigIntEnum,
|
847 | 942 | BigPosIntEnum,
|
@@ -1294,6 +1389,30 @@ def post_params(self):
|
1294 | 1389 | 'non_strict_int': self.SmallPosIntEnum.VAL1
|
1295 | 1390 | }
|
1296 | 1391 |
|
| 1392 | + @property |
| 1393 | + def field_filter_properties(self): |
| 1394 | + return { |
| 1395 | + 'small_pos_int': ['value', 'name', 'label'], |
| 1396 | + 'small_int': ['value', 'name', 'label'], |
| 1397 | + 'pos_int': ['value', 'name', 'label'], |
| 1398 | + 'int': ['value', 'name', 'label'], |
| 1399 | + 'big_pos_int': ['value', 'name', 'label'], |
| 1400 | + 'big_int': ['value', 'name', 'label', 'pos'], |
| 1401 | + 'constant': ['value', 'name', 'label', 'symbol'], |
| 1402 | + 'text': ['value', 'name', 'label', 'aliases'], |
| 1403 | + 'dj_int_enum': ['value'], |
| 1404 | + 'dj_text_enum': ['value'], |
| 1405 | + 'non_strict_int': ['value', 'name', 'label'], |
| 1406 | + } |
| 1407 | + |
| 1408 | + if DJANGO_FILTERS_INSTALLED: |
| 1409 | + def test_django_filter(self): |
| 1410 | + self.do_test_django_filter( |
| 1411 | + reverse(f'{self.NAMESPACE}:enum-filter-symmetric') |
| 1412 | + ) |
| 1413 | + else: |
| 1414 | + pass # pragma: no cover |
| 1415 | + |
1297 | 1416 | class TestExamples(TestCase):
|
1298 | 1417 |
|
1299 | 1418 | def test_readme(self):
|
@@ -1334,7 +1453,6 @@ class AutoEnum(IntegerChoices):
|
1334 | 1453 | THREE = auto(), 'Three'
|
1335 | 1454 | """
|
1336 | 1455 |
|
1337 |
| - |
1338 | 1456 | class ResetModelsMixin:
|
1339 | 1457 |
|
1340 | 1458 | @classmethod
|
@@ -2055,7 +2173,7 @@ def test_migration_test_marker_tag():
|
2055 | 2173 | assert MIGRATION_TEST_MARKER in TestAddIntEnumMigration.tags
|
2056 | 2174 |
|
2057 | 2175 |
|
2058 |
| - class TestOptionalDependencies(TestChoices): |
| 2176 | + class TestChoicesEnumProp(TestChoices): |
2059 | 2177 |
|
2060 | 2178 | MODEL_CLASS = EnumTester
|
2061 | 2179 |
|
@@ -2281,5 +2399,5 @@ def test_single_field_perf_diff(self):
|
2281 | 2399 | # tends to be about 1.8x slower
|
2282 | 2400 | self.assertTrue((enum_time / choice_time) < 2.5)
|
2283 | 2401 |
|
2284 |
| -except (ImportError, ModuleNotFoundError): # pragma: no cover |
2285 |
| - pass |
| 2402 | +else: |
| 2403 | + pass # pragma: no cover |
0 commit comments