Coverage for tests / test_request_params / test_file / test_optional_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, 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-list-bytes") 1abcd

16async def read_optional_list_bytes(p: Annotated[Optional[list[bytes]], File()] = None): 1abcd

17 return {"file_size": [len(file) for file in p] if p else None} 1efghij

18 

19 

20@app.post("/optional-list-uploadfile") 1abcd

21async def read_optional_list_uploadfile( 1abcd

22 p: Annotated[Optional[list[UploadFile]], File()] = None, 

23): 

24 return {"file_size": [file.size for file in p] if p else None} 1efghij

25 

26 

27@pytest.mark.parametrize( 1abcd

28 "path", 

29 [ 

30 "/optional-list-bytes", 

31 "/optional-list-uploadfile", 

32 ], 

33) 

34def test_optional_list_schema(path: str): 1abcd

35 openapi = app.openapi() 1OPQ

36 body_model_name = get_body_model_name(openapi, path) 1OPQ

37 

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

39 "properties": { 

40 "p": { 

41 "anyOf": [ 

42 { 

43 "type": "array", 

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

45 }, 

46 {"type": "null"}, 

47 ], 

48 "title": "P", 

49 } 

50 }, 

51 "title": body_model_name, 

52 "type": "object", 

53 } 

54 

55 

56@pytest.mark.parametrize( 1abcd

57 "path", 

58 [ 

59 "/optional-list-bytes", 

60 "/optional-list-uploadfile", 

61 ], 

62) 

63def test_optional_list_missing(path: str): 1abcd

64 client = TestClient(app) 1fhj

65 response = client.post(path) 1fhj

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

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

68 

69 

70@pytest.mark.parametrize( 1abcd

71 "path", 

72 [ 

73 "/optional-list-bytes", 

74 "/optional-list-uploadfile", 

75 ], 

76) 

77def test_optional_list(path: str): 1abcd

78 client = TestClient(app) 1egi

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

80 assert response.status_code == 200 1egi

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

82 

83 

84# ===================================================================================== 

85# Alias 

86 

87 

88@app.post("/optional-list-bytes-alias") 1abcd

89async def read_optional_list_bytes_alias( 1abcd

90 p: Annotated[Optional[list[bytes]], File(alias="p_alias")] = None, 

91): 

92 return {"file_size": [len(file) for file in p] if p else None} 1klmnopqrs

93 

94 

95@app.post("/optional-list-uploadfile-alias") 1abcd

96async def read_optional_list_uploadfile_alias( 1abcd

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

98): 

99 return {"file_size": [file.size for file in p] if p else None} 1klmnopqrs

100 

101 

102@pytest.mark.parametrize( 1abcd

103 "path", 

104 [ 

105 "/optional-list-bytes-alias", 

106 "/optional-list-uploadfile-alias", 

107 ], 

108) 

109def test_optional_list_alias_schema(path: str): 1abcd

110 openapi = app.openapi() 1RST

111 body_model_name = get_body_model_name(openapi, path) 1RST

112 

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

114 "properties": { 

115 "p_alias": { 

116 "anyOf": [ 

117 { 

118 "type": "array", 

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

120 }, 

121 {"type": "null"}, 

122 ], 

123 "title": "P Alias", 

124 } 

125 }, 

126 "title": body_model_name, 

127 "type": "object", 

128 } 

129 

130 

131@pytest.mark.parametrize( 1abcd

132 "path", 

133 [ 

134 "/optional-list-bytes-alias", 

135 "/optional-list-uploadfile-alias", 

136 ], 

137) 

138def test_optional_list_alias_missing(path: str): 1abcd

139 client = TestClient(app) 1mps

140 response = client.post(path) 1mps

141 assert response.status_code == 200 1mps

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

143 

144 

145@pytest.mark.parametrize( 1abcd

146 "path", 

147 [ 

148 "/optional-list-bytes-alias", 

149 "/optional-list-uploadfile-alias", 

150 ], 

151) 

152def test_optional_list_alias_by_name(path: str): 1abcd

153 client = TestClient(app) 1lor

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

155 assert response.status_code == 200, response.text 1lor

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

157 

158 

159@pytest.mark.parametrize( 1abcd

160 "path", 

161 [ 

162 "/optional-list-bytes-alias", 

163 "/optional-list-uploadfile-alias", 

164 ], 

165) 

166def test_optional_list_alias_by_alias(path: str): 1abcd

167 client = TestClient(app) 1knq

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

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

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

171 

172 

173# ===================================================================================== 

174# Validation alias 

175 

176 

177@app.post("/optional-list-bytes-validation-alias") 1abcd

178def read_optional_list_bytes_validation_alias( 1abcd

179 p: Annotated[Optional[list[bytes]], File(validation_alias="p_val_alias")] = None, 

180): 

181 return {"file_size": [len(file) for file in p] if p else None} 1tuvwxyzAB

182 

183 

184@app.post("/optional-list-uploadfile-validation-alias") 1abcd

185def read_optional_list_uploadfile_validation_alias( 1abcd

186 p: Annotated[ 

187 Optional[list[UploadFile]], File(validation_alias="p_val_alias") 

188 ] = None, 

189): 

190 return {"file_size": [file.size for file in p] if p else None} 1tuvwxyzAB

