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-04-06 01:24 +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} 1efghijk

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} 1lmnopqr

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( 2lbmbnbobpbqbrbsb

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) 1mfngpirk

62 response = client.get(path) 1mfngpirk

63 assert response.status_code == 200, response.text 1mfngpirk

64 assert response.json() == {"p": None} 1mfngpirk

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) 1le|}ohqj

73 response = client.get(f"{path}?p=hello&p=world") 1le|}ohqj

74 assert response.status_code == 200 1le|}ohqj

75 assert response.json() == {"p": ["hello", "world"]} 1le|}ohqj

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} 1stuvwxyzAB

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} 1CDEFGHIJKL

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( 2tbubvbwbxbybzbAb

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) 1EuFvIyLB

129 response = client.get(path) 1EuFvIyLB

130 assert response.status_code == 200 1EuFvIyLB

131 assert response.json() == {"p": None} 1EuFvIyLB

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) 2D t ~ abH x K A

140 response = client.get(f"{path}?p=hello&p=world") 2D t ~ abH x K A

141 assert response.status_code == 200 2D t ~ abH x K A

142 assert response.json() == {"p": None} 2D t ~ abH x K A

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) 2C s bbcbG w J z

154 response = client.get(f"{path}?p_alias=hello&p_alias=world") 2C s bbcbG w J z

155 assert response.status_code == 200 2C s bbcbG w J z

156 assert response.json() == {"p": ["hello", "world"]} 2C s bbcbG w J z

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} 1MNOPQRSTUVW

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} 1XYZ01234567

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( 2BbCbDbEbFbGbHb

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) 1ZO1Q4T7W

210 response = client.get(path) 1ZO1Q4T7W

211 assert response.status_code == 200 1ZO1Q4T7W

212 assert response.json() == {"p": None} 1ZO1Q4T7W

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) 2X M dbeb2 R 5 U

224 response = client.get(f"{path}?p=hello&p=world") 2X M dbeb2 R 5 U

225 assert response.status_code == 200 2X M dbeb2 R 5 U

226 assert response.json() == {"p": None} 2X M dbeb2 R 5 U

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) 1YN0P3S6V

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

236 assert response.status_code == 200, response.text 1YN0P3S6V

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

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} 189!#$%'()*+,-

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} 1./:;=?@[]^_`{

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( 2IbJbKbLbMbNbObPb

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) 1;#=$]){-

299 response = client.get(path) 1;#=$]){-

300 assert response.status_code == 200 1;#=$]){-

301 assert response.json() == {"p": None} 1;#=$]){-

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) 2/ 9 fbgb@ ' _ +

313 response = client.get(f"{path}?p=hello&p=world") 2/ 9 fbgb@ ' _ +

314 assert response.status_code == 200 2/ 9 fbgb@ ' _ +

315 assert response.json() == {"p": None} 2/ 9 fbgb@ ' _ +

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) 2. 8 hbib? % ^ *

327 response = client.get(f"{path}?p_alias=hello&p_alias=world") 2. 8 hbib? % ^ *

328 assert response.status_code == 200 2. 8 hbib? % ^ *

329 assert response.json() == {"p": None} 2. 8 hbib? % ^ *

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) 2: ! jbkb[ ( ` ,

341 response = client.get(f"{path}?p_val_alias=hello&p_val_alias=world") 2: ! jbkb[ ( ` ,

342 assert response.status_code == 200, response.text 2: ! jbkb[ ( ` ,

343 assert response.json() == { 2: ! jbkb[ ( ` ,

344 "p": [ 

345 "hello", 

346 "world", 

347 ] 

348 }