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

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

16async def read_optional_bytes(p: Annotated[bytes | None, File()] = None): 1abcd

17 return {"file_size": len(p) if p else None} 1efghijk

18 

19 

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

21async def read_optional_uploadfile(p: Annotated[UploadFile | None, File()] = None): 1abcd

22 return {"file_size": p.size if p else None} 1lmnopqr

23 

24 

25@pytest.mark.parametrize( 1abcd

26 "path", 

27 [ 

28 "/optional-bytes", 

29 "/optional-uploadfile", 

30 ], 

31) 

32def test_optional_schema(path: str): 1abcd

33 openapi = app.openapi() 2ibjbkblbmbnbobpb

34 body_model_name = get_body_model_name(openapi, path) 2ibjbkblbmbnbobpb

35 

36 assert app.openapi()["components"]["schemas"][body_model_name] == { 2ibjbkblbmbnbobpb

37 "properties": { 

38 "p": { 

39 "anyOf": [ 

40 {"type": "string", "contentMediaType": "application/octet-stream"}, 

41 {"type": "null"}, 

42 ], 

43 "title": "P", 

44 } 

45 }, 

46 "title": body_model_name, 

47 "type": "object", 

48 } 

49 

50 

51@pytest.mark.parametrize( 1abcd

52 "path", 

53 [ 

54 "/optional-bytes", 

55 "/optional-uploadfile", 

56 ], 

57) 

58def test_optional_missing(path: str): 1abcd

59 client = TestClient(app) 1fmgnipkr

60 response = client.post(path) 1fmgnipkr

61 assert response.status_code == 200, response.text 1fmgnipkr

62 assert response.json() == {"file_size": None} 1fmgnipkr

63 

64 

65@pytest.mark.parametrize( 1abcd

66 "path", 

67 [ 

68 "/optional-bytes", 

69 "/optional-uploadfile", 

70 ], 

71) 

72def test_optional(path: str): 1abcd

73 client = TestClient(app) 1el|}hojq

74 response = client.post(path, files=[("p", b"hello")]) 1el|}hojq

75 assert response.status_code == 200 1el|}hojq

76 assert response.json() == {"file_size": 5} 1el|}hojq

77 

78 

79# ===================================================================================== 

80# Alias 

81 

82 

83@app.post("/optional-bytes-alias", operation_id="optional_bytes_alias") 1abcd

84async def read_optional_bytes_alias( 1abcd

85 p: Annotated[bytes | None, File(alias="p_alias")] = None, 

86): 

87 return {"file_size": len(p) if p else None} 1stuvwxyzABC

88 

89 

90@app.post("/optional-uploadfile-alias", operation_id="optional_uploadfile_alias") 1abcd

91async def read_optional_uploadfile_alias( 1abcd

92 p: Annotated[UploadFile | None, File(alias="p_alias")] = None, 

93): 

94 return {"file_size": p.size if p else None} 1DEFGHIJKLMN

95 

96 

97@pytest.mark.parametrize( 1abcd

98 "path", 

99 [ 

100 "/optional-bytes-alias", 

101 "/optional-uploadfile-alias", 

102 ], 

103) 

104def test_optional_alias_schema(path: str): 1abcd

105 openapi = app.openapi() 2qbrbsbtbubvbwb

106 body_model_name = get_body_model_name(openapi, path) 2qbrbsbtbubvbwb

107 

108 assert app.openapi()["components"]["schemas"][body_model_name] == { 2qbrbsbtbubvbwb

109 "properties": { 

110 "p_alias": { 

111 "anyOf": [ 

112 {"type": "string", "contentMediaType": "application/octet-stream"}, 

113 {"type": "null"}, 

114 ], 

115 "title": "P Alias", 

116 } 

117 }, 

118 "title": body_model_name, 

119 "type": "object", 

120 } 

121 

122 

123@pytest.mark.parametrize( 1abcd

124 "path", 

125 [ 

126 "/optional-bytes-alias", 

127 "/optional-uploadfile-alias", 

128 ], 

129) 

130def test_optional_alias_missing(path: str): 1abcd

131 client = TestClient(app) 1uFwHzKCN

132 response = client.post(path) 1uFwHzKCN

133 assert response.status_code == 200 1uFwHzKCN

134 assert response.json() == {"file_size": None} 1uFwHzKCN

135 

136 

137@pytest.mark.parametrize( 1abcd

138 "path", 

139 [ 

140 "/optional-bytes-alias", 

141 "/optional-uploadfile-alias", 

142 ], 

143) 

