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

136 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1from typing import Annotated, Union 1abcd

2 

3import pytest 1abcd

4from dirty_equals import IsOneOf, IsPartialDict 1abcd

5from fastapi import Body, FastAPI 1abcd

6from fastapi.testclient import TestClient 1abcd

7from pydantic import BaseModel, Field 1abcd

8 

9from .utils import get_body_model_name 1abcd

10 

11app = FastAPI() 1abcd

12 

13# ===================================================================================== 

14# Without aliases 

15 

16 

17@app.post("/required-list-str", operation_id="required_list_str") 1abcd

18async def read_required_list_str(p: Annotated[list[str], Body(embed=True)]): 1abcd

19 return {"p": p} 1efg

20 

21 

22class BodyModelRequiredListStr(BaseModel): 1abcd

23 p: list[str] 1abcd

24 

25 

26@app.post("/model-required-list-str", operation_id="model_required_list_str") 1abcd

27def read_model_required_list_str(p: BodyModelRequiredListStr): 1abcd

28 return {"p": p.p} 1efg

29 

30 

31@pytest.mark.parametrize( 1abcd

32 "path", 

33 ["/required-list-str", "/model-required-list-str"], 

34) 

35def test_required_list_str_schema(path: str): 1abcd

36 openapi = app.openapi() 1OPQ

37 body_model_name = get_body_model_name(openapi, path) 1OPQ

38 

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

40 "properties": { 

41 "p": { 

42 "items": {"type": "string"}, 

43 "title": "P", 

44 "type": "array", 

45 }, 

46 }, 

47 "required": ["p"], 

48 "title": body_model_name, 

49 "type": "object", 

50 } 

51 

52 

53@pytest.mark.parametrize("json", [None, {}]) 1abcd

54@pytest.mark.parametrize( 1abcd

55 "path", 

56 ["/required-list-str", "/model-required-list-str"], 

57) 

58def test_required_list_str_missing(path: str, json: Union[dict, None]): 1abcd

59 client = TestClient(app) 1qrs

60 response = client.post(path, json=json) 1qrs

61 assert response.status_code == 422 1qrs

62 assert response.json() == { 1qrs

63 "detail": [ 

64 { 

65 "type": "missing", 

66 "loc": IsOneOf(["body", "p"], ["body"]), 

67 "msg": "Field required", 

68 "input": IsOneOf(None, {}), 

69 } 

70 ] 

71 } 

72 

73 

74@pytest.mark.parametrize( 1abcd

75 "path", 

76 ["/required-list-str", "/model-required-list-str"], 

77) 

78def test_required_list_str(path: str): 1abcd

79 client = TestClient(app) 1efg

80 response = client.post(path, json={"p": ["hello", "world"]}) 1efg

81 assert response.status_code == 200 1efg

82 assert response.json() == {"p": ["hello", "world"]} 1efg

83 

84 

85# ===================================================================================== 

86# Alias 

87 

88 

89@app.post("/required-list-alias", operation_id="required_list_alias") 1abcd

90async def read_required_list_alias( 1abcd

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

92): 

93 return {"p": p} 1hij

94 

95 

96class BodyModelRequiredListAlias(BaseModel): 1abcd

97 p: list[str] = Field(alias="p_alias") 1abcd

98 

99 

100@app.post("/model-required-list-alias", operation_id="model_required_list_alias") 1abcd

101async def read_model_required_list_alias(p: BodyModelRequiredListAlias): 1abcd

102 return {"p": p.p} 1hij

103 

104 

105@pytest.mark.parametrize( 1abcd

106 "path", 

107 [ 

108 "/required-list-alias", 

109 "/model-required-list-alias", 

110 ], 

111) 

112def test_required_list_str_alias_schema(path: str): 1abcd

113 openapi = app.openapi() 1RST

114 body_model_name = get_body_model_name(openapi, path) 1RST

115 

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

117 "properties": { 

118 "p_alias": { 

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

120 "title": "P Alias", 

121 "type": "array", 

122 }, 

123 }, 

124 "required": ["p_alias"], 

125 "title": body_model_name, 

126 "type": "object", 

127 } 

128 

129 

130@pytest.mark.parametrize("json", [None, {}]) 1abcd

131@pytest.mark.parametrize( 1abcd

132 "path", 

133 ["/required-list-alias", "/model-required-list-alias"], 

134) 

135def test_required_list_alias_missing(path: str, json: Union[dict, None]): 1abcd

136 client = TestClient(app) 1tuv

137 response = client.post(path, json=json) 1tuv

138 assert response.status_code == 422 1tuv