191 

192 

193@pytest.mark.parametrize( 1abcd

194 "path", 

195 [ 

196 "/optional-list-bytes-validation-alias", 

197 "/optional-list-uploadfile-validation-alias", 

198 ], 

199) 

200def test_optional_validation_alias_schema(path: str): 1abcd

201 openapi = app.openapi() 1UVW

202 body_model_name = get_body_model_name(openapi, path) 1UVW

203 

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

205 "properties": { 

206 "p_val_alias": { 

207 "anyOf": [ 

208 { 

209 "type": "array", 

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

211 }, 

212 {"type": "null"}, 

213 ], 

214 "title": "P Val Alias", 

215 } 

216 }, 

217 "title": body_model_name, 

218 "type": "object", 

219 } 

220 

221 

222@pytest.mark.parametrize( 1abcd

223 "path", 

224 [ 

225 "/optional-list-bytes-validation-alias", 

226 "/optional-list-uploadfile-validation-alias", 

227 ], 

228) 

229def test_optional_validation_alias_missing(path: str): 1abcd

230 client = TestClient(app) 1vyB

231 response = client.post(path) 1vyB

232 assert response.status_code == 200 1vyB

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

234 

235 

236@pytest.mark.parametrize( 1abcd

237 "path", 

238 [ 

239 "/optional-list-bytes-validation-alias", 

240 "/optional-list-uploadfile-validation-alias", 

241 ], 

242) 

243def test_optional_validation_alias_by_name(path: str): 1abcd

244 client = TestClient(app) 1twz

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

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

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

248 

249 

250@pytest.mark.parametrize( 1abcd

251 "path", 

252 [ 

253 "/optional-list-bytes-validation-alias", 

254 "/optional-list-uploadfile-validation-alias", 

255 ], 

256) 

257def test_optional_validation_alias_by_validation_alias(path: str): 1abcd

258 client = TestClient(app) 1uxA

259 response = client.post( 1uxA

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

261 ) 

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

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

264 

265 

266# ===================================================================================== 

267# Alias and validation alias 

268 

269 

270@app.post("/optional-list-bytes-alias-and-validation-alias") 1abcd

271def read_optional_list_bytes_alias_and_validation_alias( 1abcd

272 p: Annotated[ 

273 Optional[list[bytes]], File(alias="p_alias", validation_alias="p_val_alias") 

274 ] = None, 

275): 

276 return {"file_size": [len(file) for file in p] if p else None} 1CDEFGHIJKLMN

277 

278 

279@app.post("/optional-list-uploadfile-alias-and-validation-alias") 1abcd

280def read_optional_list_uploadfile_alias_and_validation_alias( 1abcd

281 p: Annotated[ 

282 Optional[list[UploadFile]], 

283 File(alias="p_alias", validation_alias="p_val_alias"), 

284 ] = None, 

285): 

286 return {"file_size": [file.size for file in p] if p else None} 1CDEFGHIJKLMN

287 

288 

289@pytest.mark.parametrize( 1abcd

290 "path", 

291 [ 

292 "/optional-list-bytes-alias-and-validation-alias", 

293 "/optional-list-uploadfile-alias-and-validation-alias", 

294 ], 

295) 

296def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd

297 openapi = app.openapi() 1XYZ

298 body_model_name = get_body_model_name(openapi, path) 1XYZ

299 

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

301 "properties": { 

302 "p_val_alias": { 

303 "anyOf": [ 

304 { 

305 "type": "array", 

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

307 }, 

308 {"type": "null"}, 

309 ], 

310 "title": "P Val Alias", 

311 } 

312 }, 

313 "title": body_model_name, 

314 "type": "object", 

315 } 

316 

317 

318@pytest.mark.parametrize( 1abcd

319 "path", 

320 [ 

321 "/optional-list-bytes-alias-and-validation-alias", 

322 "/optional-list-uploadfile-alias-and-validation-alias", 

323 ], 

324) 

325def test_optional_list_alias_and_validation_alias_missing(path: str): 1abcd

326 client = TestClient(app) 1FJN

327 response = client.post(path) 1FJN

328 assert response.status_code == 200 1FJN

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

330 

331 

332@pytest.mark.parametrize( 1abcd

333 "path", 

334 [ 

335 "/optional-list-bytes-alias-and-validation-alias", 

336 "/optional-list-uploadfile-alias-and-validation-alias", 

337 ], 

338) 

339def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd

340 client = TestClient(app) 1DHL

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

342 assert response.status_code == 200 1DHL

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

344 

345 

346@pytest.mark.parametrize( 1abcd

347 "path", 

348 [ 

349 "/optional-list-bytes-alias-and-validation-alias", 

350 "/optional-list-uploadfile-alias-and-validation-alias", 

351 ], 

352) 

353def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd

354 client = TestClient(app) 1CGK

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

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

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

358 

359 

360@pytest.mark.parametrize( 1abcd

361 "path", 

362 [ 

363 "/optional-list-bytes-alias-and-validation-alias", 

364 "/optional-list-uploadfile-alias-and-validation-alias", 

365 ], 

366) 

367def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

368 client = TestClient(app) 1EIM

369 response = client.post( 1EIM

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

371 ) 

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

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