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

122 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, 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("/required-bytes", operation_id="required_bytes") 1abcd

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

17 return {"file_size": len(p)} 1efg

18 

19 

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

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

22 return {"file_size": p.size} 1efg

23 

24 

25@pytest.mark.parametrize( 1abcd

26 "path", 

27 [ 

28 "/required-bytes", 

29 "/required-uploadfile", 

30 ], 

31) 

32def test_required_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 "title": "P", 

40 "type": "string", 

41 "contentMediaType": "application/octet-stream", 

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 "/required-bytes", 

54 "/required-uploadfile", 

55 ], 

56) 

57def test_required_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 "/required-bytes", 

77 "/required-uploadfile", 

78 ], 

79) 

80def test_required(path: str): 1abcd

81 client = TestClient(app) 1efg

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

83 assert response.status_code == 200 1efg

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

85 

86 

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

88# Alias 

89 

90 

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

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

93 return {"file_size": len(p)} 1hij

94 

95 

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

97async def read_required_uploadfile_alias( 1abcd

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

99): 

100 return {"file_size": p.size} 1hij

101 

102 

103@pytest.mark.parametrize( 1abcd

104 "path", 

105 [ 

106 "/required-bytes-alias", 

107 "/required-uploadfile-alias", 

108 ], 

109) 

110def test_required_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 "title": "P Alias", 

118 "type": "string", 

119 "contentMediaType": "application/octet-stream", 

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 "/required-bytes-alias", 

132 "/required-uploadfile-alias", 

133 ], 

134) 

135def test_required_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 "/required-bytes-alias", 

155 "/required-uploadfile-alias", 

156 ], 

157) 

158def test_required_alias_by_name(path: str): 1abcd

159 client = TestClient(app) 1wxy

160 response = client.post(path, files=[("p", b"hello")]) 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 "/required-bytes-alias", 

178 "/required-uploadfile-alias", 

179 ], 

180) 

181def test_required_alias_by_alias(path: str): 1abcd

182 client = TestClient(app) 1hij

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

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

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

186 

187 

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

189# Validation alias 

190 

191 

192@app.post( 1abcd

193 "/required-bytes-validation-alias", operation_id="required_bytes_validation_alias" 

194) 

195def read_required_bytes_validation_alias( 1abcd

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

197): 

198 return {"file_size": len(p)} 1klm

199 

200 

201@app.post( 1abcd

202 "/required-uploadfile-validation-alias", 

203 operation_id="required_uploadfile_validation_alias", 

204) 

205def read_required_uploadfile_validation_alias( 1abcd

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

207): 

208 return {"file_size": p.size} 1klm

209 

210 

211@pytest.mark.parametrize( 1abcd

212 "path", 

213 [ 

214 "/required-bytes-validation-alias", 

215 "/required-uploadfile-validation-alias", 

216 ], 

217) 

218def test_required_validation_alias_schema(path: str): 1abcd

219 openapi = app.openapi() 1UVW

220 body_model_name = get_body_model_name(openapi, path) 1UVW

221 

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

223 "properties": { 

224 "p_val_alias": { 

225 "title": "P Val Alias", 

226 "type": "string", 

227 "contentMediaType": "application/octet-stream", 

228 }, 

229 }, 

230 "required": ["p_val_alias"], 

231 "title": body_model_name, 

232 "type": "object", 

233 } 

234 

235 

236@pytest.mark.parametrize( 1abcd

237 "path", 

238 [ 

239 "/required-bytes-validation-alias", 

240 "/required-uploadfile-validation-alias", 

241 ], 

242) 

243def test_required_validation_alias_missing(path: str): 1abcd

244 client = TestClient(app) 1zAB

245 response = client.post(path) 1zAB

246 assert response.status_code == 422 1zAB

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

248 "detail": [ 

249 { 

250 "type": "missing", 

251 "loc": [ 

252 "body", 

253 "p_val_alias", 

254 ], 

255 "msg": "Field required", 

256 "input": None, 

257 } 

258 ] 

259 } 

260 

261 

262@pytest.mark.parametrize( 1abcd

263 "path", 

264 [ 

265 "/required-bytes-validation-alias", 

266 "/required-uploadfile-validation-alias", 

267 ], 

268) 

269def test_required_validation_alias_by_name(path: str): 1abcd

270 client = TestClient(app) 1CDE

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

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

273 

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

