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

131 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 FastAPI, Form 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-str", operation_id="optional_str") 1abcd

17async def read_optional_str(p: Annotated[str | None, Form()] = None): 1abcd

18 return {"p": p} 1efghijk

19 

20 

21class FormModelOptionalStr(BaseModel): 1abcd

22 p: str | None = None 1abcd

23 

24 

25@app.post("/model-optional-str", operation_id="model_optional_str") 1abcd

26async def read_model_optional_str(p: Annotated[FormModelOptionalStr, Form()]): 1abcd

27 return {"p": p.p} 2l m Mbn o p q r

28 

29 

30@pytest.mark.parametrize( 1abcd

31 "path", 

32 ["/optional-str", "/model-optional-str"], 

33) 

34def test_optional_str_schema(path: str): 1abcd

35 openapi = app.openapi() 2hbibjbkblbmbnbob

36 body_model_name = get_body_model_name(openapi, path) 2hbibjbkblbmbnbob

37 

38 assert app.openapi()["components"]["schemas"][body_model_name] == { 2hbibjbkblbmbnbob

39 "properties": { 

40 "p": { 

41 "anyOf": [{"type": "string"}, {"type": "null"}], 

42 "title": "P", 

43 }, 

44 }, 

45 "title": body_model_name, 

46 "type": "object", 

47 } 

48 

49 

50@pytest.mark.parametrize( 1abcd

51 "path", 

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

53) 

54def test_optional_str_missing(path: str): 1abcd

55 client = TestClient(app) 1mfngpirk

56 response = client.post(path) 1mfngpirk

57 assert response.status_code == 200 1mfngpirk

58 assert response.json() == {"p": None} 1mfngpirk

59 

60 

61@pytest.mark.parametrize( 1abcd

62 "path", 

63 ["/optional-str", "/model-optional-str"], 

64) 

65def test_optional_str(path: str): 1abcd

66 client = TestClient(app) 1le`ohqj

67 response = client.post(path, data={"p": "hello"}) 1le`ohqj

68 assert response.status_code == 200 1le`ohqj

69 assert response.json() == {"p": "hello"} 1le`ohqj

70 

71 

72# ===================================================================================== 

73# Alias 

74 

75 

76@app.post("/optional-alias", operation_id="optional_alias") 1abcd

77async def read_optional_alias( 1abcd

78 p: Annotated[str | None, Form(alias="p_alias")] = None, 

79): 

80 return {"p": p} 1stuvwxyzAB

81 

82 

83class FormModelOptionalAlias(BaseModel): 1abcd

84 p: str | None = Field(None, alias="p_alias") 1abcd

85 

86 

87@app.post("/model-optional-alias", operation_id="model_optional_alias") 1abcd

88async def read_model_optional_alias(p: Annotated[FormModelOptionalAlias, Form()]): 1abcd

89 return {"p": p.p} 2C D E F NbG H I J K L

90 

91 

92@pytest.mark.parametrize( 1abcd

93 "path", 

94 [ 

95 "/optional-alias", 

96 "/model-optional-alias", 

97 ], 

98) 

99def test_optional_str_alias_schema(path: str): 1abcd

100 openapi = app.openapi() 2pbqbrbsbtbubvb

101 body_model_name = get_body_model_name(openapi, path) 2pbqbrbsbtbubvb

102 

103 assert app.openapi()["components"]["schemas"][body_model_name] == { 2pbqbrbsbtbubvb

104 "properties": { 

105 "p_alias": { 

106 "anyOf": [{"type": "string"}, {"type": "null"}], 

107 "title": "P Alias", 

108 }, 

109 }, 

110 "title": body_model_name, 

111 "type": "object", 

112 } 

113 

114 

115@pytest.mark.parametrize( 1abcd

116 "path", 

117 ["/optional-alias", "/model-optional-alias"], 

118) 

119def test_optional_alias_missing(path: str): 1abcd

120 client = TestClient(app) 1EuvIyLB

121 response = client.post(path) 1EuvIyLB

122 assert response.status_code == 200 1EuvIyLB

123 assert response.json() == {"p": None} 1EuvIyLB

124 

125 

126@pytest.mark.parametrize( 1abcd

127 "path", 

128 ["/optional-alias", "/model-optional-alias"], 

129) 