144def test_optional_alias_by_name(path: str): 1abcd

145 client = TestClient(app) 2t E ~ aby J B M

146 response = client.post(path, files=[("p", b"hello")]) 2t E ~ aby J B M

147 assert response.status_code == 200 2t E ~ aby J B M

148 assert response.json() == {"file_size": None} 2t E ~ aby J B M

149 

150 

151@pytest.mark.parametrize( 1abcd

152 "path", 

153 [ 

154 "/optional-bytes-alias", 

155 "/optional-uploadfile-alias", 

156 ], 

157) 

158def test_optional_alias_by_alias(path: str): 1abcd

159 client = TestClient(app) 1sDvGxIAL

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

161 assert response.status_code == 200, response.text 1sDvGxIAL

162 assert response.json() == {"file_size": 5} 1sDvGxIAL

163 

164 

165# ===================================================================================== 

166# Validation alias 

167 

168 

169@app.post( 1abcd

170 "/optional-bytes-validation-alias", operation_id="optional_bytes_validation_alias" 

171) 

172def read_optional_bytes_validation_alias( 1abcd

173 p: Annotated[bytes | None, File(validation_alias="p_val_alias")] = None, 

174): 

175 return {"file_size": len(p) if p else None} 1OPQRSTUVWX

176 

177 

178@app.post( 1abcd

179 "/optional-uploadfile-validation-alias", 

180 operation_id="optional_uploadfile_validation_alias", 

181) 

182def read_optional_uploadfile_validation_alias( 1abcd

183 p: Annotated[UploadFile | None, File(validation_alias="p_val_alias")] = None, 

184): 

185 return {"file_size": p.size if p else None} 2Y Z 0 Nb1 2 3 4 5 6 7

186 

187 

188@pytest.mark.parametrize( 1abcd

189 "path", 

190 [ 

191 "/optional-bytes-validation-alias", 

192 "/optional-uploadfile-validation-alias", 

193 ], 

194) 

195def test_optional_validation_alias_schema(path: str): 1abcd

196 openapi = app.openapi() 2xbybzbAbBbCbDbEb

197 body_model_name = get_body_model_name(openapi, path) 2xbybzbAbBbCbDbEb

198 

199 assert app.openapi()["components"]["schemas"][body_model_name] == { 2xbybzbAbBbCbDbEb

200 "properties": { 

201 "p_val_alias": { 

202 "anyOf": [ 

203 {"type": "string", "contentMediaType": "application/octet-stream"}, 

204 {"type": "null"}, 

205 ], 

206 "title": "P Val Alias", 

207 } 

208 }, 

209 "title": body_model_name, 

210 "type": "object", 

211 } 

212 

213 

214@pytest.mark.parametrize( 1abcd

215 "path", 

216 [ 

217 "/optional-bytes-validation-alias", 

218 "/optional-uploadfile-validation-alias", 

219 ], 

220) 

221def test_optional_validation_alias_missing(path: str): 1abcd

222 client = TestClient(app) 1Q0R1U4X7

223 response = client.post(path) 1Q0R1U4X7

224 assert response.status_code == 200 1Q0R1U4X7

225 assert response.json() == {"file_size": None} 1Q0R1U4X7

226 

227 

228@pytest.mark.parametrize( 1abcd

229 "path", 

230 [ 

231 "/optional-bytes-validation-alias", 

232 "/optional-uploadfile-validation-alias", 

233 ], 

234) 

235def test_optional_validation_alias_by_name(path: str): 1abcd

236 client = TestClient(app) 2O Y bbcbS 2 V 5

237 response = client.post(path, files=[("p", b"hello")]) 2O Y bbcbS 2 V 5

238 assert response.status_code == 200, response.text 2O Y bbcbS 2 V 5

239 assert response.json() == {"file_size": None} 2O Y bbcbS 2 V 5

240 

241 

242@pytest.mark.parametrize( 1abcd

243 "path", 

244 [ 

245 "/optional-bytes-validation-alias", 

246 "/optional-uploadfile-validation-alias", 

247 ], 

248) 

249def test_optional_validation_alias_by_validation_alias(path: str): 1abcd

250 client = TestClient(app) 2P Z dbT 3 W 6

251 response = client.post(path, files=[("p_val_alias", b"hello")]) 2P Z dbT 3 W 6

252 assert response.status_code == 200, response.text 2P Z dbT 3 W 6

