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

122 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 fastapi import FastAPI, File, UploadFile 1abcd

5from fastapi.testclient import TestClient 1abcd

6 

7from .utils import get_body_model_name 1abcd

8 

9app = FastAPI() 1abcd

10 

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

12# Without aliases 

13 

14 

15@app.post("/list-bytes", operation_id="list_bytes") 1abcd

16async def read_list_bytes(p: Annotated[list[bytes], File()]): 1abcd

17 return {"file_size": [len(file) for file in p]} 1efg

18 

19 

20@app.post("/list-uploadfile", operation_id="list_uploadfile") 1abcd

21async def read_list_uploadfile(p: Annotated[list[UploadFile], File()]): 1abcd

22 return {"file_size": [file.size for file in p]} 1efg

23 

24 

25@pytest.mark.parametrize( 1abcd

26 "path", 

27 [ 

28 "/list-bytes", 

29 "/list-uploadfile", 

30 ], 

31) 

32def test_list_schema(path: str): 1abcd

33 openapi = app.openapi() 1OPQ

34 body_model_name = get_body_model_name(openapi, path) 1OPQ

35 

36 assert app.openapi()["components"]["schemas"][body_model_name] == { 1OPQ

37 "properties": { 

38 "p": { 

39 "type": "array", 

40 "items": {"type": "string", "format": "binary"}, 

41 "title": "P", 

42 }, 

43 }, 

44 "required": ["p"], 

45 "title": body_model_name, 

46 "type": "object", 

47 } 

48 

49 

50@pytest.mark.parametrize( 1abcd

51 "path", 

52 [ 

53 "/list-bytes", 

54 "/list-uploadfile", 

55 ], 

56) 

57def test_list_missing(path: str): 1abcd

58 client = TestClient(app) 1qrs

59 response = client.post(path) 1qrs

60 assert response.status_code == 422 1qrs

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

62 "detail": [ 

63 { 

64 "type": "missing", 

65 "loc": ["body", "p"], 

66 "msg": "Field required", 

67 "input": None, 

68 } 

69 ] 

70 } 

71 

72 

73@pytest.mark.parametrize( 1abcd

74 "path", 

75 [ 

76 "/list-bytes", 

77 "/list-uploadfile", 

78 ], 

79) 

80def test_list(path: str): 1abcd

81 client = TestClient(app) 1efg

82 response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) 1efg

83 assert response.status_code == 200 1efg

84 assert response.json() == {"file_size": [5, 5]} 1efg

85 

86 

87# ===================================================================================== 

88# Alias 

89 

90 

91@app.post("/list-bytes-alias", operation_id="list_bytes_alias") 1abcd

92async def read_list_bytes_alias(p: Annotated[list[bytes], File(alias="p_alias")]): 1abcd

93 return {"file_size": [len(file) for file in p]} 1hij

94 

95 

96@app.post("/list-uploadfile-alias", operation_id="list_uploadfile_alias") 1abcd

97async def read_list_uploadfile_alias( 1abcd

98 p: Annotated[list[UploadFile], File(alias="p_alias")], 

99): 

100 return {"file_size": [file.size for file in p]} 1hij

101 

102 

103@pytest.mark.parametrize( 1abcd

104 "path", 

105 [ 

106 "/list-bytes-alias", 

107 "/list-uploadfile-alias", 

108 ], 

109) 

110def test_list_alias_schema(path: str): 1abcd

111 openapi = app.openapi() 1RST

112 body_model_name = get_body_model_name(openapi, path) 1RST

113 

114 assert app.openapi()["components"]["schemas"][body_model_name] == { 1RST

115 "properties": { 

116 "p_alias": { 

117 "type": "array", 

118 "items": {"type": "string", "format": "binary"}, 

119 "title": "P Alias", 

120 }, 

121 }, 

122 "required": ["p_alias"], 

123 "title": body_model_name, 

124 "type": "object", 

125 } 

126 

127 

128@pytest.mark.parametrize( 1abcd

129 "path", 

130 [ 

131 "/list-bytes-alias", 

132 "/list-uploadfile-alias", 

133 ], 

134) 

135def test_list_alias_missing(path: str): 1abcd

136 client = TestClient(app) 1tuv

137 response = client.post(path) 1tuv

138 assert response.status_code == 422 1tuv

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

140 "detail": [ 

141 { 

142 "type": "missing", 

143 "loc": ["body", "p_alias"], 

144 "msg": "Field required", 

145 "input": None, 

146 } 

147 ] 