130def test_optional_alias_by_name(path: str): 1abcd

131 client = TestClient(app) 1Dt{HxKA

132 response = client.post(path, data={"p": "hello"}) 1Dt{HxKA

133 assert response.status_code == 200 1Dt{HxKA

134 assert response.json() == {"p": None} 1Dt{HxKA

135 

136 

137@pytest.mark.parametrize( 1abcd

138 "path", 

139 ["/optional-alias", "/model-optional-alias"], 

140) 

141def test_optional_alias_by_alias(path: str): 1abcd

142 client = TestClient(app) 1CsF|GwJz

143 response = client.post(path, data={"p_alias": "hello"}) 1CsF|GwJz

144 assert response.status_code == 200 1CsF|GwJz

145 assert response.json() == {"p": "hello"} 1CsF|GwJz

146 

147 

148# ===================================================================================== 

149# Validation alias 

150 

151 

152@app.post("/optional-validation-alias", operation_id="optional_validation_alias") 1abcd

153def read_optional_validation_alias( 1abcd

154 p: Annotated[str | None, Form(validation_alias="p_val_alias")] = None, 

155): 

156 return {"p": p} 1MNOPQRSTUVW

157 

158 

159class FormModelOptionalValidationAlias(BaseModel): 1abcd

160 p: str | None = Field(None, validation_alias="p_val_alias") 1abcd

161 

162 

163@app.post( 1abcd

164 "/model-optional-validation-alias", operation_id="model_optional_validation_alias" 

165) 

166def read_model_optional_validation_alias( 1abcd

167 p: Annotated[FormModelOptionalValidationAlias, Form()], 

168): 

169 return {"p": p.p} 2X Y Z Ob0 1 2 3 4 5

170 

171 

172@pytest.mark.parametrize( 1abcd

173 "path", 

174 ["/optional-validation-alias", "/model-optional-validation-alias"], 

175) 

176def test_optional_validation_alias_schema(path: str): 1abcd

177 openapi = app.openapi() 2wbxbybzbAbBbCbDb

178 body_model_name = get_body_model_name(openapi, path) 2wbxbybzbAbBbCbDb

179 

180 assert app.openapi()["components"]["schemas"][body_model_name] == { 2wbxbybzbAbBbCbDb

181 "properties": { 

182 "p_val_alias": { 

183 "anyOf": [{"type": "string"}, {"type": "null"}], 

184 "title": "P Val Alias", 

185 }, 

186 }, 

187 "title": body_model_name, 

188 "type": "object", 

189 } 

190 

191 

192@pytest.mark.parametrize( 1abcd

193 "path", 

194 ["/optional-validation-alias", "/model-optional-validation-alias"], 

195) 

196def test_optional_validation_alias_missing(path: str): 1abcd

197 client = TestClient(app) 1ZOQ2T5W

198 response = client.post(path) 1ZOQ2T5W

199 assert response.status_code == 200 1ZOQ2T5W

200 assert response.json() == {"p": None} 1ZOQ2T5W

201 

202 

203@pytest.mark.parametrize( 1abcd

204 "path", 

205 [ 

206 "/optional-validation-alias", 

207 "/model-optional-validation-alias", 

208 ], 

209) 

210def test_optional_validation_alias_by_name(path: str): 1abcd

211 client = TestClient(app) 1XM}P0R3U

212 response = client.post(path, data={"p": "hello"}) 1XM}P0R3U

213 assert response.status_code == 200 1XM}P0R3U

214 assert response.json() == {"p": None} 1XM}P0R3U

215 

216 

217@pytest.mark.parametrize( 1abcd

218 "path", 

219 [ 

220 "/optional-validation-alias", 

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

222 ], 

223) 

224def test_optional_validation_alias_by_validation_alias(path: str): 1abcd

225 client = TestClient(app) 2Y N ~ ab1 S 4 V

226 response = client.post(path, data={"p_val_alias": "hello"}) 2Y N ~ ab1 S 4 V

227 assert response.status_code == 200 2Y N ~ ab1 S 4 V

228 assert response.json() == {"p": "hello"} 2Y N ~ ab1 S 4 V

229 

230 

