Skip to content

Commit 2950b2e

Browse files
committed
Add unit tests for all_fields_optional_model decorator
1 parent 5077533 commit 2950b2e

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
from typing import Optional
2+
3+
import pytest
4+
from pydantic import Field
5+
6+
from mavedb.view_models.base.base import BaseModel
7+
from mavedb.view_models.utils import all_fields_optional_model
8+
9+
10+
# Test models
11+
class DummyModel(BaseModel):
12+
required_string: str = Field(..., description="Required string field")
13+
required_int: int
14+
optional_with_default: str = "default_value"
15+
optional_nullable: Optional[str] = None
16+
field_with_constraints: int = Field(..., ge=0, le=100)
17+
optional_boolean: bool = True
18+
19+
20+
def test_all_fields_optional_model_basic():
21+
"""Test that all fields become optional in the decorated model."""
22+
23+
@all_fields_optional_model()
24+
class OptionalDummyModel(DummyModel):
25+
pass
26+
27+
# Should be able to create instance with no arguments
28+
instance = OptionalDummyModel()
29+
30+
assert instance.required_string is None
31+
assert instance.required_int is None
32+
assert instance.optional_with_default is None # Default overridden to None
33+
assert instance.optional_nullable is None
34+
assert instance.field_with_constraints is None
35+
assert instance.optional_boolean is None
36+
37+
38+
def test_all_fields_optional_model_partial_assignment():
39+
"""Test that partial field assignment works correctly."""
40+
41+
@all_fields_optional_model()
42+
class OptionalDummyModel(DummyModel):
43+
pass
44+
45+
instance = OptionalDummyModel(required_string="test", required_int=42)
46+
47+
assert instance.required_string == "test"
48+
assert instance.required_int == 42
49+
assert instance.optional_with_default is None
50+
assert instance.optional_nullable is None
51+
assert instance.field_with_constraints is None
52+
assert instance.optional_boolean is None
53+
54+
55+
def test_all_fields_optional_model_all_fields_provided():
56+
"""Test that all fields can still be provided."""
57+
58+
@all_fields_optional_model()
59+
class OptionalDummyModel(DummyModel):
60+
pass
61+
62+
instance = OptionalDummyModel(
63+
required_string="test",
64+
required_int=42,
65+
optional_with_default="custom_value",
66+
optional_nullable="not_null",
67+
field_with_constraints=50,
68+
optional_boolean=False,
69+
)
70+
71+
assert instance.required_string == "test"
72+
assert instance.required_int == 42
73+
assert instance.optional_with_default == "custom_value"
74+
assert instance.optional_nullable == "not_null"
75+
assert instance.field_with_constraints == 50
76+
assert instance.optional_boolean is False
77+
78+
79+
def test_all_fields_optional_model_field_info_preserved():
80+
"""Test that field constraints and metadata are preserved."""
81+
82+
@all_fields_optional_model()
83+
class OptionalDummyModel(DummyModel):
84+
pass
85+
86+
# Check that field info is preserved
87+
required_str_field = OptionalDummyModel.model_fields["required_string"]
88+
assert required_str_field.description == "Required string field"
89+
90+
# Field should now be optional
91+
assert required_str_field.default is None
92+
93+
94+
def test_all_fields_optional_model_validation_still_works():
95+
"""Test that field validation still works when values are provided."""
96+
97+
@all_fields_optional_model()
98+
class OptionalDummyModel(DummyModel):
99+
pass
100+
101+
# Should still validate constraints when value is provided
102+
with pytest.raises(ValueError):
103+
OptionalDummyModel(field_with_constraints=150) # Exceeds max value of 100
104+
105+
106+
def test_all_fields_optional_model_type_annotations():
107+
"""Test that type annotations are correctly made optional."""
108+
109+
@all_fields_optional_model()
110+
class OptionalDummyModel(DummyModel):
111+
pass
112+
113+
# Get field annotations
114+
fields = OptionalDummyModel.model_fields
115+
116+
# Check that previously required fields are now Optional
117+
assert fields["required_string"].annotation == Optional[str]
118+
assert fields["required_int"].annotation == Optional[int]
119+
120+
# Check that already optional fields remain optional
121+
assert fields["optional_nullable"].annotation == Optional[str]
122+
assert fields["optional_boolean"].annotation == Optional[bool]
123+
124+
125+
def test_all_fields_optional_model_serialization():
126+
"""Test that the optional model serializes correctly."""
127+
128+
@all_fields_optional_model()
129+
class OptionalDummyModel(DummyModel):
130+
pass
131+
132+
instance = OptionalDummyModel(required_string="test")
133+
serialized = instance.model_dump()
134+
135+
expected = {
136+
"required_string": "test",
137+
"required_int": None,
138+
"optional_with_default": None,
139+
"optional_nullable": None,
140+
"field_with_constraints": None,
141+
"optional_boolean": None,
142+
}
143+
144+
assert serialized == expected
145+
146+
147+
def test_all_fields_optional_model_exclude_unset():
148+
"""Test that model_dump with exclude_unset works correctly."""
149+
150+
@all_fields_optional_model()
151+
class OptionalDummyModel(DummyModel):
152+
pass
153+
154+
instance = OptionalDummyModel(required_string="test")
155+
serialized = instance.model_dump(exclude_unset=True)
156+
157+
# Should only include explicitly set fields
158+
assert serialized == {"required_string": "test"}
159+
160+
161+
def test_all_fields_optional_model_inheritance():
162+
"""Test that inheritance still works with the decorated model."""
163+
164+
@all_fields_optional_model()
165+
class OptionalDummyModel(DummyModel):
166+
pass
167+
168+
# Should inherit from DummyModel
169+
assert issubclass(OptionalDummyModel, DummyModel)
170+
assert issubclass(OptionalDummyModel, BaseModel)
171+
172+
173+
def test_all_fields_optional_model_field_defaults_overridden():
174+
"""Test that original defaults are overridden to None."""
175+
176+
@all_fields_optional_model()
177+
class OptionalDummyModel(DummyModel):
178+
pass
179+
180+
instance = OptionalDummyModel()
181+
182+
# Originally had default True, should now be None
183+
assert instance.optional_boolean is None
184+
185+
# Originally had default None, should still be None
186+
assert instance.optional_nullable is None

0 commit comments

Comments
 (0)