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

1from typing import Annotated, Optional 1abcd

2 

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

8 

9app = FastAPI() 1abcd

10 

11# ===================================================================================== 

12# Without aliases 

13 

14 

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

20 

21 

22class QueryModelOptionalListStr(BaseModel): 1abcd

23 p: Optional[list[str]] = None 1abcd

24 

25 

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

31 

32 

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 ) 

54 

55 

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

65 

66 

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

76 

77 

78# ===================================================================================== 

79# Alias 

80 

81 

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

87 

88 

89class QueryModelOptionalListAlias(BaseModel): 1abcd

90 p: Optional[list[str]] = Field(None, alias="p_alias") 1abcd

91 

92 

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

98 

99 

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 ) 

121 

122 

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

132 

133 

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

143 

144 

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

157 

158 

159# ===================================================================================== 

160# Validation alias 

161 

162 

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

168 

169 

170class QueryModelOptionalListValidationAlias(BaseModel): 1abcd

171 p: Optional[list[str]] = Field(None, validation_alias="p_val_alias") 1abcd

172 

173 

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

179 

180 

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 ) 

202 

203 

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

213 

214 

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

227 

228 

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

238 

239 

240# ===================================================================================== 

241# Alias and validation alias 

242 

243 

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

251 

252 

253class QueryModelOptionalListAliasAndValidationAlias(BaseModel): 1abcd

254 p: Optional[list[str]] = Field( 1abcd

255 None, alias="p_alias", validation_alias="p_val_alias" 

256 ) 

257 

258 

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

264 

265 

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 ) 

290 

291 

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

304 

305 

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

318 

319 

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

332 

333 

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 }