Coverage for tests / test_request_params / test_body / test_optional_list.py: 100%

171 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 Body, FastAPI 1abcd

5from fastapi.testclient import TestClient 1abcd

6from pydantic import BaseModel, Field 1abcd

7 

8from .utils import get_body_model_name 1abcd

9 

10app = FastAPI() 1abcd

11 

12# ===================================================================================== 

13# Without aliases 

14 

15 

16@app.post("/optional-list-str", operation_id="optional_list_str") 1abcd

17async def read_optional_list_str( 1abcd

18 p: Annotated[list[str] | None, Body(embed=True)] = None, 

19): 

20 return {"p": p} 1efghijklmn

21 

22 

23class BodyModelOptionalListStr(BaseModel): 1abcd

24 p: list[str] | None = None 1abcd

25 

26 

27@app.post("/model-optional-list-str", operation_id="model_optional_list_str") 1abcd

28async def read_model_optional_list_str(p: BodyModelOptionalListStr): 1abcd

29 return {"p": p.p} 2o p .bq r s t u

30 

31 

32@pytest.mark.parametrize( 1abcd

33 "path", 

34 ["/optional-list-str", "/model-optional-list-str"], 

35) 

36def test_optional_list_str_schema(path: str): 1abcd

37 openapi = app.openapi() 2PbQbRbSbTbUbVbWb

38 body_model_name = get_body_model_name(openapi, path) 2PbQbRbSbTbUbVbWb

39 

40 assert app.openapi()["components"]["schemas"][body_model_name] == { 2PbQbRbSbTbUbVbWb

41 "properties": { 

42 "p": { 

43 "anyOf": [ 

44 {"items": {"type": "string"}, "type": "array"}, 

45 {"type": "null"}, 

46 ], 

47 "title": "P", 

48 }, 

49 }, 

50 "title": body_model_name, 

51 "type": "object", 

52 } 

53 

54 

55def test_optional_list_str_missing(): 1abcd

56 client = TestClient(app) 1ghkn

57 response = client.post("/optional-list-str") 1ghkn

58 assert response.status_code == 200, response.text 1ghkn

59 assert response.json() == {"p": None} 1ghkn

60 

61 

62def test_model_optional_list_str_missing(): 1abcd

63 client = TestClient(app) 2hbibjbkb

64 response = client.post("/model-optional-list-str") 2hbibjbkb

65 assert response.status_code == 422, response.text 2hbibjbkb

66 assert response.json() == { 2hbibjbkb

67 "detail": [ 

68 { 

69 "input": None, 

70 "loc": ["body"], 

71 "msg": "Field required", 

72 "type": "missing", 

73 }, 

74 ], 

75 } 

76 

77 

78@pytest.mark.parametrize( 1abcd

79 "path", 

80 ["/optional-list-str", "/model-optional-list-str"], 

81) 

82def test_optional_list_str_missing_empty_dict(path: str): 1abcd

83 client = TestClient(app) 2p f q lbs j u m

84 response = client.post(path, json={}) 2p f q lbs j u m

85 assert response.status_code == 200, response.text 2p f q lbs j u m

86 assert response.json() == {"p": None} 2p f q lbs j u m

87 

88 

89@pytest.mark.parametrize( 1abcd

90 "path", 

91 ["/optional-list-str", "/model-optional-list-str"], 

92) 

93def test_optional_list_str(path: str): 1abcd

94 client = TestClient(app) 2o e mbr i t l

95 response = client.post(path, json={"p": ["hello", "world"]}) 2o e mbr i t l

96 assert response.status_code == 200 2o e mbr i t l

97 assert response.json() == {"p": ["hello", "world"]} 2o e mbr i t l

98 

99 

100# ===================================================================================== 

101# Alias 

102 

103 

104@app.post("/optional-list-alias", operation_id="optional_list_alias") 1abcd

105async def read_optional_list_alias( 1abcd

106 p: Annotated[list[str] | None, Body(embed=True, alias="p_alias")] = None, 

107): 

108 return {"p": p} 1vwxyzABCDEFGHI

109 

110 

111class BodyModelOptionalListAlias(BaseModel): 1abcd

112 p: list[str] | None = Field(None, alias="p_alias") 1abcd

113 

114 

115@app.post("/model-optional-list-alias", operation_id="model_optional_list_alias") 1abcd

116async def read_model_optional_list_alias(p: BodyModelOptionalListAlias): 1abcd

