Coverage for tests / test_request_params / test_form / test_optional_list.py: 100%

131 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, Form 1abcd

5from fastapi.testclient import TestClient 1abcd

6from pydantic import BaseModel, Field 1abcd

7 

8from .utils import get_body_model_name 1abcd

9 

10app = FastAPI() 1abcd

11 

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

13# Without aliases 

14 

15 

16@app.post("/optional-list-str", operation_id="optional_list_str") 1abcd

17async def read_optional_list_str( 1abcd

18 p: Annotated[list[str] | None, Form()] = None, 

19): 

20 return {"p": p} 1efghijkl

21 

22 

23class FormModelOptionalListStr(BaseModel): 1abcd

24 p: list[str] | None = None 1abcd

25 

26 

27@app.post("/model-optional-list-str", operation_id="model_optional_list_str") 1abcd

28async def read_model_optional_list_str(p: Annotated[FormModelOptionalListStr, Form()]): 1abcd

29 return {"p": p.p} 1mnopqrst

30 

31 

32@pytest.mark.parametrize( 1abcd

33 "path", 

34 ["/optional-list-str", "/model-optional-list-str"], 

35) 

36def test_optional_list_str_schema(path: str): 1abcd

37 openapi = app.openapi() 2lbmbnbobpbqbrbsb

38 body_model_name = get_body_model_name(openapi, path) 2lbmbnbobpbqbrbsb

39 

40 assert app.openapi()["components"]["schemas"][body_model_name] == { 2lbmbnbobpbqbrbsb

41 "properties": { 

42 "p": { 

43 "anyOf": [ 

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

45 {"type": "null"}, 

46 ], 

47 "title": "P", 

48 }, 

49 }, 

50 "title": body_model_name, 

51 "type": "object", 

52 } 

53 

54 

55@pytest.mark.parametrize( 1abcd

56 "path", 

57 ["/optional-list-str", "/model-optional-list-str"], 

58) 

59def test_optional_list_str_missing(path: str): 1abcd

60 client = TestClient(app) 1nfphrjtl

61 response = client.post(path) 1nfphrjtl

62 assert response.status_code == 200, response.text 1nfphrjtl

63 assert response.json() == {"p": None} 1nfphrjtl

64 

65 

66@pytest.mark.parametrize( 1abcd

67 "path", 

68 ["/optional-list-str", "/model-optional-list-str"], 

69) 

70def test_optional_list_str(path: str): 1abcd

71 client = TestClient(app) 1meogqisk

72 response = client.post(path, data={"p": ["hello", "world"]}) 1meogqisk

73 assert response.status_code == 200 1meogqisk

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

75 

76 

77# ===================================================================================== 

78# Alias 

79 

80 

81@app.post("/optional-list-alias", operation_id="optional_list_alias") 1abcd

82async def read_optional_list_alias( 1abcd

83 p: Annotated[list[str] | None, Form(alias="p_alias")] = None, 

84): 

85 return {"p": p} 1uvwxyzABCDE

86 

87 

88class FormModelOptionalListAlias(BaseModel): 1abcd

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

90 

91 

92@app.post("/model-optional-list-alias", operation_id="model_optional_list_alias") 1abcd

93async def read_model_optional_list_alias( 1abcd

94 p: Annotated[FormModelOptionalListAlias, Form()], 

95): 

96 return {"p": p.p} 1FGHIJKLMNOP

97 

98 

99@pytest.mark.parametrize( 1abcd

100 "path", 

101 [ 

102 "/optional-list-alias", 

103 "/model-optional-list-alias", 

104 ], 

105) 

106def test_optional_list_str_alias_schema(path: str): 1abcd

107 openapi = app.openapi() 2tbubvbwbxbybzbAb

108 body_model_name = get_body_model_name(openapi, path) 2tbubvbwbxbybzbAb

109 

110 assert app.openapi()["components"]["schemas"][body_model_name] == { 2tbubvbwbxbybzbAb

111 "properties": { 

112 "p_alias": { 

113 "anyOf": [ 

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

115 {"type": "null"}, 

116 ], 

117 "title": "P Alias", 

118 }, 

119 }, 

120 "title": body_model_name, 

121 "type": "object", 

122 } 