148 } 

149 

150 

151@pytest.mark.parametrize( 1abcd

152 "path", 

153 [ 

154 "/list-bytes-alias", 

155 "/list-uploadfile-alias", 

156 ], 

157) 

158def test_list_alias_by_name(path: str): 1abcd

159 client = TestClient(app) 1wxy

160 response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) 1wxy

161 assert response.status_code == 422 1wxy

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

163 "detail": [ 

164 { 

165 "type": "missing", 

166 "loc": ["body", "p_alias"], 

167 "msg": "Field required", 

168 "input": None, 

169 } 

170 ] 

171 } 

172 

173 

174@pytest.mark.parametrize( 1abcd

175 "path", 

176 [ 

177 "/list-bytes-alias", 

178 "/list-uploadfile-alias", 

179 ], 

180) 

181def test_list_alias_by_alias(path: str): 1abcd

182 client = TestClient(app) 1hij

183 response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) 1hij

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

185 assert response.json() == {"file_size": [5, 5]} 1hij

186 

187 

188# ===================================================================================== 

189# Validation alias 

190 

191 

192@app.post("/list-bytes-validation-alias", operation_id="list_bytes_validation_alias") 1abcd

193def read_list_bytes_validation_alias( 1abcd

194 p: Annotated[list[bytes], File(validation_alias="p_val_alias")], 

195): 

196 return {"file_size": [len(file) for file in p]} 1klm

197 

198 

199@app.post( 1abcd

200 "/list-uploadfile-validation-alias", 

201 operation_id="list_uploadfile_validation_alias", 

202) 

203def read_list_uploadfile_validation_alias( 1abcd

204 p: Annotated[list[UploadFile], File(validation_alias="p_val_alias")], 

205): 

206 return {"file_size": [file.size for file in p]} 1klm

207 

208 

209@pytest.mark.parametrize( 1abcd

210 "path", 

211 [ 

212 "/list-bytes-validation-alias", 

213 "/list-uploadfile-validation-alias", 

214 ], 

215) 

216def test_list_validation_alias_schema(path: str): 1abcd

217 openapi = app.openapi() 1UVW

218 body_model_name = get_body_model_name(openapi, path) 1UVW

219 

220 assert app.openapi()["components"]["schemas"][body_model_name] == { 1UVW

221 "properties": { 

222 "p_val_alias": { 

223 "type": "array", 

224 "items": {"type": "string", "format": "binary"}, 

225 "title": "P Val Alias", 

226 }, 

227 }, 

228 "required": ["p_val_alias"], 

229 "title": body_model_name, 

230 "type": "object", 

231 } 

232 

233 

234@pytest.mark.parametrize( 1abcd

235 "path", 

236 [ 

237 "/list-bytes-validation-alias", 

238 "/list-uploadfile-validation-alias", 

239 ], 

240) 

241def test_list_validation_alias_missing(path: str): 1abcd

242 client = TestClient(app) 1zAB

243 response = client.post(path) 1zAB

244 assert response.status_code == 422 1zAB

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

246 "detail": [ 

247 { 

248 "type": "missing", 

249 "loc": [ 

250 "body", 

251 "p_val_alias", 

252 ], 

253 "msg": "Field required", 

254 "input": None, 

255 } 

256 ] 

257 } 

258 

259 

260@pytest.mark.parametrize( 1abcd

261 "path", 

262 [ 

263 "/list-bytes-validation-alias", 

264 "/list-uploadfile-validation-alias", 

265 ], 

266) 

267def test_list_validation_alias_by_name(path: str): 1abcd

268 client = TestClient(app) 1CDE

269 response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) 1CDE

270 assert response.status_code == 422, response.text 1CDE

271 

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

273 "detail": [ 

274 { 

275 "type": "missing", 

276 "loc": ["body", "p_val_alias"], 

277 "msg": "Field required", 

278 "input": None, 

279 } 

280 ] 

281 } 

282 

283 

284@pytest.mark.parametrize( 1abcd

285 "path", 

286 [ 

287 "/list-bytes-validation-alias", 

288 "/list-uploadfile-validation-alias", 

289 ], 

290) 

291def test_list_validation_alias_by_validation_alias(path: str): 1abcd

292 client = TestClient(app) 1klm

293 response = client.post( 1klm

294 path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] 

295 ) 

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

297 assert response.json() == {"file_size": [5, 5]} 1klm

