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

136 statements  

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

1from typing import Annotated, Any, Union 1abcd

2 

3import pytest 1abcd

4from dirty_equals import IsOneOf 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-str", operation_id="required_str") 1abcd

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

19 return {"p": p} 1efg

20 

21 

22class BodyModelRequiredStr(BaseModel): 1abcd

23 p: str 1abcd

24 

25 

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

27async def read_model_required_str(p: BodyModelRequiredStr): 1abcd

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

29 

30 

31@pytest.mark.parametrize( 1abcd

32 "path", 

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

34) 

35def test_required_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": {"title": "P", "type": "string"}, 

42 }, 

43 "required": ["p"], 

44 "title": body_model_name, 

45 "type": "object", 

46 } 

47 

48 

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

50@pytest.mark.parametrize( 1abcd

51 "path", 

52 ["/required-str", "/model-required-str"], 

53) 

54def test_required_str_missing(path: str, json: Union[dict[str, Any], None]): 1abcd

55 client = TestClient(app) 1qrs

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

57 assert response.status_code == 422 1qrs

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

59 "detail": [ 

60 { 

61 "type": "missing", 

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

63 "msg": "Field required", 

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

65 } 

66 ] 

67 } 

68 

69 

70@pytest.mark.parametrize( 1abcd

71 "path", 

72 ["/required-str", "/model-required-str"], 

73) 

74def test_required_str(path: str): 1abcd

75 client = TestClient(app) 1efg

76 response = client.post(path, json={"p": "hello"}) 1efg

77 assert response.status_code == 200 1efg

78 assert response.json() == {"p": "hello"} 1efg

79 

80 

81# ===================================================================================== 

82# Alias 

83 

84 

85@app.post("/required-alias", operation_id="required_alias") 1abcd

86async def read_required_alias( 1abcd

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

88): 

89 return {"p": p} 1hij

90 

91 

92class BodyModelRequiredAlias(BaseModel): 1abcd

93 p: str = Field(alias="p_alias") 1abcd

94 

95 

96@app.post("/model-required-alias", operation_id="model_required_alias") 1abcd

97async def read_model_required_alias(p: BodyModelRequiredAlias): 1abcd

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

99 

100 

101@pytest.mark.parametrize( 1abcd

102 "path", 

103 [ 

104 "/required-alias", 

105 "/model-required-alias", 

106 ], 

107) 

108def test_required_str_alias_schema(path: str): 1abcd

109 openapi = app.openapi() 1RST

110 body_model_name = get_body_model_name(openapi, path) 1RST

111 

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

113 "properties": { 

114 "p_alias": {"title": "P Alias", "type": "string"}, 

115 }, 

116 "required": ["p_alias"], 

117 "title": body_model_name, 

118 "type": "object", 

119 } 

120 

121 

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

123@pytest.mark.parametrize( 1abcd

124 "path", 

125 ["/required-alias", "/model-required-alias"], 

126) 

127def test_required_alias_missing(path: str, json: Union[dict[str, Any], None]): 1abcd

128 client = TestClient(app) 1tuv

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

130 assert response.status_code == 422 1tuv

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

132 "detail": [ 

133 { 

134 "type": "missing", 

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

136 "msg": "Field required", 

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

138 } 

139 ] 

140 } 

141 

142 

143@pytest.mark.parametrize( 1abcd

144 "path", 

145 ["/required-alias", "/model-required-alias"], 

146) 

147def test_required_alias_by_name(path: str): 1abcd

148 client = TestClient(app) 1wxy

149 response = client.post(path, json={"p": "hello"}) 1wxy

150 assert response.status_code == 422 1wxy

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

152 "detail": [ 

153 { 

154 "type": "missing", 

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

156 "msg": "Field required", 

157 "input": IsOneOf(None, {"p": "hello"}), 

158 } 

159 ] 

160 } 

161 

162 

163@pytest.mark.parametrize( 1abcd

164 "path", 

165 ["/required-alias", "/model-required-alias"], 

166) 

167def test_required_alias_by_alias(path: str): 1abcd

168 client = TestClient(app) 1hij

169 response = client.post(path, json={"p_alias": "hello"}) 1hij

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

171 assert response.json() == {"p": "hello"} 1hij

172 

173 

174# ===================================================================================== 

175# Validation alias 

176 

177 

178@app.post("/required-validation-alias", operation_id="required_validation_alias") 1abcd

179def read_required_validation_alias( 1abcd

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

181): 

182 return {"p": p} 1klm

183 

184 

185class BodyModelRequiredValidationAlias(BaseModel): 1abcd

186 p: str = Field(validation_alias="p_val_alias") 1abcd

187 

188 

189@app.post( 1abcd

190 "/model-required-validation-alias", operation_id="model_required_validation_alias" 

191) 

192def read_model_required_validation_alias( 1abcd

193 p: BodyModelRequiredValidationAlias, 

194): 

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

196 

197 

198@pytest.mark.parametrize( 1abcd

199 "path", 

200 ["/required-validation-alias", "/model-required-validation-alias"], 

201) 

202def test_required_validation_alias_schema(path: str): 1abcd

203 openapi = app.openapi() 1UVW

204 body_model_name = get_body_model_name(openapi, path) 1UVW

205 

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

207 "properties": { 

208 "p_val_alias": {"title": "P Val Alias", "type": "string"}, 

209 }, 

210 "required": ["p_val_alias"], 

211 "title": body_model_name, 

212 "type": "object", 

213 } 

214 

215 

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

