Coverage for tests / test_tutorial / test_body_nested_models / test_tutorial001_tutorial002_tutorial003.py: 100%
47 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
1import importlib 1abdc
3import pytest 1abdc
4from dirty_equals import IsList 1abdc
5from fastapi.testclient import TestClient 1abdc
6from inline_snapshot import Is, snapshot 1abdc
8from ...utils import needs_py310 1abdc
10UNTYPED_LIST_SCHEMA = {"type": "array", "items": {}} 1abdc
12LIST_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}} 1abdc
14SET_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}, "uniqueItems": True} 1abdc
17@pytest.fixture( 1abdc
18 name="mod_name",
19 params=[
20 pytest.param("tutorial001_py310", marks=needs_py310),
21 pytest.param("tutorial002_py310", marks=needs_py310),
22 pytest.param("tutorial003_py310", marks=needs_py310),
23 ],
24)
25def get_mod_name(request: pytest.FixtureRequest): 1abdc
26 return request.param 1abc
29@pytest.fixture(name="client") 1abdc
30def get_client(mod_name: str): 1abdc
31 mod = importlib.import_module(f"docs_src.body_nested_models.{mod_name}") 1abc
33 client = TestClient(mod.app) 1abc
34 return client 1abc
37def test_put_all(client: TestClient, mod_name: str): 1abdc
38 if mod_name.startswith("tutorial003"): 1hij
39 tags_expected = IsList("foo", "bar", check_order=False) 1hij
40 else:
41 tags_expected = ["foo", "bar", "foo"] 1hij
43 response = client.put( 1hij
44 "/items/123",
45 json={
46 "name": "Foo",
47 "description": "A very nice Item",
48 "price": 35.4,
49 "tax": 3.2,
50 "tags": ["foo", "bar", "foo"],
51 },
52 )
53 assert response.status_code == 200, response.text 1hij
54 assert response.json() == { 1hij
55 "item_id": 123,
56 "item": {
57 "name": "Foo",
58 "description": "A very nice Item",
59 "price": 35.4,
60 "tax": 3.2,
61 "tags": tags_expected,
62 },
63 }
66def test_put_only_required(client: TestClient): 1abdc
67 response = client.put( 1klm
68 "/items/5",
69 json={"name": "Foo", "price": 35.4},
70 )
71 assert response.status_code == 200, response.text 1klm
72 assert response.json() == { 1klm
73 "item_id": 5,
74 "item": {
75 "name": "Foo",
76 "description": None,
77 "price": 35.4,
78 "tax": None,
79 "tags": [],
80 },
81 }
84def test_put_empty_body(client: TestClient): 1abdc
85 response = client.put( 1nop
86 "/items/5",
87 json={},
88 )
89 assert response.status_code == 422, response.text 1nop
90 assert response.json() == { 1nop
91 "detail": [
92 {
93 "loc": ["body", "name"],
94 "input": {},
95 "msg": "Field required",
96 "type": "missing",
97 },
98 {
99 "loc": ["body", "price"],
100 "input": {},
101 "msg": "Field required",
102 "type": "missing",
103 },
104 ]
105 }
108def test_put_missing_required(client: TestClient): 1abdc
109 response = client.put( 1qrs
110 "/items/5",
111 json={"description": "A very nice Item"},
112 )
113 assert response.status_code == 422, response.text 1qrs
114 assert response.json() == { 1qrs
115 "detail": [
116 {
117 "loc": ["body", "name"],
118 "input": {"description": "A very nice Item"},
119 "msg": "Field required",
120 "type": "missing",
121 },
122 {
123 "loc": ["body", "price"],
124 "input": {"description": "A very nice Item"},
125 "msg": "Field required",
126 "type": "missing",
127 },
128 ]
129 }
132def test_openapi_schema(client: TestClient, mod_name: str): 1abdc
133 tags_schema = {"default": [], "title": "Tags"} 1efg
134 if mod_name.startswith("tutorial001"): 1efg
135 tags_schema.update(UNTYPED_LIST_SCHEMA) 1efg
136 elif mod_name.startswith("tutorial002"): 1efg
137 tags_schema.update(LIST_OF_STR_SCHEMA) 1efg
138 elif mod_name.startswith("tutorial003"): 1efg
139 tags_schema.update(SET_OF_STR_SCHEMA) 1efg
141 response = client.get("/openapi.json") 1efg
142 assert response.status_code == 200, response.text 1efg
143 assert response.json() == snapshot( 1efg
144 {
145 "openapi": "3.1.0",
146 "info": {"title": "FastAPI", "version": "0.1.0"},
147 "paths": {
148 "/items/{item_id}": {
149 "put": {
150 "parameters": [
151 {
152 "in": "path",
153 "name": "item_id",
154 "required": True,
155 "schema": {
156 "title": "Item Id",
157 "type": "integer",
158 },
159 },
160 ],
161 "responses": {
162 "200": {
163 "description": "Successful Response",
164 "content": {"application/json": {"schema": {}}},
165 },
166 "422": {
167 "description": "Validation Error",
168 "content": {
169 "application/json": {
170 "schema": {
171 "$ref": "#/components/schemas/HTTPValidationError"
172 }
173 }
174 },
175 },
176 },
177 "summary": "Update Item",
178 "operationId": "update_item_items__item_id__put",
179 "requestBody": {
180 "content": {
181 "application/json": {
182 "schema": {
183 "$ref": "#/components/schemas/Item",
184 }
185 }
186 },
187 "required": True,
188 },
189 }
190 }
191 },
192 "components": {
193 "schemas": {
194 "Item": {
195 "properties": {
196 "name": {
197 "title": "Name",
198 "type": "string",
199 },
200 "description": {
201 "title": "Description",
202 "anyOf": [{"type": "string"}, {"type": "null"}],
203 },
204 "price": {
205 "title": "Price",
206 "type": "number",
207 },
208 "tax": {
209 "title": "Tax",
210 "anyOf": [{"type": "number"}, {"type": "null"}],
211 },
212 "tags": Is(tags_schema),
213 },
214 "required": [
215 "name",
216 "price",
217 ],
218 "title": "Item",
219 "type": "object",
220 },
221 "ValidationError": {
222 "title": "ValidationError",
223 "required": ["loc", "msg", "type"],
224 "type": "object",
225 "properties": {
226 "loc": {
227 "title": "Location",
228 "type": "array",
229 "items": {
230 "anyOf": [{"type": "string"}, {"type": "integer"}]
231 },
232 },
233 "msg": {"title": "Message", "type": "string"},
234 "type": {"title": "Error Type", "type": "string"},
235 "input": {"title": "Input"},
236 "ctx": {"title": "Context", "type": "object"},
237 },
238 },
239 "HTTPValidationError": {
240 "title": "HTTPValidationError",
241 "type": "object",
242 "properties": {
243 "detail": {
244 "title": "Detail",
245 "type": "array",
246 "items": {
247 "$ref": "#/components/schemas/ValidationError"
248 },
249 }
250 },
251 },
252 }
253 },
254 }
255 )