117 return {"p": p.p} 2J K L /bM N O P Q R

118 

119 

120@pytest.mark.parametrize( 1abcd

121 "path", 

122 [ 

123 "/optional-list-alias", 

124 "/model-optional-list-alias", 

125 ], 

126) 

127def test_optional_list_str_alias_schema(path: str): 1abcd

128 openapi = app.openapi() 2XbYbZb0b1b2b3b4b

129 body_model_name = get_body_model_name(openapi, path) 2XbYbZb0b1b2b3b4b

130 

131 assert app.openapi()["components"]["schemas"][body_model_name] == { 2XbYbZb0b1b2b3b4b

132 "properties": { 

133 "p_alias": { 

134 "anyOf": [ 

135 {"items": {"type": "string"}, "type": "array"}, 

136 {"type": "null"}, 

137 ], 

138 "title": "P Alias", 

139 }, 

140 }, 

141 "title": body_model_name, 

142 "type": "object", 

143 } 

144 

145 

146def test_optional_list_alias_missing(): 1abcd

147 client = TestClient(app) 1yAEI

148 response = client.post("/optional-list-alias") 1yAEI

149 assert response.status_code == 200, response.text 1yAEI

150 assert response.json() == {"p": None} 1yAEI

151 

152 

153def test_model_optional_list_alias_missing(): 1abcd

154 client = TestClient(app) 2nbobpbqb

155 response = client.post("/model-optional-list-alias") 2nbobpbqb

156 assert response.status_code == 422, response.text 2nbobpbqb

157 assert response.json() == { 2nbobpbqb

158 "detail": [ 

159 { 

160 "input": None, 

161 "loc": ["body"], 

162 "msg": "Field required", 

163 "type": "missing", 

164 }, 

165 ], 

166 } 

167 

168 

169@pytest.mark.parametrize( 1abcd

170 "path", 

171 ["/optional-list-alias", "/model-optional-list-alias"], 

172) 

173def test_optional_list_alias_missing_empty_dict(path: str): 1abcd

174 client = TestClient(app) 1LxzODRH

175 response = client.post(path, json={}) 1LxzODRH

176 assert response.status_code == 200, response.text 1LxzODRH

177 assert response.json() == {"p": None} 1LxzODRH

178 

179 

180@pytest.mark.parametrize( 1abcd

181 "path", 

182 ["/optional-list-alias", "/model-optional-list-alias"], 

183) 

184def test_optional_list_alias_by_name(path: str): 1abcd

185 client = TestClient(app) 2K w rbsbN C Q G

186 response = client.post(path, json={"p": ["hello", "world"]}) 2K w rbsbN C Q G

187 assert response.status_code == 200 2K w rbsbN C Q G

188 assert response.json() == {"p": None} 2K w rbsbN C Q G

189 

190 

191@pytest.mark.parametrize( 1abcd

192 "path", 

193 ["/optional-list-alias", "/model-optional-list-alias"], 

194) 

195def test_optional_list_alias_by_alias(path: str): 1abcd

196 client = TestClient(app) 2J v tbubM B P F

197 response = client.post(path, json={"p_alias": ["hello", "world"]}) 2J v tbubM B P F

198 assert response.status_code == 200 2J v tbubM B P F

199 assert response.json() == {"p": ["hello", "world"]} 2J v tbubM B P F

200 

201 

202# ===================================================================================== 

203# Validation alias 

204 

205 

206@app.post( 1abcd

207 "/optional-list-validation-alias", operation_id="optional_list_validation_alias" 

208) 

209def read_optional_list_validation_alias( 1abcd

210 p: Annotated[ 

211 list[str] | None, Body(embed=True, validation_alias="p_val_alias") 

212 ] = None, 

213): 

214 return {"p": p} 1STUVWXYZ01234

215 

216 

217class BodyModelOptionalListValidationAlias(BaseModel): 1abcd

218 p: list[str] | None = Field(None, validation_alias="p_val_alias") 1abcd

219 

220 

221@app.post( 1abcd

222 "/model-optional-list-validation-alias", 

223 operation_id="model_optional_list_validation_alias", 

224) 

225def read_model_optional_list_validation_alias( 1abcd

226 p: BodyModelOptionalListValidationAlias, 

227): 

228 return {"p": p.p} 156789!#$%'

229 

230 

231@pytest.mark.parametrize( 1abcd

232 "path", 

233 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"], 

234) 

