Coverage for tests / test_request_params / test_body / test_optional_list.py: 100%
171 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 Annotated, Optional 1abcd
3import pytest 1abcd
4from fastapi import Body, FastAPI 1abcd
5from fastapi.testclient import TestClient 1abcd
6from pydantic import BaseModel, Field 1abcd
8from .utils import get_body_model_name 1abcd
10app = FastAPI() 1abcd
12# =====================================================================================
13# Without aliases
16@app.post("/optional-list-str", operation_id="optional_list_str") 1abcd
17async def read_optional_list_str( 1abcd
18 p: Annotated[Optional[list[str]], Body(embed=True)] = None,
19):
20 return {"p": p} 1eOfgPhiQj
23class BodyModelOptionalListStr(BaseModel): 1abcd
24 p: Optional[list[str]] = None 1abcd
27@app.post("/model-optional-list-str", operation_id="model_optional_list_str") 1abcd
28async def read_model_optional_list_str(p: BodyModelOptionalListStr): 1abcd
29 return {"p": p.p} 1efghij
32@pytest.mark.parametrize( 1abcd
33 "path",
34 ["/optional-list-str", "/model-optional-list-str"],
35)
36def test_optional_list_str_schema(path: str): 1abcd
37 openapi = app.openapi() 1$%'
38 body_model_name = get_body_model_name(openapi, path) 1$%'
40 assert app.openapi()["components"]["schemas"][body_model_name] == { 1$%'
41 "properties": {
42 "p": {
43 "anyOf": [
44 {"items": {"type": "string"}, "type": "array"},
45 {"type": "null"},
46 ],
47 "title": "P",
48 },
49 },
50 "title": body_model_name,
51 "type": "object",
52 }
55def test_optional_list_str_missing(): 1abcd
56 client = TestClient(app) 1OPQ
57 response = client.post("/optional-list-str") 1OPQ
58 assert response.status_code == 200, response.text 1OPQ
59 assert response.json() == {"p": None} 1OPQ
62def test_model_optional_list_str_missing(): 1abcd
63 client = TestClient(app) 1012
64 response = client.post("/model-optional-list-str") 1012
65 assert response.status_code == 422, response.text 1012
66 assert response.json() == { 1012
67 "detail": [
68 {
69 "input": None,
70 "loc": ["body"],
71 "msg": "Field required",
72 "type": "missing",
73 },
74 ],
75 }
78@pytest.mark.parametrize( 1abcd
79 "path",
80 ["/optional-list-str", "/model-optional-list-str"],
81)
82def test_optional_list_str_missing_empty_dict(path: str): 1abcd
83 client = TestClient(app) 1fhj
84 response = client.post(path, json={}) 1fhj
85 assert response.status_code == 200, response.text 1fhj
86 assert response.json() == {"p": None} 1fhj
89@pytest.mark.parametrize( 1abcd
90 "path",
91 ["/optional-list-str", "/model-optional-list-str"],
92)
93def test_optional_list_str(path: str): 1abcd
94 client = TestClient(app) 1egi
95 response = client.post(path, json={"p": ["hello", "world"]}) 1egi
96 assert response.status_code == 200 1egi
97 assert response.json() == {"p": ["hello", "world"]} 1egi
100# =====================================================================================
101# Alias
104@app.post("/optional-list-alias", operation_id="optional_list_alias") 1abcd
105async def read_optional_list_alias( 1abcd
106 p: Annotated[Optional[list[str]], Body(embed=True, alias="p_alias")] = None,
107):
108 return {"p": p} 1klRmnoSpqrTs
111class BodyModelOptionalListAlias(BaseModel): 1abcd
112 p: Optional[list[str]] = Field(None, alias="p_alias") 1abcd
115@app.post("/model-optional-list-alias", operation_id="model_optional_list_alias") 1abcd
116async def read_model_optional_list_alias(p: BodyModelOptionalListAlias): 1abcd
117 return {"p": p.p} 1klmnopqrs
120@pytest.mark.parametrize( 1abcd
121 "path",
122 [
123 "/optional-list-alias",
124 "/model-optional-list-alias",
125 ],
126)
127def test_optional_list_str_alias_schema(path: str): 1abcd
128 openapi = app.openapi() 1()*
129 body_model_name = get_body_model_name(openapi, path) 1()*
131 assert app.openapi()["components"]["schemas"][body_model_name] == { 1()*
132 "properties": {
133 "p_alias": {
134 "anyOf": [
135 {"items": {"type": "string"}, "type": "array"},
136 {"type": "null"},
137 ],
138 "title": "P Alias",
139 },
140 },
141 "title": body_model_name,
142 "type": "object",
143 }
146def test_optional_list_alias_missing(): 1abcd
147 client = TestClient(app) 1RST
148 response = client.post("/optional-list-alias") 1RST
149 assert response.status_code == 200, response.text 1RST
150 assert response.json() == {"p": None} 1RST
153def test_model_optional_list_alias_missing(): 1abcd
154 client = TestClient(app) 1345
155 response = client.post("/model-optional-list-alias") 1345
156 assert response.status_code == 422, response.text 1345
157 assert response.json() == { 1345
158 "detail": [
159 {
160 "input": None,
161 "loc": ["body"],
162 "msg": "Field required",
163 "type": "missing",
164 },
165 ],
166 }
169@pytest.mark.parametrize( 1abcd
170 "path",
171 ["/optional-list-alias", "/model-optional-list-alias"],
172)
173def test_optional_list_alias_missing_empty_dict(path: str): 1abcd
174 client = TestClient(app) 1mps
175 response = client.post(path, json={}) 1mps
176 assert response.status_code == 200, response.text 1mps
177 assert response.json() == {"p": None} 1mps
180@pytest.mark.parametrize( 1abcd
181 "path",
182 ["/optional-list-alias", "/model-optional-list-alias"],
183)
184def test_optional_list_alias_by_name(path: str): 1abcd
185 client = TestClient(app) 1lor
186 response = client.post(path, json={"p": ["hello", "world"]}) 1lor
187 assert response.status_code == 200 1lor
188 assert response.json() == {"p": None} 1lor
191@pytest.mark.parametrize( 1abcd
192 "path",
193 ["/optional-list-alias", "/model-optional-list-alias"],
194)
195def test_optional_list_alias_by_alias(path: str): 1abcd
196 client = TestClient(app) 1knq
197 response = client.post(path, json={"p_alias": ["hello", "world"]}) 1knq
198 assert response.status_code == 200 1knq
199 assert response.json() == {"p": ["hello", "world"]} 1knq
202# =====================================================================================
203# Validation alias
206@app.post( 1abcd
207 "/optional-list-validation-alias", operation_id="optional_list_validation_alias"
208)
209def read_optional_list_validation_alias( 1abcd
210 p: Annotated[
211 Optional[list[str]], Body(embed=True, validation_alias="p_val_alias")
212 ] = None,
213):
214 return {"p": p} 1tuUvwxVyzAWB
217class BodyModelOptionalListValidationAlias(BaseModel): 1abcd
218 p: Optional[list[str]] = Field(None, validation_alias="p_val_alias") 1abcd
221@app.post( 1abcd
222 "/model-optional-list-validation-alias",
223 operation_id="model_optional_list_validation_alias",
224)
225def read_model_optional_list_validation_alias( 1abcd
226 p: BodyModelOptionalListValidationAlias,
227):
228 return {"p": p.p} 1tuvwxyzAB
231@pytest.mark.parametrize( 1abcd
232 "path",
233 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
234)
235def test_optional_list_validation_alias_schema(path: str): 1abcd
236 openapi = app.openapi() 1+,-
237 body_model_name = get_body_model_name(openapi, path) 1+,-
239 assert app.openapi()["components"]["schemas"][body_model_name] == { 1+,-
240 "properties": {
241 "p_val_alias": {
242 "anyOf": [
243 {"items": {"type": "string"}, "type": "array"},
244 {"type": "null"},
245 ],
246 "title": "P Val Alias",
247 },
248 },
249 "title": body_model_name,
250 "type": "object",
251 }
254def test_optional_list_validation_alias_missing(): 1abcd
255 client = TestClient(app) 1UVW
256 response = client.post("/optional-list-validation-alias") 1UVW
257 assert response.status_code == 200, response.text 1UVW
258 assert response.json() == {"p": None} 1UVW
261def test_model_optional_list_validation_alias_missing(): 1abcd
262 client = TestClient(app) 1678
263 response = client.post("/model-optional-list-validation-alias") 1678
264 assert response.status_code == 422, response.text 1678
265 assert response.json() == { 1678
266 "detail": [
267 {
268 "input": None,
269 "loc": ["body"],
270 "msg": "Field required",
271 "type": "missing",
272 },
273 ],
274 }
277@pytest.mark.parametrize( 1abcd
278 "path",
279 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
280)
281def test_optional_list_validation_alias_missing_empty_dict(path: str): 1abcd
282 client = TestClient(app) 1vyB
283 response = client.post(path, json={}) 1vyB
284 assert response.status_code == 200, response.text 1vyB
285 assert response.json() == {"p": None} 1vyB
288@pytest.mark.parametrize( 1abcd
289 "path",
290 [
291 "/optional-list-validation-alias",
292 "/model-optional-list-validation-alias",
293 ],
294)
295def test_optional_list_validation_alias_by_name(path: str): 1abcd
296 client = TestClient(app) 1twz
297 response = client.post(path, json={"p": ["hello", "world"]}) 1twz
298 assert response.status_code == 200 1twz
299 assert response.json() == {"p": None} 1twz
302@pytest.mark.parametrize( 1abcd
303 "path",
304 [
305 "/optional-list-validation-alias",
306 "/model-optional-list-validation-alias",
307 ],
308)
309def test_optional_list_validation_alias_by_validation_alias(path: str): 1abcd
310 client = TestClient(app) 1uxA
311 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 1uxA
312 assert response.status_code == 200, response.text 1uxA
313 assert response.json() == {"p": ["hello", "world"]} 1uxA
316# =====================================================================================
317# Alias and validation alias
320@app.post( 1abcd
321 "/optional-list-alias-and-validation-alias",
322 operation_id="optional_list_alias_and_validation_alias",
323)
324def read_optional_list_alias_and_validation_alias( 1abcd
325 p: Annotated[
326 Optional[list[str]],
327 Body(embed=True, alias="p_alias", validation_alias="p_val_alias"),
328 ] = None,
329):
330 return {"p": p} 1CDEXFGHIYJKLMZN
333class BodyModelOptionalListAliasAndValidationAlias(BaseModel): 1abcd
334 p: Optional[list[str]] = Field( 1abcd
335 None, alias="p_alias", validation_alias="p_val_alias"
336 )
339@app.post( 1abcd
340 "/model-optional-list-alias-and-validation-alias",
341 operation_id="model_optional_list_alias_and_validation_alias",
342)
343def read_model_optional_list_alias_and_validation_alias( 1abcd
344 p: BodyModelOptionalListAliasAndValidationAlias,
345):
346 return {"p": p.p} 1CDEFGHIJKLMN
349@pytest.mark.parametrize( 1abcd
350 "path",
351 [
352 "/optional-list-alias-and-validation-alias",
353 "/model-optional-list-alias-and-validation-alias",
354 ],
355)
356def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd
357 openapi = app.openapi() 1./:
358 body_model_name = get_body_model_name(openapi, path) 1./:
360 assert app.openapi()["components"]["schemas"][body_model_name] == { 1./:
361 "properties": {
362 "p_val_alias": {
363 "anyOf": [
364 {"items": {"type": "string"}, "type": "array"},
365 {"type": "null"},
366 ],
367 "title": "P Val Alias",
368 },
369 },
370 "title": body_model_name,
371 "type": "object",
372 }
375def test_optional_list_alias_and_validation_alias_missing(): 1abcd
376 client = TestClient(app) 1XYZ
377 response = client.post("/optional-list-alias-and-validation-alias") 1XYZ
378 assert response.status_code == 200, response.text 1XYZ
379 assert response.json() == {"p": None} 1XYZ
382def test_model_optional_list_alias_and_validation_alias_missing(): 1abcd
383 client = TestClient(app) 19!#
384 response = client.post("/model-optional-list-alias-and-validation-alias") 19!#
385 assert response.status_code == 422, response.text 19!#
386 assert response.json() == { 19!#
387 "detail": [
388 {
389 "input": None,
390 "loc": ["body"],
391 "msg": "Field required",
392 "type": "missing",
393 },
394 ],
395 }
398@pytest.mark.parametrize( 1abcd
399 "path",
400 [
401 "/optional-list-alias-and-validation-alias",
402 "/model-optional-list-alias-and-validation-alias",
403 ],
404)
405def test_optional_list_alias_and_validation_alias_missing_empty_dict(path: str): 1abcd
406 client = TestClient(app) 1FJN
407 response = client.post(path, json={}) 1FJN
408 assert response.status_code == 200, response.text 1FJN
409 assert response.json() == {"p": None} 1FJN
412@pytest.mark.parametrize( 1abcd
413 "path",
414 [
415 "/optional-list-alias-and-validation-alias",
416 "/model-optional-list-alias-and-validation-alias",
417 ],
418)
419def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd
420 client = TestClient(app) 1DHL
421 response = client.post(path, json={"p": ["hello", "world"]}) 1DHL
422 assert response.status_code == 200 1DHL
423 assert response.json() == {"p": None} 1DHL
426@pytest.mark.parametrize( 1abcd
427 "path",
428 [
429 "/optional-list-alias-and-validation-alias",
430 "/model-optional-list-alias-and-validation-alias",
431 ],
432)
433def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd
434 client = TestClient(app) 1CGK
435 response = client.post(path, json={"p_alias": ["hello", "world"]}) 1CGK
436 assert response.status_code == 200 1CGK
437 assert response.json() == {"p": None} 1CGK
440@pytest.mark.parametrize( 1abcd
441 "path",
442 [
443 "/optional-list-alias-and-validation-alias",
444 "/model-optional-list-alias-and-validation-alias",
445 ],
446)
447def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
448 client = TestClient(app) 1EIM
449 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 1EIM
450 assert response.status_code == 200, response.text 1EIM
451 assert response.json() == { 1EIM
452 "p": [
453 "hello",
454 "world",
455 ]
456 }