Coverage for tests / test_request_params / test_query / test_list.py: 100%

124 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1from typing import Annotated 1abcd

2 

3import pytest 1abcd

4from dirty_equals import IsOneOf 1abcd

5from fastapi import FastAPI, Query 1abcd

6from fastapi.testclient import TestClient 1abcd

7from inline_snapshot import snapshot 1abcd

8from pydantic import BaseModel, Field 1abcd

9 

10app = FastAPI() 1abcd

11 

12# ===================================================================================== 

13# Without aliases 

14 

15 

16@app.get("/required-list-str") 1abcd

17async def read_required_list_str(p: Annotated[list[str], Query()]): 1abcd

18 return {"p": p} 1efg

19 

20 

21class QueryModelRequiredListStr(BaseModel): 1abcd

22 p: list[str] 1abcd

23 

24 

25@app.get("/model-required-list-str") 1abcd

26def read_model_required_list_str(p: Annotated[QueryModelRequiredListStr, Query()]): 1abcd

27 return {"p": p.p} 1efg

28 

29 

30@pytest.mark.parametrize( 1abcd

31 "path", 

32 ["/required-list-str", "/model-required-list-str"], 

33) 

34def test_required_list_str_schema(path: str): 1abcd

35 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1OPQ

36 [ 

37 { 

38 "required": True, 

39 "schema": { 

40 "title": "P", 

41 "type": "array", 

42 "items": {"type": "string"}, 

43 }, 

44 "name": "p", 

45 "in": "query", 

46 } 

47 ] 

48 ) 

49 

50 

51@pytest.mark.parametrize( 1abcd

52 "path", 

53 ["/required-list-str", "/model-required-list-str"], 

54) 

55def test_required_list_str_missing(path: str): 1abcd

56 client = TestClient(app) 1qrs

57 response = client.get(path) 1qrs

58 assert response.status_code == 422 1qrs

59 assert response.json() == { 1qrs

60 "detail": [ 

61 { 

62 "type": "missing", 

63 "loc": ["query", "p"], 

64 "msg": "Field required", 

65 "input": IsOneOf(None, {}), 

66 } 

67 ] 

68 } 

69 

70 

71@pytest.mark.parametrize( 1abcd

72 "path", 

73 ["/required-list-str", "/model-required-list-str"], 

74) 

75def test_required_list_str(path: str): 1abcd

76 client = TestClient(app) 1efg

77 response = client.get(f"{path}?p=hello&p=world") 1efg

78 assert response.status_code == 200 1efg

79 assert response.json() == {"p": ["hello", "world"]} 1efg

80 

81 

82# ===================================================================================== 

83# Alias 

84 

85 

86@app.get("/required-list-alias") 1abcd

87async def read_required_list_alias(p: Annotated[list[str], Query(alias="p_alias")]): 1abcd

88 return {"p": p} 1hij

89 

90 

91class QueryModelRequiredListAlias(BaseModel): 1abcd

92 p: list[str] = Field(alias="p_alias") 1abcd

93 

94 

95@app.get("/model-required-list-alias") 1abcd

96async def read_model_required_list_alias( 1abcd

97 p: Annotated[QueryModelRequiredListAlias, Query()], 

98): 

99 return {"p": p.p} 1hij

100 

101 

102@pytest.mark.parametrize( 1abcd

103 "path", 

104 ["/required-list-alias", "/model-required-list-alias"], 

105) 

106def test_required_list_str_alias_schema(path: str): 1abcd

107 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1RST

108 [ 

109 { 

110 "required": True, 

111 "schema": { 

112 "title": "P Alias", 

113 "type": "array", 

114 "items": {"type": "string"}, 

115 }, 

116 "name": "p_alias", 

117 "in": "query", 

118 } 

119 ] 

120 ) 

121 

122 

123@pytest.mark.parametrize( 1abcd

124 "path", 

125 ["/required-list-alias", "/model-required-list-alias"], 

126) 

127def test_required_list_alias_missing(path: str): 1abcd

128 client = TestClient(app) 1tuv

129 response = client.get(path) 1tuv

130 assert response.status_code == 422 1tuv

131 assert response.json() == { 1tuv

132 "detail": [ 

133 { 

134 "type": "missing", 

135 "loc": ["query", "p_alias"], 

136 "msg": "Field required", 

137 "input": IsOneOf(None, {}), 

138 } 

139 ] 

140 } 

141 

142 