235def test_optional_list_validation_alias_schema(path: str): 1abcd

236 openapi = app.openapi() 25b6b7b8b9b!b#b$b

237 body_model_name = get_body_model_name(openapi, path) 25b6b7b8b9b!b#b$b

238 

239 assert app.openapi()["components"]["schemas"][body_model_name] == { 25b6b7b8b9b!b#b$b

240 "properties": { 

241 "p_val_alias": { 

242 "anyOf": [ 

243 {"items": {"type": "string"}, "type": "array"}, 

244 {"type": "null"}, 

245 ], 

246 "title": "P Val Alias", 

247 }, 

248 }, 

249 "title": body_model_name, 

250 "type": "object", 

251 } 

252 

253 

254def test_optional_list_validation_alias_missing(): 1abcd

255 client = TestClient(app) 1VW04

256 response = client.post("/optional-list-validation-alias") 1VW04

257 assert response.status_code == 200, response.text 1VW04

258 assert response.json() == {"p": None} 1VW04

259 

260 

261def test_model_optional_list_validation_alias_missing(): 1abcd

262 client = TestClient(app) 2vbwbxbyb

263 response = client.post("/model-optional-list-validation-alias") 2vbwbxbyb

264 assert response.status_code == 422, response.text 2vbwbxbyb

265 assert response.json() == { 2vbwbxbyb

266 "detail": [ 

267 { 

268 "input": None, 

269 "loc": ["body"], 

270 "msg": "Field required", 

271 "type": "missing", 

272 }, 

273 ], 

274 } 

275 

276 

277@pytest.mark.parametrize( 1abcd

278 "path", 

279 ["/optional-list-validation-alias", "/model-optional-list-validation-alias"], 

280) 

281def test_optional_list_validation_alias_missing_empty_dict(path: str): 1abcd

282 client = TestClient(app) 27 U 8 zb# Z ' 3

283 response = client.post(path, json={}) 27 U 8 zb# Z ' 3

284 assert response.status_code == 200, response.text 27 U 8 zb# Z ' 3

285 assert response.json() == {"p": None} 27 U 8 zb# Z ' 3

286 

287 

288@pytest.mark.parametrize( 1abcd

289 "path", 

290 [ 

291 "/optional-list-validation-alias", 

292 "/model-optional-list-validation-alias", 

293 ], 

294) 

295def test_optional_list_validation_alias_by_name(path: str): 1abcd

296 client = TestClient(app) 25 S AbBb9 X $ 1

297 response = client.post(path, json={"p": ["hello", "world"]}) 25 S AbBb9 X $ 1

298 assert response.status_code == 200 25 S AbBb9 X $ 1

299 assert response.json() == {"p": None} 25 S AbBb9 X $ 1

300 

301 

302@pytest.mark.parametrize( 1abcd

303 "path", 

304 [ 

305 "/optional-list-validation-alias", 

306 "/model-optional-list-validation-alias", 

307 ], 

308) 

309def test_optional_list_validation_alias_by_validation_alias(path: str): 1abcd

310 client = TestClient(app) 26 T CbDb! Y % 2

311 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 26 T CbDb! Y % 2

312 assert response.status_code == 200, response.text 26 T CbDb! Y % 2

313 assert response.json() == {"p": ["hello", "world"]} 26 T CbDb! Y % 2

314 

315 

316# ===================================================================================== 

317# Alias and validation alias 

318 

319 

320@app.post( 1abcd

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

322 operation_id="optional_list_alias_and_validation_alias", 

323) 

324def read_optional_list_alias_and_validation_alias( 1abcd

325 p: Annotated[ 

326 list[str] | None, 

327 Body(embed=True, alias="p_alias", validation_alias="p_val_alias"), 

328 ] = None, 

329): 

330 return {"p": p} 1()*+,-./:;=?@[]^

331 

332 

333class BodyModelOptionalListAliasAndValidationAlias(BaseModel): 1abcd

334 p: list[str] | None = Field(None, alias="p_alias", validation_alias="p_val_alias") 1abcd

335 

336 

337@app.post( 1abcd

338 "/model-optional-list-alias-and-validation-alias", 

339 operation_id="model_optional_list_alias_and_validation_alias", 

340) 

341def read_model_optional_list_alias_and_validation_alias( 1abcd

342 p: BodyModelOptionalListAliasAndValidationAlias, 

343): 