139 assert response.json() == { 1tuv

140 "detail": [ 

141 { 

142 "type": "missing", 

143 "loc": IsOneOf(["body", "p_alias"], ["body"]), 

144 "msg": "Field required", 

145 "input": IsOneOf(None, {}), 

146 } 

147 ] 

148 } 

149 

150 

151@pytest.mark.parametrize( 1abcd

152 "path", 

153 ["/required-list-alias", "/model-required-list-alias"], 

154) 

155def test_required_list_alias_by_name(path: str): 1abcd

156 client = TestClient(app) 1wxy

157 response = client.post(path, json={"p": ["hello", "world"]}) 1wxy

158 assert response.status_code == 422 1wxy

159 assert response.json() == { 1wxy

160 "detail": [ 

161 { 

162 "type": "missing", 

163 "loc": ["body", "p_alias"], 

164 "msg": "Field required", 

165 "input": IsOneOf(None, {"p": ["hello", "world"]}), 

166 } 

167 ] 

168 } 

169 

170 

171@pytest.mark.parametrize( 1abcd

172 "path", 

173 ["/required-list-alias", "/model-required-list-alias"], 

174) 

175def test_required_list_alias_by_alias(path: str): 1abcd

176 client = TestClient(app) 1hij

177 response = client.post(path, json={"p_alias": ["hello", "world"]}) 1hij

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

179 assert response.json() == {"p": ["hello", "world"]} 1hij

180 

181 

182# ===================================================================================== 

183# Validation alias 

184 

185 

186@app.post( 1abcd

187 "/required-list-validation-alias", operation_id="required_list_validation_alias" 

188) 

189def read_required_list_validation_alias( 1abcd

190 p: Annotated[list[str], Body(embed=True, validation_alias="p_val_alias")], 

191): 

192 return {"p": p} 1klm

193 

194 

195class BodyModelRequiredListValidationAlias(BaseModel): 1abcd

196 p: list[str] = Field(validation_alias="p_val_alias") 1abcd

197 

198 

199@app.post( 1abcd

200 "/model-required-list-validation-alias", 

201 operation_id="model_required_list_validation_alias", 

202) 

203async def read_model_required_list_validation_alias( 1abcd

204 p: BodyModelRequiredListValidationAlias, 

205): 

206 return {"p": p.p} 1klm

207 

208 

209@pytest.mark.parametrize( 1abcd

210 "path", 

211 ["/required-list-validation-alias", "/model-required-list-validation-alias"], 

212) 

213def test_required_list_validation_alias_schema(path: str): 1abcd

214 openapi = app.openapi() 1UVW

215 body_model_name = get_body_model_name(openapi, path) 1UVW

216 

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

218 "properties": { 

219 "p_val_alias": { 

220 "items": {"type": "string"}, 

221 "title": "P Val Alias", 

222 "type": "array", 

223 }, 

224 }, 

225 "required": ["p_val_alias"], 

226 "title": body_model_name, 

227 "type": "object", 

228 } 

229 

230 

231@pytest.mark.parametrize("json", [None, {}]) 1abcd

232@pytest.mark.parametrize( 1abcd

233 "path", 

234 [ 

235 "/required-list-validation-alias", 

236 "/model-required-list-validation-alias", 

237 ], 

238) 

239def test_required_list_validation_alias_missing(path: str, json: Union[dict, None]): 1abcd

240 client = TestClient(app) 1zAB

241 response = client.post(path, json=json) 1zAB

242 assert response.status_code == 422 1zAB

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

244 "detail": [ 

245 { 

246 "type": "missing", 

247 "loc": IsOneOf(["body"], ["body", "p_val_alias"]), 

248 "msg": "Field required", 

249 "input": IsOneOf(None, {}), 

250 } 

251 ] 

252 } 

253 

254 

255@pytest.mark.parametrize( 1abcd

256 "path", 

257 [ 

258 "/required-list-validation-alias", 

259 "/model-required-list-validation-alias", 

260 ], 

261) 

262def test_required_list_validation_alias_by_name(path: str): 1abcd

263 client = TestClient(app) 1CDE

264 response = client.post(path, json={"p": ["hello", "world"]}) 1CDE

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

266 

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

268 "detail": [ 

269 { 

270 "type": "missing", 

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

272 "msg": "Field required", 

273 "input": IsOneOf(None, IsPartialDict({"p": ["hello", "world"]})), 

274 } 

275 ] 

276 } 

277 

278 

279@pytest.mark.parametrize( 1abcd

280 "path", 

281 [ 

282 "/required-list-validation-alias", 

283 "/model-required-list-validation-alias", 

284 ], 

285) 