143@pytest.mark.parametrize( 1abcd

144 "path", 

145 [ 

146 "/required-list-alias", 

147 "/model-required-list-alias", 

148 ], 

149) 

150def test_required_list_alias_by_name(path: str): 1abcd

151 client = TestClient(app) 1wxy

152 response = client.get(f"{path}?p=hello&p=world") 1wxy

153 assert response.status_code == 422 1wxy

154 assert response.json() == { 1wxy

155 "detail": [ 

156 { 

157 "type": "missing", 

158 "loc": ["query", "p_alias"], 

159 "msg": "Field required", 

160 "input": IsOneOf(None, {"p": ["hello", "world"]}), 

161 } 

162 ] 

163 } 

164 

165 

166@pytest.mark.parametrize( 1abcd

167 "path", 

168 [ 

169 "/required-list-alias", 

170 "/model-required-list-alias", 

171 ], 

172) 

173def test_required_list_alias_by_alias(path: str): 1abcd

174 client = TestClient(app) 1hij

175 response = client.get(f"{path}?p_alias=hello&p_alias=world") 1hij

176 assert response.status_code == 200, response.text 1hij

177 assert response.json() == {"p": ["hello", "world"]} 1hij

178 

179 

180# ===================================================================================== 

181# Validation alias 

182 

183 

184@app.get("/required-list-validation-alias") 1abcd

185def read_required_list_validation_alias( 1abcd

186 p: Annotated[list[str], Query(validation_alias="p_val_alias")], 

187): 

188 return {"p": p} 1klm

189 

190 

191class QueryModelRequiredListValidationAlias(BaseModel): 1abcd

192 p: list[str] = Field(validation_alias="p_val_alias") 1abcd

193 

194 

195@app.get("/model-required-list-validation-alias") 1abcd

196async def read_model_required_list_validation_alias( 1abcd

197 p: Annotated[QueryModelRequiredListValidationAlias, Query()], 

198): 

199 return {"p": p.p} 1klm

200 

201 

202@pytest.mark.parametrize( 1abcd

203 "path", 

204 ["/required-list-validation-alias", "/model-required-list-validation-alias"], 

205) 

206def test_required_list_validation_alias_schema(path: str): 1abcd

207 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1UVW

208 [ 

209 { 

210 "required": True, 

211 "schema": { 

212 "title": "P Val Alias", 

213 "type": "array", 

214 "items": {"type": "string"}, 

215 }, 

216 "name": "p_val_alias", 

217 "in": "query", 

218 } 

219 ] 

220 ) 

221 

222 

223@pytest.mark.parametrize( 1abcd

224 "path", 

225 [ 

226 "/required-list-validation-alias", 

227 "/model-required-list-validation-alias", 

228 ], 

229) 

230def test_required_list_validation_alias_missing(path: str): 1abcd

231 client = TestClient(app) 1zAB

232 response = client.get(path) 1zAB

233 assert response.status_code == 422 1zAB

234 assert response.json() == { 1zAB

235 "detail": [ 

236 { 

237 "type": "missing", 

238 "loc": [ 

239 "query", 

240 "p_val_alias", 

241 ], 

242 "msg": "Field required", 

243 "input": IsOneOf(None, {}), 

244 } 

245 ] 

246 } 

247 

248 

249@pytest.mark.parametrize( 1abcd

250 "path", 

251 [ 

252 "/required-list-validation-alias", 

253 "/model-required-list-validation-alias", 

254 ], 

255) 

256def test_required_list_validation_alias_by_name(path: str): 1abcd

257 client = TestClient(app) 1CDE

258 response = client.get(f"{path}?p=hello&p=world") 1CDE

259 assert response.status_code == 422 1CDE

260 

261 assert response.json() == { 1CDE

262 "detail": [ 

263 { 

264 "type": "missing", 

265 "loc": ["query", "p_val_alias"], 

266 "msg": "Field required", 

267 "input": IsOneOf(None, {"p": ["hello", "world"]}), 

268 } 

269 ] 

270 } 

271 

272 

273@pytest.mark.parametrize( 1abcd

274 "path", 

275 ["/required-list-validation-alias", "/model-required-list-validation-alias"], 

276) 

277def test_required_list_validation_alias_by_validation_alias(path: str): 1abcd

278 client = TestClient(app) 1klm

279 response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world") 1klm

280 assert response.status_code == 200, response.text 1klm

281 

282 assert response.json() == {"p": ["hello", "world"]} 1klm

283 

284 

285# ===================================================================================== 

286# Alias and validation alias 