231# ===================================================================================== 

232# Alias and validation alias 

233 

234 

235@app.post( 1abcd

236 "/optional-alias-and-validation-alias", 

237 operation_id="optional_alias_and_validation_alias", 

238) 

239def read_optional_alias_and_validation_alias( 1abcd

240 p: Annotated[ 

241 str | None, Form(alias="p_alias", validation_alias="p_val_alias") 

242 ] = None, 

243): 

244 return {"p": p} 16789!#$%'()*+

245 

246 

247class FormModelOptionalAliasAndValidationAlias(BaseModel): 1abcd

248 p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias") 1abcd

249 

250 

251@app.post( 1abcd

252 "/model-optional-alias-and-validation-alias", 

253 operation_id="model_optional_alias_and_validation_alias", 

254) 

255def read_model_optional_alias_and_validation_alias( 1abcd

256 p: Annotated[FormModelOptionalAliasAndValidationAlias, Form()], 

257): 

258 return {"p": p.p} 1,-./:;=?@[]^_

259 

260 

261@pytest.mark.parametrize( 1abcd

262 "path", 

263 [ 

264 "/optional-alias-and-validation-alias", 

265 "/model-optional-alias-and-validation-alias", 

266 ], 

267) 

268def test_optional_alias_and_validation_alias_schema(path: str): 1abcd

269 openapi = app.openapi() 2EbFbGbHbIbJbKbLb

270 body_model_name = get_body_model_name(openapi, path) 2EbFbGbHbIbJbKbLb

271 

272 assert app.openapi()["components"]["schemas"][body_model_name] == { 2EbFbGbHbIbJbKbLb

273 "properties": { 

274 "p_val_alias": { 

275 "anyOf": [{"type": "string"}, {"type": "null"}], 

276 "title": "P Val Alias", 

277 }, 

278 }, 

279 "title": body_model_name, 

280 "type": "object", 

281 } 

282 

283 

284@pytest.mark.parametrize( 1abcd

285 "path", 

286 [ 

287 "/optional-alias-and-validation-alias", 

288 "/model-optional-alias-and-validation-alias", 

289 ], 

290) 

291def test_optional_alias_and_validation_alias_missing(path: str): 1abcd

292 client = TestClient(app) 1/9:!@'_+

293 response = client.post(path) 1/9:!@'_+

294 assert response.status_code == 200 1/9:!@'_+

295 assert response.json() == {"p": None} 1/9:!@'_+

296 

297 

298@pytest.mark.parametrize( 1abcd

299 "path", 

300 [ 

301 "/optional-alias-and-validation-alias", 

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

303 ], 

304) 

305def test_optional_alias_and_validation_alias_by_name(path: str): 1abcd

306 client = TestClient(app) 2- 7 bbcb= $ ] )

307 response = client.post(path, data={"p": "hello"}) 2- 7 bbcb= $ ] )

308 assert response.status_code == 200 2- 7 bbcb= $ ] )

309 assert response.json() == {"p": None} 2- 7 bbcb= $ ] )

310 

311 

312@pytest.mark.parametrize( 1abcd

313 "path", 

314 [ 

315 "/optional-alias-and-validation-alias", 

316 "/model-optional-alias-and-validation-alias", 

317 ], 

318) 

319def test_optional_alias_and_validation_alias_by_alias(path: str): 1abcd

320 client = TestClient(app) 2, 6 dbeb; # [ (

321 response = client.post(path, data={"p_alias": "hello"}) 2, 6 dbeb; # [ (

322 assert response.status_code == 200 2, 6 dbeb; # [ (

323 assert response.json() == {"p": None} 2, 6 dbeb; # [ (

324 

325 

326@pytest.mark.parametrize( 1abcd

327 "path", 

328 [ 

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

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

331 ], 

332) 

333def test_optional_alias_and_validation_alias_by_validation_alias(path: str): 1abcd

334 client = TestClient(app) 2. 8 fbgb? % ^ *

335 response = client.post(path, data={"p_val_alias": "hello"}) 2. 8 fbgb? % ^ *

336 assert response.status_code == 200 2. 8 fbgb? % ^ *

337 assert response.json() == {"p": "hello"} 2. 8 fbgb? % ^ *