Coverage for tests / test_request_params / test_body / test_optional_str.py: 100%
171 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1from typing import Annotated, Optional 1abcd
3import pytest 1abcd
4from fastapi import Body, FastAPI 1abcd
5from fastapi.testclient import TestClient 1abcd
6from pydantic import BaseModel, Field 1abcd
8from .utils import get_body_model_name 1abcd
10app = FastAPI() 1abcd
12# =====================================================================================
13# Without aliases
16@app.post("/optional-str", operation_id="optional_str") 1abcd
17async def read_optional_str(p: Annotated[Optional[str], Body(embed=True)] = None): 1abcd
18 return {"p": p} 1eOfgPhiQj
21class BodyModelOptionalStr(BaseModel): 1abcd
22 p: Optional[str] = None 1abcd
25@app.post("/model-optional-str", operation_id="model_optional_str") 1abcd
26async def read_model_optional_str(p: BodyModelOptionalStr): 1abcd
27 return {"p": p.p} 1efghij
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() 1$%'
36 body_model_name = get_body_model_name(openapi, path) 1$%'
38 assert app.openapi()["components"]["schemas"][body_model_name] == { 1$%'
39 "properties": {
40 "p": {
41 "anyOf": [{"type": "string"}, {"type": "null"}],
42 "title": "P",
43 },
44 },
45 "title": body_model_name,
46 "type": "object",
47 }
50def test_optional_str_missing(): 1abcd
51 client = TestClient(app) 1OPQ
52 response = client.post("/optional-str") 1OPQ
53 assert response.status_code == 200, response.text 1OPQ
54 assert response.json() == {"p": None} 1OPQ
57def test_model_optional_str_missing(): 1abcd
58 client = TestClient(app) 1012
59 response = client.post("/model-optional-str") 1012
60 assert response.status_code == 422, response.text 1012
61 assert response.json() == { 1012
62 "detail": [
63 {
64 "input": None,
65 "loc": ["body"],
66 "msg": "Field required",
67 "type": "missing",
68 },
69 ],
70 }
73@pytest.mark.parametrize( 1abcd
74 "path",
75 ["/optional-str", "/model-optional-str"],
76)
77def test_optional_str_missing_empty_dict(path: str): 1abcd
78 client = TestClient(app) 1fhj
79 response = client.post(path, json={}) 1fhj
80 assert response.status_code == 200, response.text 1fhj
81 assert response.json() == {"p": None} 1fhj
84@pytest.mark.parametrize( 1abcd
85 "path",
86 ["/optional-str", "/model-optional-str"],
87)
88def test_optional_str(path: str): 1abcd
89 client = TestClient(app) 1egi
90 response = client.post(path, json={"p": "hello"}) 1egi
91 assert response.status_code == 200 1egi
92 assert response.json() == {"p": "hello"} 1egi
95# =====================================================================================
96# Alias
99@app.post("/optional-alias", operation_id="optional_alias") 1abcd
100async def read_optional_alias( 1abcd
101 p: Annotated[Optional[str], Body(embed=True, alias="p_alias")] = None,
102):
103 return {"p": p} 1klmRnopSqrsT
106class BodyModelOptionalAlias(BaseModel): 1abcd
107 p: Optional[str] = Field(None, alias="p_alias") 1abcd
110@app.post("/model-optional-alias", operation_id="model_optional_alias") 1abcd
111async def read_model_optional_alias(p: BodyModelOptionalAlias): 1abcd
112 return {"p": p.p} 1klmnopqrs
115@pytest.mark.parametrize( 1abcd
116 "path",
117 [
118 "/optional-alias",
119 "/model-optional-alias",
120 ],
121)
122def test_optional_str_alias_schema(path: str): 1abcd
123 openapi = app.openapi() 1()*
124 body_model_name = get_body_model_name(openapi, path) 1()*
126 assert app.openapi()["components"]["schemas"][body_model_name] == { 1()*
127 "properties": {
128 "p_alias": {
129 "anyOf": [{"type": "string"}, {"type": "null"}],
130 "title": "P Alias",
131 },
132 },
133 "title": body_model_name,
134 "type": "object",
135 }
138def test_optional_alias_missing(): 1abcd
139 client = TestClient(app) 1RST
140 response = client.post("/optional-alias") 1RST
141 assert response.status_code == 200 1RST
142 assert response.json() == {"p": None} 1RST
145def test_model_optional_alias_missing(): 1abcd
146 client = TestClient(app) 1345
147 response = client.post("/model-optional-alias") 1345
148 assert response.status_code == 422, response.text 1345
149 assert response.json() == { 1345
150 "detail": [
151 {
152 "input": None,
153 "loc": ["body"],
154 "msg": "Field required",
155 "type": "missing",
156 },
157 ],
158 }
161@pytest.mark.parametrize( 1abcd
162 "path",
163 ["/optional-alias", "/model-optional-alias"],
164)
165def test_model_optional_alias_missing_empty_dict(path: str): 1abcd
166 client = TestClient(app) 1knq
167 response = client.post(path, json={}) 1knq
168 assert response.status_code == 200, response.text 1knq
169 assert response.json() == {"p": None} 1knq
172@pytest.mark.parametrize( 1abcd
173 "path",
174 ["/optional-alias", "/model-optional-alias"],
175)
176def test_optional_alias_by_name(path: str): 1abcd
177 client = TestClient(app) 1mps
178 response = client.post(path, json={"p": "hello"}) 1mps
179 assert response.status_code == 200 1mps
180 assert response.json() == {"p": None} 1mps
183@pytest.mark.parametrize( 1abcd
184 "path",
185 ["/optional-alias", "/model-optional-alias"],
186)
187def test_optional_alias_by_alias(path: str): 1abcd
188 client = TestClient(app) 1lor
189 response = client.post(path, json={"p_alias": "hello"}) 1lor
190 assert response.status_code == 200 1lor
191 assert response.json() == {"p": "hello"} 1lor
194# =====================================================================================
195# Validation alias
198@app.post("/optional-validation-alias", operation_id="optional_validation_alias") 1abcd
199def read_optional_validation_alias( 1abcd
200 p: Annotated[
201 Optional[str], Body(embed=True, validation_alias="p_val_alias")
202 ] = None,
203):
204 return {"p": p} 1tuvUwxyVzABW
207class BodyModelOptionalValidationAlias(BaseModel): 1abcd
208 p: Optional[str] = Field(None, validation_alias="p_val_alias") 1abcd
211@app.post( 1abcd
212 "/model-optional-validation-alias", operation_id="model_optional_validation_alias"
213)
214def read_model_optional_validation_alias( 1abcd
215 p: BodyModelOptionalValidationAlias,
216):
217 return {"p": p.p} 1tuvwxyzAB
220@pytest.mark.parametrize( 1abcd
221 "path",
222 ["/optional-validation-alias", "/model-optional-validation-alias"],
223)
224def test_optional_validation_alias_schema(path: str): 1abcd
225 openapi = app.openapi() 1+,-
226 body_model_name = get_body_model_name(openapi, path) 1+,-
228 assert app.openapi()["components"]["schemas"][body_model_name] == { 1+,-
229 "properties": {
230 "p_val_alias": {
231 "anyOf": [{"type": "string"}, {"type": "null"}],
232 "title": "P Val Alias",
233 },
234 },
235 "title": body_model_name,
236 "type": "object",
237 }
240def test_optional_validation_alias_missing(): 1abcd
241 client = TestClient(app) 1UVW
242 response = client.post("/optional-validation-alias") 1UVW
243 assert response.status_code == 200 1UVW
244 assert response.json() == {"p": None} 1UVW
247def test_model_optional_validation_alias_missing(): 1abcd
248 client = TestClient(app) 1678
249 response = client.post("/model-optional-validation-alias") 1678
250 assert response.status_code == 422, response.text 1678
251 assert response.json() == { 1678
252 "detail": [
253 {
254 "input": None,
255 "loc": ["body"],
256 "msg": "Field required",
257 "type": "missing",
258 },
259 ],
260 }
263@pytest.mark.parametrize( 1abcd
264 "path",
265 ["/optional-validation-alias", "/model-optional-validation-alias"],
266)
267def test_model_optional_validation_alias_missing_empty_dict(path: str): 1abcd
268 client = TestClient(app) 1twz
269 response = client.post(path, json={}) 1twz
270 assert response.status_code == 200, response.text 1twz
271 assert response.json() == {"p": None} 1twz
274@pytest.mark.parametrize( 1abcd
275 "path",
276 [
277 "/optional-validation-alias",
278 "/model-optional-validation-alias",
279 ],
280)
281def test_optional_validation_alias_by_name(path: str): 1abcd
282 client = TestClient(app) 1uxA
283 response = client.post(path, json={"p": "hello"}) 1uxA
284 assert response.status_code == 200 1uxA
285 assert response.json() == {"p": None} 1uxA
288@pytest.mark.parametrize( 1abcd
289 "path",
290 [
291 "/optional-validation-alias",
292 "/model-optional-validation-alias",
293 ],
294)
295def test_optional_validation_alias_by_validation_alias(path: str): 1abcd
296 client = TestClient(app) 1vyB
297 response = client.post(path, json={"p_val_alias": "hello"}) 1vyB
298 assert response.status_code == 200 1vyB
299 assert response.json() == {"p": "hello"} 1vyB
302# =====================================================================================
303# Alias and validation alias
306@app.post( 1abcd
307 "/optional-alias-and-validation-alias",
308 operation_id="optional_alias_and_validation_alias",
309)
310def read_optional_alias_and_validation_alias( 1abcd
311 p: Annotated[
312 Optional[str], Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
313 ] = None,
314):
315 return {"p": p} 1CDEFXGHIJYKLMNZ
318class BodyModelOptionalAliasAndValidationAlias(BaseModel): 1abcd
319 p: Optional[str] = Field(None, alias="p_alias", validation_alias="p_val_alias") 1abcd
322@app.post( 1abcd
323 "/model-optional-alias-and-validation-alias",
324 operation_id="model_optional_alias_and_validation_alias",
325)
326def read_model_optional_alias_and_validation_alias( 1abcd
327 p: BodyModelOptionalAliasAndValidationAlias,
328):
329 return {"p": p.p} 1CDEFGHIJKLMN
332@pytest.mark.parametrize( 1abcd
333 "path",
334 [
335 "/optional-alias-and-validation-alias",
336 "/model-optional-alias-and-validation-alias",
337 ],
338)
339def test_optional_alias_and_validation_alias_schema(path: str): 1abcd
340 openapi = app.openapi() 1./:
341 body_model_name = get_body_model_name(openapi, path) 1./:
343 assert app.openapi()["components"]["schemas"][body_model_name] == { 1./:
344 "properties": {
345 "p_val_alias": {
346 "anyOf": [{"type": "string"}, {"type": "null"}],
347 "title": "P Val Alias",
348 },
349 },
350 "title": body_model_name,
351 "type": "object",
352 }
355def test_optional_alias_and_validation_alias_missing(): 1abcd
356 client = TestClient(app) 1XYZ
357 response = client.post("/optional-alias-and-validation-alias") 1XYZ
358 assert response.status_code == 200 1XYZ
359 assert response.json() == {"p": None} 1XYZ
362def test_model_optional_alias_and_validation_alias_missing(): 1abcd
363 client = TestClient(app) 19!#
364 response = client.post("/model-optional-alias-and-validation-alias") 19!#
365 assert response.status_code == 422, response.text 19!#
366 assert response.json() == { 19!#
367 "detail": [
368 {
369 "input": None,
370 "loc": ["body"],
371 "msg": "Field required",
372 "type": "missing",
373 },
374 ],
375 }
378@pytest.mark.parametrize( 1abcd
379 "path",
380 [
381 "/optional-alias-and-validation-alias",
382 "/model-optional-alias-and-validation-alias",
383 ],
384)
385def test_model_optional_alias_and_validation_alias_missing_empty_dict(path: str): 1abcd
386 client = TestClient(app) 1CGK
387 response = client.post(path, json={}) 1CGK
388 assert response.status_code == 200, response.text 1CGK
389 assert response.json() == {"p": None} 1CGK
392@pytest.mark.parametrize( 1abcd
393 "path",
394 [
395 "/optional-alias-and-validation-alias",
396 "/model-optional-alias-and-validation-alias",
397 ],
398)
399def test_optional_alias_and_validation_alias_by_name(path: str): 1abcd
400 client = TestClient(app) 1EIM
401 response = client.post(path, json={"p": "hello"}) 1EIM
402 assert response.status_code == 200 1EIM
403 assert response.json() == {"p": None} 1EIM
406@pytest.mark.parametrize( 1abcd
407 "path",
408 [
409 "/optional-alias-and-validation-alias",
410 "/model-optional-alias-and-validation-alias",
411 ],
412)
413def test_optional_alias_and_validation_alias_by_alias(path: str): 1abcd
414 client = TestClient(app) 1DHL
415 response = client.post(path, json={"p_alias": "hello"}) 1DHL
416 assert response.status_code == 200 1DHL
417 assert response.json() == {"p": None} 1DHL
420@pytest.mark.parametrize( 1abcd
421 "path",
422 [
423 "/optional-alias-and-validation-alias",
424 "/model-optional-alias-and-validation-alias",
425 ],
426)
427def test_optional_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
428 client = TestClient(app) 1FJN
429 response = client.post(path, json={"p_val_alias": "hello"}) 1FJN
430 assert response.status_code == 200 1FJN
431 assert response.json() == {"p": "hello"} 1FJN