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-02-12 18:15 +0000

1from typing import Annotated, Optional 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[Optional[str], Form()] = None): 1abcd

18 return {"p": p} 1efghij

19 

20 

21class FormModelOptionalStr(BaseModel): 1abcd

22 p: Optional[str] = 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} 1efghij

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() 1OPQ

36 body_model_name = get_body_model_name(openapi, path) 1OPQ

37 

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

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) 1fhj

56 response = client.post(path) 1fhj

57 assert response.status_code == 200 1fhj

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

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) 1egi

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

68 assert response.status_code == 200 1egi

69 assert response.json() == {"p": "hello"} 1egi

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[Optional[str], Form(alias="p_alias")] = None, 

79): 

80 return {"p": p} 1klmnopqrs

81 

82 

83class FormModelOptionalAlias(BaseModel): 1abcd

84 p: Optional[str] = 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} 1klmnopqrs

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() 1RST

101 body_model_name = get_body_model_name(openapi, path) 1RST

102 

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

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) 1mps

121 response = client.post(path) 1mps

122 assert response.status_code == 200 1mps

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

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) 1lor

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

133 assert response.status_code == 200 1lor

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

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) 1knq

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

144 assert response.status_code == 200 1knq

145 assert response.json() == {"p": "hello"} 1knq

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[Optional[str], Form(validation_alias="p_val_alias")] = None, 

155): 

156 return {"p": p} 1tuvwxyzAB

157 

158 

159class FormModelOptionalValidationAlias(BaseModel): 1abcd

160 p: Optional[str] = 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} 1tuvwxyzAB

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() 1UVW

178 body_model_name = get_body_model_name(openapi, path) 1UVW

179 

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

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) 1vyB

198 response = client.post(path) 1vyB

199 assert response.status_code == 200 1vyB

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

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) 1twz

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

213 assert response.status_code == 200 1twz

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

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) 1uxA

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

227 assert response.status_code == 200 1uxA

228 assert response.json() == {"p": "hello"} 1uxA

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 Optional[str], Form(alias="p_alias", validation_alias="p_val_alias") 

242 ] = None, 

243): 

244 return {"p": p} 1CDEFGHIJKLMN

245 

246 

247class FormModelOptionalAliasAndValidationAlias(BaseModel): 1abcd

248 p: Optional[str] = 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} 1CDEFGHIJKLMN

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() 1XYZ

270 body_model_name = get_body_model_name(openapi, path) 1XYZ

271 

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

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) 1FJN

293 response = client.post(path) 1FJN

294 assert response.status_code == 200 1FJN

295 assert response.json() == {"p": None} 1FJN

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) 1DHL

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

308 assert response.status_code == 200 1DHL

309 assert response.json() == {"p": None} 1DHL

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) 1CGK

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

322 assert response.status_code == 200 1CGK

323 assert response.json() == {"p": None} 1CGK

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) 1EIM

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

336 assert response.status_code == 200 1EIM

337 assert response.json() == {"p": "hello"} 1EIM