Skip to content

Commit 63d426b

Browse files
committed
Clean up new tests
1 parent 26f3044 commit 63d426b

File tree

1 file changed

+30
-263
lines changed

1 file changed

+30
-263
lines changed

tests/test_models.py

Lines changed: 30 additions & 263 deletions
Original file line numberDiff line numberDiff line change
@@ -149,167 +149,19 @@ def view_multi_model_union(request, data: MultiModelUnion):
149149
return data.model_dump()
150150

151151

152-
# Create models that will cause field name collisions during flattening
153-
class ModelA(BaseModel):
154-
name: str
155-
156-
157-
class ModelB(BaseModel):
158-
name: str # Same field name as ModelA
159-
160-
161-
# Test case to trigger model field collision error
162-
try:
163-
router_collision = Router()
164-
165-
@router_collision.post("/test-model-collision")
166-
def view_model_collision(
167-
model_a: ModelA,
168-
model_b: ModelB, # Both have 'name' field - should cause collision during flattening
169-
):
170-
return {"result": "collision"}
171-
172-
except Exception:
173-
# Expected to fail during router creation if collision detection works
174-
pass
175-
176-
177-
# Test to trigger ConfigError - duplicate name collision in union with Query(None)
178-
def test_union_query_name_collision():
179-
"""Test that duplicate union parameter names with Query(None) raise ConfigError."""
180-
181-
# Create a test that should cause a name collision during flattening
182-
try:
183-
api = NinjaAPI()
184-
router_test = Router()
185-
186-
# This should trigger the ConfigError when both parameters
187-
# have the same alias and are processed as Union[Model, None] = Query(None)
188-
@router_test.post("/collision-test")
189-
def collision_endpoint(
190-
# Both parameters have same alias "person" - should cause collision
191-
person1: Union[PersonSchema, None] = Query(None, alias="person"),
192-
person2: Union[PersonSchema, None] = Query(
193-
None, alias="person"
194-
), # Same alias!
195-
):
196-
return {"result": "should not reach here"}
197-
198-
api.add_router("/test", router_test)
199-
200-
# This should fail during router creation due to name collision
201-
assert False, "Expected ConfigError for duplicate name collision" # noqa: B011
202-
203-
except ConfigError as e:
204-
# This is the expected behavior
205-
assert "Duplicated name" in str(e)
206-
assert "person" in str(e)
207-
208-
209152
@router.post("/test-union-with-none")
210153
def view_union_with_none(request, optional: Union[str, None] = Query(None)):
211154
"""Test Union[str, None]"""
212155
return {"optional": optional}
213156

214157

215-
# Test union field with multiple pydantic models
216-
class UnionFieldTestModel(BaseModel):
217-
choice: Union[SomeModel, OtherModel]
218-
219-
220-
@router.post("/test-union-field-model")
221-
def view_union_field_model(request, model: UnionFieldTestModel):
222-
"""Test union field with multiple pydantic models."""
223-
return model.model_dump()
224-
225-
226-
# Test for collection detection with unions
227158
class CollectionUnionModel(BaseModel):
228159
items: List[str]
229160
nested: Union[SomeModel, None] = None
230161

231162

