Skip to content

Commit 820673e

Browse files
committed
add tests
1 parent a104694 commit 820673e

File tree

5 files changed

+496
-14
lines changed

5 files changed

+496
-14
lines changed

pyproject.toml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ dependencies = [
3737
]
3838

3939
[project.optional-dependencies]
40-
test = ["pytest>=7"]
4140
docs = [ "sphinx>=7"]
4241

4342
[project.urls]
@@ -49,18 +48,6 @@ Tracker = "https://github.com/mongodb-labs/django-mongodb/issues"
4948
[tool.setuptools.dynamic]
5049
version = {attr = "django_mongodb.__version__"}
5150

52-
[tool.pytest.ini_options]
53-
minversion = "7"
54-
addopts = ["-ra", "--strict-config", "--strict-markers", "--junitxml=xunit-results/TEST-results.xml"]
55-
testpaths = ["test"]
56-
log_cli_level = "INFO"
57-
norecursedirs = ["test/*"]
58-
faulthandler_timeout = 1500
59-
xfail_strict = true
60-
filterwarnings = [
61-
"error"
62-
]
63-
6451
[tool.mypy]
6552
strict = true
6653
show_error_codes = true
@@ -87,7 +74,6 @@ select = [
8774
"PGH", # pygrep-hooks
8875
"PIE", # flake8-pie
8976
"PL", # pylint
90-
"PT", # flake8-pytest-style
9177
"PTH", # flake8-use-pathlib
9278
"RET", # flake8-return
9379
"RUF", # Ruff-specific

test/mongo_fields/__init__.py

Whitespace-only changes.

test/mongo_fields/models.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from django.db import models
2+
3+
from django_mongodb.fields import EmbeddedModelField, ListField
4+
5+
6+
def count_calls(func):
7+
def wrapper(*args, **kwargs):
8+
wrapper.calls += 1
9+
return func(*args, **kwargs)
10+
11+
wrapper.calls = 0
12+
13+
return wrapper
14+
15+
16+
class ReferenceList(models.Model):
17+
keys = ListField(models.ForeignKey("Model", models.CASCADE))
18+
19+
20+
class Model(models.Model):
21+
pass
22+
23+
24+
class Target(models.Model):
25+
index = models.IntegerField()
26+
27+
28+
class DecimalModel(models.Model):
29+
decimal = models.DecimalField(max_digits=9, decimal_places=2)
30+
31+
32+
class DecimalKey(models.Model):
33+
decimal = models.DecimalField(max_digits=9, decimal_places=2, primary_key=True)
34+
35+
36+
class DecimalParent(models.Model):
37+
child = models.ForeignKey(DecimalKey, models.CASCADE)
38+
39+
40+
class DecimalsList(models.Model):
41+
decimals = ListField(models.ForeignKey(DecimalKey, models.CASCADE))
42+
43+
44+
class OrderedListModel(models.Model):
45+
ordered_ints = ListField(
46+
models.IntegerField(max_length=500),
47+
default=[],
48+
ordering=count_calls(lambda x: x),
49+
null=True,
50+
)
51+
ordered_nullable = ListField(ordering=lambda x: x, null=True)
52+
53+
54+
class ListModel(models.Model):
55+
integer = models.IntegerField(primary_key=True)
56+
floating_point = models.FloatField()
57+
names = ListField(models.CharField)
58+
names_with_default = ListField(models.CharField(max_length=500), default=[])
59+
names_nullable = ListField(models.CharField(max_length=500), null=True)
60+
61+
62+
class EmbeddedModelFieldModel(models.Model):
63+
simple = EmbeddedModelField("EmbeddedModel", null=True)
64+
simple_untyped = EmbeddedModelField(null=True)
65+
decimal_parent = EmbeddedModelField(DecimalParent, null=True)
66+
# typed_list = ListField(EmbeddedModelField('SetModel'))
67+
typed_list2 = ListField(EmbeddedModelField("EmbeddedModel"))
68+
untyped_list = ListField(EmbeddedModelField())
69+
# untyped_dict = DictField(EmbeddedModelField())
70+
ordered_list = ListField(EmbeddedModelField(), ordering=lambda obj: obj.index)
71+
72+
73+
class EmbeddedModel(models.Model):
74+
some_relation = models.ForeignKey(Target, models.CASCADE, null=True)
75+
someint = models.IntegerField(db_column="custom")
76+
auto_now = models.DateTimeField(auto_now=True)
77+
auto_now_add = models.DateTimeField(auto_now_add=True)
78+
79+
80+
class Child(models.Model):
81+
pass
82+
83+
84+
class Parent(models.Model):
85+
id = models.IntegerField(primary_key=True)
86+
integer_list = ListField(models.IntegerField)
87+
88+
# integer_dict = DictField(models.IntegerField)
89+
embedded_list = ListField(EmbeddedModelField(Child))
90+
91+
92+
# embedded_dict = DictField(EmbeddedModelField(Child))
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import time
2+
from decimal import Decimal
3+
4+
from django.db import models
5+
from django.test import TestCase
6+
7+
from .models import (
8+
Child,
9+
DecimalKey,
10+
DecimalParent,
11+
EmbeddedModel,
12+
EmbeddedModelFieldModel,
13+
OrderedListModel,
14+
Parent,
15+
Target,
16+
)
17+
18+
19+
class EmbeddedModelFieldTests(TestCase):
20+
def assertEqualDatetime(self, d1, d2):
21+
"""Compares d1 and d2, ignoring microseconds."""
22+
self.assertEqual(d1.replace(microsecond=0), d2.replace(microsecond=0))
23+
24+
def assertNotEqualDatetime(self, d1, d2):
25+
self.assertNotEqual(d1.replace(microsecond=0), d2.replace(microsecond=0))
26+
27+
def _simple_instance(self):
28+
EmbeddedModelFieldModel.objects.create(simple=EmbeddedModel(someint="5"))
29+
return EmbeddedModelFieldModel.objects.get()
30+
31+
def test_simple(self):
32+
instance = self._simple_instance()
33+
self.assertIsInstance(instance.simple, EmbeddedModel)
34+
# Make sure get_prep_value is called.
35+
self.assertEqual(instance.simple.someint, 5)
36+
# Primary keys should not be populated...
37+
self.assertEqual(instance.simple.id, None)
38+
# ... unless set explicitly.
39+
instance.simple.id = instance.id
40+
instance.save()
41+
instance = EmbeddedModelFieldModel.objects.get()
42+
self.assertEqual(instance.simple.id, instance.id)
43+
44+
def _test_pre_save(self, instance, get_field):
45+
# Make sure field.pre_save is called for embedded objects.
46+
47+
instance.save()
48+
auto_now = get_field(instance).auto_now
49+
auto_now_add = get_field(instance).auto_now_add
50+
self.assertNotEqual(auto_now, None)
51+
self.assertNotEqual(auto_now_add, None)
52+
53+
time.sleep(1) # FIXME
54+
instance.save()
55+
self.assertNotEqualDatetime(get_field(instance).auto_now, get_field(instance).auto_now_add)
56+
57+
instance = EmbeddedModelFieldModel.objects.get()
58+
instance.save()
59+
# auto_now_add shouldn't have changed now, but auto_now should.
60+
self.assertEqualDatetime(get_field(instance).auto_now_add, auto_now_add)
61+
self.assertGreater(get_field(instance).auto_now, auto_now)
62+
63+
def test_pre_save(self):
64+
obj = EmbeddedModelFieldModel(simple=EmbeddedModel())
65+
self._test_pre_save(obj, lambda instance: instance.simple)
66+
67+
def test_pre_save_untyped(self):
68+
obj = EmbeddedModelFieldModel(simple_untyped=EmbeddedModel())
69+
self._test_pre_save(obj, lambda instance: instance.simple_untyped)
70+
71+
def test_pre_save_in_list(self):
72+
obj = EmbeddedModelFieldModel(untyped_list=[EmbeddedModel()])
73+
self._test_pre_save(obj, lambda instance: instance.untyped_list[0])
74+
75+
def _test_pre_save_in_dict(self):
76+
obj = EmbeddedModelFieldModel(untyped_dict={"a": EmbeddedModel()})
77+
self._test_pre_save(obj, lambda instance: instance.untyped_dict["a"])
78+
79+
def test_pre_save_list(self):
80+
# Also make sure auto_now{,add} works for embedded object *lists*.
81+
EmbeddedModelFieldModel.objects.create(typed_list2=[EmbeddedModel()])
82+
instance = EmbeddedModelFieldModel.objects.get()
83+
84+
auto_now = instance.typed_list2[0].auto_now
85+
auto_now_add = instance.typed_list2[0].auto_now_add
86+
self.assertNotEqual(auto_now, None)
87+
self.assertNotEqual(auto_now_add, None)
88+
89+
instance.typed_list2.append(EmbeddedModel())
90+
instance.save()
91+
instance = EmbeddedModelFieldModel.objects.get()
92+
93+
self.assertEqualDatetime(instance.typed_list2[0].auto_now_add, auto_now_add)
94+
self.assertGreater(instance.typed_list2[0].auto_now, auto_now)
95+
self.assertNotEqual(instance.typed_list2[1].auto_now, None)
96+
self.assertNotEqual(instance.typed_list2[1].auto_now_add, None)
97+
98+
def test_error_messages(self):
99+
for kwargs, expected in (
100+
({"simple": 42}, EmbeddedModel),
101+
({"simple_untyped": 42}, models.Model),
102+
# ({"typed_list": [EmbeddedModel()]},), # SetModel),
103+
):
104+
self.assertRaisesMessage(
105+
TypeError,
106+
"Expected instance of type %r" % expected,
107+
EmbeddedModelFieldModel(**kwargs).save,
108+
)
109+
110+
def test_typed_listfield(self):
111+
EmbeddedModelFieldModel.objects.create(
112+
# typed_list=[SetModel(setfield=range(3)), SetModel(setfield=range(9))],
113+
ordered_list=[Target(index=i) for i in range(5, 0, -1)],
114+
)
115+
obj = EmbeddedModelFieldModel.objects.get()
116+
# self.assertIn(5, obj.typed_list[1].setfield)
117+
self.assertEqual([target.index for target in obj.ordered_list], list(range(1, 6)))
118+
119+
def test_untyped_listfield(self):
120+
EmbeddedModelFieldModel.objects.create(
121+
untyped_list=[
122+
EmbeddedModel(someint=7),
123+
OrderedListModel(ordered_ints=list(range(5, 0, -1))),
124+
# SetModel(setfield=[1, 2, 2, 3]),
125+
]
126+
)
127+
instances = EmbeddedModelFieldModel.objects.get().untyped_list
128+
for instance, cls in zip(
129+
instances,
130+
[EmbeddedModel, OrderedListModel], # SetModel]
131+
strict=True,
132+
):
133+
self.assertIsInstance(instance, cls)
134+
self.assertNotEqual(instances[0].auto_now, None)
135+
self.assertEqual(instances[1].ordered_ints, list(range(1, 6)))
136+
137+
def _test_untyped_dict(self):
138+
EmbeddedModelFieldModel.objects.create(
139+
untyped_dict={
140+
# "a": SetModel(setfield=range(3)),
141+
# "b": DictModel(dictfield={"a": 1, "b": 2}),
142+
# "c": DictModel(dictfield={}, auto_now={"y": 1}),
143+
}
144+
)
145+
# data = EmbeddedModelFieldModel.objects.get().untyped_dict
146+
# self.assertIsInstance(data["a"], SetModel)
147+
# self.assertNotEqual(data["c"].auto_now["y"], None)
148+
149+
def test_foreign_key_in_embedded_object(self):
150+
simple = EmbeddedModel(some_relation=Target.objects.create(index=1))
151+
obj = EmbeddedModelFieldModel.objects.create(simple=simple)
152+
simple = EmbeddedModelFieldModel.objects.get().simple
153+
self.assertNotIn("some_relation", simple.__dict__)
154+
self.assertIsInstance(simple.__dict__["some_relation_id"], type(obj.id))
155+
self.assertIsInstance(simple.some_relation, Target)
156+
157+
def test_embedded_field_with_foreign_conversion(self):
158+
decimal = DecimalKey.objects.create(decimal=Decimal("1.5"))
159+
decimal_parent = DecimalParent.objects.create(child=decimal)
160+
EmbeddedModelFieldModel.objects.create(decimal_parent=decimal_parent)
161+
162+
def test_update(self):
163+
"""
164+
Test that update can be used on an a subset of objects
165+
containing collections of embedded instances; see issue #13.
166+
Also ensure that updated values are coerced according to
167+
collection field.
168+
"""
169+
child1 = Child.objects.create()
170+
child2 = Child.objects.create()
171+
parent = Parent.objects.create(
172+
pk=1,
173+
integer_list=[1],
174+
# integer_dict={"a": 2},
175+
embedded_list=[child1],
176+
# embedded_dict={"a": child2},
177+
)
178+
Parent.objects.filter(pk=1).update(
179+
integer_list=["3"],
180+
# integer_dict={"b": "3"},
181+
embedded_list=[child2],
182+
# embedded_dict={"b": child1},
183+
)
184+
parent = Parent.objects.get()
185+
self.assertEqual(parent.integer_list, [3])
186+
# self.assertEqual(parent.integer_dict, {"b": 3})
187+
self.assertEqual(parent.embedded_list, [child2])
188+
# self.assertEqual(parent.embedded_dict, {"b": child1})

0 commit comments

Comments
 (0)