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-04-06 01:24 +0000

1from typing import Annotated 1adbc

2 

3import pytest 1adbc

4from dirty_equals import IsOneOf, IsPartialDict 1adbc

5from fastapi import Body, FastAPI 1adbc

6from fastapi.testclient import TestClient 1adbc

7from pydantic import BaseModel, Field 1adbc

8 

9from .utils import get_body_model_name 1adbc

10 

11app = FastAPI() 1adbc

12 

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

14# Without aliases 

15 

16 

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

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

19 return {"p": p} 1efgh

20 

21 

22class BodyModelRequiredListStr(BaseModel): 1adbc

23 p: list[str] 1abc

24 

25 

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

27def read_model_required_list_str(p: BodyModelRequiredListStr): 1adbc

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

29 

30 

31@pytest.mark.parametrize( 1adbc

32 "path", 

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

34) 

35def test_required_list_str_schema(path: str): 1adbc

36 openapi = app.openapi() 2HbIbJbKbLbMbNb

37 body_model_name = get_body_model_name(openapi, path) 2HbIbJbKbLbMbNb

38 

39 assert app.openapi()["components"]["schemas"][body_model_name] == { 2HbIbJbKbLbMbNb

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, {}]) 1adbc

54@pytest.mark.parametrize( 1adbc

55 "path", 

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

57) 

58def test_required_list_str_missing(path: str, json: dict | None): 1adbc

59 client = TestClient(app) 1KLMNOPQRSTUVWX

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

61 assert response.status_code == 422 1KLMNOPQRSTUVWX

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

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( 1adbc

75 "path", 

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

77) 

78def test_required_list_str(path: str): 1adbc

79 client = TestClient(app) 1iejfkglh

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

81 assert response.status_code == 200 1iejfkglh

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

83 

84 

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

86# Alias 

87 

88 

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

90async def read_required_list_alias( 1adbc

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

92): 

93 return {"p": p} 1mnop

94 

95 

96class BodyModelRequiredListAlias(BaseModel): 1adbc

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

98 

99 

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

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

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

103 

104 

105@pytest.mark.parametrize( 1adbc

106 "path", 

107 [ 

108 "/required-list-alias", 

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

110 ], 

111) 

112def test_required_list_str_alias_schema(path: str): 1adbc

113 openapi = app.openapi() 2ObPbQbRbSbTbUbVb

114 body_model_name = get_body_model_name(openapi, path) 2ObPbQbRbSbTbUbVb

115 

116 assert app.openapi()["components"]["schemas"][body_model_name] == { 2ObPbQbRbSbTbUbVb

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, {}]) 1adbc

131@pytest.mark.parametrize( 1adbc

132 "path", 

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

134) 

135def test_required_list_alias_missing(path: str, json: dict | None): 1adbc

136 client = TestClient(app) 1YZ0123456789!#

137 response = client.post(path, json=json) 1YZ0123456789!#

138 assert response.status_code == 422 1YZ0123456789!#

139 assert response.json() == { 1YZ0123456789!#

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( 1adbc

152 "path", 

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

154) 

155def test_required_list_alias_by_name(path: str): 1adbc

156 client = TestClient(app) 1$%'()*+

157 response = client.post(path, json={"p": ["hello", "world"]}) 1$%'()*+

158 assert response.status_code == 422 1$%'()*+

159 assert response.json() == { 1$%'()*+

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( 1adbc

172 "path", 

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

174) 

175def test_required_list_alias_by_alias(path: str): 1adbc

176 client = TestClient(app) 1qmrnsotp

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

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

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

180 

181 

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

183# Validation alias 

184 

185 

186@app.post( 1adbc

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

188) 

189def read_required_list_validation_alias( 1adbc

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

191): 

192 return {"p": p} 1uvwx

193 

194 

195class BodyModelRequiredListValidationAlias(BaseModel): 1adbc

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

197 

198 

199@app.post( 1adbc

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

201 operation_id="model_required_list_validation_alias", 

202) 

203async def read_model_required_list_validation_alias( 1adbc

204 p: BodyModelRequiredListValidationAlias, 

205): 

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

207 

208 

209@pytest.mark.parametrize( 1adbc

210 "path", 

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

212) 

213def test_required_list_validation_alias_schema(path: str): 1adbc

214 openapi = app.openapi() 2WbXbYbZb0b1b2b3b

215 body_model_name = get_body_model_name(openapi, path) 2WbXbYbZb0b1b2b3b

216 

217 assert app.openapi()["components"]["schemas"][body_model_name] == { 2WbXbYbZb0b1b2b3b

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, {}]) 1adbc

232@pytest.mark.parametrize( 1adbc

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: dict | None): 1adbc

240 client = TestClient(app) 1,-./:;=?@[]^_`

241 response = client.post(path, json=json) 1,-./:;=?@[]^_`

242 assert response.status_code == 422 1,-./:;=?@[]^_`

243 assert response.json() == { 1,-./:;=?@[]^_`

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( 1adbc

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): 1adbc

263 client = TestClient(app) 2{ | } ~ abbbcb

264 response = client.post(path, json={"p": ["hello", "world"]}) 2{ | } ~ abbbcb

265 assert response.status_code == 422, response.text 2{ | } ~ abbbcb

266 

267 assert response.json() == { 2{ | } ~ abbbcb

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( 1adbc

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): 1adbc

287 client = TestClient(app) 1yuzvAwBx

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

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

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

291 

292 

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

294# Alias and validation alias 

295 

296 

297@app.post( 1adbc

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( 1adbc

302 p: Annotated[ 

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

304 ], 

305): 

306 return {"p": p} 1CDEF

307 

308 

309class BodyModelRequiredListAliasAndValidationAlias(BaseModel): 1adbc

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

311 

312 

313@app.post( 1adbc

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( 1adbc

318 p: BodyModelRequiredListAliasAndValidationAlias, 

319): 

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

321 

322 

323@pytest.mark.parametrize( 1adbc

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): 1adbc

331 openapi = app.openapi() 24b5b6b7b8b9b!b#b

332 body_model_name = get_body_model_name(openapi, path) 24b5b6b7b8b9b!b#b

333 

334 assert app.openapi()["components"]["schemas"][body_model_name] == { 24b5b6b7b8b9b!b#b

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, {}]) 1adbc

349@pytest.mark.parametrize( 1adbc

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): 1adbc

357 client = TestClient(app) 2dbebfbgbhbibjbkblbmbnbobpbqb

358 response = client.post(path, json=json) 2dbebfbgbhbibjbkblbmbnbobpbqb

359 assert response.status_code == 422 2dbebfbgbhbibjbkblbmbnbobpbqb

360 assert response.json() == { 2dbebfbgbhbibjbkblbmbnbobpbqb

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( 1adbc

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): 1adbc

380 client = TestClient(app) 2rbsbtbubvbwbxbyb

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

382 assert response.status_code == 422 2rbsbtbubvbwbxbyb

383 assert response.json() == { 2rbsbtbubvbwbxbyb

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( 1adbc

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): 1adbc

406 client = TestClient(app) 2zbAbBbCbDbEbFbGb

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

408 assert response.status_code == 422, response.text 2zbAbBbCbDbEbFbGb

409 

410 assert response.json() == { 2zbAbBbCbDbEbFbGb

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( 1adbc

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): 1adbc

430 client = TestClient(app) 1GCHDIEJF

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

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

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