Coverage for tests / test_request_params / test_form / test_list.py: 100%
132 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 1abcd
3import pytest 1abcd
4from dirty_equals import IsOneOf, IsPartialDict 1abcd
5from fastapi import FastAPI, Form 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], Form()]): 1abcd
19 return {"p": p} 1efg
22class FormModelRequiredListStr(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: Annotated[FormModelRequiredListStr, Form()]): 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( 1abcd
54 "path",
55 ["/required-list-str", "/model-required-list-str"],
56)
57def test_required_list_str_missing(path: str): 1abcd
58 client = TestClient(app) 1qrs
59 response = client.post(path) 1qrs
60 assert response.status_code == 422 1qrs
61 assert response.json() == { 1qrs
62 "detail": [
63 {
64 "type": "missing",
65 "loc": ["body", "p"],
66 "msg": "Field required",
67 "input": IsOneOf(None, {}),
68 }
69 ]
70 }
73@pytest.mark.parametrize( 1abcd
74 "path",
75 ["/required-list-str", "/model-required-list-str"],
76)
77def test_required_list_str(path: str): 1abcd
78 client = TestClient(app) 1efg
79 response = client.post(path, data={"p": ["hello", "world"]}) 1efg
80 assert response.status_code == 200 1efg
81 assert response.json() == {"p": ["hello", "world"]} 1efg
84# =====================================================================================
85# Alias
88@app.post("/required-list-alias", operation_id="required_list_alias") 1abcd
89async def read_required_list_alias(p: Annotated[list[str], Form(alias="p_alias")]): 1abcd
90 return {"p": p} 1hij
93class FormModelRequiredListAlias(BaseModel): 1abcd
94 p: list[str] = Field(alias="p_alias") 1abcd
97@app.post("/model-required-list-alias", operation_id="model_required_list_alias") 1abcd
98async def read_model_required_list_alias( 1abcd
99 p: Annotated[FormModelRequiredListAlias, Form()],
100):
101 return {"p": p.p} 1hij
104@pytest.mark.parametrize( 1abcd
105 "path",
106 [
107 "/required-list-alias",
108 "/model-required-list-alias",
109 ],
110)
111def test_required_list_str_alias_schema(path: str): 1abcd
112 openapi = app.openapi() 1RST
113 body_model_name = get_body_model_name(openapi, path) 1RST
115 assert app.openapi()["components"]["schemas"][body_model_name] == { 1RST
116 "properties": {
117 "p_alias": {
118 "items": {"type": "string"},
119 "title": "P Alias",
120 "type": "array",
121 },
122 },
123 "required": ["p_alias"],
124 "title": body_model_name,
125 "type": "object",
126 }
129@pytest.mark.parametrize( 1abcd
130 "path",
131 ["/required-list-alias", "/model-required-list-alias"],
132)
133def test_required_list_alias_missing(path: str): 1abcd
134 client = TestClient(app) 1tuv
135 response = client.post(path) 1tuv
136 assert response.status_code == 422 1tuv
137 assert response.json() == { 1tuv
138 "detail": [
139 {
140 "type": "missing",
141 "loc": ["body", "p_alias"],
142 "msg": "Field required",
143 "input": IsOneOf(None, {}),
144 }
145 ]
146 }
149@pytest.mark.parametrize( 1abcd
150 "path",
151 [
152 "/required-list-alias",
153 "/model-required-list-alias",
154 ],
155)
156def test_required_list_alias_by_name(path: str): 1abcd
157 client = TestClient(app) 1wxy
158 response = client.post(path, data={"p": ["hello", "world"]}) 1wxy
159 assert response.status_code == 422 1wxy
160 assert response.json() == { 1wxy
161 "detail": [
162 {
163 "type": "missing",
164 "loc": ["body", "p_alias"],
165 "msg": "Field required",
166 "input": IsOneOf(None, {"p": ["hello", "world"]}),
167 }
168 ]
169 }
172@pytest.mark.parametrize( 1abcd
173 "path",
174 ["/required-list-alias", "/model-required-list-alias"],
175)
176def test_required_list_alias_by_alias(path: str): 1abcd
177 client = TestClient(app) 1hij
178 response = client.post(path, data={"p_alias": ["hello", "world"]}) 1hij
179 assert response.status_code == 200, response.text 1hij
180 assert response.json() == {"p": ["hello", "world"]} 1hij
183# =====================================================================================
184# Validation alias
187@app.post( 1abcd
188 "/required-list-validation-alias", operation_id="required_list_validation_alias"
189)
190def read_required_list_validation_alias( 1abcd
191 p: Annotated[list[str], Form(validation_alias="p_val_alias")],
192):
193 return {"p": p} 1klm
196class FormModelRequiredListValidationAlias(BaseModel): 1abcd
197 p: list[str] = Field(validation_alias="p_val_alias") 1abcd
200@app.post( 1abcd
201 "/model-required-list-validation-alias",
202 operation_id="model_required_list_validation_alias",
203)
204async def read_model_required_list_validation_alias( 1abcd
205 p: Annotated[FormModelRequiredListValidationAlias, Form()],
206):
207 return {"p": p.p} 1klm
210@pytest.mark.parametrize( 1abcd
211 "path",
212 ["/required-list-validation-alias", "/model-required-list-validation-alias"],
213)
214def test_required_list_validation_alias_schema(path: str): 1abcd
215 openapi = app.openapi() 1UVW
216 body_model_name = get_body_model_name(openapi, path) 1UVW
218 assert app.openapi()["components"]["schemas"][body_model_name] == { 1UVW
219 "properties": {
220 "p_val_alias": {
221 "items": {"type": "string"},
222 "title": "P Val Alias",
223 "type": "array",
224 },
225 },
226 "required": ["p_val_alias"],
227 "title": body_model_name,
228 "type": "object",
229 }
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): 1abcd
240 client = TestClient(app) 1zAB
241 response = client.post(path) 1zAB
242 assert response.status_code == 422 1zAB
243 assert response.json() == { 1zAB
244 "detail": [
245 {
246 "type": "missing",
247 "loc": [
248 "body",
249 "p_val_alias",
250 ],
251 "msg": "Field required",
252 "input": IsOneOf(None, {}),
253 }
254 ]
255 }
258@pytest.mark.parametrize( 1abcd
259 "path",
260 [
261 "/required-list-validation-alias",
262 "/model-required-list-validation-alias",
263 ],
264)
265def test_required_list_validation_alias_by_name(path: str): 1abcd
266 client = TestClient(app) 1CDE
267 response = client.post(path, data={"p": ["hello", "world"]}) 1CDE
268 assert response.status_code == 422, response.text 1CDE
270 assert response.json() == { 1CDE
271 "detail": [
272 {
273 "type": "missing",
274 "loc": ["body", "p_val_alias"],
275 "msg": "Field required",
276 "input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})),
277 }
278 ]
279 }
282@pytest.mark.parametrize( 1abcd
283 "path",
284 ["/required-list-validation-alias", "/model-required-list-validation-alias"],
285)
286def test_required_list_validation_alias_by_validation_alias(path: str): 1abcd
287 client = TestClient(app) 1klm
288 response = client.post(path, data={"p_val_alias": ["hello", "world"]}) 1klm
289 assert response.status_code == 200, response.text 1klm
291 assert response.json() == {"p": ["hello", "world"]} 1klm
294# =====================================================================================
295# Alias and validation alias
298@app.post( 1abcd
299 "/required-list-alias-and-validation-alias",
300 operation_id="required_list_alias_and_validation_alias",
301)
302def read_required_list_alias_and_validation_alias( 1abcd
303 p: Annotated[list[str], Form(alias="p_alias", validation_alias="p_val_alias")],
304):
305 return {"p": p} 1nop
308class FormModelRequiredListAliasAndValidationAlias(BaseModel): 1abcd
309 p: list[str] = Field(alias="p_alias", validation_alias="p_val_alias") 1abcd
312@app.post( 1abcd
313 "/model-required-list-alias-and-validation-alias",
314 operation_id="model_required_list_alias_and_validation_alias",
315)
316def read_model_required_list_alias_and_validation_alias( 1abcd
317 p: Annotated[FormModelRequiredListAliasAndValidationAlias, Form()],
318):
319 return {"p": p.p} 1nop
322@pytest.mark.parametrize( 1abcd
323 "path",
324 [
325 "/required-list-alias-and-validation-alias",
326 "/model-required-list-alias-and-validation-alias",
327 ],
328)
329def test_required_list_alias_and_validation_alias_schema(path: str): 1abcd
330 openapi = app.openapi() 1XYZ
331 body_model_name = get_body_model_name(openapi, path) 1XYZ
333 assert app.openapi()["components"]["schemas"][body_model_name] == { 1XYZ
334 "properties": {
335 "p_val_alias": {
336 "items": {"type": "string"},
337 "title": "P Val Alias",
338 "type": "array",
339 },
340 },
341 "required": ["p_val_alias"],
342 "title": body_model_name,
343 "type": "object",
344 }
347@pytest.mark.parametrize( 1abcd
348 "path",
349 [
350 "/required-list-alias-and-validation-alias",
351 "/model-required-list-alias-and-validation-alias",
352 ],
353)
354def test_required_list_alias_and_validation_alias_missing(path: str): 1abcd
355 client = TestClient(app) 1FGH
356 response = client.post(path) 1FGH
357 assert response.status_code == 422 1FGH
358 assert response.json() == { 1FGH
359 "detail": [
360 {
361 "type": "missing",
362 "loc": [
363 "body",
364 "p_val_alias",
365 ],
366 "msg": "Field required",
367 "input": IsOneOf(None, {}),
368 }
369 ]
370 }
373@pytest.mark.parametrize( 1abcd
374 "path",
375 [
376 "/required-list-alias-and-validation-alias",
377 "/model-required-list-alias-and-validation-alias",
378 ],
379)
380def test_required_list_alias_and_validation_alias_by_name(path: str): 1abcd
381 client = TestClient(app) 1IJK
382 response = client.post(path, data={"p": ["hello", "world"]}) 1IJK
383 assert response.status_code == 422 1IJK
384 assert response.json() == { 1IJK
385 "detail": [
386 {
387 "type": "missing",
388 "loc": [
389 "body",
390 "p_val_alias",
391 ],
392 "msg": "Field required",
393 "input": IsOneOf(
394 None,
395 {"p": ["hello", "world"]},
396 ),
397 }
398 ]
399 }
402@pytest.mark.parametrize( 1abcd
403 "path",
404 [
405 "/required-list-alias-and-validation-alias",
406 "/model-required-list-alias-and-validation-alias",
407 ],
408)
409def test_required_list_alias_and_validation_alias_by_alias(path: str): 1abcd
410 client = TestClient(app) 1LMN
411 response = client.post(path, data={"p_alias": ["hello", "world"]}) 1LMN
412 assert response.status_code == 422 1LMN
413 assert response.json() == { 1LMN
414 "detail": [
415 {
416 "type": "missing",
417 "loc": ["body", "p_val_alias"],
418 "msg": "Field required",
419 "input": IsOneOf(None, {"p_alias": ["hello", "world"]}),
420 }
421 ]
422 }
425@pytest.mark.parametrize( 1abcd
426 "path",
427 [
428 "/required-list-alias-and-validation-alias",
429 "/model-required-list-alias-and-validation-alias",
430 ],
431)
432def test_required_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
433 client = TestClient(app) 1nop
434 response = client.post(path, data={"p_val_alias": ["hello", "world"]}) 1nop
435 assert response.status_code == 200, response.text 1nop
436 assert response.json() == {"p": ["hello", "world"]} 1nop