275 "detail": [ 

276 { 

277 "type": "missing", 

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

279 "msg": "Field required", 

280 "input": None, 

281 } 

282 ] 

283 } 

284 

285 

286@pytest.mark.parametrize( 1abcd

287 "path", 

288 [ 

289 "/required-bytes-validation-alias", 

290 "/required-uploadfile-validation-alias", 

291 ], 

292) 

293def test_required_validation_alias_by_validation_alias(path: str): 1abcd

294 client = TestClient(app) 1klm

295 response = client.post(path, files=[("p_val_alias", b"hello")]) 1klm

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

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

298 

299 

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

301# Alias and validation alias 

302 

303 

304@app.post( 1abcd

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

306 operation_id="required_bytes_alias_and_validation_alias", 

307) 

308def read_required_bytes_alias_and_validation_alias( 1abcd

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

310): 

311 return {"file_size": len(p)} 1nop

312 

313 

314@app.post( 1abcd

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

316 operation_id="required_uploadfile_alias_and_validation_alias", 

317) 

318def read_required_uploadfile_alias_and_validation_alias( 1abcd

319 p: Annotated[UploadFile, File(alias="p_alias", validation_alias="p_val_alias")], 

320): 

321 return {"file_size": p.size} 1nop

322 

323 

324@pytest.mark.parametrize( 1abcd

325 "path", 

326 [ 

327 "/required-bytes-alias-and-validation-alias", 

328 "/required-uploadfile-alias-and-validation-alias", 

329 ], 

330) 

331def test_required_alias_and_validation_alias_schema(path: str): 1abcd

332 openapi = app.openapi() 1XYZ

333 body_model_name = get_body_model_name(openapi, path) 1XYZ

334 

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

336 "properties": { 

337 "p_val_alias": { 

338 "title": "P Val Alias", 

339 "type": "string", 

340 "contentMediaType": "application/octet-stream", 

341 }, 

342 }, 

343 "required": ["p_val_alias"], 

344 "title": body_model_name, 

345 "type": "object", 

346 } 

347 

348 

349@pytest.mark.parametrize( 1abcd

350 "path", 

351 [ 

352 "/required-bytes-alias-and-validation-alias", 

353 "/required-uploadfile-alias-and-validation-alias", 

354 ], 

355) 

356def test_required_alias_and_validation_alias_missing(path: str): 1abcd

357 client = TestClient(app) 1FGH

358 response = client.post(path) 1FGH

359 assert response.status_code == 422 1FGH

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

361 "detail": [ 

362 { 

363 "type": "missing", 

364 "loc": [ 

365 "body", 

366 "p_val_alias", 

367 ], 

368 "msg": "Field required", 

369 "input": None, 

370 } 

371 ] 

372 } 

373 

374 

375@pytest.mark.parametrize( 1abcd

376 "path", 

377 [ 

378 "/required-bytes-alias-and-validation-alias", 

379 "/required-uploadfile-alias-and-validation-alias", 

380 ], 

381) 

382def test_required_alias_and_validation_alias_by_name(path: str): 1abcd

383 client = TestClient(app) 1IJK

384 response = client.post(path, files={"p": "hello"}) 1IJK

385 assert response.status_code == 422 1IJK

386 

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

388 "detail": [ 

389 { 

390 "type": "missing", 

391 "loc": [ 

392 "body", 

393 "p_val_alias", 

394 ], 

395 "msg": "Field required", 

396 "input": None, 

397 } 

398 ] 

399 } 

400 

401 

402@pytest.mark.parametrize( 1abcd

403 "path", 

404 [ 

405 "/required-bytes-alias-and-validation-alias", 

406 "/required-uploadfile-alias-and-validation-alias", 

407 ], 

408) 

409def test_required_alias_and_validation_alias_by_alias(path: str): 1abcd

410 client = TestClient(app) 1LMN

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

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

413 

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

415 "detail": [ 

416 { 

417 "type": "missing", 

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

419 "msg": "Field required", 

420 "input": None, 

421 } 

422 ] 

423 } 

424 

425 

426@pytest.mark.parametrize( 1abcd

427 "path", 

428 [ 

429 "/required-bytes-alias-and-validation-alias", 

430 "/required-uploadfile-alias-and-validation-alias", 

431 ], 

432) 

433def test_required_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

434 client = TestClient(app) 1nop

435 response = client.post(path, files=[("p_val_alias", b"hello")]) 1nop

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

437 assert response.json() == {"file_size": 5} 1nop