232163
@router.post("/test-collection-union")
233164
def view_collection_union(request, data: CollectionUnionModel):
234-
"""Test collection fields with union to trigger detect_collection_fields."""
235-
return data.model_dump()
236-
237-
238-
# Additional model for testing complex nested union fields
239-
class ComplexUnionField(BaseModel):
240-
model_choice: Union[SomeModel, OtherModel] # No None, no default
241-
name: str = "test"
242-
243-
244-
@router.post("/test-complex-union-field")
245-
def view_complex_union_field(request, complex_data: ComplexUnionField):
246-
"""Test complex union field processing."""
247-
return complex_data.model_dump()
248-
249-
250-
# Model with union field that has NO default
251-
class NoDefaultUnionModel(BaseModel):
252-
# This union field has NO default value and contains pydantic models
253-
required_union: Union[SomeModel, OtherModel]
254-
255-
256-
@router.post("/test-no-default-union")
257-
def view_no_default_union(request, no_default: NoDefaultUnionModel):
258-
"""Test union field with no default."""
259-
return no_default.model_dump()
260-
261-
262-
# Complex nested model to trigger detect_collection_fields union logic
263-
class NestedWithCollections(BaseModel):
264-
items: List[str] # Collection field
265-
266-
267-
class DeepModel(BaseModel):
268-
# Nested model that contains a union field
269-
nested: Union[NestedWithCollections, SomeModel]
270-
simple_field: str = "test"
271-
272-
273-
class VeryDeepModel(BaseModel):
274-
# Multiple levels of nesting to create longer flatten paths
275-
deep: DeepModel
276-
extra_items: List[int] = []
277-
278-
279-
@router.post("/test-deep-nested-union")
280-
def view_deep_nested_union(request, deep_data: VeryDeepModel):
281-
"""Test deeply nested structure with unions to trigger detect_collection_fields logic."""
282-
return deep_data.model_dump()
283-
284-
285-
# trigger _model_flatten_map with Union containing None
286-
@router.post("/test-flatten-union-with-none")
287-
def view_flatten_union_with_none(request, data: Union[SomeModel, None]):
288-
"""Test direct Union[Model, None]"""
289-
return data.model_dump() if data else {"result": "none"}
290-
291-
292-
# nested union with None
293-
class ModelWithUnionField(BaseModel):
294-
union_field: Union[SomeModel, None] = (
295-
None # This should trigger _model_flatten_map with Union
296-
)
297-
298-
299-
@router.post("/test-nested-union-with-none")
300-
def view_nested_union_with_none(request, data: ModelWithUnionField):
301-
"""Test nested Union[Model, None]"""
302-
return data.model_dump()
303-
304-
305-
# Union parameter that gets flattened
306-
class OuterModel(BaseModel):
307-
inner: Union[SomeModel, OtherModel, None] # Union with None at top level
308-
309-
310-
@router.post("/test-direct-union-flattening")
311-
def view_direct_union_flattening(request, data: OuterModel):
312-
"""Test direct union flattening."""
313165
return data.model_dump()
314166

315167

