Skip to content

Commit 5c2f29c

Browse files
committed
add robust filter tests
1 parent 872d531 commit 5c2f29c

File tree

9 files changed

+154
-36
lines changed

9 files changed

+154
-36
lines changed

django_enum/tests/djenum/migrations/0001_initial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# Generated by Django 3.2.14 on 2022-07-25 18:39
1+
# Generated by Django 3.2.14 on 2022-07-26 03:42
22

3-
import django_enum.fields
43
from django.db import migrations, models
4+
import django_enum.fields
55

66

77
class Migration(migrations.Migration):

django_enum/tests/djenum/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ class EnumTester(models.Model):
6666
)
6767
################################################
6868

69-
dj_int_enum = EnumField(DJIntEnum, default=DJIntEnum.ONE.value)
70-
dj_text_enum = EnumField(DJTextEnum, default=DJTextEnum.A.value)
69+
dj_int_enum = EnumField(DJIntEnum, default=DJIntEnum.ONE)
70+
dj_text_enum = EnumField(DJTextEnum, default=DJTextEnum.A)
7171

7272
# Non-strict
7373
non_strict_int = EnumField(

django_enum/tests/djenum/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636
name='enum-filter'
3737
),
3838
path(
39-
'enum/filter_explicit/',
39+
'enum/filter/symmetric/',
4040
EnumTesterFilterViewSet.as_view(),
41-
name='enum-filter_explicit'
41+
name='enum-filter-symmetric'
4242
)
4343
])
4444
except ImportError: # pragma: no cover

django_enum/tests/djenum/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class EnumTesterDeleteView(URLMixin, DeleteView):
6363
model = EnumTester
6464
template_name = 'enumtester_form.html'
6565

66-
def get_success_url(self):
66+
def get_success_url(self): # pragma: no cover
6767
return reverse(f'{self.NAMESPACE}:enum-list')
6868

6969

django_enum/tests/enum_prop/migrations/0001_initial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# Generated by Django 3.2.14 on 2022-07-25 18:39
1+
# Generated by Django 3.2.14 on 2022-07-26 03:42
22

3-
import django_enum.fields
43
from django.db import migrations, models
4+
import django_enum.fields
55

66

77
class Migration(migrations.Migration):

django_enum/tests/enum_prop/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ class EnumTester(models.Model):
6868
)
6969
################################################
7070

71-
dj_int_enum = EnumField(DJIntEnum, default=DJIntEnum.ONE.value)
72-
dj_text_enum = EnumField(DJTextEnum, default=DJTextEnum.A.value)
71+
dj_int_enum = EnumField(DJIntEnum, default=DJIntEnum.ONE)
72+
dj_text_enum = EnumField(DJTextEnum, default=DJTextEnum.A)
7373

7474
# Non-strict
7575
non_strict_int = EnumField(

django_enum/tests/enum_prop/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
name='enum-filter'
3838
),
3939
path(
40-
'enum/filter_explicit/',
40+
'enum/filter/symmetric/',
4141
EnumTesterFilterViewSet.as_view(),
42-
name='enum-filter_explicit'
42+
name='enum-filter-symmetric'
4343
)
4444
])
4545
except (ImportError, ModuleNotFoundError): # pragma: no cover

django_enum/tests/tests.py

Lines changed: 138 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
from django_enum.tests.djenum.models import EnumTester
2626
from django_test_migrations.constants import MIGRATION_TEST_MARKER
2727
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
2841

2942

3043
def set_models(version):
@@ -368,8 +381,9 @@ def test_django_filters_missing(self):
368381
with patch.dict(sys.modules, {'django_filters': None}):
369382
reload(sys.modules['django_enum.filters'])
370383
self.do_django_filters_missing()
384+
reload(sys.modules['django_enum.filters'])
371385
else:
372-
self.do_django_filters_missing()
386+
self.do_django_filters_missing() # pragma: no cover
373387

374388
def do_enum_properties_missing(self):
375389
import enum
@@ -427,8 +441,9 @@ def test_enum_properties_missing(self):
427441
from django_enum import choices
428442
reload(sys.modules['django_enum.choices'])
429443
self.do_enum_properties_missing()
444+
reload(sys.modules['django_enum.choices'])
430445
else:
431-
self.do_enum_properties_missing()
446+
self.do_enum_properties_missing() # pragma: no cover
432447

433448

434449
class TestFieldTypeResolution(EnumTypeMixin, TestCase):
@@ -535,20 +550,11 @@ class TestRequests(EnumTypeMixin, TestCase):
535550
NAMESPACE = 'django_enum_tests_djenum'
536551

