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

132 statements  

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

1from typing import Annotated 1abcd

2 

3import pytest 1abcd

4from dirty_equals import IsOneOf 1abcd

5from fastapi import FastAPI, Form 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, Form()]): 1abcd

19 return {"p": p} 1efg

20 

21 

22class FormModelRequiredStr(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: Annotated[FormModelRequiredStr, Form()]): 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( 1abcd

50 "path", 

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

52) 

53def test_required_str_missing(path: str): 1abcd

54 client = TestClient(app) 1qrs

55 response = client.post(path) 1qrs

56 assert response.status_code == 422 1qrs

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

58 "detail": [ 

59 { 

60 "type": "missing", 

61 "loc": ["body", "p"], 

62 "msg": "Field required", 

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

64 } 

65 ] 

66 } 

67 

68 

69@pytest.mark.parametrize( 1abcd

70 "path", 

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

72) 

73def test_required_str(path: str): 1abcd

74 client = TestClient(app) 1efg

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

76 assert response.status_code == 200 1efg

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

78 

79 

80# ===================================================================================== 

81# Alias 

82 

83 

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

85async def read_required_alias(p: Annotated[str, Form(alias="p_alias")]): 1abcd

86 return {"p": p} 1hij

87 

88 

89class FormModelRequiredAlias(BaseModel): 1abcd

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

91 

92 

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

94async def read_model_required_alias(p: Annotated[FormModelRequiredAlias, Form()]): 1abcd

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

96 

97 

98@pytest.mark.parametrize( 1abcd

99 "path", 

100 [ 

101 "/required-alias", 

102 "/model-required-alias", 

103 ], 

104) 

105def test_required_str_alias_schema(path: str): 1abcd

106 openapi = app.openapi() 1RST

107 body_model_name = get_body_model_name(openapi, path) 1RST

108 

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

110 "properties": { 

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

112 }, 

113 "required": ["p_alias"], 

114 "title": body_model_name, 

115 "type": "object", 

116 } 

117 

118 

119@pytest.mark.parametrize( 1abcd

120 "path", 

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

122) 

123def test_required_alias_missing(path: str): 1abcd

124 client = TestClient(app) 1tuv

125 response = client.post(path) 1tuv

126 assert response.status_code == 422 1tuv

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

128 "detail": [ 

129 { 

130 "type": "missing", 

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

132 "msg": "Field required", 

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

134 } 

135 ] 

136 } 

137 

138 

139@pytest.mark.parametrize( 1abcd

140 "path", 

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

142) 

143def test_required_alias_by_name(path: str): 1abcd

144 client = TestClient(app) 1wxy

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

146 assert response.status_code == 422 1wxy

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

148 "detail": [ 

149 { 

150 "type": "missing", 

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

152 "msg": "Field required", 

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

154 } 

155 ] 

156 } 

157 

158 

159@pytest.mark.parametrize( 1abcd

160 "path", 

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

162) 

163def test_required_alias_by_alias(path: str): 1abcd

164 client = TestClient(app) 1hij

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

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

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

168 

169 

170# ===================================================================================== 

171# Validation alias 

172 

173 

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

175def read_required_validation_alias( 1abcd

176 p: Annotated[str, Form(validation_alias="p_val_alias")], 

177): 

178 return {"p": p} 1klm

179 

180 

181class FormModelRequiredValidationAlias(BaseModel): 1abcd

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

183 

184 

185@app.post( 1abcd

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

187) 

188def read_model_required_validation_alias( 1abcd

189 p: Annotated[FormModelRequiredValidationAlias, Form()], 

190): 

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

192 

193 

194@pytest.mark.parametrize( 1abcd

195 "path", 

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

197) 

198def test_required_validation_alias_schema(path: str): 1abcd

199 openapi = app.openapi() 1UVW

200 body_model_name = get_body_model_name(openapi, path) 1UVW

201 

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

203 "properties": { 

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

205 }, 

206 "required": ["p_val_alias"], 

207 "title": body_model_name, 

208 "type": "object", 

209 } 

210 

211 

212@pytest.mark.parametrize( 1abcd

213 "path", 

214 [ 

215 "/required-validation-alias", 

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

217 ], 

218) 

