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-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1from typing import Annotated 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[list[str] | None, Body(embed=True)] = None,
19):
20 return {"p": p} 1efghijklmn
23class BodyModelOptionalListStr(BaseModel): 1abcd
24 p: list[str] | None = 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} 2o p .bq r s t u
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() 2PbQbRbSbTbUbVbWb
38 body_model_name = get_body_model_name(openapi, path) 2PbQbRbSbTbUbVbWb
40 assert app.openapi()["components"]["schemas"][body_model_name] == { 2PbQbRbSbTbUbVbWb
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) 1ghkn
57 response = client.post("/optional-list-str") 1ghkn
58 assert response.status_code == 200, response.text 1ghkn
59 assert response.json() == {"p": None} 1ghkn
62def test_model_optional_list_str_missing(): 1abcd
63 client = TestClient(app) 2hbibjbkb
64 response = client.post("/model-optional-list-str") 2hbibjbkb
65 assert response.status_code == 422, response.text 2hbibjbkb
66 assert response.json() == { 2hbibjbkb
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) 2p f q lbs j u m
84 response = client.post(path, json={}) 2p f q lbs j u m
85 assert response.status_code == 200, response.text 2p f q lbs j u m
86 assert response.json() == {"p": None} 2p f q lbs j u m
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) 2o e mbr i t l
95 response = client.post(path, json={"p": ["hello", "world"]}) 2o e mbr i t l
96 assert response.status_code == 200 2o e mbr i t l
97 assert response.json() == {"p": ["hello", "world"]} 2o e mbr i t l
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[list[str] | None, Body(embed=True, alias="p_alias")] = None,
107):
108 return {"p": p} 1vwxyzABCDEFGHI
111class BodyModelOptionalListAlias(BaseModel): 1abcd
112 p: list[str] | None = 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} 2J K L /bM N O P Q R
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() 2XbYbZb0b1b2b3b4b
129 body_model_name = get_body_model_name(openapi, path) 2XbYbZb0b1b2b3b4b
131 assert app.openapi()["components"]["schemas"][body_model_name] == { 2XbYbZb0b1b2b3b4b
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) 1yAEI
148 response = client.post("/optional-list-alias") 1yAEI
149 assert response.status_code == 200, response.text 1yAEI
150 assert response.json() == {"p": None} 1yAEI
153def test_model_optional_list_alias_missing(): 1abcd
154 client = TestClient(app) 2nbobpbqb
155 response = client.post("/model-optional-list-alias") 2nbobpbqb
156 assert response.status_code == 422, response.text 2nbobpbqb
157 assert response.json() == { 2nbobpbqb
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) 1LxzODRH
175 response = client.post(path, json={}) 1LxzODRH
176 assert response.status_code == 200, response.text 1LxzODRH
177 assert response.json() == {"p": None} 1LxzODRH
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) 2K w rbsbN C Q G
186 response = client.post(path, json={"p": ["hello", "world"]}) 2K w rbsbN C Q G
187 assert response.status_code == 200 2K w rbsbN C Q G
188 assert response.json() == {"p": None} 2K w rbsbN C Q G
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) 2J v tbubM B P F
197 response = client.post(path, json={"p_alias": ["hello", "world"]}) 2J v tbubM B P F
198 assert response.status_code == 200 2J v tbubM B P F
199 assert response.json() == {"p": ["hello", "world"]} 2J v tbubM B P F
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 list[str] | None, Body(embed=True, validation_alias="p_val_alias")
212 ] = None,
213):
214 return {"p": p} 1STUVWXYZ01234
217class BodyModelOptionalListValidationAlias(BaseModel): 1abcd
218 p: list[str] | None = 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} 156789!#$%'
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() 25b6b7b8b9b!b#b$b
237 body_model_name = get_body_model_name(openapi, path) 25b6b7b8b9b!b#b$b
239 assert app.openapi()["components"]["schemas"][body_model_name] == { 25b6b7b8b9b!b#b$b
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) 1VW04
256 response = client.post("/optional-list-validation-alias") 1VW04
257 assert response.status_code == 200, response.text 1VW04
258 assert response.json() == {"p": None} 1VW04
261def test_model_optional_list_validation_alias_missing(): 1abcd
262 client = TestClient(app) 2vbwbxbyb
263 response = client.post("/model-optional-list-validation-alias") 2vbwbxbyb
264 assert response.status_code == 422, response.text 2vbwbxbyb
265 assert response.json() == { 2vbwbxbyb
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) 27 U 8 zb# Z ' 3
283 response = client.post(path, json={}) 27 U 8 zb# Z ' 3
284 assert response.status_code == 200, response.text 27 U 8 zb# Z ' 3
285 assert response.json() == {"p": None} 27 U 8 zb# Z ' 3
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) 25 S AbBb9 X $ 1
297 response = client.post(path, json={"p": ["hello", "world"]}) 25 S AbBb9 X $ 1
298 assert response.status_code == 200 25 S AbBb9 X $ 1
299 assert response.json() == {"p": None} 25 S AbBb9 X $ 1
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) 26 T CbDb! Y % 2
311 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 26 T CbDb! Y % 2
312 assert response.status_code == 200, response.text 26 T CbDb! Y % 2
313 assert response.json() == {"p": ["hello", "world"]} 26 T CbDb! Y % 2
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 list[str] | None,
327 Body(embed=True, alias="p_alias", validation_alias="p_val_alias"),
328 ] = None,
329):
330 return {"p": p} 1()*+,-./:;=?@[]^
333class BodyModelOptionalListAliasAndValidationAlias(BaseModel): 1abcd
334 p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias") 1abcd
337@app.post( 1abcd
338 "/model-optional-list-alias-and-validation-alias",
339 operation_id="model_optional_list_alias_and_validation_alias",
340)
341def read_model_optional_list_alias_and_validation_alias( 1abcd
342 p: BodyModelOptionalListAliasAndValidationAlias,
343):
344 return {"p": p.p} 2_ ` { | } ~ abbbcbdbebfbgb
347@pytest.mark.parametrize( 1abcd
348 "path",
349 [
350 "/optional-list-alias-and-validation-alias",
351 "/model-optional-list-alias-and-validation-alias",
352 ],
353)
354def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd
355 openapi = app.openapi() 2%b'b(b)b*b+b,b-b
356 body_model_name = get_body_model_name(openapi, path) 2%b'b(b)b*b+b,b-b
358 assert app.openapi()["components"]["schemas"][body_model_name] == { 2%b'b(b)b*b+b,b-b
359 "properties": {
360 "p_val_alias": {
361 "anyOf": [
362 {"items": {"type": "string"}, "type": "array"},
363 {"type": "null"},
364 ],
365 "title": "P Val Alias",
366 },
367 },
368 "title": body_model_name,
369 "type": "object",
370 }
373def test_optional_list_alias_and_validation_alias_missing(): 1abcd
374 client = TestClient(app) 1,-=^
375 response = client.post("/optional-list-alias-and-validation-alias") 1,-=^
376 assert response.status_code == 200, response.text 1,-=^
377 assert response.json() == {"p": None} 1,-=^
380def test_model_optional_list_alias_and_validation_alias_missing(): 1abcd
381 client = TestClient(app) 2EbFbGbHb
382 response = client.post("/model-optional-list-alias-and-validation-alias") 2EbFbGbHb
383 assert response.status_code == 422, response.text 2EbFbGbHb
384 assert response.json() == { 2EbFbGbHb
385 "detail": [
386 {
387 "input": None,
388 "loc": ["body"],
389 "msg": "Field required",
390 "type": "missing",
391 },
392 ],
393 }
396@pytest.mark.parametrize( 1abcd
397 "path",
398 [
399 "/optional-list-alias-and-validation-alias",
400 "/model-optional-list-alias-and-validation-alias",
401 ],
402)
403def test_optional_list_alias_and_validation_alias_missing_empty_dict(path: str): 1abcd
404 client = TestClient(app) 2| + } Ibcb; gb]
405 response = client.post(path, json={}) 2| + } Ibcb; gb]
406 assert response.status_code == 200, response.text 2| + } Ibcb; gb]
407 assert response.json() == {"p": None} 2| + } Ibcb; gb]
410@pytest.mark.parametrize( 1abcd
411 "path",
412 [
413 "/optional-list-alias-and-validation-alias",
414 "/model-optional-list-alias-and-validation-alias",
415 ],
416)
417def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd
418 client = TestClient(app) 2` ) JbKbab/ eb@
419 response = client.post(path, json={"p": ["hello", "world"]}) 2` ) JbKbab/ eb@
420 assert response.status_code == 200 2` ) JbKbab/ eb@
421 assert response.json() == {"p": None} 2` ) JbKbab/ eb@
424@pytest.mark.parametrize( 1abcd
425 "path",
426 [
427 "/optional-list-alias-and-validation-alias",
428 "/model-optional-list-alias-and-validation-alias",
429 ],
430)
431def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd
432 client = TestClient(app) 2_ ( LbMb~ . db?
433 response = client.post(path, json={"p_alias": ["hello", "world"]}) 2_ ( LbMb~ . db?
434 assert response.status_code == 200 2_ ( LbMb~ . db?
435 assert response.json() == {"p": None} 2_ ( LbMb~ . db?
438@pytest.mark.parametrize( 1abcd
439 "path",
440 [
441 "/optional-list-alias-and-validation-alias",
442 "/model-optional-list-alias-and-validation-alias",
443 ],
444)
445def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
446 client = TestClient(app) 2{ * NbObbb: fb[
447 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 2{ * NbObbb: fb[
448 assert response.status_code == 200, response.text 2{ * NbObbb: fb[
449 assert response.json() == { 2{ * NbObbb: fb[
450 "p": [
451 "hello",
452 "world",
453 ]
454 }