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-21 17:29 +0000

1from typing import Annotated 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[list[str] | None, Query()] = None, 

18): 

19 return {"p": p} 1efghij

20 

21 

22class QueryModelOptionalListStr(BaseModel): 1abcd

23 p: list[str] | None = 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[list[str] | None, Query(alias="p_alias")] = None, 

85): 

86 return {"p": p} 1klmnopqrs

87 

88 

89class QueryModelOptionalListAlias(BaseModel): 1abcd

90 p: list[str] | None = 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[list[str] | None, Query(validation_alias="p_val_alias")] = None, 

166): 

167 return {"p": p} 1tuvwxyzAB

168 

169 

170class QueryModelOptionalListValidationAlias(BaseModel): 1abcd

171 p: list[str] | None = 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 list[str] | None, 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: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias") 1abcd

255 

256 

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

258def read_model_optional_list_alias_and_validation_alias( 1abcd

259 p: Annotated[QueryModelOptionalListAliasAndValidationAlias, Query()], 

260): 

261 return {"p": p.p} 1CDEFGHIJKLMN

262 

263 

264@pytest.mark.parametrize( 1abcd

265 "path", 

266 [ 

267 "/optional-list-alias-and-validation-alias", 

268 "/model-optional-list-alias-and-validation-alias", 

269 ], 

270) 

271def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd

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

273 [ 

274 { 

275 "required": False, 

276 "schema": { 

277 "anyOf": [ 

278 {"items": {"type": "string"}, "type": "array"}, 

279 {"type": "null"}, 

280 ], 

281 "title": "P Val Alias", 

282 }, 

283 "name": "p_val_alias", 

284 "in": "query", 

285 } 

286 ] 

287 ) 

288 

289 

290@pytest.mark.parametrize( 1abcd

291 "path", 

292 [ 

293 "/optional-list-alias-and-validation-alias", 

294 "/model-optional-list-alias-and-validation-alias", 

295 ], 

296) 

297def test_optional_list_alias_and_validation_alias_missing(path: str): 1abcd

298 client = TestClient(app) 1FJN

299 response = client.get(path) 1FJN

300 assert response.status_code == 200 1FJN

301 assert response.json() == {"p": None} 1FJN

302 

303 

304@pytest.mark.parametrize( 1abcd

305 "path", 

306 [ 

307 "/optional-list-alias-and-validation-alias", 

308 "/model-optional-list-alias-and-validation-alias", 

309 ], 

310) 

311def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd

312 client = TestClient(app) 1DHL

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

314 assert response.status_code == 200 1DHL

315 assert response.json() == {"p": None} 1DHL

316 

317 

318@pytest.mark.parametrize( 1abcd

319 "path", 

320 [ 

321 "/optional-list-alias-and-validation-alias", 

322 "/model-optional-list-alias-and-validation-alias", 

323 ], 

324) 

325def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd

326 client = TestClient(app) 1CGK

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

328 assert response.status_code == 200 1CGK

329 assert response.json() == {"p": None} 1CGK

330 

331 

332@pytest.mark.parametrize( 1abcd

333 "path", 

334 [ 

335 "/optional-list-alias-and-validation-alias", 

336 "/model-optional-list-alias-and-validation-alias", 

337 ], 

338) 

339def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

340 client = TestClient(app) 1EIM

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

342 assert response.status_code == 200, response.text 1EIM

343 assert response.json() == { 1EIM

344 "p": [ 

345 "hello", 

346 "world", 

347 ] 

348 }