219def test_required_validation_alias_missing(path: str): 1abcd

220 client = TestClient(app) 1zAB

221 response = client.post(path) 1zAB

222 assert response.status_code == 422 1zAB

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

224 "detail": [ 

225 { 

226 "type": "missing", 

227 "loc": [ 

228 "body", 

229 "p_val_alias", 

230 ], 

231 "msg": "Field required", 

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

233 } 

234 ] 

235 } 

236 

237 

238@pytest.mark.parametrize( 1abcd

239 "path", 

240 [ 

241 "/required-validation-alias", 

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

243 ], 

244) 

245def test_required_validation_alias_by_name(path: str): 1abcd

246 client = TestClient(app) 1CDE

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

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

249 

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

251 "detail": [ 

252 { 

253 "type": "missing", 

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

255 "msg": "Field required", 

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

257 } 

258 ] 

259 } 

260 

261 

262@pytest.mark.parametrize( 1abcd

263 "path", 

264 [ 

265 "/required-validation-alias", 

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

267 ], 

268) 

269def test_required_validation_alias_by_validation_alias(path: str): 1abcd

270 client = TestClient(app) 1klm

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

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

273 

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

275 

276 

277# ===================================================================================== 

278# Alias and validation alias 

279 

280 

281@app.post( 1abcd

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

283 operation_id="required_alias_and_validation_alias", 

284) 

285def read_required_alias_and_validation_alias( 1abcd

286 p: Annotated[str, Form(alias="p_alias", validation_alias="p_val_alias")], 

287): 

288 return {"p": p} 1nop

289 

290 

291class FormModelRequiredAliasAndValidationAlias(BaseModel): 1abcd

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

293 

294 

295@app.post( 1abcd

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

297 operation_id="model_required_alias_and_validation_alias", 

298) 

299def read_model_required_alias_and_validation_alias( 1abcd

300 p: Annotated[FormModelRequiredAliasAndValidationAlias, Form()], 

301): 

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

303 

304 

305@pytest.mark.parametrize( 1abcd

306 "path", 

307 [ 

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

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

310 ], 

311) 

312def test_required_alias_and_validation_alias_schema(path: str): 1abcd

313 openapi = app.openapi() 1XYZ

314 body_model_name = get_body_model_name(openapi, path) 1XYZ

315 

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

317 "properties": { 

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

319 }, 

320 "required": ["p_val_alias"], 

321 "title": body_model_name, 

322 "type": "object", 

323 } 

324 

325 

326@pytest.mark.parametrize( 1abcd

327 "path", 

328 [ 

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

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

331 ], 

332) 

333def test_required_alias_and_validation_alias_missing(path: str): 1abcd

334 client = TestClient(app) 1FGH

335 response = client.post(path) 1FGH

336 assert response.status_code == 422 1FGH

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

338 "detail": [ 

339 { 

340 "type": "missing", 

341 "loc": [ 

342 "body", 

343 "p_val_alias", 

344 ], 

345 "msg": "Field required", 

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

347 } 

348 ] 

349 } 

350 

351 

352@pytest.mark.parametrize( 1abcd

353 "path", 

354 [ 

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

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

357 ], 

358) 

359def test_required_alias_and_validation_alias_by_name(path: str): 1abcd

360 client = TestClient(app) 1IJK

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

362 assert response.status_code == 422 1IJK

363 

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

365 "detail": [ 

366 { 

367 "type": "missing", 

368 "loc": [ 

369 "body", 

370 "p_val_alias", 

371 ], 

372 "msg": "Field required", 

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

374 } 

375 ] 

376 } 

377 

378 

379@pytest.mark.parametrize( 1abcd

380 "path", 

381 [ 

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

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

384 ], 

385) 

386def test_required_alias_and_validation_alias_by_alias(path: str): 1abcd

387 client = TestClient(app) 1LMN

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

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

390 

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

392 "detail": [ 

393 { 

394 "type": "missing", 

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

396 "msg": "Field required", 

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

398 } 

399 ] 

400 } 

401 

402 

403@pytest.mark.parametrize( 1abcd

404 "path", 

405 [ 

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

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

408 ], 

409) 

410def test_required_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

411 client = TestClient(app) 1nop

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

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

414 

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