537552
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 = {}
550554

551555
def setUp(self):
556+
self.values = {val: {} for val in self.compared_attributes}
557+
self.objects = []
552558
self.MODEL_CLASS.objects.all().delete()
553559
self.objects.append(
554560
self.MODEL_CLASS.objects.create()
@@ -597,6 +603,9 @@ def setUp(self):
597603
self.values[attr].setdefault(getattr(obj, attr), [])
598604
self.values[attr][getattr(obj, attr)].append(obj.pk)
599605

606+
def tearDown(self):
607+
self.MODEL_CLASS.objects.all().delete()
608+
600609
@property
601610
def post_params(self):
602611
return {
@@ -674,6 +683,8 @@ def test_post(self):
674683

675684
@staticmethod
676685
def get_enum_val(enum, value):
686+
if value is None or value == '':
687+
return None
677688
if int in enum.__mro__:
678689
return enum(int(value))
679690
if float in enum.__mro__:
@@ -765,6 +776,91 @@ def test_update_form(self):
765776
if not field.null and not field.blank:
766777
self.assertFalse(null_opt, "An unexpected null option is present") # pragma: no cover
767778

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+
768864

769865
class TestBulkOperations(EnumTypeMixin, TestCase):
770866
"""Tests bulk insertions and updates"""
@@ -838,10 +934,9 @@ def test_bulk_update(self):
838934
)
839935

840936

841-
try:
937+
if ENUM_PROPERTIES_INSTALLED:
842938

843939
from enum_properties import s
844-
from django_enum import filters
845940
from django_enum.tests.enum_prop.enums import (
846941
BigIntEnum,
847942
BigPosIntEnum,
@@ -1294,6 +1389,30 @@ def post_params(self):
12941389
'non_strict_int': self.SmallPosIntEnum.VAL1
12951390
}
12961391

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+
12971416
class TestExamples(TestCase):
12981417

12991418
def test_readme(self):
@@ -1334,7 +1453,6 @@ class AutoEnum(IntegerChoices):
13341453
THREE = auto(), 'Three'
13351454
"""
13361455

1337-
13381456
class ResetModelsMixin:
13391457

13401458
@classmethod
@@ -2055,7 +2173,7 @@ def test_migration_test_marker_tag():
20552173
assert MIGRATION_TEST_MARKER in TestAddIntEnumMigration.tags
20562174

20572175

2058-
class TestOptionalDependencies(TestChoices):
2176+
class TestChoicesEnumProp(TestChoices):
20592177

20602178
MODEL_CLASS = EnumTester
20612179

@@ -2281,5 +2399,5 @@ def test_single_field_perf_diff(self):
22812399
# tends to be about 1.8x slower
22822400
self.assertTrue((enum_time / choice_time) < 2.5)
22832401

2284-
except (ImportError, ModuleNotFoundError): # pragma: no cover
2285-
pass
2402+
else:
2403+
pass # pragma: no cover

django_enum/tests/tmpls/templates/enumtester_list.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{% block content %}
44
<h1>EnumTester Objects</h1>
55
{% for object in object_list %}
6-
<div id="{{object.pk}}" class="enum">
6+
<div id="{{object.pk}}" class="enum" data-obj-id="{{ object.pk }}">
77
<h2>{{ object.pk }}</h2>
88
<p class="small_pos_int">small_pos_int: <b><span class="value">{{ object.small_pos_int.value }}</span></b> <b><span class="label">{{ object.small_pos_int.label }}</span></b></p>
99
<p class="small_int">small_int: <b><span class="value">{{ object.small_int.value }}</span></b> <b><span class="label">{{ object.small_int.label }}</span></b></p>
@@ -18,8 +18,8 @@ <h2>{{ object.pk }}</h2>
1818
<p class="non_strict_int">non_strict_int: <b><span class="value">{{ object.non_strict_int.value}}</span></b> <b><span class="label">{{ object.non_strict_int.label }}</span></b></p>
1919

2020
<p>
21-
<a href="{% url 'django_enum_tests_enum_prop:enum-update' pk=object.pk %}">Edit</a>
22-
<a href="{% url 'django_enum_tests_enum_prop:enum-delete' pk=object.pk %}">Delete</a>
21+
<a href="{{update_path}}">Edit</a>
22+
<a href="{{delete_path}}">Delete</a>
2323
</p>
2424
</div>
2525
{% endfor %}

0 commit comments

Comments
 (0)