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-21 17:29 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-21 17:29 +0000
1from typing import Annotated 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[str | None, Body(embed=True)] = None): 1abcd
18 return {"p": p} 1eOfgPhiQj
21class BodyModelOptionalStr(BaseModel): 1abcd
22 p: str | None = 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[str | None, Body(embed=True, alias="p_alias")] = None,
102):
103 return {"p": p} 1klmRnopSqrsT
106class BodyModelOptionalAlias(BaseModel): 1abcd
107 p: str | None = 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[str | None, Body(embed=True, validation_alias="p_val_alias")] = None,
201):
202 return {"p": p} 1tuvUwxyVzABW
205class BodyModelOptionalValidationAlias(BaseModel): 1abcd
206 p: str | None = Field(None, validation_alias="p_val_alias") 1abcd
209@app.post( 1abcd
210 "/model-optional-validation-alias", operation_id="model_optional_validation_alias"
211)
212def read_model_optional_validation_alias( 1abcd
213 p: BodyModelOptionalValidationAlias,
214):
215 return {"p": p.p} 1tuvwxyzAB
218@pytest.mark.parametrize( 1abcd
219 "path",
220 ["/optional-validation-alias", "/model-optional-validation-alias"],
221)
222def test_optional_validation_alias_schema(path: str): 1abcd
223 openapi = app.openapi() 1+,-
224 body_model_name = get_body_model_name(openapi, path) 1+,-
226 assert app.openapi()["components"]["schemas"][body_model_name] == { 1+,-
227 "properties": {
228 "p_val_alias": {
229 "anyOf": [{"type": "string"}, {"type": "null"}],
230 "title": "P Val Alias",
231 },
232 },
233 "title": body_model_name,
234 "type": "object",
235 }
238def test_optional_validation_alias_missing(): 1abcd
239 client = TestClient(app) 1UVW
240 response = client.post("/optional-validation-alias") 1UVW
241 assert response.status_code == 200 1UVW
242 assert response.json() == {"p": None} 1UVW
245def test_model_optional_validation_alias_missing(): 1abcd
246 client = TestClient(app) 1678
247 response = client.post("/model-optional-validation-alias") 1678
248 assert response.status_code == 422, response.text 1678
249 assert response.json() == { 1678
250 "detail": [
251 {
252 "input": None,
253 "loc": ["body"],
254 "msg": "Field required",
255 "type": "missing",
256 },
257 ],
258 }
261@pytest.mark.parametrize( 1abcd
262 "path",
263 ["/optional-validation-alias", "/model-optional-validation-alias"],
264)
265def test_model_optional_validation_alias_missing_empty_dict(path: str): 1abcd
266 client = TestClient(app) 1twz
267 response = client.post(path, json={}) 1twz
268 assert response.status_code == 200, response.text 1twz
269 assert response.json() == {"p": None} 1twz
272@pytest.mark.parametrize( 1abcd
273 "path",
274 [
275 "/optional-validation-alias",
276 "/model-optional-validation-alias",
277 ],
278)
279def test_optional_validation_alias_by_name(path: str): 1abcd
280 client = TestClient(app) 1uxA
281 response = client.post(path, json={"p": "hello"}) 1uxA
282 assert response.status_code == 200 1uxA
283 assert response.json() == {"p": None} 1uxA
286@pytest.mark.parametrize( 1abcd
287 "path",
288 [
289 "/optional-validation-alias",
290 "/model-optional-validation-alias",
291 ],
292)
293def test_optional_validation_alias_by_validation_alias(path: str): 1abcd
294 client = TestClient(app) 1vyB
295 response = client.post(path, json={"p_val_alias": "hello"}) 1vyB
296 assert response.status_code == 200 1vyB
297 assert response.json() == {"p": "hello"} 1vyB
300# =====================================================================================
301# Alias and validation alias
304@app.post( 1abcd
305 "/optional-alias-and-validation-alias",
306 operation_id="optional_alias_and_validation_alias",
307)
308def read_optional_alias_and_validation_alias( 1abcd
309 p: Annotated[
310 str | None, Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
311 ] = None,
312):
313 return {"p": p} 1CDEFXGHIJYKLMNZ
316class BodyModelOptionalAliasAndValidationAlias(BaseModel): 1abcd
317 p: str | None = Field(None, alias="p_alias", validation_alias="p_val_alias") 1abcd
320@app.post( 1abcd
321 "/model-optional-alias-and-validation-alias",
322 operation_id="model_optional_alias_and_validation_alias",
323)
324def read_model_optional_alias_and_validation_alias( 1abcd
325 p: BodyModelOptionalAliasAndValidationAlias,
326):
327 return {"p": p.p} 1CDEFGHIJKLMN
330@pytest.mark.parametrize( 1abcd
331 "path",
332 [
333 "/optional-alias-and-validation-alias",
334 "/model-optional-alias-and-validation-alias",
335 ],
336)
337def test_optional_alias_and_validation_alias_schema(path: str): 1abcd
338 openapi = app.openapi() 1./:
339 body_model_name = get_body_model_name(openapi, path) 1./:
341 assert app.openapi()["components"]["schemas"][body_model_name] == { 1./:
342 "properties": {
343 "p_val_alias": {
344 "anyOf": [{"type": "string"}, {"type": "null"}],
345 "title": "P Val Alias",
346 },
347 },
348 "title": body_model_name,
349 "type": "object",
350 }
353def test_optional_alias_and_validation_alias_missing(): 1abcd
354 client = TestClient(app) 1XYZ
355 response = client.post("/optional-alias-and-validation-alias") 1XYZ
356 assert response.status_code == 200 1XYZ
357 assert response.json() == {"p": None} 1XYZ
360def test_model_optional_alias_and_validation_alias_missing(): 1abcd
361 client = TestClient(app) 19!#
362 response = client.post("/model-optional-alias-and-validation-alias") 19!#
363 assert response.status_code == 422, response.text 19!#
364 assert response.json() == { 19!#
365 "detail": [
366 {
367 "input": None,
368 "loc": ["body"],
369 "msg": "Field required",
370 "type": "missing",
371 },
372 ],
373 }
376@pytest.mark.parametrize( 1abcd
377 "path",
378 [
379 "/optional-alias-and-validation-alias",
380 "/model-optional-alias-and-validation-alias",
381 ],
382)
383def test_model_optional_alias_and_validation_alias_missing_empty_dict(path: str): 1abcd
384 client = TestClient(app) 1CGK
385 response = client.post(path, json={}) 1CGK
386 assert response.status_code == 200, response.text 1CGK
387 assert response.json() == {"p": None} 1CGK
390@pytest.mark.parametrize( 1abcd
391 "path",
392 [
393 "/optional-alias-and-validation-alias",
394 "/model-optional-alias-and-validation-alias",
395 ],
396)
397def test_optional_alias_and_validation_alias_by_name(path: str): 1abcd
398 client = TestClient(app) 1EIM
399 response = client.post(path, json={"p": "hello"}) 1EIM
400 assert response.status_code == 200 1EIM
401 assert response.json() == {"p": None} 1EIM
404@pytest.mark.parametrize( 1abcd
405 "path",
406 [
407 "/optional-alias-and-validation-alias",
408 "/model-optional-alias-and-validation-alias",
409 ],
410)
411def test_optional_alias_and_validation_alias_by_alias(path: str): 1abcd
412 client = TestClient(app) 1DHL
413 response = client.post(path, json={"p_alias": "hello"}) 1DHL
414 assert response.status_code == 200 1DHL
415 assert response.json() == {"p": None} 1DHL
418@pytest.mark.parametrize( 1abcd
419 "path",
420 [
421 "/optional-alias-and-validation-alias",
422 "/model-optional-alias-and-validation-alias",
423 ],
424)
425def test_optional_alias_and_validation_alias_by_validation_alias(path: str): 1abcd
426 client = TestClient(app) 1FJN
427 response = client.post(path, json={"p_val_alias": "hello"}) 1FJN
428 assert response.status_code == 200 1FJN
429 assert response.json() == {"p": "hello"} 1FJN