123 

124 

125@pytest.mark.parametrize( 1abcd

126 "path", 

127 ["/optional-list-alias", "/model-optional-list-alias"], 

128) 

129def test_optional_list_alias_missing(path: str): 1abcd

130 client = TestClient(app) 1HwJyMBPE

131 response = client.post(path) 1HwJyMBPE

132 assert response.status_code == 200 1HwJyMBPE

133 assert response.json() == {"p": None} 1HwJyMBPE

134 

135 

136@pytest.mark.parametrize( 1abcd

137 "path", 

138 ["/optional-list-alias", "/model-optional-list-alias"], 

139) 

140def test_optional_list_alias_by_name(path: str): 1abcd

141 client = TestClient(app) 1GvIxLAOD

142 response = client.post(path, data={"p": ["hello", "world"]}) 1GvIxLAOD

143 assert response.status_code == 200 1GvIxLAOD

144 assert response.json() == {"p": None} 1GvIxLAOD

145 

146 

147@pytest.mark.parametrize( 1abcd

148 "path", 

149 ["/optional-list-alias", "/model-optional-list-alias"], 

150) 

151def test_optional_list_alias_by_alias(path: str): 1abcd

152 client = TestClient(app) 2F u bbcbK z N C

153 response = client.post(path, data={"p_alias": ["hello", "world"]}) 2F u bbcbK z N C

154 assert response.status_code == 200 2F u bbcbK z N C

155 assert response.json() == {"p": ["hello", "world"]} 2F u bbcbK z N C

156 

157 

158# ===================================================================================== 

159# Validation alias 

160 

161 

162@app.post( 1abcd

163 "/optional-list-validation-alias", operation_id="optional_list_validation_alias" 

164) 

165def read_optional_list_validation_alias( 1abcd

166 p: Annotated[list[str] | None, Form(validation_alias="p_val_alias")] = None, 

167): 

168 return {"p": p} 1QRSTUVWXYZ

169 

170 

171class FormModelOptionalListValidationAlias(BaseModel): 1abcd

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

173 

174 

175@app.post( 1abcd

176 "/model-optional-list-validation-alias", 

177 operation_id="model_optional_list_validation_alias", 

178) 

179def read_model_optional_list_validation_alias( 1abcd

180 p: Annotated[FormModelOptionalListValidationAlias, Form()], 

181): 

182 return {"p": p.p} 10123456789

183 

184 

185@pytest.mark.parametrize( 1abcd

186 "path", 

187 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"], 

188) 

189def test_optional_list_validation_alias_schema(path: str): 1abcd

190 openapi = app.openapi() 2BbCbDbEbFbGbHbIb

191 body_model_name = get_body_model_name(openapi, path) 2BbCbDbEbFbGbHbIb

192 

193 assert app.openapi()["components"]["schemas"][body_model_name] == { 2BbCbDbEbFbGbHbIb

194 "properties": { 

195 "p_val_alias": { 

196 "anyOf": [ 

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

198 {"type": "null"}, 

199 ], 

200 "title": "P Val Alias", 

201 }, 

202 }, 

203 "title": body_model_name, 

204 "type": "object", 

205 } 

206 

207 

208@pytest.mark.parametrize( 1abcd

209 "path", 

210 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"], 

211) 

212def test_optional_list_validation_alias_missing(path: str): 1abcd

213 client = TestClient(app) 12S3T6W9Z

214 response = client.post(path) 12S3T6W9Z

215 assert response.status_code == 200 12S3T6W9Z

216 assert response.json() == {"p": None} 12S3T6W9Z

217 

218 

219@pytest.mark.parametrize( 1abcd

220 "path", 

221 [ 

222 "/optional-list-validation-alias", 

223 "/model-optional-list-validation-alias", 

224 ], 

225) 

226def test_optional_list_validation_alias_by_name(path: str): 1abcd

227 client = TestClient(app) 20 Q dbeb4 U 7 X

228 response = client.post(path, data={"p": ["hello", "world"]}) 20 Q dbeb4 U 7 X

229 assert response.status_code == 200 20 Q dbeb4 U 7 X

230 assert response.json() == {"p": None} 20 Q dbeb4 U 7 X