344 return {"p": p.p} 2_ ` { | } ~ abbbcbdbebfbgb

345 

346 

347@pytest.mark.parametrize( 1abcd

348 "path", 

349 [ 

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

351 "/model-optional-list-alias-and-validation-alias", 

352 ], 

353) 

354def test_optional_list_alias_and_validation_alias_schema(path: str): 1abcd

355 openapi = app.openapi() 2%b'b(b)b*b+b,b-b

356 body_model_name = get_body_model_name(openapi, path) 2%b'b(b)b*b+b,b-b

357 

358 assert app.openapi()["components"]["schemas"][body_model_name] == { 2%b'b(b)b*b+b,b-b

359 "properties": { 

360 "p_val_alias": { 

361 "anyOf": [ 

362 {"items": {"type": "string"}, "type": "array"}, 

363 {"type": "null"}, 

364 ], 

365 "title": "P Val Alias", 

366 }, 

367 }, 

368 "title": body_model_name, 

369 "type": "object", 

370 } 

371 

372 

373def test_optional_list_alias_and_validation_alias_missing(): 1abcd

374 client = TestClient(app) 1,-=^

375 response = client.post("/optional-list-alias-and-validation-alias") 1,-=^

376 assert response.status_code == 200, response.text 1,-=^

377 assert response.json() == {"p": None} 1,-=^

378 

379 

380def test_model_optional_list_alias_and_validation_alias_missing(): 1abcd

381 client = TestClient(app) 2EbFbGbHb

382 response = client.post("/model-optional-list-alias-and-validation-alias") 2EbFbGbHb

383 assert response.status_code == 422, response.text 2EbFbGbHb

384 assert response.json() == { 2EbFbGbHb

385 "detail": [ 

386 { 

387 "input": None, 

388 "loc": ["body"], 

389 "msg": "Field required", 

390 "type": "missing", 

391 }, 

392 ], 

393 } 

394 

395 

396@pytest.mark.parametrize( 1abcd

397 "path", 

398 [ 

399 "/optional-list-alias-and-validation-alias", 

400 "/model-optional-list-alias-and-validation-alias", 

401 ], 

402) 

403def test_optional_list_alias_and_validation_alias_missing_empty_dict(path: str): 1abcd

404 client = TestClient(app) 2| + } Ibcb; gb]

405 response = client.post(path, json={}) 2| + } Ibcb; gb]

406 assert response.status_code == 200, response.text 2| + } Ibcb; gb]

407 assert response.json() == {"p": None} 2| + } Ibcb; gb]

408 

409 

410@pytest.mark.parametrize( 1abcd

411 "path", 

412 [ 

413 "/optional-list-alias-and-validation-alias", 

414 "/model-optional-list-alias-and-validation-alias", 

415 ], 

416) 

417def test_optional_list_alias_and_validation_alias_by_name(path: str): 1abcd

418 client = TestClient(app) 2` ) JbKbab/ eb@

419 response = client.post(path, json={"p": ["hello", "world"]}) 2` ) JbKbab/ eb@

420 assert response.status_code == 200 2` ) JbKbab/ eb@

421 assert response.json() == {"p": None} 2` ) JbKbab/ eb@

422 

423 

424@pytest.mark.parametrize( 1abcd

425 "path", 

426 [ 

427 "/optional-list-alias-and-validation-alias", 

428 "/model-optional-list-alias-and-validation-alias", 

429 ], 

430) 

431def test_optional_list_alias_and_validation_alias_by_alias(path: str): 1abcd

432 client = TestClient(app) 2_ ( LbMb~ . db?

433 response = client.post(path, json={"p_alias": ["hello", "world"]}) 2_ ( LbMb~ . db?

434 assert response.status_code == 200 2_ ( LbMb~ . db?

435 assert response.json() == {"p": None} 2_ ( LbMb~ . db?

436 

437 

438@pytest.mark.parametrize( 1abcd

439 "path", 

440 [ 

441 "/optional-list-alias-and-validation-alias", 

442 "/model-optional-list-alias-and-validation-alias", 

443 ], 

444) 

445def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

446 client = TestClient(app) 2{ * NbObbb: fb[

447 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 2{ * NbObbb: fb[

448 assert response.status_code == 200, response.text 2{ * NbObbb: fb[

449 assert response.json() == { 2{ * NbObbb: fb[

450 "p": [ 

451 "hello", 

452 "world", 

453 ] 

454 }