286def test_required_list_validation_alias_by_validation_alias(path: str): 1abcd

287 client = TestClient(app) 1klm

288 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 1klm

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

290 assert response.json() == {"p": ["hello", "world"]} 1klm

291 

292 

293# ===================================================================================== 

294# Alias and validation alias 

295 

296 

297@app.post( 1abcd

298 "/required-list-alias-and-validation-alias", 

299 operation_id="required_list_alias_and_validation_alias", 

300) 

301def read_required_list_alias_and_validation_alias( 1abcd

302 p: Annotated[ 

303 list[str], Body(embed=True, alias="p_alias", validation_alias="p_val_alias") 

304 ], 

305): 

306 return {"p": p} 1nop

307 

308 

309class BodyModelRequiredListAliasAndValidationAlias(BaseModel): 1abcd

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

311 

312 

313@app.post( 1abcd

314 "/model-required-list-alias-and-validation-alias", 

315 operation_id="model_required_list_alias_and_validation_alias", 

316) 

317def read_model_required_list_alias_and_validation_alias( 1abcd

318 p: BodyModelRequiredListAliasAndValidationAlias, 

319): 

320 return {"p": p.p} 1nop

321 

322 

323@pytest.mark.parametrize( 1abcd

324 "path", 

325 [ 

326 "/required-list-alias-and-validation-alias", 

327 "/model-required-list-alias-and-validation-alias", 

328 ], 

329) 

330def test_required_list_alias_and_validation_alias_schema(path: str): 1abcd

331 openapi = app.openapi() 1XYZ

332 body_model_name = get_body_model_name(openapi, path) 1XYZ

333 

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

335 "properties": { 

336 "p_val_alias": { 

337 "items": {"type": "string"}, 

338 "title": "P Val Alias", 

339 "type": "array", 

340 }, 

341 }, 

342 "required": ["p_val_alias"], 

343 "title": body_model_name, 

344 "type": "object", 

345 } 

346 

347 

348@pytest.mark.parametrize("json", [None, {}]) 1abcd

349@pytest.mark.parametrize( 1abcd

350 "path", 

351 [ 

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

353 "/model-required-list-alias-and-validation-alias", 

354 ], 

355) 

356def test_required_list_alias_and_validation_alias_missing(path: str, json): 1abcd

357 client = TestClient(app) 1FGH

358 response = client.post(path, json=json) 1FGH

359 assert response.status_code == 422 1FGH

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

361 "detail": [ 

362 { 

363 "type": "missing", 

364 "loc": IsOneOf(["body"], ["body", "p_val_alias"]), 

365 "msg": "Field required", 

366 "input": IsOneOf(None, {}), 

367 } 

368 ] 

369 } 

370 

371 

372@pytest.mark.parametrize( 1abcd

373 "path", 

374 [ 

375 "/required-list-alias-and-validation-alias", 

376 "/model-required-list-alias-and-validation-alias", 

377 ], 

378) 

379def test_required_list_alias_and_validation_alias_by_name(path: str): 1abcd

380 client = TestClient(app) 1IJK

381 response = client.post(path, json={"p": ["hello", "world"]}) 1IJK

382 assert response.status_code == 422 1IJK

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

384 "detail": [ 

385 { 

386 "type": "missing", 

387 "loc": [ 

388 "body", 

389 "p_val_alias", 

390 ], 

391 "msg": "Field required", 

392 "input": IsOneOf(None, {"p": ["hello", "world"]}), 

393 } 

394 ] 

395 } 

396 

397 

398@pytest.mark.parametrize( 1abcd

399 "path", 

400 [ 

401 "/required-list-alias-and-validation-alias", 

402 "/model-required-list-alias-and-validation-alias", 

403 ], 

404) 

405def test_required_list_alias_and_validation_alias_by_alias(path: str): 1abcd

406 client = TestClient(app) 1LMN

407 response = client.post(path, json={"p_alias": ["hello", "world"]}) 1LMN

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

409 

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

411 "detail": [ 

412 { 

413 "type": "missing", 

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

415 "msg": "Field required", 

416 "input": IsOneOf(None, {"p_alias": ["hello", "world"]}), 

417 } 

418 ] 

419 } 

420 

421 

422@pytest.mark.parametrize( 1abcd

423 "path", 

424 [ 

425 "/required-list-alias-and-validation-alias", 

426 "/model-required-list-alias-and-validation-alias", 

427 ], 

428) 

429def test_required_list_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

430 client = TestClient(app) 1nop

431 response = client.post(path, json={"p_val_alias": ["hello", "world"]}) 1nop

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

433 assert response.json() == {"p": ["hello", "world"]} 1nop