231 

232 

233@pytest.mark.parametrize( 1abcd

234 "path", 

235 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"], 

236) 

237def test_optional_list_validation_alias_by_validation_alias(path: str): 1abcd

238 client = TestClient(app) 21 R fbgb5 V 8 Y

239 response = client.post(path, data={"p_val_alias": ["hello", "world"]}) 21 R fbgb5 V 8 Y

240 assert response.status_code == 200, response.text 21 R fbgb5 V 8 Y

241 assert response.json() == {"p": ["hello", "world"]} 21 R fbgb5 V 8 Y

242 

243 

244# ===================================================================================== 

245# Alias and validation alias 

246 

247 

248@app.post( 1abcd

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

250 operation_id="optional_list_alias_and_validation_alias", 

251) 

252def read_optional_list_alias_and_validation_alias( 1abcd

253 p: Annotated[ 

254 list[str] | None, Form(alias="p_alias", validation_alias="p_val_alias") 

255 ] = None, 

256): 

257 return {"p": p} 1!#$%'()*+,-./:

258 

259 

260class FormModelOptionalListAliasAndValidationAlias(BaseModel): 1abcd

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

262 

263 

264@app.post( 1abcd

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

266 operation_id="model_optional_list_alias_and_validation_alias", 

267) 

268def read_model_optional_list_alias_and_validation_alias( 1abcd

269 p: Annotated[FormModelOptionalListAliasAndValidationAlias, Form()], 

270): 

271 return {"p": p.p} 2; = ? @ [ ] ^ _ ` { | } ~ ab

272 

273 

274@pytest.mark.parametrize( 1abcd

275 "path", 

276 [ 

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

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

279 ], 

280) 

281def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd

282 openapi = app.openapi() 2JbKbLbMbNbObPb

283 body_model_name = get_body_model_name(openapi, path) 2JbKbLbMbNbObPb

284 

285 assert app.openapi()["components"]["schemas"][body_model_name] == { 2JbKbLbMbNbObPb

286 "properties": { 

287 "p_val_alias": { 

288 "anyOf": [ 

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

290 {"type": "null"}, 

291 ], 

292 "title": "P Val Alias", 

293 }, 

294 }, 

295 "title": body_model_name, 

296 "type": "object", 

297 } 

298 

299 

300@pytest.mark.parametrize( 1abcd

301 "path", 

302 [ 

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

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

305 ], 

306) 

307def test_optional_list_alias_and_validation_alias_missing(path: str): 1abcd

308 client = TestClient(app) 2@ % ] ( { , ab:

309 response = client.post(path) 2@ % ] ( { , ab:

310 assert response.status_code == 200 2@ % ] ( { , ab:

311 assert response.json() == {"p": None} 2@ % ] ( { , ab:

312 

313 

314@pytest.mark.parametrize( 1abcd

315 "path", 

316 [ 

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

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

319 ], 

320) 

321def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd

322 client = TestClient(app) 2= # hbib_ * } .

323 response = client.post(path, data={"p": ["hello", "world"]}) 2= # hbib_ * } .

324 assert response.status_code == 200 2= # hbib_ * } .

325 assert response.json() == {"p": None} 2= # hbib_ * } .

326 

327 

328@pytest.mark.parametrize( 1abcd

329 "path", 

330 [ 

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

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

333 ], 

334) 

335def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd

336 client = TestClient(app) 1;!['^)|-

337 response = client.post(path, data={"p_alias": ["hello", "world"]}) 1;!['^)|-

338 assert response.status_code == 200 1;!['^)|-

339 assert response.json() == {"p": None} 1;!['^)|-

340 

341 

342@pytest.mark.parametrize( 1abcd

343 "path", 

344 [ 

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

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

347 ], 

348) 

349def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

350 client = TestClient(app) 2? $ jbkb` + ~ /

351 response = client.post(path, data={"p_val_alias": ["hello", "world"]}) 2? $ jbkb` + ~ /

352 assert response.status_code == 200, response.text 2? $ jbkb` + ~ /

353 assert response.json() == { 2? $ jbkb` + ~ /

354 "p": [ 

355 "hello", 

356 "world", 

357 ] 

358 }