@@ -403,17 +255,6 @@ def view_direct_union_flattening(request, data: OuterModel):
403255
dict(json=None),
404256
{"optional": "test"},
405257
),
406-
# Test union field model
407-
(
408-
"/test-union-field-model",
409-
dict(json={"choice": {"i": 1, "s": "test", "f": 1.5}}),
410-
{"choice": {"i": 1, "s": "test", "f": 1.5, "n": None}},
411-
),
412-
(
413-
"/test-union-field-model",
414-
dict(json={"choice": {"x": 10, "y": 20}}),
415-
{"choice": {"x": 10, "y": 20}},
416-
),
417258
# Test collection union model
418259
(
419260
"/test-collection-union",
@@ -425,110 +266,6 @@ def view_direct_union_flattening(request, data: OuterModel):
425266
dict(json={"items": ["x"], "nested": {"i": 5, "s": "test", "f": 2.0}}),
426267
{"items": ["x"], "nested": {"i": 5, "s": "test", "f": 2.0, "n": None}},
427268
),
428-
# Test complex union field
429-
(
430-
"/test-complex-union-field",
431-
dict(
432-
json={
433-
"model_choice": {"i": 1, "s": "test", "f": 1.5},
434-
"name": "example",
435-
}
436-
),
437-
{
438-
"model_choice": {"i": 1, "s": "test", "f": 1.5, "n": None},
439-
"name": "example",
440-
},
441-
),
442-
(
443-
"/test-complex-union-field",
444-
dict(json={"model_choice": {"x": 10, "y": 20}, "name": "example"}),
445-
{"model_choice": {"x": 10, "y": 20}, "name": "example"},
446-
),
447-
# Test no default union
448-
(
449-
"/test-no-default-union",
450-
dict(json={"required_union": {"i": 2, "s": "required", "f": 2.5}}),
451-
{"required_union": {"i": 2, "s": "required", "f": 2.5, "n": None}},
452-
),
453-
(
454-
"/test-no-default-union",
455-
dict(json={"required_union": {"x": 5, "y": 10}}),
456-
{"required_union": {"x": 5, "y": 10}},
457-
),
458-
# Test deeply nested union
459-
(
460-
"/test-deep-nested-union",
461-
dict(
462-
json={
463-
"deep": {
464-
"nested": {"items": ["a", "b"]},
465-
"simple_field": "deep_test",
466-
},
467-
"extra_items": [1, 2, 3],
468-
}
469-
),
470-
{
471-
"deep": {"nested": {"items": ["a", "b"]}, "simple_field": "deep_test"},
472-
"extra_items": [1, 2, 3],
473-
},
474-
),
475-
(
476-
"/test-deep-nested-union",
477-
dict(
478-
json={
479-
"deep": {
480-
"nested": {"i": 1, "s": "nested", "f": 1.0},
481-
"simple_field": "deep_test2",
482-
},
483-
"extra_items": [],
484-
}
485-
),
486-
{
487-
"deep": {
488-
"nested": {"i": 1, "s": "nested", "f": 1.0, "n": None},
489-
"simple_field": "deep_test2",
490-
},
491-
"extra_items": [],
492-
},
493-
),
494-
# Test to trigger _model_flatten_map with Union containing None
495-
(
496-
"/test-flatten-union-with-none",
497-
dict(json={"i": 1, "s": "test", "f": 1.5}),
498-
{"i": 1, "s": "test", "f": 1.5, "n": None},
499-
),
500-
(
501-
"/test-flatten-union-with-none",
502-
dict(json=None),
503-
{"result": "none"},
504-
),
505-
# Test nested union with None
506-
(
507-
"/test-nested-union-with-none",
508-
dict(json={"union_field": {"i": 1, "s": "test", "f": 1.5}}),
509-
{"union_field": {"i": 1, "s": "test", "f": 1.5, "n": None}},
510-
),
511-
(
512-
"/test-nested-union-with-none",
513-
dict(json={"union_field": None}),
514-
{"union_field": None},
515-
),
516-
# Test direct union flattening
517-
(
518-
"/test-direct-union-flattening",
519-
dict(json={"inner": {"i": 1, "s": "test", "f": 1.5}}),
520-
{"inner": {"i": 1, "s": "test", "f": 1.5, "n": None}},
521-
),
522-
(
523-
"/test-direct-union-flattening",
524-
dict(json={"inner": {"x": 10, "y": 20}}),
525-
{"inner": {"x": 10, "y": 20}},
526-
),
527-
(
528-
"/test-direct-union-flattening",
529-
dict(json={"inner": None}),
530-
{"inner": None},
531-
),
532269
(
533270
"/test-multi-model-union",
534271
dict(json={"models": {"i": 1, "s": "test", "f": 1.5}}),
@@ -564,3 +301,33 @@ def test_invalid_body():
564301
assert response.json() == {
565302
"detail": "Cannot parse request body",
566303
}
304+
305+
306+
def test_union_query_name_collision():
307+
"""Test that duplicate union parameter names with Query(None) raise ConfigError."""
308+
309+
with pytest.raises(ConfigError, match=r"Duplicated name.*person"):
310+
api = NinjaAPI()
311+
router_test = Router()
312+
313+
@router_test.post("/collision-test")
314+
def collision_endpoint(
315+
person1: Union[PersonSchema, None] = Query(None, alias="person"),
316+
person2: Union[PersonSchema, None] = Query(None, alias="person"),
317+
):
318+
return {"result": "should not reach here"}
319+
320+
api.add_router("/test", router_test)
321+
322+
323+
def test_union_with_none_body_param():
324+
"""Test Union[Model, None] parameter"""
325+
326+
test_router = Router()
327+
328+
@test_router.post("/test-union-none-body")
329+
def test_union_none_body(request, data: Union[SomeModel, None]):
330+
return data.model_dump() if data else {"result": "none"}
331+
332+
# Verify the router was created successfully and has one registered operation
333+
assert len(test_router.path_operations) == 1

0 commit comments

Comments
 (0)