Coverage for tests / test_request_params / test_query / test_optional_list.py: 100%
123 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 FastAPI, Query 1abcd
5from fastapi.testclient import TestClient 1abcd
6from inline_snapshot import snapshot 1abcd
7from pydantic import BaseModel, Field 1abcd
9app = FastAPI() 1abcd
11# =====================================================================================
12# Without aliases
15@app.get("/optional-list-str") 1abcd
16async def read_optional_list_str( 1abcd
17 p: Annotated[Optional[list[str]], Query()] = None,
18):
19 return {"p": p} 1efghij
22class QueryModelOptionalListStr(BaseModel): 1abcd
23 p: Optional[list[str]] = None 1abcd
26@app.get("/model-optional-list-str") 1abcd
27async def read_model_optional_list_str( 1abcd
28 p: Annotated[QueryModelOptionalListStr, Query()],
29):
30 return {"p": p.p} 1efghij
33@pytest.mark.parametrize( 1abcd
34 "path",
35 ["/optional-list-str", "/model-optional-list-str"],
36)
37def test_optional_list_str_schema(path: str): 1abcd
38 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1OPQ
39 [
40 {
41 "required": False,
42 "schema": {
43 "anyOf": [
44 {"items": {"type": "string"}, "type": "array"},
45 {"type": "null"},
46 ],
47 "title": "P",
48 },
49 "name": "p",
50 "in": "query",
51 }
52 ]
53 )
56@pytest.mark.parametrize( 1abcd
57 "path",
58 ["/optional-list-str", "/model-optional-list-str"],
59)
60def test_optional_list_str_missing(path: str): 1abcd
61 client = TestClient(app) 1fhj
62 response = client.get(path) 1fhj
63 assert response.status_code == 200, response.text 1fhj
64 assert response.json() == {"p": None} 1fhj
67@pytest.mark.parametrize( 1abcd
68 "path",
69 ["/optional-list-str", "/model-optional-list-str"],
70)
71def test_optional_list_str(path: str): 1abcd
72 client = TestClient(app) 1egi
73 response = client.get(f"{path}?p=hello&p=world") 1egi
74 assert response.status_code == 200 1egi
75 assert response.json() == {"p": ["hello", "world"]} 1egi
78# =====================================================================================
79# Alias
82@app.get("/optional-list-alias") 1abcd
83async def read_optional_list_alias( 1abcd
84 p: Annotated[Optional[list[str]], Query(alias="p_alias")] = None,
85):
86 return {"p": p} 1klmnopqrs
89class QueryModelOptionalListAlias(BaseModel): 1abcd
90 p: Optional[list[str]] = Field(None, alias="p_alias") 1abcd
93@app.get("/model-optional-list-alias") 1abcd
94async def read_model_optional_list_alias( 1abcd
95 p: Annotated[QueryModelOptionalListAlias, Query()],
96):
97 return {"p": p.p} 1klmnopqrs
100@pytest.mark.parametrize( 1abcd
101 "path",
102 ["/optional-list-alias", "/model-optional-list-alias"],
103)
104def test_optional_list_str_alias_schema(path: str): 1abcd
105 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1RST
106 [
107 {
108 "required": False,
109 "schema": {
110 "anyOf": [
111 {"items": {"type": "string"}, "type": "array"},
112 {"type": "null"},
113 ],
114 "title": "P Alias",
115 },
116 "name": "p_alias",
117 "in": "query",
118 }
119 ]
120 )
123@pytest.mark.parametrize( 1abcd
124 "path",
125 ["/optional-list-alias", "/model-optional-list-alias"],
126)
127def test_optional_list_alias_missing(path: str): 1abcd
128 client = TestClient(app) 1mps
129 response = client.get(path) 1mps
130 assert response.status_code == 200 1mps
131 assert response.json() == {"p": None} 1mps
134@pytest.mark.parametrize( 1abcd
135 "path",
136 ["/optional-list-alias", "/model-optional-list-alias"],
137)
138def test_optional_list_alias_by_name(path: str): 1abcd
139 client = TestClient(app) 1lor
140 response = client.get(f"{path}?p=hello&p=world") 1lor
141 assert response.status_code == 200 1lor
142 assert response.json() == {"p": None} 1lor
145@pytest.mark.parametrize( 1abcd
146 "path",
147 [
148 "/optional-list-alias",
149 "/model-optional-list-alias",
150 ],
151)
152def test_optional_list_alias_by_alias(path: str): 1abcd
153 client = TestClient(app) 1knq
154 response = client.get(f"{path}?p_alias=hello&p_alias=world") 1knq
155 assert response.status_code == 200 1knq
156 assert response.json() == {"p": ["hello", "world"]} 1knq
159# =====================================================================================
160# Validation alias
163@app.get("/optional-list-validation-alias") 1abcd
164def read_optional_list_validation_alias( 1abcd
165 p: Annotated[Optional[list[str]], Query(validation_alias="p_val_alias")] = None,
166):
167 return {"p": p} 1tuvwxyzAB
170class QueryModelOptionalListValidationAlias(BaseModel): 1abcd
171 p: Optional[list[str]] = Field(None, validation_alias="p_val_alias") 1abcd
174@app.get("/model-optional-list-validation-alias") 1abcd
175def read_model_optional_list_validation_alias( 1abcd
176 p: Annotated[QueryModelOptionalListValidationAlias, Query()],
177):
178 return {"p": p.p} 1tuvwxyzAB
181@pytest.mark.parametrize( 1abcd
182 "path",
183 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
184)
185def test_optional_list_validation_alias_schema(path: str): 1abcd
186 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1UVW
187 [
188 {
189 "required": False,
190 "schema": {
191 "anyOf": [
192 {"items": {"type": "string"}, "type": "array"},
193 {"type": "null"},
194 ],
195 "title": "P Val Alias",
196 },
197 "name": "p_val_alias",
198 "in": "query",
199 }
200 ]
201 )
204@pytest.mark.parametrize( 1abcd
205 "path",
206 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
207)
208def test_optional_list_validation_alias_missing(path: str): 1abcd
209 client = TestClient(app) 1vyB
210 response = client.get(path) 1vyB
211 assert response.status_code == 200 1vyB
212 assert response.json() == {"p": None} 1vyB
215@pytest.mark.parametrize( 1abcd
216 "path",
217 [
218 "/optional-list-validation-alias",
219 "/model-optional-list-validation-alias",
220 ],
221)
222def test_optional_list_validation_alias_by_name(path: str): 1abcd
223 client = TestClient(app) 1twz
224 response = client.get(f"{path}?p=hello&p=world") 1twz
225 assert response.status_code == 200 1twz
226 assert response.json() == {"p": None} 1twz
229@pytest.mark.parametrize( 1abcd
230 "path",
231 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"],
232)
233def test_optional_list_validation_alias_by_validation_alias(path: str): 1abcd
234 client = TestClient(app) 1uxA
235 response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world") 1uxA
236 assert response.status_code == 200, response.text 1uxA
237 assert response.json() == {"p": ["hello", "world"]} 1uxA
240# =====================================================================================
241# Alias and validation alias
244@app.get("/optional-list-alias-and-validation-alias") 1abcd
245def read_optional_list_alias_and_validation_alias( 1abcd
246 p: Annotated[
247 Optional[list[str]], Query(alias="p_alias", validation_alias="p_val_alias")
248 ] = None,
249):
250 return {"p": p} 1CDEFGHIJKLMN
253class QueryModelOptionalListAliasAndValidationAlias(BaseModel): 1abcd
254 p: Optional[list[str]] = Field( 1abcd
255 None, alias="p_alias", validation_alias="p_val_alias"
256 )
259@app.get("/model-optional-list-alias-and-validation-alias") 1abcd
260def read_model_optional_list_alias_and_validation_alias( 1abcd
261 p: Annotated[QueryModelOptionalListAliasAndValidationAlias, Query()],
262):
263 return {"p": p.p} 1CDEFGHIJKLMN
266@pytest.mark.parametrize( 1abcd
267 "path",
268 [
269 "/optional-list-alias-and-validation-alias",
270 "/model-optional-list-alias-and-validation-alias",
271 ],
272)
273def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd
274 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1XYZ
275 [
276 {
277 "required": False,
278 "schema": {
279 "anyOf": [
280 {"items": {"type": "string"}, "type": "array"},
281 {"type": "null"},
282 ],
283 "title": "P Val Alias",
284 },
285 "name": "p_val_alias",
286 "in": "query",
287 }
288 ]
289 )
292@pytest.mark.parametrize( 1abcd
293 "path",
294 [
295 "/optional-list-alias-and-validation-alias",
296 "/model-optional-list-alias-and-validation-alias",
297 ],
298)
299def test_optional_list_alias_and_validation_alias_missing(path: str): 1abcd
300 client = TestClient(app) 1FJN
301 response = client.get(path) 1FJN
302 assert response.status_code == 200 1FJN
303 assert response.json() == {"p": None} 1FJN
306@pytest.mark.parametrize( 1abcd
307 "path",
308 [
309 "/optional-list-alias-and-validation-alias",
310 "/model-optional-list-alias-and-validation-alias",
311 ],
312)
313def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd
314 client = TestClient(app) 1DHL
315 response = client.get(f"{path}?p=hello&p=world") 1DHL
316 assert response.status_code == 200 1DHL
317 assert response.json() == {"p": None} 1DHL
320@pytest.mark.parametrize( 1abcd
321 "path",
322 [
323 "/optional-list-alias-and-validation-alias",
324 "/model-optional-list-alias-and-validation-alias",
325 ],
326)
327def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd
328 client = TestClient(app) 1CGK
329 response = client.get(f"{path}?p_alias=hello&p_alias=world") 1CGK
330 assert response.status_code == 200 1CGK
331 assert response.json() == {"p": None} 1CGK
334@pytest.mark.parametrize( 1abcd
335 "path",
336 [
337 "/optional-list-alias-and-validation-alias",
338 "/model-optional-list-alias-and-validation-alias",
339 ],
340)
341def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
342 client = TestClient(app) 1EIM
343 response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world") 1EIM
344 assert response.status_code == 200, response.text 1EIM
345 assert response.json() == { 1EIM
346 "p": [
347 "hello",
348 "world",
349 ]
350 }