287 

288 

289@app.get("/required-list-alias-and-validation-alias") 1abcd

290def read_required_list_alias_and_validation_alias( 1abcd

291 p: Annotated[list[str], Query(alias="p_alias", validation_alias="p_val_alias")], 

292): 

293 return {"p": p} 1nop

294 

295 

296class QueryModelRequiredListAliasAndValidationAlias(BaseModel): 1abcd

297 p: list[str] = Field(alias="p_alias", validation_alias="p_val_alias") 1abcd

298 

299 

300@app.get("/model-required-list-alias-and-validation-alias") 1abcd

301def read_model_required_list_alias_and_validation_alias( 1abcd

302 p: Annotated[QueryModelRequiredListAliasAndValidationAlias, Query()], 

303): 

304 return {"p": p.p} 1nop

305 

306 

307@pytest.mark.parametrize( 1abcd

308 "path", 

309 [ 

310 "/required-list-alias-and-validation-alias", 

311 "/model-required-list-alias-and-validation-alias", 

312 ], 

313) 

314def test_required_list_alias_and_validation_alias_schema(path: str): 1abcd

315 assert app.openapi()["paths"][path]["get"]["parameters"] == snapshot( 1XYZ

316 [ 

317 { 

318 "required": True, 

319 "schema": { 

320 "title": "P Val Alias", 

321 "type": "array", 

322 "items": {"type": "string"}, 

323 }, 

324 "name": "p_val_alias", 

325 "in": "query", 

326 } 

327 ] 

328 ) 

329 

330 

331@pytest.mark.parametrize( 1abcd

332 "path", 

333 [ 

334 "/required-list-alias-and-validation-alias", 

335 "/model-required-list-alias-and-validation-alias", 

336 ], 

337) 

338def test_required_list_alias_and_validation_alias_missing(path: str): 1abcd

339 client = TestClient(app) 1FGH

340 response = client.get(path) 1FGH

341 assert response.status_code == 422 1FGH

342 assert response.json() == { 1FGH

343 "detail": [ 

344 { 

345 "type": "missing", 

346 "loc": [ 

347 "query", 

348 "p_val_alias", 

349 ], 

350 "msg": "Field required", 

351 "input": IsOneOf(None, {}), 

352 } 

353 ] 

354 } 

355 

356 

357@pytest.mark.parametrize( 1abcd

358 "path", 

359 [ 

360 "/required-list-alias-and-validation-alias", 

361 "/model-required-list-alias-and-validation-alias", 

362 ], 

363) 

364def test_required_list_alias_and_validation_alias_by_name(path: str): 1abcd

365 client = TestClient(app) 1IJK

366 response = client.get(f"{path}?p=hello&p=world") 1IJK

367 assert response.status_code == 422 1IJK

368 assert response.json() == { 1IJK

369 "detail": [ 

370 { 

371 "type": "missing", 

372 "loc": [ 

373 "query", 

374 "p_val_alias", 

375 ], 

376 "msg": "Field required", 

377 "input": IsOneOf( 

378 None, 

379 { 

380 "p": [ 

381 "hello", 

382 "world", 

383 ] 

384 }, 

385 ), 

386 } 

387 ] 

388 } 

389 

390 

391@pytest.mark.parametrize( 1abcd

392 "path", 

393 [ 

394 "/required-list-alias-and-validation-alias", 

395 "/model-required-list-alias-and-validation-alias", 

396 ], 

397) 

398def test_required_list_alias_and_validation_alias_by_alias(path: str): 1abcd

399 client = TestClient(app) 1LMN

400 response = client.get(f"{path}?p_alias=hello&p_alias=world") 1LMN

401 assert response.status_code == 422 1LMN

402 assert response.json() == { 1LMN

403 "detail": [ 

404 { 

405 "type": "missing", 

406 "loc": ["query", "p_val_alias"], 

407 "msg": "Field required", 

408 "input": IsOneOf( 

409 None, 

410 {"p_alias": ["hello", "world"]}, 

411 ), 

412 } 

413 ] 

414 } 

415 

416 

417@pytest.mark.parametrize( 1abcd

418 "path", 

419 [ 

420 "/required-list-alias-and-validation-alias", 

421 "/model-required-list-alias-and-validation-alias", 

422 ], 

423) 

424def test_required_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

425 client = TestClient(app) 1nop

426 response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world") 1nop

427 assert response.status_code == 200, response.text 1nop

428 assert response.json() == {"p": ["hello", "world"]} 1nop