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-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +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} 1efghijklmn
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} 1opqrstu
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() 2RbSbTbUbVbWbXbYb
36 body_model_name = get_body_model_name(openapi, path) 2RbSbTbUbVbWbXbYb
38 assert app.openapi()["components"]["schemas"][body_model_name] == { 2RbSbTbUbVbWbXbYb
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) 1ghkn
52 response = client.post("/optional-str") 1ghkn
53 assert response.status_code == 200, response.text 1ghkn
54 assert response.json() == {"p": None} 1ghkn
57def test_model_optional_str_missing(): 1abcd
58 client = TestClient(app) 2kblbmbnb
59 response = client.post("/model-optional-str") 2kblbmbnb
60 assert response.status_code == 422, response.text 2kblbmbnb
61 assert response.json() == { 2kblbmbnb
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) 2p f q obs j u m
79 response = client.post(path, json={}) 2p f q obs j u m
80 assert response.status_code == 200, response.text 2p f q obs j u m
81 assert response.json() == {"p": None} 2p f q obs j u m
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) 2o e pbqbr i t l
90 response = client.post(path, json={"p": "hello"}) 2o e pbqbr i t l
91 assert response.status_code == 200 2o e pbqbr i t l
92 assert response.json() == {"p": "hello"} 2o e pbqbr i t l
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} 1vwxyzABCDEFGHI
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} 1JKLMNOPQRS
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() 2Zb0b1b2b3b4b5b6b
124 body_model_name = get_body_model_name(openapi, path) 2Zb0b1b2b3b4b5b6b
126 assert app.openapi()["components"]["schemas"][body_model_name] == { 2Zb0b1b2b3b4b5b6b
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) 1yAEI
140 response = client.post("/optional-alias") 1yAEI
141 assert response.status_code == 200 1yAEI
142 assert response.json() == {"p": None} 1yAEI
145def test_model_optional_alias_missing(): 1abcd
146 client = TestClient(app) 2rbsbtbub
147 response = client.post("/model-optional-alias") 2rbsbtbub
148 assert response.status_code == 422, response.text 2rbsbtbub
149 assert response.json() == { 2rbsbtbub
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) 1JvMzNBQF
167 response = client.post(path, json={}) 1JvMzNBQF
168 assert response.status_code == 200, response.text 1JvMzNBQF
169 assert response.json() == {"p": None} 1JvMzNBQF
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) 2L x vbwbP D S H
178 response = client.post(path, json={"p": "hello"}) 2L x vbwbP D S H
179 assert response.status_code == 200 2L x vbwbP D S H
180 assert response.json() == {"p": None} 2L x vbwbP D S H
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) 2K w xbybO C R G
189 response = client.post(path, json={"p_alias": "hello"}) 2K w xbybO C R G
190 assert response.status_code == 200 2K w xbybO C R G
191 assert response.json() == {"p": "hello"} 2K w xbybO C R G
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} 1TUVWXYZ0123456
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} 1789!#$%'()*
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() 27b8b9b!b#b$b%b'b
224 body_model_name = get_body_model_name(openapi, path) 27b8b9b!b#b$b%b'b
226 assert app.openapi()["components"]["schemas"][body_model_name] == { 27b8b9b!b#b$b%b'b
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) 1WY26
240 response = client.post("/optional-validation-alias") 1WY26
241 assert response.status_code == 200 1WY26
242 assert response.json() == {"p": None} 1WY26
245def test_model_optional_validation_alias_missing(): 1abcd
246 client = TestClient(app) 2zbAbBbCb
247 response = client.post("/model-optional-validation-alias") 2zbAbBbCb
248 assert response.status_code == 422, response.text 2zbAbBbCb
249 assert response.json() == { 2zbAbBbCb
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) 17T!X$Z(3
267 response = client.post(path, json={}) 17T!X$Z(3
268 assert response.status_code == 200, response.text 17T!X$Z(3
269 assert response.json() == {"p": None} 17T!X$Z(3
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) 28 U # Db% 0 ) 4
281 response = client.post(path, json={"p": "hello"}) 28 U # Db% 0 ) 4
282 assert response.status_code == 200 28 U # Db% 0 ) 4
283 assert response.json() == {"p": None} 28 U # Db% 0 ) 4
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) 29 V EbFb' 1 * 5
295 response = client.post(path, json={"p_val_alias": "hello"}) 29 V EbFb' 1 * 5
296 assert response.status_code == 200 29 V EbFb' 1 * 5
297 assert response.json() == {"p": "hello"} 29 V EbFb' 1 * 5
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} 1+,-./:;=?@[]^_`{
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} 2| } ~ abbbcbdbebfbgbhbibjb
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() 2(b)b*b+b,b-b.b/b
339 body_model_name = get_body_model_name(openapi, path) 2(b)b*b+b,b-b.b/b
341 assert app.openapi()["components"]["schemas"][body_model_name] == { 2(b)b*b+b,b-b.b/b
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) 1/:[{
355 response = client.post("/optional-alias-and-validation-alias") 1/:[{
356 assert response.status_code == 200 1/:[{
357 assert response.json() == {"p": None} 1/:[{
360def test_model_optional_alias_and_validation_alias_missing(): 1abcd
361 client = TestClient(app) 2GbHbIbJb
362 response = client.post("/model-optional-alias-and-validation-alias") 2GbHbIbJb
363 assert response.status_code == 422, response.text 2GbHbIbJb
364 assert response.json() == { 2GbHbIbJb
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) 2| + bbKbcb; gb]
385 response = client.post(path, json={}) 2| + bbKbcb; gb]
386 assert response.status_code == 200, response.text 2| + bbKbcb; gb]
387 assert response.json() == {"p": None} 2| + bbKbcb; gb]
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) 2~ - LbMbeb? ib_
399 response = client.post(path, json={"p": "hello"}) 2~ - LbMbeb? ib_
400 assert response.status_code == 200 2~ - LbMbeb? ib_
401 assert response.json() == {"p": None} 2~ - LbMbeb? ib_
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) 2} , NbObdb= hb^
413 response = client.post(path, json={"p_alias": "hello"}) 2} , NbObdb= hb^
414 assert response.status_code == 200 2} , NbObdb= hb^
415 assert response.json() == {"p": None} 2} , NbObdb= hb^
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) 2ab. PbQbfb@ jb`
427 response = client.post(path, json={"p_val_alias": "hello"}) 2ab. PbQbfb@ jb`
428 assert response.status_code == 200 2ab. PbQbfb@ jb`
429 assert response.json() == {"p": "hello"} 2ab. PbQbfb@ jb`