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-02-12 18:15 +0000

1from typing import Annotated, Optional 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[Optional[bytes], File()] = None): 1abcd

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

18 

19 

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

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

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

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() 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 "anyOf": [ 

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

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

60 response = client.post(path) 1fhj

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

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

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

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

75 assert response.status_code == 200 1egi

76 assert response.json() == {"file_size": 5} 1egi

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[Optional[bytes], File(alias="p_alias")] = None, 

86): 

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

88 

89 

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

91async def read_optional_uploadfile_alias( 1abcd

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

93): 

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

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() 1RST

106 body_model_name = get_body_model_name(openapi, path) 1RST

107 

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

109 "properties": { 

110 "p_alias": { 

111 "anyOf": [ 

112 {"type": "string", "format": "binary"}, 

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

132 response = client.post(path) 1mps

133 assert response.status_code == 200 1mps

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

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

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

147 assert response.status_code == 200 1lor

148 assert response.json() == {"file_size": None} 1lor

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

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

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

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

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[Optional[bytes], File(validation_alias="p_val_alias")] = None, 

174): 

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

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[Optional[UploadFile], File(validation_alias="p_val_alias")] = None, 

184): 

185 return {"file_size": p.size if p else None} 1tuvwxyzAB

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() 1UVW

197 body_model_name = get_body_model_name(openapi, path) 1UVW

198 

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

200 "properties": { 

201 "p_val_alias": { 

202 "anyOf": [ 

203 {"type": "string", "format": "binary"}, 

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

223 response = client.post(path) 1vyB

224 assert response.status_code == 200 1vyB

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

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

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

238 assert response.status_code == 200, response.text 1twz

239 assert response.json() == {"file_size": None} 1twz

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

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

252 assert response.status_code == 200, response.text 1uxA

253 assert response.json() == {"file_size": 5} 1uxA

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 Optional[bytes], File(alias="p_alias", validation_alias="p_val_alias") 

267 ] = None, 

268): 

269 return {"file_size": len(p) if p else None} 1CDEFGHIJKLMN

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 Optional[UploadFile], File(alias="p_alias", validation_alias="p_val_alias") 

279 ] = None, 

280): 

281 return {"file_size": p.size if p else None} 1CDEFGHIJKLMN

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() 1XYZ

293 body_model_name = get_body_model_name(openapi, path) 1XYZ

294 

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

296 "properties": { 

297 "p_val_alias": { 

298 "anyOf": [ 

299 {"type": "string", "format": "binary"}, 

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

319 response = client.post(path) 1FJN

320 assert response.status_code == 200 1FJN

321 assert response.json() == {"file_size": None} 1FJN

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

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

334 assert response.status_code == 200 1DHL

335 assert response.json() == {"file_size": None} 1DHL

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

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

348 assert response.status_code == 200, response.text 1CGK

349 assert response.json() == {"file_size": None} 1CGK

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

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

362 assert response.status_code == 200, response.text 1EIM

363 assert response.json() == {"file_size": 5} 1EIM