217@pytest.mark.parametrize( 1abcd

218 "path", 

219 [ 

220 "/required-validation-alias", 

221 "/model-required-validation-alias", 

222 ], 

223) 

224def test_required_validation_alias_missing( 1abcd

225 path: str, json: Union[dict[str, Any], None] 

226): 

227 client = TestClient(app) 1zAB

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

229 assert response.status_code == 422 1zAB

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

231 "detail": [ 

232 { 

233 "type": "missing", 

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

235 "msg": "Field required", 

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

237 } 

238 ] 

239 } 

240 

241 

242@pytest.mark.parametrize( 1abcd

243 "path", 

244 [ 

245 "/required-validation-alias", 

246 "/model-required-validation-alias", 

247 ], 

248) 

249def test_required_validation_alias_by_name(path: str): 1abcd

250 client = TestClient(app) 1CDE

251 response = client.post(path, json={"p": "hello"}) 1CDE

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

253 

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

255 "detail": [ 

256 { 

257 "type": "missing", 

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

259 "msg": "Field required", 

260 "input": IsOneOf(None, {"p": "hello"}), 

261 } 

262 ] 

263 } 

264 

265 

266@pytest.mark.parametrize( 1abcd

267 "path", 

268 [ 

269 "/required-validation-alias", 

270 "/model-required-validation-alias", 

271 ], 

272) 

273def test_required_validation_alias_by_validation_alias(path: str): 1abcd

274 client = TestClient(app) 1klm

275 response = client.post(path, json={"p_val_alias": "hello"}) 1klm

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

277 

278 assert response.json() == {"p": "hello"} 1klm

279 

280 

281# ===================================================================================== 

282# Alias and validation alias 

283 

284 

285@app.post( 1abcd

286 "/required-alias-and-validation-alias", 

287 operation_id="required_alias_and_validation_alias", 

288) 

289def read_required_alias_and_validation_alias( 1abcd

290 p: Annotated[ 

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

292 ], 

293): 

294 return {"p": p} 1nop

295 

296 

297class BodyModelRequiredAliasAndValidationAlias(BaseModel): 1abcd

298 p: str = Field(alias="p_alias", validation_alias="p_val_alias") 1abcd

299 

300 

301@app.post( 1abcd

302 "/model-required-alias-and-validation-alias", 

303 operation_id="model_required_alias_and_validation_alias", 

304) 

305def read_model_required_alias_and_validation_alias( 1abcd

306 p: BodyModelRequiredAliasAndValidationAlias, 

307): 

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

309 

310 

311@pytest.mark.parametrize( 1abcd

312 "path", 

313 [ 

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

315 "/model-required-alias-and-validation-alias", 

316 ], 

317) 

318def test_required_alias_and_validation_alias_schema(path: str): 1abcd

319 openapi = app.openapi() 1XYZ

320 body_model_name = get_body_model_name(openapi, path) 1XYZ

321 

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

323 "properties": { 

324 "p_val_alias": {"title": "P Val Alias", "type": "string"}, 

325 }, 

326 "required": ["p_val_alias"], 

327 "title": body_model_name, 

328 "type": "object", 

329 } 

330 

331 

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

333@pytest.mark.parametrize( 1abcd

334 "path", 

335 [ 

336 "/required-alias-and-validation-alias", 

337 "/model-required-alias-and-validation-alias", 

338 ], 

339) 

340def test_required_alias_and_validation_alias_missing( 1abcd

341 path: str, json: Union[dict[str, Any], None] 

342): 

343 client = TestClient(app) 1FGH

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

345 assert response.status_code == 422 1FGH

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

347 "detail": [ 

348 { 

349 "type": "missing", 

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

351 "msg": "Field required", 

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

353 } 

354 ] 

355 } 

356 

357 

358@pytest.mark.parametrize( 1abcd

359 "path", 

360 [ 

361 "/required-alias-and-validation-alias", 

362 "/model-required-alias-and-validation-alias", 

363 ], 

364) 

365def test_required_alias_and_validation_alias_by_name(path: str): 1abcd

366 client = TestClient(app) 1IJK

367 response = client.post(path, json={"p": "hello"}) 1IJK

368 assert response.status_code == 422 1IJK

369 

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

371 "detail": [ 

372 { 

373 "type": "missing", 

374 "loc": [ 

375 "body", 

376 "p_val_alias", 

377 ], 

378 "msg": "Field required", 

379 "input": IsOneOf(None, {"p": "hello"}), 

380 } 

381 ] 

382 } 

383 

384 

385@pytest.mark.parametrize( 1abcd

386 "path", 

387 [ 

388 "/required-alias-and-validation-alias", 

389 "/model-required-alias-and-validation-alias", 

390 ], 

391) 

392def test_required_alias_and_validation_alias_by_alias(path: str): 1abcd

393 client = TestClient(app) 1LMN

394 response = client.post(path, json={"p_alias": "hello"}) 1LMN

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

396 

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

398 "detail": [ 

399 { 

400 "type": "missing", 

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

402 "msg": "Field required", 

403 "input": IsOneOf(None, {"p_alias": "hello"}), 

404 } 

405 ] 

406 } 

407 

408 

409@pytest.mark.parametrize( 1abcd

410 "path", 

411 [ 

412 "/required-alias-and-validation-alias", 

413 "/model-required-alias-and-validation-alias", 

414 ], 

415) 

416def test_required_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

417 client = TestClient(app) 1nop

418 response = client.post(path, json={"p_val_alias": "hello"}) 1nop

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

420 

421 assert response.json() == {"p": "hello"} 1nop