Coverage for tests / test_request_params / test_body / test_list.py: 100%
136 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, Union 1abcd
3import pytest 1abcd
4from dirty_equals import IsOneOf, IsPartialDict 1abcd
5from fastapi import Body, FastAPI 1abcd
6from fastapi.testclient import TestClient 1abcd
7from pydantic import BaseModel, Field 1abcd
9from .utils import get_body_model_name 1abcd
11app = FastAPI() 1abcd
13# =====================================================================================
14# Without aliases
17@app.post("/required-list-str", operation_id="required_list_str") 1abcd
18async def read_required_list_str(p: Annotated[list[str], Body(embed=True)]): 1abcd
19 return {"p": p} 1efg
22class BodyModelRequiredListStr(BaseModel): 1abcd
23 p: list[str] 1abcd
26@app.post("/model-required-list-str", operation_id="model_required_list_str") 1abcd
27def read_model_required_list_str(p: BodyModelRequiredListStr): 1abcd
28 return {"p": p.p} 1efg
31@pytest.mark.parametrize( 1abcd
32 "path",
33 ["/required-list-str", "/model-required-list-str"],
34)
35def test_required_list_str_schema(path: str): 1abcd
36 openapi = app.openapi() 1OPQ
37 body_model_name = get_body_model_name(openapi, path) 1OPQ
39 assert app.openapi()["components"]["schemas"][body_model_name] == { 1OPQ
40 "properties": {
41 "p": {
42 "items": {"type": "string"},
43 "title": "P",
44 "type": "array",
45 },
46 },
47 "required": ["p"],
48 "title": body_model_name,
49 "type": "object",
50 }
53@pytest.mark.parametrize("json", [None, {}]) 1abcd
54@pytest.mark.parametrize( 1abcd
55 "path",
56 ["/required-list-str", "/model-required-list-str"],
57)
58def test_required_list_str_missing(path: str, json: Union[dict, None]): 1abcd
59 client = TestClient(app) 1qrs
60 response = client.post(path, json=json) 1qrs
61 assert response.status_code == 422 1qrs
62 assert response.json() == { 1qrs
63 "detail": [
64 {
65 "type": "missing",
66 "loc": IsOneOf(["body", "p"], ["body"]),
67 "msg": "Field required",
68 "input": IsOneOf(None, {}),
69 }
70 ]
71 }
74@pytest.mark.parametrize( 1abcd
75 "path",
76 ["/required-list-str", "/model-required-list-str"],
77)
78def test_required_list_str(path: str): 1abcd
79 client = TestClient(app) 1efg
80 response = client.post(path, json={"p": ["hello", "world"]}) 1efg
81 assert response.status_code == 200 1efg
82 assert response.json() == {"p": ["hello", "world"]} 1efg
85# =====================================================================================
86# Alias
89@app.post("/required-list-alias", operation_id="required_list_alias") 1abcd
90async def read_required_list_alias( 1abcd
91 p: Annotated[list[str], Body(embed=True, alias="p_alias")],
92):
93 return {"p": p} 1hij
96class BodyModelRequiredListAlias(BaseModel): 1abcd
97 p: list[str] = Field(alias="p_alias") 1abcd
100@app.post("/model-required-list-alias", operation_id="model_required_list_alias") 1abcd
101async def read_model_required_list_alias(p: BodyModelRequiredListAlias): 1abcd
102 return {"p": p.p} 1hij
105@pytest.mark.parametrize( 1abcd
106 "path",
107 [
108 "/required-list-alias",
109 "/model-required-list-alias",
110 ],
111)
112def test_required_list_str_alias_schema(path: str): 1abcd
113 openapi = app.openapi() 1RST
114 body_model_name = get_body_model_name(openapi, path) 1RST
116 assert app.openapi()["components"]["schemas"][body_model_name] == { 1RST
117 "properties": {
118 "p_alias": {
119 "items": {"type": "string"},
120 "title": "P Alias",
121 "type": "array",
122 },
123 },
124 "required": ["p_alias"],
125 "title": body_model_name,
126 "type": "object",
127 }
130@pytest.mark.parametrize("json", [None, {}]) 1abcd
131@pytest.mark.parametrize( 1abcd
132 "path",
133 ["/required-list-alias", "/model-required-list-alias"],
134)
135def test_required_list_alias_missing(path: str, json: Union[dict, None]): 1abcd
136 client = TestClient(app) 1tuv
137 response = client.post(path, json=json) 1tuv
138 assert response.status_code == 422 1tuv
139 assert response.json() == { 1tuv
140 "detail": [
141 {
142 "type": "missing",
143 "loc": IsOneOf(["body", "p_alias"], ["body"]),
144 "msg": "Field required",
145 "input": IsOneOf(None, {}),
146 }
147 ]
148 }
151@pytest.mark.parametrize( 1abcd
152 "path",
153 ["/required-list-alias", "/model-required-list-alias"],
154)
155def test_required_list_alias_by_name(path: str): 1abcd
156 client = TestClient(app) 1wxy
157 response = client.post(path, json={"p": ["hello", "world"]}) 1wxy
158 assert response.status_code == 422 1wxy
159 assert response.json() == { 1wxy
160 "detail": [
161 {
162 "type": "missing",
163 "loc": ["body", "p_alias"],
164 "msg": "Field required",
165 "input": IsOneOf(None, {"p": ["hello", "world"]}),
166 }
167 ]
168 }
171@pytest.mark.parametrize( 1abcd
172 "path",
173 ["/required-list-alias", "/model-required-list-alias"],
174)
175def test_required_list_alias_by_alias(path: str): 1abcd
176 client = TestClient(app) 1hij
177 response = client.post(path, json={"p_alias": ["hello", "world"]}) 1hij
178 assert response.status_code == 200, response.text 1hij
179 assert response.json() == {"p": ["hello", "world"]} 1hij
182# =====================================================================================
183# Validation alias
186@app.post( 1abcd
187 "/required-list-validation-alias", operation_id="required_list_validation_alias"
188)
189def read_required_list_validation_alias( 1abcd
190 p: Annotated[list[str], Body(embed=True, validation_alias="p_val_alias")],
191):
192 return {"p": p} 1klm
195class BodyModelRequiredListValidationAlias(BaseModel): 1abcd
196 p: list[str] = Field(validation_alias="p_val_alias") 1abcd
199@app.post( 1abcd
200 "/model-required-list-validation-alias",
201 operation_id="model_required_list_validation_alias",
202)
203async def read_model_required_list_validation_alias( 1abcd
204 p: BodyModelRequiredListValidationAlias,
205):
206 return {"p": p.p} 1klm
209@pytest.mark.parametrize( 1abcd
210 "path",
211 ["/required-list-validation-alias", "/model-required-list-validation-alias"],
212)
213def test_required_list_validation_alias_schema(path: str): 1abcd
214 openapi = app.openapi() 1UVW
215 body_model_name = get_body_model_name(openapi, path) 1UVW
217 assert app.openapi()["components"]["schemas"][body_model_name] == { 1UVW
218 "properties": {
219 "p_val_alias": {
220 "items": {"type": "string"},
221 "title": "P Val Alias",
222 "type": "array",
223 },
224 },
225 "required": ["p_val_alias"],
226 "title": body_model_name,
227 "type": "object",
228 }
231@pytest.mark.parametrize("json", [None, {}]) 1abcd
232@pytest.mark.parametrize( 1abcd
233 "path",
234 [
235 "/required-list-validation-alias",
236 "/model-required-list-validation-alias",
237 ],
238)
239def test_required_list_validation_alias_missing(path: str, json: Union[dict, None]): 1abcd
240 client = TestClient(app) 1zAB
241 response = client.post(path, json=json) 1zAB
242 assert response.status_code == 422 1zAB
243 assert response.json() == { 1zAB
244 "detail": [
245 {
246 "type": "missing",
247 "loc": IsOneOf(["body"], ["body", "p_val_alias"]),
248 "msg": "Field required",
249 "input": IsOneOf(None, {}),
250 }
251 ]
252 }
255@pytest.mark.parametrize( 1abcd
256 "path",
257 [
258 "/required-list-validation-alias",
259 "/model-required-list-validation-alias",
260 ],
261)
262def test_required_list_validation_alias_by_name(path: str): 1abcd
263 client = TestClient(app) 1CDE
264 response = client.post(path, json={"p": ["hello", "world"]}) 1CDE
265 assert response.status_code == 422, response.text 1CDE
267 assert response.json() == { 1CDE
268 "detail": [
269 {
270 "type": "missing",
271 "loc": ["body", "p_val_alias"],
272 "msg": "Field required",
273 "input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})),
274 }
275 ]
276 }
279@pytest.mark.parametrize( 1abcd
280 "path",
281 [
282 "/required-list-validation-alias",
283 "/model-required-list-validation-alias",
284 ],
285)
286def test_required_list_validation_alias_by_validation_alias(path: str): 1abcd
287 client = TestClient(app) 1klm
288 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 1klm
289 assert response.status_code == 200, response.text 1klm
290 assert response.json() == {"p": ["hello", "world"]} 1klm
293# =====================================================================================
294# Alias and validation alias
297@app.post( 1abcd
298 "/required-list-alias-and-validation-alias",
299 operation_id="required_list_alias_and_validation_alias",
300)
301def read_required_list_alias_and_validation_alias( 1abcd
302 p: Annotated[
303 list[str], Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
304 ],
305):
306 return {"p": p} 1nop
309class BodyModelRequiredListAliasAndValidationAlias(BaseModel): 1abcd
310 p: list[str] = Field(alias="p_alias", validation_alias="p_val_alias") 1abcd
313@app.post( 1abcd
314 "/model-required-list-alias-and-validation-alias",
315 operation_id="model_required_list_alias_and_validation_alias",
316)
317def read_model_required_list_alias_and_validation_alias( 1abcd
318 p: BodyModelRequiredListAliasAndValidationAlias,
319):
320 return {"p": p.p} 1nop
323@pytest.mark.parametrize( 1abcd
324 "path",
325 [
326 "/required-list-alias-and-validation-alias",
327 "/model-required-list-alias-and-validation-alias",
328 ],
329)
330def test_required_list_alias_and_validation_alias_schema(path: str): 1abcd
331 openapi = app.openapi() 1XYZ
332 body_model_name = get_body_model_name(openapi, path) 1XYZ
334 assert app.openapi()["components"]["schemas"][body_model_name] == { 1XYZ
335 "properties": {
336 "p_val_alias": {
337 "items": {"type": "string"},
338 "title": "P Val Alias",
339 "type": "array",
340 },
341 },
342 "required": ["p_val_alias"],
343 "title": body_model_name,
344 "type": "object",
345 }
348@pytest.mark.parametrize("json", [None, {}]) 1abcd
349@pytest.mark.parametrize( 1abcd
350 "path",
351 [
352 "/required-list-alias-and-validation-alias",
353 "/model-required-list-alias-and-validation-alias",
354 ],
355)
356def test_required_list_alias_and_validation_alias_missing(path: str, json): 1abcd
357 client = TestClient(app) 1FGH
358 response = client.post(path, json=json) 1FGH
359 assert response.status_code == 422 1FGH
360 assert response.json() == { 1FGH
361 "detail": [
362 {
363 "type": "missing",
364 "loc": IsOneOf(["body"], ["body", "p_val_alias"]),
365 "msg": "Field required",
366 "input": IsOneOf(None, {}),
367 }
368 ]
369 }
372@pytest.mark.parametrize( 1abcd
373 "path",
374 [
375 "/required-list-alias-and-validation-alias",
376 "/model-required-list-alias-and-validation-alias",
377 ],
378)
379def test_required_list_alias_and_validation_alias_by_name(path: str): 1abcd
380 client = TestClient(app) 1IJK
381 response = client.post(path, json={"p": ["hello", "world"]}) 1IJK
382 assert response.status_code == 422 1IJK
383 assert response.json() == { 1IJK
384 "detail": [
385 {
386 "type": "missing",
387 "loc": [
388 "body",
389 "p_val_alias",
390 ],
391 "msg": "Field required",
392 "input": IsOneOf(None, {"p": ["hello", "world"]}),
393 }
394 ]
395 }
398@pytest.mark.parametrize( 1abcd
399 "path",
400 [
401 "/required-list-alias-and-validation-alias",
402 "/model-required-list-alias-and-validation-alias",
403 ],
404)
405def test_required_list_alias_and_validation_alias_by_alias(path: str): 1abcd
406 client = TestClient(app) 1LMN
407 response = client.post(path, json={"p_alias": ["hello", "world"]}) 1LMN
408 assert response.status_code == 422, response.text 1LMN
410 assert response.json() == { 1LMN
411 "detail": [
412 {
413 "type": "missing",
414 "loc": ["body", "p_val_alias"],
415 "msg": "Field required",
416 "input": IsOneOf(None, {"p_alias": ["hello", "world"]}),
417 }
418 ]
419 }
422@pytest.mark.parametrize( 1abcd
423 "path",
424 [
425 "/required-list-alias-and-validation-alias",
426 "/model-required-list-alias-and-validation-alias",
427 ],
428)
429def test_required_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
430 client = TestClient(app) 1nop
431 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 1nop
432 assert response.status_code == 200, response.text 1nop
433 assert response.json() == {"p": ["hello", "world"]} 1nop