253 assert response.json() == {"file_size": 5} 2P Z dbT 3 W 6

254 

255 

256# ===================================================================================== 

257# Alias and validation alias 

258 

259 

260@app.post( 1abcd

261 "/optional-bytes-alias-and-validation-alias", 

262 operation_id="optional_bytes_alias_and_validation_alias", 

263) 

264def read_optional_bytes_alias_and_validation_alias( 1abcd

265 p: Annotated[ 

266 bytes | None, File(alias="p_alias", validation_alias="p_val_alias") 

267 ] = None, 

268): 

269 return {"file_size": len(p) if p else None} 189!#$%'()*+,-.

270 

271 

272@app.post( 1abcd

273 "/optional-uploadfile-alias-and-validation-alias", 

274 operation_id="optional_uploadfile_alias_and_validation_alias", 

275) 

276def read_optional_uploadfile_alias_and_validation_alias( 1abcd

277 p: Annotated[ 

278 UploadFile | None, File(alias="p_alias", validation_alias="p_val_alias") 

279 ] = None, 

280): 

281 return {"file_size": p.size if p else None} 2/ : ; = ObPb? @ [ ] ^ _ ` {

282 

283 

284@pytest.mark.parametrize( 1abcd

285 "path", 

286 [ 

287 "/optional-bytes-alias-and-validation-alias", 

288 "/optional-uploadfile-alias-and-validation-alias", 

289 ], 

290) 

291def test_optional_alias_and_validation_alias_schema(path: str): 1abcd

292 openapi = app.openapi() 2FbGbHbIbJbKbLbMb

293 body_model_name = get_body_model_name(openapi, path) 2FbGbHbIbJbKbLbMb

294 

295 assert app.openapi()["components"]["schemas"][body_model_name] == { 2FbGbHbIbJbKbLbMb

296 "properties": { 

297 "p_val_alias": { 

298 "anyOf": [ 

299 {"type": "string", "contentMediaType": "application/octet-stream"}, 

300 {"type": "null"}, 

301 ], 

302 "title": "P Val Alias", 

303 } 

304 }, 

305 "title": body_model_name, 

306 "type": "object", 

307 } 

308 

309 

310@pytest.mark.parametrize( 1abcd

311 "path", 

312 [ 

313 "/optional-bytes-alias-and-validation-alias", 

314 "/optional-uploadfile-alias-and-validation-alias", 

315 ], 

316) 

317def test_optional_alias_and_validation_alias_missing(path: str): 1abcd

318 client = TestClient(app) 1#=%*].{

319 response = client.post(path) 1#=%*].{

320 assert response.status_code == 200 1#=%*].{

321 assert response.json() == {"file_size": None} 1#=%*].{

322 

323 

324@pytest.mark.parametrize( 1abcd

325 "path", 

326 [ 

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

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

329 ], 

330) 

331def test_optional_alias_and_validation_alias_by_name(path: str): 1abcd

332 client = TestClient(app) 29 : $ eb( @ , _

333 response = client.post(path, files={"p": "hello"}) 29 : $ eb( @ , _

334 assert response.status_code == 200 29 : $ eb( @ , _

335 assert response.json() == {"file_size": None} 29 : $ eb( @ , _

336 

337 

338@pytest.mark.parametrize( 1abcd

339 "path", 

340 [ 

341 "/optional-bytes-alias-and-validation-alias", 

342 "/optional-uploadfile-alias-and-validation-alias", 

343 ], 

344) 

345def test_optional_alias_and_validation_alias_by_alias(path: str): 1abcd

346 client = TestClient(app) 28 / fb' ? + ^

347 response = client.post(path, files=[("p_alias", b"hello")]) 28 / fb' ? + ^

348 assert response.status_code == 200, response.text 28 / fb' ? + ^

349 assert response.json() == {"file_size": None} 28 / fb' ? + ^

350 

351 

352@pytest.mark.parametrize( 1abcd

353 "path", 

354 [ 

355 "/optional-bytes-alias-and-validation-alias", 

356 "/optional-uploadfile-alias-and-validation-alias", 

357 ], 

358) 

359def test_optional_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

360 client = TestClient(app) 2! ; gbhb) [ - `

361 response = client.post(path, files=[("p_val_alias", b"hello")]) 2! ; gbhb) [ - `

362 assert response.status_code == 200, response.text 2! ; gbhb) [ - `

363 assert response.json() == {"file_size": 5} 2! ; gbhb) [ - `