298 

299 

300# ===================================================================================== 

301# Alias and validation alias 

302 

303 

304@app.post( 1abcd

305 "/list-bytes-alias-and-validation-alias", 

306 operation_id="list_bytes_alias_and_validation_alias", 

307) 

308def read_list_bytes_alias_and_validation_alias( 1abcd

309 p: Annotated[list[bytes], File(alias="p_alias", validation_alias="p_val_alias")], 

310): 

311 return {"file_size": [len(file) for file in p]} 1nop

312 

313 

314@app.post( 1abcd

315 "/list-uploadfile-alias-and-validation-alias", 

316 operation_id="list_uploadfile_alias_and_validation_alias", 

317) 

318def read_list_uploadfile_alias_and_validation_alias( 1abcd

319 p: Annotated[ 

320 list[UploadFile], File(alias="p_alias", validation_alias="p_val_alias") 

321 ], 

322): 

323 return {"file_size": [file.size for file in p]} 1nop

324 

325 

326@pytest.mark.parametrize( 1abcd

327 "path", 

328 [ 

329 "/list-bytes-alias-and-validation-alias", 

330 "/list-uploadfile-alias-and-validation-alias", 

331 ], 

332) 

333def test_list_alias_and_validation_alias_schema(path: str): 1abcd

334 openapi = app.openapi() 1XYZ

335 body_model_name = get_body_model_name(openapi, path) 1XYZ

336 

337 assert app.openapi()["components"]["schemas"][body_model_name] == { 1XYZ

338 "properties": { 

339 "p_val_alias": { 

340 "type": "array", 

341 "items": {"type": "string", "format": "binary"}, 

342 "title": "P Val Alias", 

343 }, 

344 }, 

345 "required": ["p_val_alias"], 

346 "title": body_model_name, 

347 "type": "object", 

348 } 

349 

350 

351@pytest.mark.parametrize( 1abcd

352 "path", 

353 [ 

354 "/list-bytes-alias-and-validation-alias", 

355 "/list-uploadfile-alias-and-validation-alias", 

356 ], 

357) 

358def test_list_alias_and_validation_alias_missing(path: str): 1abcd

359 client = TestClient(app) 1FGH

360 response = client.post(path) 1FGH

361 assert response.status_code == 422 1FGH

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

363 "detail": [ 

364 { 

365 "type": "missing", 

366 "loc": [ 

367 "body", 

368 "p_val_alias", 

369 ], 

370 "msg": "Field required", 

371 "input": None, 

372 } 

373 ] 

374 } 

375 

376 

377@pytest.mark.parametrize( 1abcd

378 "path", 

379 [ 

380 "/list-bytes-alias-and-validation-alias", 

381 "/list-uploadfile-alias-and-validation-alias", 

382 ], 

383) 

384def test_list_alias_and_validation_alias_by_name(path: str): 1abcd

385 client = TestClient(app) 1IJK

386 response = client.post(path, files=[("p", "hello"), ("p", "world")]) 1IJK

387 assert response.status_code == 422 1IJK

388 

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

390 "detail": [ 

391 { 

392 "type": "missing", 

393 "loc": [ 

394 "body", 

395 "p_val_alias", 

396 ], 

397 "msg": "Field required", 

398 "input": None, 

399 } 

400 ] 

401 } 

402 

403 

404@pytest.mark.parametrize( 1abcd

405 "path", 

406 [ 

407 "/list-bytes-alias-and-validation-alias", 

408 "/list-uploadfile-alias-and-validation-alias", 

409 ], 

410) 

411def test_list_alias_and_validation_alias_by_alias(path: str): 1abcd

412 client = TestClient(app) 1LMN

413 response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) 1LMN

414 assert response.status_code == 422, response.text 1LMN

415 

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

417 "detail": [ 

418 { 

419 "type": "missing", 

420 "loc": ["body", "p_val_alias"], 

421 "msg": "Field required", 

422 "input": None, 

423 } 

424 ] 

425 } 

426 

427 

428@pytest.mark.parametrize( 1abcd

429 "path", 

430 [ 

431 "/list-bytes-alias-and-validation-alias", 

432 "/list-uploadfile-alias-and-validation-alias", 

433 ], 

434) 

435def test_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

436 client = TestClient(app) 1nop

437 response = client.post( 1nop

438 path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] 

439 ) 

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

441 assert response.json() == {"file_size": [5, 5]} 1nop