Coverage for tests/test_tutorial/test_body/test_tutorial001_py310.py: 100%
95 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1from unittest.mock import patch 1deabc
3import pytest 1deabc
4from dirty_equals import IsDict 1deabc
5from fastapi.testclient import TestClient 1deabc
7from ...utils import needs_py310 1deabc
10@pytest.fixture 1deabc
11def client(): 1deabc
12 from docs_src.body.tutorial001_py310 import app 1abc
14 client = TestClient(app) 1abc
15 return client 1abc
18@needs_py310 1deabc
19def test_body_float(client: TestClient): 1deabc
20 response = client.post("/items/", json={"name": "Foo", "price": 50.5}) 1abc
21 assert response.status_code == 200 1abc
22 assert response.json() == { 1abc
23 "name": "Foo",
24 "price": 50.5,
25 "description": None,
26 "tax": None,
27 }
30@needs_py310 1deabc
31def test_post_with_str_float(client: TestClient): 1deabc
32 response = client.post("/items/", json={"name": "Foo", "price": "50.5"}) 1abc
33 assert response.status_code == 200 1abc
34 assert response.json() == { 1abc
35 "name": "Foo",
36 "price": 50.5,
37 "description": None,
38 "tax": None,
39 }
42@needs_py310 1deabc
43def test_post_with_str_float_description(client: TestClient): 1deabc
44 response = client.post( 1abc
45 "/items/", json={"name": "Foo", "price": "50.5", "description": "Some Foo"}
46 )
47 assert response.status_code == 200 1abc
48 assert response.json() == { 1abc
49 "name": "Foo",
50 "price": 50.5,
51 "description": "Some Foo",
52 "tax": None,
53 }
56@needs_py310 1deabc
57def test_post_with_str_float_description_tax(client: TestClient): 1deabc
58 response = client.post( 1abc
59 "/items/",
60 json={"name": "Foo", "price": "50.5", "description": "Some Foo", "tax": 0.3},
61 )
62 assert response.status_code == 200 1abc
63 assert response.json() == { 1abc
64 "name": "Foo",
65 "price": 50.5,
66 "description": "Some Foo",
67 "tax": 0.3,
68 }
71@needs_py310 1deabc
72def test_post_with_only_name(client: TestClient): 1deabc
73 response = client.post("/items/", json={"name": "Foo"}) 1abc
74 assert response.status_code == 422 1abc
75 assert response.json() == IsDict( 1abc
76 {
77 "detail": [
78 {
79 "type": "missing",
80 "loc": ["body", "price"],
81 "msg": "Field required",
82 "input": {"name": "Foo"},
83 }
84 ]
85 }
86 ) | IsDict(
87 # TODO: remove when deprecating Pydantic v1
88 {
89 "detail": [
90 {
91 "loc": ["body", "price"],
92 "msg": "field required",
93 "type": "value_error.missing",
94 }
95 ]
96 }
97 )
100@needs_py310 1deabc
101def test_post_with_only_name_price(client: TestClient): 1deabc
102 response = client.post("/items/", json={"name": "Foo", "price": "twenty"}) 1abc
103 assert response.status_code == 422 1abc
104 assert response.json() == IsDict( 1abc
105 {
106 "detail": [
107 {
108 "type": "float_parsing",
109 "loc": ["body", "price"],
110 "msg": "Input should be a valid number, unable to parse string as a number",
111 "input": "twenty",
112 }
113 ]
114 }
115 ) | IsDict(
116 # TODO: remove when deprecating Pydantic v1
117 {
118 "detail": [
119 {
120 "loc": ["body", "price"],
121 "msg": "value is not a valid float",
122 "type": "type_error.float",
123 }
124 ]
125 }
126 )
129@needs_py310 1deabc
130def test_post_with_no_data(client: TestClient): 1deabc
131 response = client.post("/items/", json={}) 1abc
132 assert response.status_code == 422 1abc
133 assert response.json() == IsDict( 1abc
134 {
135 "detail": [
136 {
137 "type": "missing",
138 "loc": ["body", "name"],
139 "msg": "Field required",
140 "input": {},
141 },
142 {
143 "type": "missing",
144 "loc": ["body", "price"],
145 "msg": "Field required",
146 "input": {},
147 },
148 ]
149 }
150 ) | IsDict(
151 # TODO: remove when deprecating Pydantic v1
152 {
153 "detail": [
154 {
155 "loc": ["body", "name"],
156 "msg": "field required",
157 "type": "value_error.missing",
158 },
159 {
160 "loc": ["body", "price"],
161 "msg": "field required",
162 "type": "value_error.missing",
163 },
164 ]
165 }
166 )
169@needs_py310 1deabc
170def test_post_with_none(client: TestClient): 1deabc
171 response = client.post("/items/", json=None) 1abc
172 assert response.status_code == 422 1abc
173 assert response.json() == IsDict( 1abc
174 {
175 "detail": [
176 {
177 "type": "missing",
178 "loc": ["body"],
179 "msg": "Field required",
180 "input": None,
181 }
182 ]
183 }
184 ) | IsDict(
185 # TODO: remove when deprecating Pydantic v1
186 {
187 "detail": [
188 {
189 "loc": ["body"],
190 "msg": "field required",
191 "type": "value_error.missing",
192 }
193 ]
194 }
195 )
198@needs_py310 1deabc
199def test_post_broken_body(client: TestClient): 1deabc
200 response = client.post( 1abc
201 "/items/",
202 headers={"content-type": "application/json"},
203 content="{some broken json}",
204 )
205 assert response.status_code == 422, response.text 1abc
206 assert response.json() == IsDict( 1abc
207 {
208 "detail": [
209 {
210 "type": "json_invalid",
211 "loc": ["body", 1],
212 "msg": "JSON decode error",
213 "input": {},
214 "ctx": {
215 "error": "Expecting property name enclosed in double quotes"
216 },
217 }
218 ]
219 }
220 ) | IsDict(
221 # TODO: remove when deprecating Pydantic v1
222 {
223 "detail": [
224 {
225 "loc": ["body", 1],
226 "msg": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
227 "type": "value_error.jsondecode",
228 "ctx": {
229 "msg": "Expecting property name enclosed in double quotes",
230 "doc": "{some broken json}",
231 "pos": 1,
232 "lineno": 1,
233 "colno": 2,
234 },
235 }
236 ]
237 }
238 )
241@needs_py310 1deabc
242def test_post_form_for_json(client: TestClient): 1deabc
243 response = client.post("/items/", data={"name": "Foo", "price": 50.5}) 1abc
244 assert response.status_code == 422, response.text 1abc
245 assert response.json() == IsDict( 1abc
246 {
247 "detail": [
248 {
249 "type": "model_attributes_type",
250 "loc": ["body"],
251 "msg": "Input should be a valid dictionary or object to extract fields from",
252 "input": "name=Foo&price=50.5",
253 }
254 ]
255 }
256 ) | IsDict(
257 # TODO: remove when deprecating Pydantic v1
258 {
259 "detail": [
260 {
261 "loc": ["body"],
262 "msg": "value is not a valid dict",
263 "type": "type_error.dict",
264 }
265 ]
266 }
267 )
270@needs_py310 1deabc
271def test_explicit_content_type(client: TestClient): 1deabc
272 response = client.post( 1abc
273 "/items/",
274 content='{"name": "Foo", "price": 50.5}',
275 headers={"Content-Type": "application/json"},
276 )
277 assert response.status_code == 200, response.text 1abc
280@needs_py310 1deabc
281def test_geo_json(client: TestClient): 1deabc
282 response = client.post( 1abc
283 "/items/",
284 content='{"name": "Foo", "price": 50.5}',
285 headers={"Content-Type": "application/geo+json"},
286 )
287 assert response.status_code == 200, response.text 1abc
290@needs_py310 1deabc
291def test_no_content_type_is_json(client: TestClient): 1deabc
292 response = client.post( 1abc
293 "/items/",
294 content='{"name": "Foo", "price": 50.5}',
295 )
296 assert response.status_code == 200, response.text 1abc
297 assert response.json() == { 1abc
298 "name": "Foo",
299 "description": None,
300 "price": 50.5,
301 "tax": None,
302 }
305@needs_py310 1deabc
306def test_wrong_headers(client: TestClient): 1deabc
307 data = '{"name": "Foo", "price": 50.5}' 1abc
308 response = client.post( 1abc
309 "/items/", content=data, headers={"Content-Type": "text/plain"}
310 )
311 assert response.status_code == 422, response.text 1abc
312 assert response.json() == IsDict( 1abc
313 {
314 "detail": [
315 {
316 "type": "model_attributes_type",
317 "loc": ["body"],
318 "msg": "Input should be a valid dictionary or object to extract fields from",
319 "input": '{"name": "Foo", "price": 50.5}',
320 }
321 ]
322 }
323 ) | IsDict(
324 # TODO: remove when deprecating Pydantic v1
325 {
326 "detail": [
327 {
328 "loc": ["body"],
329 "msg": "value is not a valid dict",
330 "type": "type_error.dict",
331 }
332 ]
333 }
334 )
336 response = client.post( 1abc
337 "/items/", content=data, headers={"Content-Type": "application/geo+json-seq"}
338 )
339 assert response.status_code == 422, response.text 1abc
340 assert response.json() == IsDict( 1abc
341 {
342 "detail": [
343 {
344 "type": "model_attributes_type",
345 "loc": ["body"],
346 "msg": "Input should be a valid dictionary or object to extract fields from",
347 "input": '{"name": "Foo", "price": 50.5}',
348 }
349 ]
350 }
351 ) | IsDict(
352 # TODO: remove when deprecating Pydantic v1
353 {
354 "detail": [
355 {
356 "loc": ["body"],
357 "msg": "value is not a valid dict",
358 "type": "type_error.dict",
359 }
360 ]
361 }
362 )
363 response = client.post( 1abc
364 "/items/", content=data, headers={"Content-Type": "application/not-really-json"}
365 )
366 assert response.status_code == 422, response.text 1abc
367 assert response.json() == IsDict( 1abc
368 {
369 "detail": [
370 {
371 "type": "model_attributes_type",
372 "loc": ["body"],
373 "msg": "Input should be a valid dictionary or object to extract fields from",
374 "input": '{"name": "Foo", "price": 50.5}',
375 }
376 ]
377 }
378 ) | IsDict(
379 # TODO: remove when deprecating Pydantic v1
380 {
381 "detail": [
382 {
383 "loc": ["body"],
384 "msg": "value is not a valid dict",
385 "type": "type_error.dict",
386 }
387 ]
388 }
389 )
392@needs_py310 1deabc
393def test_other_exceptions(client: TestClient): 1deabc
394 with patch("json.loads", side_effect=Exception): 1abc
395 response = client.post("/items/", json={"test": "test2"}) 1abc
396 assert response.status_code == 400, response.text 1abc
399@needs_py310 1deabc
400def test_openapi_schema(client: TestClient): 1deabc
401 response = client.get("/openapi.json") 1abc
402 assert response.status_code == 200, response.text 1abc
403 assert response.json() == { 1abc
404 "openapi": "3.1.0",
405 "info": {"title": "FastAPI", "version": "0.1.0"},
406 "paths": {
407 "/items/": {
408 "post": {
409 "responses": {
410 "200": {
411 "description": "Successful Response",
412 "content": {"application/json": {"schema": {}}},
413 },
414 "422": {
415 "description": "Validation Error",
416 "content": {
417 "application/json": {
418 "schema": {
419 "$ref": "#/components/schemas/HTTPValidationError"
420 }
421 }
422 },
423 },
424 },
425 "summary": "Create Item",
426 "operationId": "create_item_items__post",
427 "requestBody": {
428 "content": {
429 "application/json": {
430 "schema": {"$ref": "#/components/schemas/Item"}
431 }
432 },
433 "required": True,
434 },
435 }
436 }
437 },
438 "components": {
439 "schemas": {
440 "Item": {
441 "title": "Item",
442 "required": ["name", "price"],
443 "type": "object",
444 "properties": {
445 "name": {"title": "Name", "type": "string"},
446 "price": {"title": "Price", "type": "number"},
447 "description": IsDict(
448 {
449 "title": "Description",
450 "anyOf": [{"type": "string"}, {"type": "null"}],
451 }
452 )
453 | IsDict(
454 # TODO: remove when deprecating Pydantic v1
455 {"title": "Description", "type": "string"}
456 ),
457 "tax": IsDict(
458 {
459 "title": "Tax",
460 "anyOf": [{"type": "number"}, {"type": "null"}],
461 }
462 )
463 | IsDict(
464 # TODO: remove when deprecating Pydantic v1
465 {"title": "Tax", "type": "number"}
466 ),
467 },
468 },
469 "ValidationError": {
470 "title": "ValidationError",
471 "required": ["loc", "msg", "type"],
472 "type": "object",
473 "properties": {
474 "loc": {
475 "title": "Location",
476 "type": "array",
477 "items": {
478 "anyOf": [{"type": "string"}, {"type": "integer"}]
479 },
480 },
481 "msg": {"title": "Message", "type": "string"},
482 "type": {"title": "Error Type", "type": "string"},
483 },
484 },
485 "HTTPValidationError": {
486 "title": "HTTPValidationError",
487 "type": "object",
488 "properties": {
489 "detail": {
490 "title": "Detail",
491 "type": "array",
492 "items": {"$ref": "#/components/schemas/ValidationError"},
493 }
494 },
495 },
496 }
497 },
498 }