Coverage for tests / test_compat.py: 100%
73 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1from typing import Union 1defg
3from fastapi import FastAPI, UploadFile 1defg
4from fastapi._compat import ( 1defg
5 Undefined,
6 is_uploadfile_sequence_annotation,
7)
8from fastapi._compat.shared import is_bytes_sequence_annotation 1defg
9from fastapi.testclient import TestClient 1defg
10from pydantic import BaseModel, ConfigDict 1defg
11from pydantic.fields import FieldInfo 1defg
13from .utils import needs_py310 1defg
16def test_model_field_default_required(): 1defg
17 from fastapi._compat import v2 1tuv
19 # For coverage
20 field_info = FieldInfo(annotation=str) 1tuv
21 field = v2.ModelField(name="foo", field_info=field_info) 1tuv
22 assert field.default is Undefined 1tuv
25def test_complex(): 1defg
26 app = FastAPI() 1hij
28 @app.post("/") 1hij
29 def foo(foo: Union[str, list[int]]): 1hij
30 return foo 1hij
32 client = TestClient(app) 1hij
34 response = client.post("/", json="bar") 1hij
35 assert response.status_code == 200, response.text 1hij
36 assert response.json() == "bar" 1hij
38 response2 = client.post("/", json=[1, 2]) 1hij
39 assert response2.status_code == 200, response2.text 1hij
40 assert response2.json() == [1, 2] 1hij
43def test_propagates_pydantic2_model_config(): 1defg
44 app = FastAPI() 1abc
46 class Missing: 1abc
47 def __bool__(self): 1abc
48 return False 1abc
50 class EmbeddedModel(BaseModel): 1abc
51 model_config = ConfigDict(arbitrary_types_allowed=True) 1abc
52 value: Union[str, Missing] = Missing() 1abc
54 class Model(BaseModel): 1abc
55 model_config = ConfigDict( 1abc
56 arbitrary_types_allowed=True,
57 )
58 value: Union[str, Missing] = Missing() 1abc
59 embedded_model: EmbeddedModel = EmbeddedModel() 1abc
61 @app.post("/") 1abc
62 def foo(req: Model) -> dict[str, Union[str, None]]: 1abc
63 return { 1abc
64 "value": req.value or None,
65 "embedded_value": req.embedded_model.value or None,
66 }
68 client = TestClient(app) 1abc
70 response = client.post("/", json={}) 1abc
71 assert response.status_code == 200, response.text 1abc
72 assert response.json() == { 1abc
73 "value": None,
74 "embedded_value": None,
75 }
77 response2 = client.post( 1abc
78 "/", json={"value": "foo", "embedded_model": {"value": "bar"}}
79 )
80 assert response2.status_code == 200, response2.text 1abc
81 assert response2.json() == { 1abc
82 "value": "foo",
83 "embedded_value": "bar",
84 }
87def test_is_bytes_sequence_annotation_union(): 1defg
88 # For coverage
89 # TODO: in theory this would allow declaring types that could be lists of bytes
90 # to be read from files and other types, but I'm not even sure it's a good idea
91 # to support it as a first class "feature"
92 assert is_bytes_sequence_annotation(Union[list[str], list[bytes]]) 1wxy
95def test_is_uploadfile_sequence_annotation(): 1defg
96 # For coverage
97 # TODO: in theory this would allow declaring types that could be lists of UploadFile
98 # and other types, but I'm not even sure it's a good idea to support it as a first
99 # class "feature"
100 assert is_uploadfile_sequence_annotation(Union[list[str], list[UploadFile]]) 1zAB
103def test_serialize_sequence_value_with_optional_list(): 1defg
104 """Test that serialize_sequence_value handles optional lists correctly."""
105 from fastapi._compat import v2 1klm
107 field_info = FieldInfo(annotation=Union[list[str], None]) 1klm
108 field = v2.ModelField(name="items", field_info=field_info) 1klm
109 result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"]) 1klm
110 assert result == ["a", "b", "c"] 1klm
111 assert isinstance(result, list) 1klm
114@needs_py310 1defg
115def test_serialize_sequence_value_with_optional_list_pipe_union(): 1defg
116 """Test that serialize_sequence_value handles optional lists correctly (with new syntax)."""
117 from fastapi._compat import v2 1nop
119 field_info = FieldInfo(annotation=list[str] | None) 1nop
120 field = v2.ModelField(name="items", field_info=field_info) 1nop
121 result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"]) 1nop
122 assert result == ["a", "b", "c"] 1nop
123 assert isinstance(result, list) 1nop
126def test_serialize_sequence_value_with_none_first_in_union(): 1defg
127 """Test that serialize_sequence_value handles Union[None, List[...]] correctly."""
128 from fastapi._compat import v2 1qrs
130 field_info = FieldInfo(annotation=Union[None, list[str]]) 1qrs
131 field = v2.ModelField(name="items", field_info=field_info) 1qrs
132 result = v2.serialize_sequence_value(field=field, value=["x", "y"]) 1qrs
133 assert result == ["x", "y"] 1qrs
134 assert isinstance(result, list) 1qrs