Aber FastAPI unterstützt auf die gleiche Weise auch die Verwendung von <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>:
-{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *}
+{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Das ist dank **Pydantic** ebenfalls möglich, da es <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">`dataclasses` intern unterstützt</a>.
Sie können `dataclasses` auch im Parameter `response_model` verwenden:
-{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *}
+{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Die Datenklasse wird automatisch in eine Pydantic-Datenklasse konvertiert.
In diesem Fall können Sie einfach die Standard-`dataclasses` durch `pydantic.dataclasses` ersetzen, was einen direkten Ersatz darstellt:
-{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
+{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Wir importieren `field` weiterhin von Standard-`dataclasses`.
Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können:
-{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
+{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Weitere Informationen zu Strawberry finden Sie in der <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry-Dokumentation</a>.
But FastAPI also supports using <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> the same way:
-{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *}
+{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
This is still supported thanks to **Pydantic**, as it has <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">internal support for `dataclasses`</a>.
You can also use `dataclasses` in the `response_model` parameter:
-{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *}
+{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
The dataclass will be automatically converted to a Pydantic dataclass.
In that case, you can simply swap the standard `dataclasses` with `pydantic.dataclasses`, which is a drop-in replacement:
-{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
+{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. We still import `field` from standard `dataclasses`.
Here's a small preview of how you could integrate Strawberry with FastAPI:
-{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
+{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>.
Pero FastAPI también soporta el uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la misma manera:
-{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *}
+{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Esto sigue siendo soportado gracias a **Pydantic**, ya que tiene <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">soporte interno para `dataclasses`</a>.
También puedes usar `dataclasses` en el parámetro `response_model`:
-{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *}
+{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
El dataclass será automáticamente convertido a un dataclass de Pydantic.
En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `pydantic.dataclasses`, que es un reemplazo directo:
-{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
+{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Todavía importamos `field` de los `dataclasses` estándar.
Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI:
-{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
+{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Puedes aprender más sobre Strawberry en la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentación de Strawberry</a>.
Mas o FastAPI também suporta o uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> da mesma forma:
-{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *}
+{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Isso ainda é suportado graças ao **Pydantic**, pois ele tem <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">suporte interno para `dataclasses`</a>.
Você também pode usar `dataclasses` no parâmetro `response_model`:
-{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *}
+{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
A dataclass será automaticamente convertida para uma dataclass Pydantic.
Nesse caso, você pode simplesmente trocar as `dataclasses` padrão por `pydantic.dataclasses`, que é um substituto direto:
-{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
+{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Ainda importamos `field` das `dataclasses` padrão.
Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI:
-{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
+{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Você pode aprender mais sobre Strawberry na <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentação do Strawberry</a>.
Но FastAPI также поддерживает использование <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> тем же способом:
-{* ../../docs_src/dataclasses/tutorial001_py310.py hl[1,6:11,18:19] *}
+{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
Это по-прежнему поддерживается благодаря **Pydantic**, так как в нём есть <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">встроенная поддержка `dataclasses`</a>.
Вы также можете использовать `dataclasses` в параметре `response_model`:
-{* ../../docs_src/dataclasses/tutorial002_py310.py hl[1,6:12,18] *}
+{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
Этот dataclass будет автоматически преобразован в Pydantic dataclass.
В таком случае вы можете просто заменить стандартные `dataclasses` на `pydantic.dataclasses`, которая является полностью совместимой заменой (drop-in replacement):
-{* ../../docs_src/dataclasses/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
+{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
1. Мы по-прежнему импортируем `field` из стандартных `dataclasses`.
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
-{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
+{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
Подробнее о Strawberry можно узнать в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>.
但 FastAPI 还可以使用数据类(<a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>):
-{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
+{* ../../docs_src/dataclasses_/tutorial001.py hl[1,7:12,19:20] *}
这还是借助于 **Pydantic** 及其<a href="https://pydantic-docs.helpmanual.io/usage/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">内置的 `dataclasses`</a>。
在 `response_model` 参数中使用 `dataclasses`:
-{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
+{* ../../docs_src/dataclasses_/tutorial002.py hl[1,7:13,19] *}
本例把数据类自动转换为 Pydantic 数据类。
本例把标准的 `dataclasses` 直接替换为 `pydantic.dataclasses`:
```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
-{!../../docs_src/dataclasses/tutorial003.py!}
+{!../../docs_src/dataclasses_/tutorial003.py!}
```
1. 本例依然要从标准的 `dataclasses` 中导入 `field`;
omit = [
"docs_src/response_model/tutorial003_04_py39.py",
"docs_src/response_model/tutorial003_04_py310.py",
+ "docs_src/dependencies/tutorial008_an_py39.py", # difficult to mock
+ "docs_src/dependencies/tutorial013_an_py310.py", # temporary code example?
+ "docs_src/dependencies/tutorial014_an_py310.py", # temporary code example?
+ # Pydantic V1
+ "docs_src/schema_extra_example/tutorial001_pv1_py310.py",
+ "docs_src/query_param_models/tutorial002_pv1_py310.py",
+ "docs_src/query_param_models/tutorial002_pv1_an_py310.py",
+ "docs_src/header_param_models/tutorial002_pv1_py310.py",
+ "docs_src/header_param_models/tutorial002_pv1_an_py310.py",
+ "docs_src/cookie_param_models/tutorial002_pv1_py310.py",
+ "docs_src/cookie_param_models/tutorial002_pv1_an_py310.py",
]
[tool.coverage.report]
dirty-equals ==0.9.0
sqlmodel==0.0.27
flask >=1.1.2,<4.0.0
+strawberry-graphql >=0.200.0,< 1.0.0
anyio[trio] >=3.2.1,<5.0.0
PyJWT==2.9.0
pyyaml >=5.3.1,<7.0.0
--- /dev/null
+import importlib
+from typing import Union
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize("price", ["50.5", 50.5])
+def test_post_with_tax(client: TestClient, price: Union[str, float]):
+ response = client.post(
+ "/items/",
+ json={"name": "Foo", "price": price, "description": "Some Foo", "tax": 0.3},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "name": "Foo",
+ "price": 50.5,
+ "description": "Some Foo",
+ "tax": 0.3,
+ "price_with_tax": 50.8,
+ }
+
+
+@pytest.mark.parametrize("price", ["50.5", 50.5])
+def test_post_without_tax(client: TestClient, price: Union[str, float]):
+ response = client.post(
+ "/items/", json={"name": "Foo", "price": price, "description": "Some Foo"}
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "name": "Foo",
+ "price": 50.5,
+ "description": "Some Foo",
+ "tax": None,
+ }
+
+
+def test_post_with_no_data(client: TestClient):
+ response = client.post("/items/", json={})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["body", "name"],
+ "msg": "Field required",
+ "input": {},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "price"],
+ "msg": "Field required",
+ "input": {},
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={"name": "Foo", "price": 50.1, "description": "Some Foo", "tax": 0.3},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 123,
+ "name": "Foo",
+ "price": 50.1,
+ "description": "Some Foo",
+ "tax": 0.3,
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={"name": "Foo", "price": 50.1},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 123,
+ "name": "Foo",
+ "price": 50.1,
+ "description": None,
+ "tax": None,
+ }
+
+
+def test_put_with_no_data(client: TestClient):
+ response = client.put("/items/123", json={})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["body", "name"],
+ "msg": "Field required",
+ "input": {},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "price"],
+ "msg": "Field required",
+ "input": {},
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={"name": "Foo", "price": 50.1, "description": "Some Foo", "tax": 0.3},
+ params={"q": "somequery"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 123,
+ "name": "Foo",
+ "price": 50.1,
+ "description": "Some Foo",
+ "tax": 0.3,
+ "q": "somequery",
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={"name": "Foo", "price": 50.1},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 123,
+ "name": "Foo",
+ "price": 50.1,
+ "description": None,
+ "tax": None,
+ }
+
+
+def test_put_with_no_data(client: TestClient):
+ response = client.put("/items/123", json={})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["body", "name"],
+ "msg": "Field required",
+ "input": {},
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "price"],
+ "msg": "Field required",
+ "input": {},
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_all(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": "Some Foo",
+ "tax": 0.1,
+ },
+ "user": {"username": "johndoe", "full_name": "John Doe"},
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": "Some Foo",
+ "tax": 0.1,
+ },
+ "user": {"username": "johndoe", "full_name": "John Doe"},
+ }
+
+
+def test_post_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {"name": "Foo", "price": 50.5},
+ "user": {"username": "johndoe"},
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": None,
+ "tax": None,
+ },
+ "user": {"username": "johndoe", "full_name": None},
+ }
+
+
+def test_post_no_body(client: TestClient):
+ response = client.put("/items/5", json=None)
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "item",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "user",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_no_item(client: TestClient):
+ response = client.put("/items/5", json={"user": {"username": "johndoe"}})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "item",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_no_user(client: TestClient):
+ response = client.put("/items/5", json={"item": {"name": "Foo", "price": 50.5}})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "user",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_missing_required_field_in_item(client: TestClient):
+ response = client.put(
+ "/items/5", json={"item": {"name": "Foo"}, "user": {"username": "johndoe"}}
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": {"name": "Foo"},
+ "loc": [
+ "body",
+ "item",
+ "price",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_missing_required_field_in_user(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"item": {"name": "Foo", "price": 50.5}, "user": {"ful_name": "John Doe"}},
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": {"ful_name": "John Doe"},
+ "loc": [
+ "body",
+ "user",
+ "username",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_id_foo(client: TestClient):
+ response = client.put(
+ "/items/foo",
+ json={
+ "item": {"name": "Foo", "price": 50.5},
+ "user": {"username": "johndoe"},
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "int_parsing",
+ "loc": ["path", "item_id"],
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "input": "foo",
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "openapi": "3.1.0",
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_update_item_items__item_id__put",
+ },
+ },
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Update Item",
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "Body_update_item_items__item_id__put": {
+ "properties": {
+ "item": {
+ "$ref": "#/components/schemas/Item",
+ },
+ "user": {
+ "$ref": "#/components/schemas/User",
+ },
+ },
+ "required": [
+ "item",
+ "user",
+ ],
+ "title": "Body_update_item_items__item_id__put",
+ "type": "object",
+ },
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "User": {
+ "properties": {
+ "username": {
+ "title": "Username",
+ "type": "string",
+ },
+ "full_name": {
+ "title": "Full Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ "required": [
+ "username",
+ ],
+ "title": "User",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ pytest.param("tutorial004_an_py39"),
+ pytest.param("tutorial004_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "importance": 2,
+ "item": {"name": "Foo", "price": 50.5},
+ "user": {"username": "Dave"},
+ },
+ params={"q": "somequery"},
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 5,
+ "importance": 2,
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": None,
+ "tax": None,
+ },
+ "user": {"username": "Dave", "full_name": None},
+ "q": "somequery",
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "importance": 2,
+ "item": {"name": "Foo", "price": 50.5},
+ "user": {"username": "Dave"},
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 5,
+ "importance": 2,
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": None,
+ "tax": None,
+ },
+ "user": {"username": "Dave", "full_name": None},
+ }
+
+
+def test_put_missing_body(client: TestClient):
+ response = client.put("/items/5")
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "item",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "user",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "importance",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_put_empty_body(client: TestClient):
+ response = client.put("/items/5", json={})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["body", "item"],
+ "msg": "Field required",
+ "input": None,
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "user"],
+ "msg": "Field required",
+ "input": None,
+ },
+ {
+ "type": "missing",
+ "loc": ["body", "importance"],
+ "msg": "Field required",
+ "input": None,
+ },
+ ]
+ }
+
+
+def test_put_invalid_importance(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "importance": 0,
+ "item": {"name": "Foo", "price": 50.5},
+ "user": {"username": "Dave"},
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "importance"],
+ "msg": "Input should be greater than 0",
+ "type": "greater_than",
+ "input": 0,
+ "ctx": {"gt": 0},
+ },
+ ],
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "integer"},
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ },
+ },
+ "User": {
+ "title": "User",
+ "required": ["username"],
+ "type": "object",
+ "properties": {
+ "username": {"title": "Username", "type": "string"},
+ "full_name": {
+ "title": "Full Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ },
+ "Body_update_item_items__item_id__put": {
+ "title": "Body_update_item_items__item_id__put",
+ "required": ["item", "user", "importance"],
+ "type": "object",
+ "properties": {
+ "item": {"$ref": "#/components/schemas/Item"},
+ "user": {"$ref": "#/components/schemas/User"},
+ "importance": {
+ "title": "Importance",
+ "type": "integer",
+ "exclusiveMinimum": 0.0,
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial005_py39"),
+ pytest.param("tutorial005_py310", marks=needs_py310),
+ pytest.param("tutorial005_an_py39"),
+ pytest.param("tutorial005_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_multiple_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_all(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": "Some Foo",
+ "tax": 0.1,
+ },
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": "Some Foo",
+ "tax": 0.1,
+ },
+ }
+
+
+def test_post_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "item": {"name": "Foo", "price": 50.5},
+ },
+ )
+ assert response.status_code == 200
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "price": 50.5,
+ "description": None,
+ "tax": None,
+ },
+ }
+
+
+def test_post_no_body(client: TestClient):
+ response = client.put("/items/5", json=None)
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "item",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_like_not_embeded(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "price": 50.5,
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": None,
+ "loc": [
+ "body",
+ "item",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_post_missing_required_field_in_item(client: TestClient):
+ response = client.put(
+ "/items/5", json={"item": {"name": "Foo"}, "user": {"username": "johndoe"}}
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "input": {"name": "Foo"},
+ "loc": [
+ "body",
+ "item",
+ "price",
+ ],
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ],
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "openapi": "3.1.0",
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_update_item_items__item_id__put",
+ },
+ },
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Update Item",
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "Body_update_item_items__item_id__put": {
+ "properties": {
+ "item": {
+ "$ref": "#/components/schemas/Item",
+ },
+ },
+ "required": ["item"],
+ "title": "Body_update_item_items__item_id__put",
+ "type": "object",
+ },
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {"title": "Price", "type": "number"},
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+UNTYPED_LIST_SCHEMA = {"type": "array", "items": {}}
+
+LIST_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}}
+
+SET_OF_STR_SCHEMA = {"type": "array", "items": {"type": "string"}, "uniqueItems": True}
+
+
+@pytest.fixture(
+ name="mod_name",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ ],
+)
+def get_mod_name(request: pytest.FixtureRequest):
+ return request.param
+
+
+@pytest.fixture(name="client")
+def get_client(mod_name: str):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{mod_name}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient, mod_name: str):
+ if mod_name.startswith("tutorial003"):
+ tags_expected = IsList("foo", "bar", check_order=False)
+ else:
+ tags_expected = ["foo", "bar", "foo"]
+
+ response = client.put(
+ "/items/123",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": ["foo", "bar", "foo"],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 123,
+ "item": {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": tags_expected,
+ },
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"name": "Foo", "price": 35.4},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "description": None,
+ "price": 35.4,
+ "tax": None,
+ "tags": [],
+ },
+ }
+
+
+def test_put_empty_body(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"description": "A very nice Item"},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {"description": "A very nice Item"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {"description": "A very nice Item"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient, mod_name: str):
+ tags_schema = {"default": [], "title": "Tags"}
+ if mod_name.startswith("tutorial001"):
+ tags_schema.update(UNTYPED_LIST_SCHEMA)
+ elif mod_name.startswith("tutorial002"):
+ tags_schema.update(LIST_OF_STR_SCHEMA)
+ elif mod_name.startswith("tutorial003"):
+ tags_schema.update(SET_OF_STR_SCHEMA)
+
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": tags_schema,
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": ["foo", "bar", "foo"],
+ "image": {"url": "http://example.com/image.png", "name": "example image"},
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 123,
+ "item": {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": IsList("foo", "bar", check_order=False),
+ "image": {"url": "http://example.com/image.png", "name": "example image"},
+ },
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"name": "Foo", "price": 35.4},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "description": None,
+ "price": 35.4,
+ "tax": None,
+ "tags": [],
+ "image": None,
+ },
+ }
+
+
+def test_put_empty_body(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required_in_item(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"description": "A very nice Item"},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {"description": "A very nice Item"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {"description": "A very nice Item"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required_in_image(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "price": 35.4,
+ "image": {"url": "http://example.com/image.png"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "image", "name"],
+ "input": {"url": "http://example.com/image.png"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Image": {
+ "properties": {
+ "url": {
+ "title": "Url",
+ "type": "string",
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ },
+ "required": ["url", "name"],
+ "title": "Image",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "default": [],
+ "type": "array",
+ "items": {"type": "string"},
+ "uniqueItems": True,
+ },
+ "image": {
+ "anyOf": [
+ {"$ref": "#/components/schemas/Image"},
+ {"type": "null"},
+ ],
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial005_py39"),
+ pytest.param("tutorial005_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": ["foo", "bar", "foo"],
+ "image": {"url": "http://example.com/image.png", "name": "example image"},
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 123,
+ "item": {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": IsList("foo", "bar", check_order=False),
+ "image": {"url": "http://example.com/image.png", "name": "example image"},
+ },
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"name": "Foo", "price": 35.4},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "description": None,
+ "price": 35.4,
+ "tax": None,
+ "tags": [],
+ "image": None,
+ },
+ }
+
+
+def test_put_empty_body(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required_in_item(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"description": "A very nice Item"},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {"description": "A very nice Item"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {"description": "A very nice Item"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required_in_image(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "price": 35.4,
+ "image": {"url": "http://example.com/image.png"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "image", "name"],
+ "input": {"url": "http://example.com/image.png"},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_wrong_url(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "price": 35.4,
+ "image": {"url": "not a valid url", "name": "example image"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "image", "url"],
+ "input": "not a valid url",
+ "msg": "Input should be a valid URL, relative URL without a base",
+ "type": "url_parsing",
+ "ctx": {"error": "relative URL without a base"},
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Image": {
+ "properties": {
+ "url": {
+ "title": "Url",
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2083,
+ "minLength": 1,
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ },
+ "required": ["url", "name"],
+ "title": "Image",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "default": [],
+ "type": "array",
+ "items": {"type": "string"},
+ "uniqueItems": True,
+ },
+ "image": {
+ "anyOf": [
+ {"$ref": "#/components/schemas/Image"},
+ {"type": "null"},
+ ],
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial006_py39"),
+ pytest.param("tutorial006_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put_all(client: TestClient):
+ response = client.put(
+ "/items/123",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": ["foo", "bar", "foo"],
+ "images": [
+ {"url": "http://example.com/image.png", "name": "example image"}
+ ],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 123,
+ "item": {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": IsList("foo", "bar", check_order=False),
+ "images": [
+ {"url": "http://example.com/image.png", "name": "example image"}
+ ],
+ },
+ }
+
+
+def test_put_only_required(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={"name": "Foo", "price": 35.4},
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "item_id": 5,
+ "item": {
+ "name": "Foo",
+ "description": None,
+ "price": 35.4,
+ "tax": None,
+ "tags": [],
+ "images": None,
+ },
+ }
+
+
+def test_put_empty_body(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_images_not_list(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "price": 35.4,
+ "images": {"url": "http://example.com/image.png", "name": "example image"},
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "images"],
+ "input": {
+ "url": "http://example.com/image.png",
+ "name": "example image",
+ },
+ "msg": "Input should be a valid list",
+ "type": "list_type",
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Image": {
+ "properties": {
+ "url": {
+ "title": "Url",
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2083,
+ "minLength": 1,
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ },
+ "required": ["url", "name"],
+ "title": "Image",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "default": [],
+ "type": "array",
+ "items": {"type": "string"},
+ "uniqueItems": True,
+ },
+ "images": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/Image",
+ },
+ "type": "array",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Images",
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial007_py39"),
+ pytest.param("tutorial007_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_all(client: TestClient):
+ data = {
+ "name": "Special Offer",
+ "description": "This is a special offer",
+ "price": 38.6,
+ "items": [
+ {
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ "tags": ["foo"],
+ "images": [
+ {
+ "url": "http://example.com/image.png",
+ "name": "example image",
+ }
+ ],
+ }
+ ],
+ }
+
+ response = client.post(
+ "/offers/",
+ json=data,
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == data
+
+
+def test_put_only_required(client: TestClient):
+ response = client.post(
+ "/offers/",
+ json={
+ "name": "Special Offer",
+ "price": 38.6,
+ "items": [
+ {
+ "name": "Foo",
+ "price": 35.4,
+ "images": [
+ {
+ "url": "http://example.com/image.png",
+ "name": "example image",
+ }
+ ],
+ }
+ ],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Special Offer",
+ "description": None,
+ "price": 38.6,
+ "items": [
+ {
+ "name": "Foo",
+ "description": None,
+ "price": 35.4,
+ "tax": None,
+ "tags": [],
+ "images": [
+ {
+ "url": "http://example.com/image.png",
+ "name": "example image",
+ }
+ ],
+ }
+ ],
+ }
+
+
+def test_put_empty_body(client: TestClient):
+ response = client.post(
+ "/offers/",
+ json={},
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "price"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "items"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required_in_items(client: TestClient):
+ response = client.post(
+ "/offers/",
+ json={
+ "name": "Special Offer",
+ "price": 38.6,
+ "items": [{}],
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "items", 0, "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "items", 0, "price"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_put_missing_required_in_images(client: TestClient):
+ response = client.post(
+ "/offers/",
+ json={
+ "name": "Special Offer",
+ "price": 38.6,
+ "items": [
+ {"name": "Foo", "price": 35.4, "images": [{}]},
+ ],
+ },
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "items", 0, "images", 0, "url"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ {
+ "loc": ["body", "items", 0, "images", 0, "name"],
+ "input": {},
+ "msg": "Field required",
+ "type": "missing",
+ },
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/offers/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create Offer",
+ "operationId": "create_offer_offers__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Offer",
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Image": {
+ "properties": {
+ "url": {
+ "title": "Url",
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2083,
+ "minLength": 1,
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ },
+ "required": ["url", "name"],
+ "title": "Image",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "default": [],
+ "type": "array",
+ "items": {"type": "string"},
+ "uniqueItems": True,
+ },
+ "images": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/Image",
+ },
+ "type": "array",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Images",
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "Offer": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "items": {
+ "title": "Items",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Item"},
+ },
+ },
+ "required": ["name", "price", "items"],
+ "title": "Offer",
+ "type": "object",
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial008_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_nested_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_body(client: TestClient):
+ data = [
+ {"url": "http://example.com/", "name": "Example"},
+ {"url": "http://fastapi.tiangolo.com/", "name": "FastAPI"},
+ ]
+ response = client.post("/images/multiple", json=data)
+ assert response.status_code == 200, response.text
+ assert response.json() == data
+
+
+def test_post_invalid_list_item(client: TestClient):
+ data = [{"url": "not a valid url", "name": "Example"}]
+ response = client.post("/images/multiple", json=data)
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", 0, "url"],
+ "input": "not a valid url",
+ "msg": "Input should be a valid URL, relative URL without a base",
+ "type": "url_parsing",
+ "ctx": {"error": "relative URL without a base"},
+ },
+ ]
+ }
+
+
+def test_post_not_a_list(client: TestClient):
+ data = {"url": "http://example.com/", "name": "Example"}
+ response = client.post("/images/multiple", json=data)
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body"],
+ "input": {
+ "name": "Example",
+ "url": "http://example.com/",
+ },
+ "msg": "Input should be a valid list",
+ "type": "list_type",
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/images/multiple/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create Multiple Images",
+ "operationId": "create_multiple_images_images_multiple__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Images",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Image"},
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Image": {
+ "properties": {
+ "url": {
+ "title": "Url",
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2083,
+ "minLength": 1,
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ },
+ "required": ["url", "name"],
+ "title": "Image",
+ "type": "object",
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.body_updates.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_get(client: TestClient):
+ response = client.get("/items/baz")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Baz",
+ "description": None,
+ "price": 50.2,
+ "tax": 10.5,
+ "tags": [],
+ }
+
+
+def test_patch_all(client: TestClient):
+ response = client.patch(
+ "/items/foo",
+ json={
+ "name": "Fooz",
+ "description": "Item description",
+ "price": 3,
+ "tax": 10.5,
+ "tags": ["tag1", "tag2"],
+ },
+ )
+ assert response.json() == {
+ "name": "Fooz",
+ "description": "Item description",
+ "price": 3,
+ "tax": 10.5,
+ "tags": ["tag1", "tag2"],
+ }
+
+
+def test_patch_name(client: TestClient):
+ response = client.patch(
+ "/items/bar",
+ json={"name": "Barz"},
+ )
+ assert response.json() == {
+ "name": "Barz",
+ "description": "The bartenders",
+ "price": 62,
+ "tax": 20.2,
+ "tags": [],
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Item",
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ },
+ "patch": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__patch",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ },
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "type": "object",
+ "title": "Item",
+ "properties": {
+ "name": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Name",
+ },
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Price",
+ },
+ "tax": {"title": "Tax", "type": "number", "default": 10.5},
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+import importlib
+
+import pytest
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial001_py39 import app
-client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial010_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.custom_response.{request.param}")
+ client = TestClient(mod.app)
+ return client
-def test_get_custom_response():
+def test_get_custom_response(client: TestClient):
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == [{"item_id": "Foo"}]
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="mod_name",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial004_py39"),
+ ],
+)
+def get_mod_name(request: pytest.FixtureRequest) -> str:
+ return request.param
+
+
+@pytest.fixture(name="client")
+def get_client(mod_name: str) -> TestClient:
+ mod = importlib.import_module(f"docs_src.custom_response.{mod_name}")
+ return TestClient(mod.app)
+
+
+html_contents = """
+ <html>
+ <head>
+ <title>Some HTML in here</title>
+ </head>
+ <body>
+ <h1>Look ma! HTML!</h1>
+ </body>
+ </html>
+ """
+
+
+def test_get_custom_response(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.text == html_contents
+
+
+def test_openapi_schema(client: TestClient, mod_name: str):
+ if mod_name.startswith("tutorial003"):
+ response_content = {"application/json": {"schema": {}}}
+ else:
+ response_content = {"text/html": {"schema": {"type": "string"}}}
+
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": response_content,
+ }
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ }
+ }
+ },
+ }
],
)
def get_client(request: pytest.FixtureRequest):
- mod = importlib.import_module(f"docs_src.dataclasses.{request.param}")
+ mod = importlib.import_module(f"docs_src.dataclasses_.{request.param}")
client = TestClient(mod.app)
client.headers.clear()
],
)
def get_client(request: pytest.FixtureRequest):
- mod = importlib.import_module(f"docs_src.dataclasses.{request.param}")
+ mod = importlib.import_module(f"docs_src.dataclasses_.{request.param}")
client = TestClient(mod.app)
client.headers.clear()
],
)
def get_client(request: pytest.FixtureRequest):
- mod = importlib.import_module(f"docs_src.dataclasses.{request.param}")
+ mod = importlib.import_module(f"docs_src.dataclasses_.{request.param}")
client = TestClient(mod.app)
client.headers.clear()
--- /dev/null
+import importlib
+import runpy
+import sys
+import unittest
+
+import pytest
+from fastapi.testclient import TestClient
+
+MOD_NAME = "docs_src.debugging.tutorial001_py39"
+
+
+@pytest.fixture(name="client")
+def get_client():
+ mod = importlib.import_module(MOD_NAME)
+ client = TestClient(mod.app)
+ return client
+
+
+def test_uvicorn_run_is_not_called_on_import():
+ if sys.modules.get(MOD_NAME):
+ del sys.modules[MOD_NAME] # pragma: no cover
+ with unittest.mock.patch("uvicorn.run") as uvicorn_run_mock:
+ importlib.import_module(MOD_NAME)
+ uvicorn_run_mock.assert_not_called()
+
+
+def test_get_root(client: TestClient):
+ response = client.get("/")
+ assert response.status_code == 200
+ assert response.json() == {"hello world": "ba"}
+
+
+def test_uvicorn_run_called_when_run_as_main(): # Just for coverage
+ if sys.modules.get(MOD_NAME):
+ del sys.modules[MOD_NAME]
+ with unittest.mock.patch("uvicorn.run") as uvicorn_run_mock:
+ runpy.run_module(MOD_NAME, run_name="__main__")
+
+ uvicorn_run_mock.assert_called_once_with(
+ unittest.mock.ANY, host="0.0.0.0", port=8000
+ )
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/": {
+ "get": {
+ "summary": "Root",
+ "operationId": "root__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ },
+ }
+ }
+ },
+ }
import importlib
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
from ...utils import needs_py310
pytest.param("tutorial001_py310", marks=needs_py310),
pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
+ pytest.param("tutorial001_02_an_py39"),
+ pytest.param("tutorial001_02_an_py310", marks=needs_py310),
],
)
def get_client(request: pytest.FixtureRequest):
"parameters": [
{
"required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Q",
+ },
"name": "q",
"in": "query",
},
"parameters": [
{
"required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Q",
+ },
"name": "q",
"in": "query",
},
import importlib
import pytest
-from dirty_equals import IsDict
from fastapi.testclient import TestClient
from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ pytest.param("tutorial002_an_py39"),
+ pytest.param("tutorial002_an_py310", marks=needs_py310),
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ pytest.param("tutorial003_an_py39"),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
pytest.param("tutorial004_an_py39"),
"parameters": [
{
"required": False,
- "schema": IsDict(
- {
- "anyOf": [{"type": "string"}, {"type": "null"}],
- "title": "Q",
- }
- )
- | IsDict(
- # TODO: remove when deprecating Pydantic v1
- {"title": "Q", "type": "string"}
- ),
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Q",
+ },
"name": "q",
"in": "query",
},
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial005_py39"),
+ pytest.param("tutorial005_py310", marks=needs_py310),
+ pytest.param("tutorial005_an_py39"),
+ pytest.param("tutorial005_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize(
+ "path,cookie,expected_status,expected_response",
+ [
+ (
+ "/items",
+ "from_cookie",
+ 200,
+ {"q_or_cookie": "from_cookie"},
+ ),
+ (
+ "/items?q=foo",
+ "from_cookie",
+ 200,
+ {"q_or_cookie": "foo"},
+ ),
+ (
+ "/items",
+ None,
+ 200,
+ {"q_or_cookie": None},
+ ),
+ ],
+)
+def test_get(path, cookie, expected_status, expected_response, client: TestClient):
+ if cookie is not None:
+ client.cookies.set("last_query", cookie)
+ else:
+ client.cookies.clear()
+ response = client.get(path)
+ assert response.status_code == expected_status
+ assert response.json() == expected_response
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Query",
+ "operationId": "read_query_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Last Query",
+ },
+ "name": "last_query",
+ "in": "cookie",
+ },
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import asyncio
+from contextlib import asynccontextmanager
+from unittest.mock import Mock, patch
+
+from docs_src.dependencies.tutorial007_py39 import get_db
+
+
+def test_get_db(): # Just for coverage
+ async def test_async_gen():
+ cm = asynccontextmanager(get_db)
+ async with cm() as db_session:
+ return db_session
+
+ dbsession_moock = Mock()
+
+ with patch(
+ "docs_src.dependencies.tutorial007_py39.DBSession",
+ return_value=dbsession_moock,
+ create=True,
+ ):
+ value = asyncio.run(test_async_gen())
+
+ assert value is dbsession_moock
+ dbsession_moock.close.assert_called_once()
--- /dev/null
+import importlib
+from types import ModuleType
+from typing import Annotated, Any
+from unittest.mock import Mock, patch
+
+import pytest
+from fastapi import Depends, FastAPI
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="module",
+ params=[
+ "tutorial008_py39",
+ # Fails with `NameError: name 'DepA' is not defined`
+ pytest.param("tutorial008_an_py39", marks=pytest.mark.xfail),
+ ],
+)
+def get_module(request: pytest.FixtureRequest):
+ mod_name = f"docs_src.dependencies.{request.param}"
+ mod = importlib.import_module(mod_name)
+ return mod
+
+
+def test_get_db(module: ModuleType):
+ app = FastAPI()
+
+ @app.get("/")
+ def read_root(c: Annotated[Any, Depends(module.dependency_c)]):
+ return {"c": str(c)}
+
+ client = TestClient(app)
+
+ a_mock = Mock()
+ b_mock = Mock()
+ c_mock = Mock()
+
+ with (
+ patch(
+ f"{module.__name__}.generate_dep_a",
+ return_value=a_mock,
+ create=True,
+ ),
+ patch(
+ f"{module.__name__}.generate_dep_b",
+ return_value=b_mock,
+ create=True,
+ ),
+ patch(
+ f"{module.__name__}.generate_dep_c",
+ return_value=c_mock,
+ create=True,
+ ),
+ ):
+ response = client.get("/")
+
+ assert response.status_code == 200
+ assert response.json() == {"c": str(c_mock)}
--- /dev/null
+from typing import Annotated, Any
+from unittest.mock import Mock, patch
+
+from fastapi import Depends, FastAPI
+from fastapi.testclient import TestClient
+
+from docs_src.dependencies.tutorial010_py39 import get_db
+
+
+def test_get_db():
+ app = FastAPI()
+
+ @app.get("/")
+ def read_root(c: Annotated[Any, Depends(get_db)]):
+ return {"c": str(c)}
+
+ client = TestClient(app)
+
+ dbsession_mock = Mock()
+
+ with patch(
+ "docs_src.dependencies.tutorial010_py39.DBSession",
+ return_value=dbsession_mock,
+ create=True,
+ ):
+ response = client.get("/")
+
+ assert response.status_code == 200
+ assert response.json() == {"c": str(dbsession_mock)}
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial011_py39",
+ pytest.param("tutorial011_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.dependencies.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize(
+ "path,expected_status,expected_response",
+ [
+ (
+ "/query-checker/",
+ 200,
+ {"fixed_content_in_query": False},
+ ),
+ (
+ "/query-checker/?q=qwerty",
+ 200,
+ {"fixed_content_in_query": False},
+ ),
+ (
+ "/query-checker/?q=foobar",
+ 200,
+ {"fixed_content_in_query": True},
+ ),
+ ],
+)
+def test_get(path, expected_status, expected_response, client: TestClient):
+ response = client.get(path)
+ assert response.status_code == expected_status
+ assert response.json() == expected_response
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/query-checker/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Query Check",
+ "operationId": "read_query_check_query_checker__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "type": "string",
+ "default": "",
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+from types import ModuleType
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="mod",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ ],
+)
+def get_module(request: pytest.FixtureRequest):
+ module = importlib.import_module(f"docs_src.encoder.{request.param}")
+ return module
+
+
+@pytest.fixture(name="client")
+def get_client(mod: ModuleType):
+ client = TestClient(mod.app)
+ return client
+
+
+def test_put(client: TestClient, mod: ModuleType):
+ fake_db = mod.fake_db
+
+ response = client.put(
+ "/items/123",
+ json={
+ "title": "Foo",
+ "timestamp": "2023-01-01T12:00:00",
+ "description": "An optional description",
+ },
+ )
+ assert response.status_code == 200
+ assert "123" in fake_db
+ assert fake_db["123"] == {
+ "title": "Foo",
+ "timestamp": "2023-01-01T12:00:00",
+ "description": "An optional description",
+ }
+
+
+def test_put_invalid_data(client: TestClient, mod: ModuleType):
+ fake_db = mod.fake_db
+
+ response = client.put(
+ "/items/345",
+ json={
+ "title": "Foo",
+ "timestamp": "not a date",
+ },
+ )
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body", "timestamp"],
+ "msg": "Input should be a valid datetime or date, invalid character in year",
+ "type": "datetime_from_date_parsing",
+ "input": "not a date",
+ "ctx": {"error": "invalid character in year"},
+ }
+ ]
+ }
+ assert "345" not in fake_db
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{id}": {
+ "put": {
+ "operationId": "update_item_items__id__put",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "required": True,
+ "schema": {
+ "title": "Id",
+ "type": "string",
+ },
+ },
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ },
+ },
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Update Item",
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "description": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Description",
+ },
+ "timestamp": {
+ "format": "date-time",
+ "title": "Timestamp",
+ "type": "string",
+ },
+ "title": {
+ "title": "Title",
+ "type": "string",
+ },
+ },
+ "required": [
+ "title",
+ "timestamp",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.extra_models.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post(client: TestClient):
+ response = client.post(
+ "/user/",
+ json={
+ "username": "johndoe",
+ "password": "secret",
+ "email": "johndoe@example.com",
+ "full_name": "John Doe",
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "username": "johndoe",
+ "email": "johndoe@example.com",
+ "full_name": "John Doe",
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/user/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UserOut",
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create User",
+ "operationId": "create_user_user__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/UserIn"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "UserIn": {
+ "title": "UserIn",
+ "required": IsList(
+ "username", "password", "email", check_order=False
+ ),
+ "type": "object",
+ "properties": {
+ "username": {"title": "Username", "type": "string"},
+ "password": {"title": "Password", "type": "string"},
+ "email": {
+ "title": "Email",
+ "type": "string",
+ "format": "email",
+ },
+ "full_name": {
+ "title": "Full Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ },
+ "UserOut": {
+ "title": "UserOut",
+ "required": ["username", "email"],
+ "type": "object",
+ "properties": {
+ "username": {"title": "Username", "type": "string"},
+ "email": {
+ "title": "Email",
+ "type": "string",
+ "format": "email",
+ },
+ "full_name": {
+ "title": "Full Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
+import importlib
+
import pytest
from fastapi.testclient import TestClient
-from docs_src.first_steps.tutorial001_py39 import app
-client = TestClient(app)
+@pytest.fixture(
+ name="client",
+ params=[
+ "tutorial001_py39",
+ "tutorial003_py39",
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.first_steps.{request.param}")
+ client = TestClient(mod.app)
+ return client
@pytest.mark.parametrize(
("/nonexistent", 404, {"detail": "Not Found"}),
],
)
-def test_get_path(path, expected_status, expected_response):
+def test_get_path(client: TestClient, path, expected_status, expected_response):
response = client.get(path)
assert response.status_code == expected_status
assert response.json() == expected_response
-def test_openapi_schema():
+def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200
assert response.json() == {
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.generate_clients.{request.param}")
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_items(client: TestClient):
+ response = client.post("/items/", json={"name": "Foo", "price": 5})
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "item received"}
+
+
+def test_get_items(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {"name": "Plumbus", "price": 3},
+ {"name": "Portal Gun", "price": 9001},
+ ]
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Get Items",
+ "operationId": "get_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Response Get Items Items Get",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Item"},
+ }
+ }
+ },
+ }
+ },
+ },
+ "post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ResponseMessage"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
+ },
+ },
+ "ResponseMessage": {
+ "title": "ResponseMessage",
+ "required": ["message"],
+ "type": "object",
+ "properties": {"message": {"title": "Message", "type": "string"}},
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+from fastapi.testclient import TestClient
+
+from docs_src.generate_clients.tutorial002_py39 import app
+
+client = TestClient(app)
+
+
+def test_post_items():
+ response = client.post("/items/", json={"name": "Foo", "price": 5})
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "Item received"}
+
+
+def test_post_users():
+ response = client.post(
+ "/users/", json={"username": "Foo", "email": "foo@example.com"}
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {"message": "User received"}
+
+
+def test_get_items():
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {"name": "Plumbus", "price": 3},
+ {"name": "Portal Gun", "price": 9001},
+ ]
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "tags": ["items"],
+ "summary": "Get Items",
+ "operationId": "get_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Response Get Items Items Get",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Item"},
+ }
+ }
+ },
+ }
+ },
+ },
+ "post": {
+ "tags": ["items"],
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ResponseMessage"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ "/users/": {
+ "post": {
+ "tags": ["users"],
+ "summary": "Create User",
+ "operationId": "create_user_users__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/User"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ResponseMessage"
+ }
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
+ },
+ },
+ "ResponseMessage": {
+ "title": "ResponseMessage",
+ "required": ["message"],
+ "type": "object",
+ "properties": {"message": {"title": "Message", "type": "string"}},
+ },
+ "User": {
+ "title": "User",
+ "required": ["username", "email"],
+ "type": "object",
+ "properties": {
+ "username": {"title": "Username", "type": "string"},
+ "email": {"title": "Email", "type": "string"},
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+import json
+import pathlib
+from unittest.mock import patch
+
+from docs_src.generate_clients import tutorial003_py39
+
+
+def test_remove_tags(tmp_path: pathlib.Path):
+ tmp_file = tmp_path / "openapi.json"
+ openapi_json = tutorial003_py39.app.openapi()
+ tmp_file.write_text(json.dumps(openapi_json))
+
+ with patch("pathlib.Path", return_value=tmp_file):
+ importlib.import_module("docs_src.generate_clients.tutorial004_py39")
+
+ modified_openapi = json.loads(tmp_file.read_text())
+ assert modified_openapi == {
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ResponseMessage": {
+ "properties": {
+ "message": {
+ "title": "Message",
+ "type": "string",
+ },
+ },
+ "required": [
+ "message",
+ ],
+ "title": "ResponseMessage",
+ "type": "object",
+ },
+ "User": {
+ "properties": {
+ "email": {
+ "title": "Email",
+ "type": "string",
+ },
+ "username": {
+ "title": "Username",
+ "type": "string",
+ },
+ },
+ "required": [
+ "username",
+ "email",
+ ],
+ "title": "User",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "openapi": "3.1.0",
+ "paths": {
+ "/items/": {
+ "get": {
+ "operationId": "get_items",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/Item",
+ },
+ "title": "Response Items-Get Items",
+ "type": "array",
+ },
+ },
+ },
+ "description": "Successful Response",
+ },
+ },
+ "summary": "Get Items",
+ "tags": [
+ "items",
+ ],
+ },
+ "post": {
+ "operationId": "create_item",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ },
+ },
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ResponseMessage",
+ },
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Create Item",
+ "tags": [
+ "items",
+ ],
+ },
+ },
+ "/users/": {
+ "post": {
+ "operationId": "create_user",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User",
+ },
+ },
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ResponseMessage",
+ },
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Create User",
+ "tags": [
+ "users",
+ ],
+ },
+ },
+ },
+ }
--- /dev/null
+import warnings
+
+import pytest
+from starlette.testclient import TestClient
+
+warnings.filterwarnings(
+ "ignore",
+ message=r"The 'lia' package has been renamed to 'cross_web'\..*",
+ category=DeprecationWarning,
+)
+
+from docs_src.graphql_.tutorial001_py39 import app # noqa: E402
+
+
+@pytest.fixture(name="client")
+def get_client() -> TestClient:
+ return TestClient(app)
+
+
+def test_query(client: TestClient):
+ response = client.post("/graphql", json={"query": "{ user { name, age } }"})
+ assert response.status_code == 200
+ assert response.json() == {"data": {"user": {"name": "Patrick", "age": 100}}}
+
+
+def test_openapi(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "openapi": "3.1.0",
+ "paths": {
+ "/graphql": {
+ "get": {
+ "operationId": "handle_http_get_graphql_get",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "The GraphiQL integrated development environment.",
+ },
+ "404": {
+ "description": "Not found if GraphiQL or query via GET are not enabled.",
+ },
+ },
+ "summary": "Handle Http Get",
+ },
+ "post": {
+ "operationId": "handle_http_post_graphql_post",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ },
+ "summary": "Handle Http Post",
+ },
+ },
+ },
+ }
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial004_py39 import app
+from docs_src.metadata.tutorial002_py39 import app
client = TestClient(app)
-html_contents = """
- <html>
- <head>
- <title>Some HTML in here</title>
- </head>
- <body>
- <h1>Look ma! HTML!</h1>
- </body>
- </html>
- """
-
-
-def test_get_custom_response():
+def test_items():
response = client.get("/items/")
assert response.status_code == 200, response.text
- assert response.text == html_contents
+ assert response.json() == [{"name": "Foo"}]
-def test_openapi_schema():
+def test_get_openapi_json_default_url():
response = client.get("/openapi.json")
+ assert response.status_code == 404, response.text
+
+
+def test_openapi_schema():
+ response = client.get("/api/v1/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
- "info": {"title": "FastAPI", "version": "0.1.0"},
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
"paths": {
"/items/": {
"get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
- "content": {"text/html": {"schema": {"type": "string"}}},
+ "content": {"application/json": {"schema": {}}},
}
},
- "summary": "Read Items",
- "operationId": "read_items_items__get",
}
}
},
--- /dev/null
+from fastapi.testclient import TestClient
+
+from docs_src.metadata.tutorial003_py39 import app
+
+client = TestClient(app)
+
+
+def test_items():
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.json() == [{"name": "Foo"}]
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ }
+ }
+ },
+ }
+
+
+def test_swagger_ui_default_url():
+ response = client.get("/docs")
+ assert response.status_code == 404, response.text
+
+
+def test_swagger_ui_custom_url():
+ response = client.get("/documentation")
+ assert response.status_code == 200, response.text
+ assert "<title>FastAPI - Swagger UI</title>" in response.text
+
+
+def test_redoc_ui_default_url():
+ response = client.get("/redoc")
+ assert response.status_code == 404, response.text
--- /dev/null
+from fastapi.testclient import TestClient
+
+from docs_src.middleware.tutorial001_py39 import app
+
+client = TestClient(app)
+
+
+def test_response_headers():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert "X-Process-Time" in response.headers
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "paths": {},
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_operation_configuration.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+def test_post_items(client: TestClient):
+ response = client.post(
+ "/items/",
+ json={
+ "name": "Foo",
+ "description": "Item description",
+ "price": 42.0,
+ "tax": 3.2,
+ "tags": ["bar", "baz"],
+ },
+ )
+ assert response.status_code == 201, response.text
+ assert response.json() == {
+ "name": "Foo",
+ "description": "Item description",
+ "price": 42.0,
+ "tax": 3.2,
+ "tags": IsList("bar", "baz", check_order=False),
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "201": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "description": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Description",
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tags": {
+ "default": [],
+ "items": {
+ "type": "string",
+ },
+ "title": "Tags",
+ "type": "array",
+ "uniqueItems": True,
+ },
+ "tax": {
+ "anyOf": [
+ {
+ "type": "number",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Tax",
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_operation_configuration.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+def test_post_items(client: TestClient):
+ response = client.post(
+ "/items/",
+ json={
+ "name": "Foo",
+ "description": "Item description",
+ "price": 42.0,
+ "tax": 3.2,
+ "tags": ["bar", "baz"],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Foo",
+ "description": "Item description",
+ "price": 42.0,
+ "tax": 3.2,
+ "tags": IsList("bar", "baz", check_order=False),
+ }
+
+
+def test_get_items(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.json() == [{"name": "Foo", "price": 42}]
+
+
+def test_get_users(client: TestClient):
+ response = client.get("/users/")
+ assert response.status_code == 200, response.text
+ assert response.json() == [{"username": "johndoe"}]
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "tags": ["items"],
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ },
+ "post": {
+ "tags": ["items"],
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ "/users/": {
+ "get": {
+ "tags": ["users"],
+ "summary": "Read Users",
+ "operationId": "read_users_users__get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "description": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Description",
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tags": {
+ "default": [],
+ "items": {
+ "type": "string",
+ },
+ "title": "Tags",
+ "type": "array",
+ "uniqueItems": True,
+ },
+ "tax": {
+ "anyOf": [
+ {
+ "type": "number",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Tax",
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+from textwrap import dedent
+
+import pytest
+from dirty_equals import IsList
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+DESCRIPTIONS = {
+ "tutorial003": "Create an item with all the information, name, description, price, tax and a set of unique tags",
+ "tutorial004": dedent("""
+ Create an item with all the information:
+
+ - **name**: each item must have a name
+ - **description**: a long description
+ - **price**: required
+ - **tax**: if the item doesn't have tax, you can omit this
+ - **tags**: a set of unique tag strings for this item
+ """).strip(),
+}
+
+
+@pytest.fixture(
+ name="mod_name",
+ params=[
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ ],
+)
+def get_mod_name(request: pytest.FixtureRequest) -> str:
+ return request.param
+
+
+@pytest.fixture(name="client")
+def get_client(mod_name: str) -> TestClient:
+ mod = importlib.import_module(f"docs_src.path_operation_configuration.{mod_name}")
+ return TestClient(mod.app)
+
+
+def test_post_items(client: TestClient):
+ response = client.post(
+ "/items/",
+ json={
+ "name": "Foo",
+ "description": "Item description",
+ "price": 42.0,
+ "tax": 3.2,
+ "tags": ["bar", "baz"],
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Foo",
+ "description": "Item description",
+ "price": 42.0,
+ "tax": 3.2,
+ "tags": IsList("bar", "baz", check_order=False),
+ }
+
+
+def test_openapi_schema(client: TestClient, mod_name: str):
+ mod_name = mod_name[:11]
+
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "summary": "Create an item",
+ "description": DESCRIPTIONS[mod_name],
+ "operationId": "create_item_items__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "Item": {
+ "properties": {
+ "description": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Description",
+ },
+ "name": {
+ "title": "Name",
+ "type": "string",
+ },
+ "price": {
+ "title": "Price",
+ "type": "number",
+ },
+ "tags": {
+ "default": [],
+ "items": {
+ "type": "string",
+ },
+ "title": "Tags",
+ "type": "array",
+ "uniqueItems": True,
+ },
+ "tax": {
+ "anyOf": [
+ {
+ "type": "number",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Tax",
+ },
+ },
+ "required": [
+ "name",
+ "price",
+ ],
+ "title": "Item",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import pytest
+from fastapi.testclient import TestClient
+
+from docs_src.path_params.tutorial001_py39 import app
+
+client = TestClient(app)
+
+
+@pytest.mark.parametrize(
+ ("item_id", "expected_response"),
+ [
+ (1, {"item_id": "1"}),
+ ("alice", {"item_id": "alice"}),
+ ],
+)
+def test_get_items(item_id, expected_response):
+ response = client.get(f"/items/{item_id}")
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Read Item",
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+from fastapi.testclient import TestClient
+
+from docs_src.path_params.tutorial002_py39 import app
+
+client = TestClient(app)
+
+
+def test_get_items():
+ response = client.get("/items/1")
+ assert response.status_code == 200, response.text
+ assert response.json() == {"item_id": 1}
+
+
+def test_get_items_invalid_id():
+ response = client.get("/items/item1")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "input": "item1",
+ "loc": ["path", "item_id"],
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "type": "int_parsing",
+ }
+ ]
+ }
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "item_id",
+ "required": True,
+ "schema": {
+ "title": "Item Id",
+ "type": "integer",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Read Item",
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import pytest
+from fastapi.testclient import TestClient
+
+from docs_src.path_params.tutorial003_py39 import app
+
+client = TestClient(app)
+
+
+@pytest.mark.parametrize(
+ ("user_id", "expected_response"),
+ [
+ ("me", {"user_id": "the current user"}),
+ ("alice", {"user_id": "alice"}),
+ ],
+)
+def test_get_users(user_id: str, expected_response: dict):
+ response = client.get(f"/users/{user_id}")
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/users/me": {
+ "get": {
+ "operationId": "read_user_me_users_me_get",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ },
+ "summary": "Read User Me",
+ },
+ },
+ "/users/{user_id}": {
+ "get": {
+ "operationId": "read_user_users__user_id__get",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "user_id",
+ "required": True,
+ "schema": {
+ "title": "User Id",
+ "type": "string",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ "summary": "Read User",
+ },
+ },
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import asyncio
+
+from fastapi.testclient import TestClient
+
+from docs_src.path_params.tutorial003b_py39 import app, read_users2
+
+client = TestClient(app)
+
+
+def test_get_users():
+ response = client.get("/users")
+ assert response.status_code == 200, response.text
+ assert response.json() == ["Rick", "Morty"]
+
+
+def test_read_users2(): # Just for coverage
+ assert asyncio.run(read_users2()) == ["Bean", "Elfo"]
+
+
+def test_openapi_schema():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/users": {
+ "get": {
+ "operationId": "read_users2_users_get",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ },
+ "summary": "Read Users2",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ pytest.param("tutorial001_an_py39"),
+ pytest.param("tutorial001_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_params_numeric_validations.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+@pytest.mark.parametrize(
+ "path,expected_response",
+ [
+ ("/items/42", {"item_id": 42}),
+ ("/items/123?item-query=somequery", {"item_id": 123, "q": "somequery"}),
+ ],
+)
+def test_read_items(client: TestClient, path, expected_response):
+ response = client.get(path)
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_read_items_invalid_item_id(client: TestClient):
+ response = client.get("/items/invalid_id")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "invalid_id",
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "type": "int_parsing",
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "title": "The ID of the item to get",
+ "type": "integer",
+ },
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ "title": "Item-Query",
+ },
+ "name": "item-query",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {},
+ }
+ },
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_an_py39"),
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_params_numeric_validations.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+@pytest.mark.parametrize(
+ "path,expected_response",
+ [
+ ("/items/42?q=", {"item_id": 42}),
+ ("/items/123?q=somequery", {"item_id": 123, "q": "somequery"}),
+ ],
+)
+def test_read_items(client: TestClient, path, expected_response):
+ response = client.get(path)
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_read_items_invalid_item_id(client: TestClient):
+ response = client.get("/items/invalid_id?q=somequery")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "invalid_id",
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "type": "int_parsing",
+ }
+ ]
+ }
+
+
+def test_read_items_missing_q(client: TestClient):
+ response = client.get("/items/42")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["query", "q"],
+ "input": None,
+ "msg": "Field required",
+ "type": "missing",
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "title": "The ID of the item to get",
+ "type": "integer",
+ },
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": True,
+ "schema": {
+ "type": "string",
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {},
+ }
+ },
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_params_numeric_validations.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+@pytest.mark.parametrize(
+ "path,expected_response",
+ [
+ ("/items/42?q=", {"item_id": 42}),
+ ("/items/1?q=somequery", {"item_id": 1, "q": "somequery"}),
+ ],
+)
+def test_read_items(client: TestClient, path, expected_response):
+ response = client.get(path)
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_read_items_non_int_item_id(client: TestClient):
+ response = client.get("/items/invalid_id?q=somequery")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "invalid_id",
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "type": "int_parsing",
+ }
+ ]
+ }
+
+
+def test_read_items_item_id_less_than_one(client: TestClient):
+ response = client.get("/items/0?q=somequery")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "0",
+ "msg": "Input should be greater than or equal to 1",
+ "type": "greater_than_equal",
+ "ctx": {"ge": 1},
+ }
+ ]
+ }
+
+
+def test_read_items_missing_q(client: TestClient):
+ response = client.get("/items/42")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["query", "q"],
+ "input": None,
+ "msg": "Field required",
+ "type": "missing",
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "title": "The ID of the item to get",
+ "type": "integer",
+ "minimum": 1,
+ },
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": True,
+ "schema": {
+ "type": "string",
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {},
+ }
+ },
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial005_py39"),
+ pytest.param("tutorial005_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_params_numeric_validations.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+@pytest.mark.parametrize(
+ "path,expected_response",
+ [
+ ("/items/1?q=", {"item_id": 1}),
+ ("/items/1000?q=somequery", {"item_id": 1000, "q": "somequery"}),
+ ],
+)
+def test_read_items(client: TestClient, path, expected_response):
+ response = client.get(path)
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_read_items_non_int_item_id(client: TestClient):
+ response = client.get("/items/invalid_id?q=somequery")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "invalid_id",
+ "msg": "Input should be a valid integer, unable to parse string as an integer",
+ "type": "int_parsing",
+ }
+ ]
+ }
+
+
+def test_read_items_item_id_less_than_one(client: TestClient):
+ response = client.get("/items/0?q=somequery")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "0",
+ "msg": "Input should be greater than 0",
+ "type": "greater_than",
+ "ctx": {"gt": 0},
+ }
+ ]
+ }
+
+
+def test_read_items_item_id_greater_than_one_thousand(client: TestClient):
+ response = client.get("/items/1001?q=somequery")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "1001",
+ "msg": "Input should be less than or equal to 1000",
+ "type": "less_than_equal",
+ "ctx": {"le": 1000},
+ }
+ ]
+ }
+
+
+def test_read_items_missing_q(client: TestClient):
+ response = client.get("/items/42")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["query", "q"],
+ "input": None,
+ "msg": "Field required",
+ "type": "missing",
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "title": "The ID of the item to get",
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "maximum": 1000,
+ },
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": True,
+ "schema": {
+ "type": "string",
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {},
+ }
+ },
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial006_py39"),
+ pytest.param("tutorial006_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest) -> TestClient:
+ mod = importlib.import_module(
+ f"docs_src.path_params_numeric_validations.{request.param}"
+ )
+ return TestClient(mod.app)
+
+
+@pytest.mark.parametrize(
+ "path,expected_response",
+ [
+ (
+ "/items/0?q=&size=0.1",
+ {"item_id": 0, "size": 0.1},
+ ),
+ (
+ "/items/1000?q=somequery&size=10.4",
+ {"item_id": 1000, "q": "somequery", "size": 10.4},
+ ),
+ ],
+)
+def test_read_items(client: TestClient, path, expected_response):
+ response = client.get(path)
+ assert response.status_code == 200, response.text
+ assert response.json() == expected_response
+
+
+def test_read_items_item_id_less_than_zero(client: TestClient):
+ response = client.get("/items/-1?q=somequery&size=5")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "-1",
+ "msg": "Input should be greater than or equal to 0",
+ "type": "greater_than_equal",
+ "ctx": {"ge": 0},
+ }
+ ]
+ }
+
+
+def test_read_items_item_id_greater_than_one_thousand(client: TestClient):
+ response = client.get("/items/1001?q=somequery&size=5")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["path", "item_id"],
+ "input": "1001",
+ "msg": "Input should be less than or equal to 1000",
+ "type": "less_than_equal",
+ "ctx": {"le": 1000},
+ }
+ ]
+ }
+
+
+def test_read_items_size_too_small(client: TestClient):
+ response = client.get("/items/1?q=somequery&size=0.0")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["query", "size"],
+ "input": "0.0",
+ "msg": "Input should be greater than 0",
+ "type": "greater_than",
+ "ctx": {"gt": 0.0},
+ }
+ ]
+ }
+
+
+def test_read_items_size_too_large(client: TestClient):
+ response = client.get("/items/1?q=somequery&size=10.5")
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["query", "size"],
+ "input": "10.5",
+ "msg": "Input should be less than 10.5",
+ "type": "less_than",
+ "ctx": {"lt": 10.5},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Items",
+ "operationId": "read_items_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "title": "The ID of the item to get",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 1000,
+ },
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": True,
+ "schema": {
+ "type": "string",
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ },
+ {
+ "in": "query",
+ "name": "size",
+ "required": True,
+ "schema": {
+ "exclusiveMaximum": 10.5,
+ "exclusiveMinimum": 0,
+ "title": "Size",
+ "type": "number",
+ },
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {},
+ }
+ },
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {
+ "$ref": "#/components/schemas/ValidationError",
+ },
+ "title": "Detail",
+ "type": "array",
+ },
+ },
+ "title": "HTTPValidationError",
+ "type": "object",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "integer",
+ },
+ ],
+ },
+ "title": "Location",
+ "type": "array",
+ },
+ "msg": {
+ "title": "Message",
+ "type": "string",
+ },
+ "type": {
+ "title": "Error Type",
+ "type": "string",
+ },
+ },
+ "required": [
+ "loc",
+ "msg",
+ "type",
+ ],
+ "title": "ValidationError",
+ "type": "object",
+ },
+ },
+ },
+ }
--- /dev/null
+import runpy
+from unittest.mock import patch
+
+import pytest
+
+
+@pytest.mark.parametrize(
+ "module_name",
+ [
+ "tutorial001_py39",
+ "tutorial002_py39",
+ ],
+)
+def test_run_module(module_name: str):
+ with patch("builtins.print") as mock_print:
+ runpy.run_module(f"docs_src.python_types.{module_name}", run_name="__main__")
+
+ mock_print.assert_called_with("John Doe")
--- /dev/null
+import pytest
+
+from docs_src.python_types.tutorial003_py39 import get_name_with_age
+
+
+def test_get_name_with_age_pass_int():
+ with pytest.raises(TypeError):
+ get_name_with_age("John", 30)
+
+
+def test_get_name_with_age_pass_str():
+ assert get_name_with_age("John", "30") == "John is this old: 30"
--- /dev/null
+from docs_src.python_types.tutorial004_py39 import get_name_with_age
+
+
+def test_get_name_with_age_pass_int():
+ assert get_name_with_age("John", 30) == "John is this old: 30"
--- /dev/null
+from docs_src.python_types.tutorial005_py39 import get_items
+
+
+def test_get_items():
+ res = get_items(
+ "item_a",
+ "item_b",
+ "item_c",
+ "item_d",
+ "item_e",
+ )
+ assert res == ("item_a", "item_b", "item_c", "item_d", "item_e")
--- /dev/null
+from unittest.mock import patch
+
+from docs_src.python_types.tutorial006_py39 import process_items
+
+
+def test_process_items():
+ with patch("builtins.print") as mock_print:
+ process_items(["item_a", "item_b", "item_c"])
+
+ assert mock_print.call_count == 3
+ call_args = [arg.args for arg in mock_print.call_args_list]
+ assert call_args == [
+ ("item_a",),
+ ("item_b",),
+ ("item_c",),
+ ]
--- /dev/null
+from docs_src.python_types.tutorial007_py39 import process_items
+
+
+def test_process_items():
+ items_t = (1, 2, "foo")
+ items_s = {b"a", b"b", b"c"}
+
+ assert process_items(items_t, items_s) == (items_t, items_s)
--- /dev/null
+from unittest.mock import patch
+
+from docs_src.python_types.tutorial008_py39 import process_items
+
+
+def test_process_items():
+ with patch("builtins.print") as mock_print:
+ process_items({"a": 1.0, "b": 2.5})
+
+ assert mock_print.call_count == 4
+ call_args = [arg.args for arg in mock_print.call_args_list]
+ assert call_args == [
+ ("a",),
+ (1.0,),
+ ("b",),
+ (2.5,),
+ ]
--- /dev/null
+import importlib
+from types import ModuleType
+from unittest.mock import patch
+
+import pytest
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="module",
+ params=[
+ pytest.param("tutorial008b_py39"),
+ pytest.param("tutorial008b_py310", marks=needs_py310),
+ ],
+)
+def get_module(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.python_types.{request.param}")
+ return mod
+
+
+def test_process_items(module: ModuleType):
+ with patch("builtins.print") as mock_print:
+ module.process_item("a")
+
+ assert mock_print.call_count == 1
+ mock_print.assert_called_with("a")
--- /dev/null
+import importlib
+from types import ModuleType
+from unittest.mock import patch
+
+import pytest
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="module",
+ params=[
+ pytest.param("tutorial009_py39"),
+ pytest.param("tutorial009_py310", marks=needs_py310),
+ pytest.param("tutorial009b_py39"),
+ ],
+)
+def get_module(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.python_types.{request.param}")
+ return mod
+
+
+def test_say_hi(module: ModuleType):
+ with patch("builtins.print") as mock_print:
+ module.say_hi("FastAPI")
+ module.say_hi()
+
+ assert mock_print.call_count == 2
+ call_args = [arg.args for arg in mock_print.call_args_list]
+ assert call_args == [
+ ("Hey FastAPI!",),
+ ("Hello World",),
+ ]
--- /dev/null
+import importlib
+import re
+from types import ModuleType
+from unittest.mock import patch
+
+import pytest
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="module",
+ params=[
+ pytest.param("tutorial009c_py39"),
+ pytest.param("tutorial009c_py310", marks=needs_py310),
+ ],
+)
+def get_module(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.python_types.{request.param}")
+ return mod
+
+
+def test_say_hi(module: ModuleType):
+ with patch("builtins.print") as mock_print:
+ module.say_hi("FastAPI")
+
+ mock_print.assert_called_once_with("Hey FastAPI!")
+
+ with pytest.raises(
+ TypeError,
+ match=re.escape("say_hi() missing 1 required positional argument: 'name'"),
+ ):
+ module.say_hi()
--- /dev/null
+from docs_src.python_types.tutorial010_py39 import Person, get_person_name
+
+
+def test_get_person_name():
+ assert get_person_name(Person("John Doe")) == "John Doe"
--- /dev/null
+import runpy
+from unittest.mock import patch
+
+import pytest
+
+from ...utils import needs_py310
+
+
+@pytest.mark.parametrize(
+ "module_name",
+ [
+ pytest.param("tutorial011_py39"),
+ pytest.param("tutorial011_py310", marks=needs_py310),
+ ],
+)
+def test_run_module(module_name: str):
+ with patch("builtins.print") as mock_print:
+ runpy.run_module(f"docs_src.python_types.{module_name}", run_name="__main__")
+
+ assert mock_print.call_count == 2
+ call_args = [str(arg.args[0]) for arg in mock_print.call_args_list]
+ assert call_args == [
+ "id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]",
+ "123",
+ ]
--- /dev/null
+from docs_src.python_types.tutorial012_py39 import User
+
+
+def test_user():
+ user = User(name="John Doe", age=30)
+ assert user.name == "John Doe"
+ assert user.age == 30
--- /dev/null
+from docs_src.python_types.tutorial013_py39 import say_hello
+
+
+def test_say_hello():
+ assert say_hello("FastAPI") == "Hello FastAPI"
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize(
+ ("path", "expected_json"),
+ [
+ (
+ "/items/",
+ [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}],
+ ),
+ (
+ "/items/?skip=1",
+ [{"item_name": "Bar"}, {"item_name": "Baz"}],
+ ),
+ (
+ "/items/?skip=1&limit=1",
+ [{"item_name": "Bar"}],
+ ),
+ ],
+)
+def test_read_user_item(client: TestClient, path, expected_json):
+ response = client.get(path)
+ assert response.status_code == 200
+ assert response.json() == expected_json
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "summary": "Read Item",
+ "operationId": "read_item_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "title": "Skip",
+ "type": "integer",
+ "default": 0,
+ },
+ "name": "skip",
+ "in": "query",
+ },
+ {
+ "required": False,
+ "schema": {
+ "title": "Limit",
+ "type": "integer",
+ "default": 10,
+ },
+ "name": "limit",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize(
+ ("path", "expected_json"),
+ [
+ (
+ "/items/foo",
+ {"item_id": "foo"},
+ ),
+ (
+ "/items/bar?q=somequery",
+ {"item_id": "bar", "q": "somequery"},
+ ),
+ ],
+)
+def test_read_user_item(client: TestClient, path, expected_json):
+ response = client.get(path)
+ assert response.status_code == 200
+ assert response.json() == expected_json
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Item",
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": False,
+ "schema": {
+ "title": "Q",
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ },
+ "name": "q",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize(
+ ("path", "expected_json"),
+ [
+ (
+ "/items/foo",
+ {
+ "item_id": "foo",
+ "description": "This is an amazing item that has a long description",
+ },
+ ),
+ (
+ "/items/bar?q=somequery",
+ {
+ "item_id": "bar",
+ "q": "somequery",
+ "description": "This is an amazing item that has a long description",
+ },
+ ),
+ (
+ "/items/baz?short=true",
+ {"item_id": "baz"},
+ ),
+ ],
+)
+def test_read_user_item(client: TestClient, path, expected_json):
+ response = client.get(path)
+ assert response.status_code == 200
+ assert response.json() == expected_json
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "get": {
+ "summary": "Read Item",
+ "operationId": "read_item_items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": False,
+ "schema": {
+ "title": "Q",
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ },
+ "name": "q",
+ "in": "query",
+ },
+ {
+ "required": False,
+ "schema": {
+ "title": "Short",
+ "type": "boolean",
+ "default": False,
+ },
+ "name": "short",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.query_params.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.parametrize(
+ ("path", "expected_json"),
+ [
+ (
+ "/users/123/items/foo",
+ {
+ "item_id": "foo",
+ "owner_id": 123,
+ "description": "This is an amazing item that has a long description",
+ },
+ ),
+ (
+ "/users/1/items/bar?q=somequery",
+ {
+ "item_id": "bar",
+ "owner_id": 1,
+ "q": "somequery",
+ "description": "This is an amazing item that has a long description",
+ },
+ ),
+ (
+ "/users/42/items/baz?short=true",
+ {"item_id": "baz", "owner_id": 42},
+ ),
+ ],
+)
+def test_read_user_item(client: TestClient, path, expected_json):
+ response = client.get(path)
+ assert response.status_code == 200
+ assert response.json() == expected_json
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/users/{user_id}/items/{item_id}": {
+ "get": {
+ "summary": "Read User Item",
+ "operationId": "read_user_item_users__user_id__items__item_id__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {"title": "User Id", "type": "integer"},
+ "name": "user_id",
+ "in": "path",
+ },
+ {
+ "required": True,
+ "schema": {"title": "Item Id", "type": "string"},
+ "name": "item_id",
+ "in": "path",
+ },
+ {
+ "required": False,
+ "schema": {
+ "title": "Q",
+ "anyOf": [
+ {
+ "type": "string",
+ },
+ {
+ "type": "null",
+ },
+ ],
+ },
+ "name": "q",
+ "in": "query",
+ },
+ {
+ "required": False,
+ "schema": {
+ "title": "Short",
+ "type": "boolean",
+ "default": False,
+ },
+ "name": "short",
+ "in": "query",
+ },
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError",
+ },
+ },
+ },
+ "description": "Validation Error",
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_empty_str(client: TestClient):
+ response = client.get("/items/", params={"q": ""})
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_query(client: TestClient):
+ response = client.get("/items/", params={"q": "query"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "query",
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {"type": "string"},
+ {"type": "null"},
+ ],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ pytest.param("tutorial002_an_py39"),
+ pytest.param("tutorial002_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_empty_str(client: TestClient):
+ response = client.get("/items/", params={"q": ""})
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_query(client: TestClient):
+ response = client.get("/items/", params={"q": "query"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "query",
+ }
+
+
+def test_query_params_str_validations_q_too_long(client: TestClient):
+ response = client.get("/items/", params={"q": "q" * 51})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_long",
+ "loc": ["query", "q"],
+ "msg": "String should have at most 50 characters",
+ "input": "q" * 51,
+ "ctx": {"max_length": 50},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string",
+ "maxLength": 50,
+ },
+ {"type": "null"},
+ ],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ pytest.param("tutorial003_an_py39"),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_query(client: TestClient):
+ response = client.get("/items/", params={"q": "query"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "query",
+ }
+
+
+def test_query_params_str_validations_q_too_short(client: TestClient):
+ response = client.get("/items/", params={"q": "qu"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_short",
+ "loc": ["query", "q"],
+ "msg": "String should have at least 3 characters",
+ "input": "qu",
+ "ctx": {"min_length": 3},
+ }
+ ]
+ }
+
+
+def test_query_params_str_validations_q_too_long(client: TestClient):
+ response = client.get("/items/", params={"q": "q" * 51})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_long",
+ "loc": ["query", "q"],
+ "msg": "String should have at most 50 characters",
+ "input": "q" * 51,
+ "ctx": {"max_length": 50},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 3,
+ "maxLength": 50,
+ },
+ {"type": "null"},
+ ],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ pytest.param("tutorial004_an_py39"),
+ pytest.param("tutorial004_an_py310", marks=needs_py310),
+ pytest.param(
+ "tutorial004_regex_an_py310",
+ marks=(
+ needs_py310,
+ pytest.mark.filterwarnings(
+ "ignore:`regex` has been deprecated, please use `pattern` instead:DeprecationWarning"
+ ),
+ ),
+ ),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_fixedquery(client: TestClient):
+ response = client.get("/items/", params={"q": "fixedquery"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "fixedquery",
+ }
+
+
+def test_query_params_str_validations_q_nonregexquery(client: TestClient):
+ response = client.get("/items/", params={"q": "nonregexquery"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_pattern_mismatch",
+ "loc": ["query", "q"],
+ "msg": "String should match pattern '^fixedquery$'",
+ "input": "nonregexquery",
+ "ctx": {"pattern": "^fixedquery$"},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 3,
+ "maxLength": 50,
+ "pattern": "^fixedquery$",
+ },
+ {"type": "null"},
+ ],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial005_py39"),
+ pytest.param("tutorial005_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "fixedquery",
+ }
+
+
+def test_query_params_str_validations_q_query(client: TestClient):
+ response = client.get("/items/", params={"q": "query"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "query",
+ }
+
+
+def test_query_params_str_validations_q_short(client: TestClient):
+ response = client.get("/items/", params={"q": "fa"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_short",
+ "loc": ["query", "q"],
+ "msg": "String should have at least 3 characters",
+ "input": "fa",
+ "ctx": {"min_length": 3},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "type": "string",
+ "default": "fixedquery",
+ "minLength": 3,
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial006_py39"),
+ pytest.param("tutorial006_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "missing",
+ "loc": ["query", "q"],
+ "msg": "Field required",
+ "input": None,
+ }
+ ]
+ }
+
+
+def test_query_params_str_validations_q_fixedquery(client: TestClient):
+ response = client.get("/items/", params={"q": "fixedquery"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "fixedquery",
+ }
+
+
+def test_query_params_str_validations_q_fixedquery_too_short(client: TestClient):
+ response = client.get("/items/", params={"q": "fa"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_short",
+ "loc": ["query", "q"],
+ "msg": "String should have at least 3 characters",
+ "input": "fa",
+ "ctx": {"min_length": 3},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "type": "string",
+ "minLength": 3,
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial006c_py39"),
+ pytest.param("tutorial006c_py310", marks=needs_py310),
+ pytest.param("tutorial006c_an_py39"),
+ pytest.param("tutorial006c_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+ client = TestClient(mod.app)
+ return client
+
+
+@pytest.mark.xfail(
+ reason="Code example is not valid. See https://github.com/fastapi/fastapi/issues/12419"
+)
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == { # pragma: no cover
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ }
+
+
+@pytest.mark.xfail(
+ reason="Code example is not valid. See https://github.com/fastapi/fastapi/issues/12419"
+)
+def test_query_params_str_validations_empty_str(client: TestClient):
+ response = client.get("/items/?q=")
+ assert response.status_code == 200
+ assert response.json() == { # pragma: no cover
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ }
+
+
+def test_query_params_str_validations_q_query(client: TestClient):
+ response = client.get("/items/", params={"q": "query"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "query",
+ }
+
+
+def test_query_params_str_validations_q_short(client: TestClient):
+ response = client.get("/items/", params={"q": "fa"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_short",
+ "loc": ["query", "q"],
+ "msg": "String should have at least 3 characters",
+ "input": "fa",
+ "ctx": {"min_length": 3},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": True,
+ "schema": {
+ "anyOf": [
+ {"type": "string", "minLength": 3},
+ {"type": "null"},
+ ],
+ "title": "Q",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial007_py39"),
+ pytest.param("tutorial007_py310", marks=needs_py310),
+ pytest.param("tutorial007_an_py39"),
+ pytest.param("tutorial007_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_fixedquery(client: TestClient):
+ response = client.get("/items/", params={"q": "fixedquery"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "fixedquery",
+ }
+
+
+def test_query_params_str_validations_q_fixedquery_too_short(client: TestClient):
+ response = client.get("/items/", params={"q": "fa"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_short",
+ "loc": ["query", "q"],
+ "msg": "String should have at least 3 characters",
+ "input": "fa",
+ "ctx": {"min_length": 3},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 3,
+ },
+ {"type": "null"},
+ ],
+ "title": "Query string",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial008_py39"),
+ pytest.param("tutorial008_py310", marks=needs_py310),
+ pytest.param("tutorial008_an_py39"),
+ pytest.param("tutorial008_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_q_fixedquery(client: TestClient):
+ response = client.get("/items/", params={"q": "fixedquery"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "fixedquery",
+ }
+
+
+def test_query_params_str_validations_q_fixedquery_too_short(client: TestClient):
+ response = client.get("/items/", params={"q": "fa"})
+ assert response.status_code == 422
+ assert response.json() == {
+ "detail": [
+ {
+ "type": "string_too_short",
+ "loc": ["query", "q"],
+ "msg": "String should have at least 3 characters",
+ "input": "fa",
+ "ctx": {"min_length": 3},
+ }
+ ]
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "description": "Query string for the items to search in the database that have a good match",
+ "required": False,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 3,
+ },
+ {"type": "null"},
+ ],
+ "title": "Query string",
+ "description": "Query string for the items to search in the database that have a good match",
+ },
+ "name": "q",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial009_py39"),
+ pytest.param("tutorial009_py310", marks=needs_py310),
+ pytest.param("tutorial009_an_py39"),
+ pytest.param("tutorial009_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(
+ f"docs_src.query_params_str_validations.{request.param}"
+ )
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_query_params_str_validations_no_query(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_query_params_str_validations_item_query_fixedquery(client: TestClient):
+ response = client.get("/items/", params={"item-query": "fixedquery"})
+ assert response.status_code == 200
+ assert response.json() == {
+ "items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
+ "q": "fixedquery",
+ }
+
+
+def test_query_params_str_validations_q_fixedquery(client: TestClient):
+ response = client.get("/items/", params={"q": "fixedquery"})
+ assert response.status_code == 200
+ assert response.json() == {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ "parameters": [
+ {
+ "schema": {
+ "anyOf": [
+ {"type": "string"},
+ {"type": "null"},
+ ],
+ "title": "Item-Query",
+ },
+ "required": False,
+ "name": "item-query",
+ "in": "query",
+ }
+ ],
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.response_directly.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_path_operation(client: TestClient):
+ expected_content = """<?xml version="1.0"?>
+ <shampoo>
+ <Header>
+ Apply shampoo here.
+ </Header>
+ <Body>
+ You'll have to use soap here.
+ </Body>
+ </shampoo>
+ """
+
+ response = client.get("/legacy/")
+ assert response.status_code == 200, response.text
+ assert response.headers["content-type"] == "application/xml"
+ assert response.text == expected_content
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "info": {
+ "title": "FastAPI",
+ "version": "0.1.0",
+ },
+ "openapi": "3.1.0",
+ "paths": {
+ "/legacy/": {
+ "get": {
+ "operationId": "get_legacy_data_legacy__get",
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {},
+ },
+ },
+ "description": "Successful Response",
+ },
+ },
+ "summary": "Get Legacy Data",
+ },
+ },
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_py310", marks=needs_py310),
+ pytest.param("tutorial001_01_py39"),
+ pytest.param("tutorial001_01_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.response_model.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_read_items(client: TestClient):
+ response = client.get("/items/")
+ assert response.status_code == 200, response.text
+ assert response.json() == [
+ {
+ "name": "Portal Gun",
+ "description": None,
+ "price": 42.0,
+ "tags": [],
+ "tax": None,
+ },
+ {
+ "name": "Plumbus",
+ "description": None,
+ "price": 32.0,
+ "tags": [],
+ "tax": None,
+ },
+ ]
+
+
+def test_create_item(client: TestClient):
+ item_data = {
+ "name": "Test Item",
+ "description": "A test item",
+ "price": 10.5,
+ "tax": 1.5,
+ "tags": ["test", "item"],
+ }
+ response = client.post("/items/", json=item_data)
+ assert response.status_code == 200, response.text
+ assert response.json() == item_data
+
+
+def test_create_item_only_required(client: TestClient):
+ response = client.post(
+ "/items/",
+ json={
+ "name": "Test Item",
+ "price": 10.5,
+ },
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "name": "Test Item",
+ "price": 10.5,
+ "description": None,
+ "tax": None,
+ "tags": [],
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/Item"},
+ "title": "Response Read Items Items Get",
+ }
+ }
+ },
+ },
+ },
+ "summary": "Read Items",
+ "operationId": "read_items_items__get",
+ },
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ },
+ },
+ },
+ "required": True,
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"},
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ },
+ }
+ },
+ "components": {
+ "schemas": {
+ "Item": {
+ "title": "Item",
+ "required": ["name", "price"],
+ "type": "object",
+ "properties": {
+ "name": {"title": "Name", "type": "string"},
+ "price": {"title": "Price", "type": "number"},
+ "description": {
+ "title": "Description",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "tax": {
+ "title": "Tax",
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ },
+ "tags": {
+ "title": "Tags",
+ "type": "array",
+ "items": {"type": "string"},
+ "default": [],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.response_model.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_user(client: TestClient):
+ user_data = {
+ "username": "foo",
+ "password": "fighter",
+ "email": "foo@example.com",
+ "full_name": "Grave Dohl",
+ }
+ response = client.post(
+ "/user/",
+ json=user_data,
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == user_data
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/user/": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/UserIn"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Create User",
+ "operationId": "create_user_user__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/UserIn"}
+ }
+ },
+ "required": True,
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "UserIn": {
+ "title": "UserIn",
+ "required": ["username", "password", "email"],
+ "type": "object",
+ "properties": {
+ "username": {"title": "Username", "type": "string"},
+ "password": {"title": "Password", "type": "string"},
+ "email": {
+ "title": "Email",
+ "type": "string",
+ "format": "email",
+ },
+ "full_name": {
+ "title": "Full Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial002_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.response_status_code.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_create_item(client: TestClient):
+ response = client.post("/items/", params={"name": "Test Item"})
+ assert response.status_code == 201, response.text
+ assert response.json() == {"name": "Test Item"}
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/": {
+ "post": {
+ "parameters": [
+ {
+ "name": "name",
+ "in": "query",
+ "required": True,
+ "schema": {"title": "Name", "type": "string"},
+ }
+ ],
+ "summary": "Create Item",
+ "operationId": "create_item_items__post",
+ "responses": {
+ "201": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.schema_extra_example.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_body_example(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ },
+ )
+ assert response.status_code == 200
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ # insert_assert(response.json())
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "name": "item_id",
+ "in": "path",
+ "required": True,
+ "schema": {"type": "integer", "title": "Item Id"},
+ }
+ ],
+ "requestBody": {
+ "required": True,
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Item"}
+ }
+ },
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "examples": ["Foo"],
+ },
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ "examples": ["A very nice Item"],
+ },
+ "price": {
+ "type": "number",
+ "title": "Price",
+ "examples": [35.4],
+ },
+ "tax": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Tax",
+ "examples": [3.2],
+ },
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial003_py39"),
+ pytest.param("tutorial003_py310", marks=needs_py310),
+ pytest.param("tutorial003_an_py39"),
+ pytest.param("tutorial003_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.schema_extra_example.{request.param}")
+
+ client = TestClient(mod.app)
+ return client
+
+
+def test_post_body_example(client: TestClient):
+ response = client.put(
+ "/items/5",
+ json={
+ "name": "Foo",
+ "description": "A very nice Item",
+ "price": 35.4,
+ "tax": 3.2,
+ },
+ )
+ assert response.status_code == 200
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ # insert_assert(response.json())
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/items/{item_id}": {
+ "put": {
+ "summary": "Update Item",
+ "operationId": "update_item_items__item_id__put",
+ "parameters": [
+ {
+ "name": "item_id",
+ "in": "path",
+ "required": True,
+ "schema": {"type": "integer", "title": "Item Id"},
+ }
+ ],
+ "requestBody": {
+ "required": True,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Item",
+ "examples": [
+ {
+ "description": "A very nice Item",
+ "name": "Foo",
+ "price": 35.4,
+ "tax": 3.2,
+ }
+ ],
+ },
+ }
+ },
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "HTTPValidationError": {
+ "properties": {
+ "detail": {
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ "type": "array",
+ "title": "Detail",
+ }
+ },
+ "type": "object",
+ "title": "HTTPValidationError",
+ },
+ "Item": {
+ "properties": {
+ "name": {"type": "string", "title": "Name"},
+ "description": {
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "title": "Description",
+ },
+ "price": {"type": "number", "title": "Price"},
+ "tax": {
+ "anyOf": [{"type": "number"}, {"type": "null"}],
+ "title": "Tax",
+ },
+ },
+ "type": "object",
+ "required": ["name", "price"],
+ "title": "Item",
+ },
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ "type": "array",
+ "title": "Location",
+ },
+ "msg": {"type": "string", "title": "Message"},
+ "type": {"type": "string", "title": "Error Type"},
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError",
+ },
+ }
+ },
+ }
--- /dev/null
+import importlib
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial002_py39"),
+ pytest.param("tutorial002_py310", marks=needs_py310),
+ pytest.param("tutorial002_an_py39"),
+ pytest.param("tutorial002_an_py310", marks=needs_py310),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.security.{request.param}")
+ client = TestClient(mod.app)
+ return client
+
+
+def test_no_token(client: TestClient):
+ response = client.get("/users/me")
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Not authenticated"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_token(client: TestClient):
+ response = client.get("/users/me", headers={"Authorization": "Bearer testtoken"})
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "username": "testtokenfakedecoded",
+ "email": "john@example.com",
+ "full_name": "John Doe",
+ "disabled": None,
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/users/me": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ "summary": "Read Users Me",
+ "operationId": "read_users_me_users_me_get",
+ "security": [{"OAuth2PasswordBearer": []}],
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {
+ "OAuth2PasswordBearer": {
+ "type": "oauth2",
+ "flows": {"password": {"scopes": {}, "tokenUrl": "token"}},
+ }
+ },
+ },
+ }
--- /dev/null
+import importlib
+from types import ModuleType
+from unittest.mock import patch
+
+import pytest
+from fastapi.testclient import TestClient
+
+from ...utils import needs_py310
+
+
+@pytest.fixture(
+ name="mod",
+ params=[
+ pytest.param("tutorial004_py39"),
+ pytest.param("tutorial004_py310", marks=needs_py310),
+ pytest.param("tutorial004_an_py39"),
+ pytest.param("tutorial004_an_py310", marks=needs_py310),
+ ],
+)
+def get_mod(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.security.{request.param}")
+
+ return mod
+
+
+def get_access_token(*, username="johndoe", password="secret", client: TestClient):
+ data = {"username": username, "password": password}
+ response = client.post("/token", data=data)
+ content = response.json()
+ access_token = content.get("access_token")
+ return access_token
+
+
+def test_login(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.post("/token", data={"username": "johndoe", "password": "secret"})
+ assert response.status_code == 200, response.text
+ content = response.json()
+ assert "access_token" in content
+ assert content["token_type"] == "bearer"
+
+
+def test_login_incorrect_password(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.post(
+ "/token", data={"username": "johndoe", "password": "incorrect"}
+ )
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Incorrect username or password"}
+
+
+def test_login_incorrect_username(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.post("/token", data={"username": "foo", "password": "secret"})
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Incorrect username or password"}
+
+
+def test_no_token(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.get("/users/me")
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Not authenticated"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_token(mod: ModuleType):
+ client = TestClient(mod.app)
+ access_token = get_access_token(client=client)
+ response = client.get(
+ "/users/me", headers={"Authorization": f"Bearer {access_token}"}
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "username": "johndoe",
+ "full_name": "John Doe",
+ "email": "johndoe@example.com",
+ "disabled": False,
+ }
+
+
+def test_incorrect_token(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.get("/users/me", headers={"Authorization": "Bearer nonexistent"})
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Could not validate credentials"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_incorrect_token_type(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.get(
+ "/users/me", headers={"Authorization": "Notexistent testtoken"}
+ )
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Not authenticated"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_verify_password(mod: ModuleType):
+ assert mod.verify_password(
+ "secret", mod.fake_users_db["johndoe"]["hashed_password"]
+ )
+
+
+def test_get_password_hash(mod: ModuleType):
+ assert mod.get_password_hash("johndoe")
+
+
+def test_create_access_token(mod: ModuleType):
+ access_token = mod.create_access_token(data={"data": "foo"})
+ assert access_token
+
+
+def test_token_no_sub(mod: ModuleType):
+ client = TestClient(mod.app)
+
+ response = client.get(
+ "/users/me",
+ headers={
+ "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiZm9vIn0.9ynBhuYb4e6aW3oJr_K_TBgwcMTDpRToQIE25L57rOE"
+ },
+ )
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Could not validate credentials"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_token_no_username(mod: ModuleType):
+ client = TestClient(mod.app)
+
+ response = client.get(
+ "/users/me",
+ headers={
+ "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28ifQ.NnExK_dlNAYyzACrXtXDrcWOgGY2JuPbI4eDaHdfK5Y"
+ },
+ )
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Could not validate credentials"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_token_nonexistent_user(mod: ModuleType):
+ client = TestClient(mod.app)
+
+ response = client.get(
+ "/users/me",
+ headers={
+ "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZTpib2IifQ.HcfCW67Uda-0gz54ZWTqmtgJnZeNem0Q757eTa9EZuw"
+ },
+ )
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Could not validate credentials"}
+ assert response.headers["WWW-Authenticate"] == "Bearer"
+
+
+def test_token_inactive_user(mod: ModuleType):
+ client = TestClient(mod.app)
+ alice_user_data = {
+ "username": "alice",
+ "full_name": "Alice Wonderson",
+ "email": "alice@example.com",
+ "hashed_password": mod.get_password_hash("secretalice"),
+ "disabled": True,
+ }
+ with patch.dict(f"{mod.__name__}.fake_users_db", {"alice": alice_user_data}):
+ access_token = get_access_token(
+ username="alice", password="secretalice", client=client
+ )
+ response = client.get(
+ "/users/me", headers={"Authorization": f"Bearer {access_token}"}
+ )
+ assert response.status_code == 400, response.text
+ assert response.json() == {"detail": "Inactive user"}
+
+
+def test_read_items(mod: ModuleType):
+ client = TestClient(mod.app)
+ access_token = get_access_token(client=client)
+ response = client.get(
+ "/users/me/items/", headers={"Authorization": f"Bearer {access_token}"}
+ )
+ assert response.status_code == 200, response.text
+ assert response.json() == [{"item_id": "Foo", "owner": "johndoe"}]
+
+
+def test_openapi_schema(mod: ModuleType):
+ client = TestClient(mod.app)
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/token": {
+ "post": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/Token"}
+ }
+ },
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ },
+ },
+ },
+ "summary": "Login For Access Token",
+ "operationId": "login_for_access_token_token_post",
+ "requestBody": {
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_login_for_access_token_token_post"
+ }
+ }
+ },
+ "required": True,
+ },
+ }
+ },
+ "/users/me/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": "#/components/schemas/User"}
+ }
+ },
+ }
+ },
+ "summary": "Read Users Me",
+ "operationId": "read_users_me_users_me__get",
+ "security": [{"OAuth2PasswordBearer": []}],
+ }
+ },
+ "/users/me/items/": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ "summary": "Read Own Items",
+ "operationId": "read_own_items_users_me_items__get",
+ "security": [{"OAuth2PasswordBearer": []}],
+ }
+ },
+ },
+ "components": {
+ "schemas": {
+ "User": {
+ "title": "User",
+ "required": ["username"],
+ "type": "object",
+ "properties": {
+ "username": {"title": "Username", "type": "string"},
+ "email": {
+ "title": "Email",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "full_name": {
+ "title": "Full Name",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "disabled": {
+ "title": "Disabled",
+ "anyOf": [{"type": "boolean"}, {"type": "null"}],
+ },
+ },
+ },
+ "Token": {
+ "title": "Token",
+ "required": ["access_token", "token_type"],
+ "type": "object",
+ "properties": {
+ "access_token": {"title": "Access Token", "type": "string"},
+ "token_type": {"title": "Token Type", "type": "string"},
+ },
+ },
+ "Body_login_for_access_token_token_post": {
+ "title": "Body_login_for_access_token_token_post",
+ "required": ["username", "password"],
+ "type": "object",
+ "properties": {
+ "grant_type": {
+ "title": "Grant Type",
+ "anyOf": [
+ {"pattern": "^password$", "type": "string"},
+ {"type": "null"},
+ ],
+ },
+ "username": {"title": "Username", "type": "string"},
+ "password": {
+ "title": "Password",
+ "type": "string",
+ "format": "password",
+ },
+ "scope": {"title": "Scope", "type": "string", "default": ""},
+ "client_id": {
+ "title": "Client Id",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ },
+ "client_secret": {
+ "title": "Client Secret",
+ "anyOf": [{"type": "string"}, {"type": "null"}],
+ "format": "password",
+ },
+ },
+ },
+ "ValidationError": {
+ "title": "ValidationError",
+ "required": ["loc", "msg", "type"],
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {
+ "anyOf": [{"type": "string"}, {"type": "integer"}]
+ },
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ },
+ "HTTPValidationError": {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": "#/components/schemas/ValidationError"},
+ }
+ },
+ },
+ },
+ "securitySchemes": {
+ "OAuth2PasswordBearer": {
+ "type": "oauth2",
+ "flows": {
+ "password": {
+ "scopes": {},
+ "tokenUrl": "token",
+ }
+ },
+ }
+ },
+ },
+ }
--- /dev/null
+import importlib
+from base64 import b64encode
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(
+ name="client",
+ params=[
+ pytest.param("tutorial007_py39"),
+ pytest.param("tutorial007_an_py39"),
+ ],
+)
+def get_client(request: pytest.FixtureRequest):
+ mod = importlib.import_module(f"docs_src.security.{request.param}")
+ return TestClient(mod.app)
+
+
+def test_security_http_basic(client: TestClient):
+ response = client.get("/users/me", auth=("stanleyjobson", "swordfish"))
+ assert response.status_code == 200, response.text
+ assert response.json() == {"username": "stanleyjobson"}
+
+
+def test_security_http_basic_no_credentials(client: TestClient):
+ response = client.get("/users/me")
+ assert response.json() == {"detail": "Not authenticated"}
+ assert response.status_code == 401, response.text
+ assert response.headers["WWW-Authenticate"] == "Basic"
+
+
+def test_security_http_basic_invalid_credentials(client: TestClient):
+ response = client.get(
+ "/users/me", headers={"Authorization": "Basic notabase64token"}
+ )
+ assert response.status_code == 401, response.text
+ assert response.headers["WWW-Authenticate"] == "Basic"
+ assert response.json() == {"detail": "Not authenticated"}
+
+
+def test_security_http_basic_non_basic_credentials(client: TestClient):
+ payload = b64encode(b"johnsecret").decode("ascii")
+ auth_header = f"Basic {payload}"
+ response = client.get("/users/me", headers={"Authorization": auth_header})
+ assert response.status_code == 401, response.text
+ assert response.headers["WWW-Authenticate"] == "Basic"
+ assert response.json() == {"detail": "Not authenticated"}
+
+
+def test_security_http_basic_invalid_username(client: TestClient):
+ response = client.get("/users/me", auth=("alice", "swordfish"))
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Incorrect username or password"}
+ assert response.headers["WWW-Authenticate"] == "Basic"
+
+
+def test_security_http_basic_invalid_password(client: TestClient):
+ response = client.get("/users/me", auth=("stanleyjobson", "wrongpassword"))
+ assert response.status_code == 401, response.text
+ assert response.json() == {"detail": "Incorrect username or password"}
+ assert response.headers["WWW-Authenticate"] == "Basic"
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/users/me": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ "summary": "Read Current User",
+ "operationId": "read_current_user_users_me_get",
+ "security": [{"HTTPBasic": []}],
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {"HTTPBasic": {"type": "http", "scheme": "basic"}}
+ },
+ }
--- /dev/null
+import importlib
+import sys
+
+import pytest
+from dirty_equals import IsAnyStr
+from fastapi.testclient import TestClient
+from pydantic import ValidationError
+from pytest import MonkeyPatch
+
+
+@pytest.fixture(
+ name="mod_name",
+ params=[
+ pytest.param("app01_py39"),
+ ],
+)
+def get_mod_name(request: pytest.FixtureRequest):
+ return f"docs_src.settings.{request.param}.main"
+
+
+@pytest.fixture(name="client")
+def get_test_client(mod_name: str, monkeypatch: MonkeyPatch) -> TestClient:
+ if mod_name in sys.modules:
+ del sys.modules[mod_name]
+ monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com")
+ main_mod = importlib.import_module(mod_name)
+ return TestClient(main_mod.app)
+
+
+def test_settings_validation_error(mod_name: str, monkeypatch: MonkeyPatch):
+ monkeypatch.delenv("ADMIN_EMAIL", raising=False)
+ if mod_name in sys.modules:
+ del sys.modules[mod_name] # pragma: no cover
+
+ with pytest.raises(ValidationError) as exc_info:
+ importlib.import_module(mod_name)
+ assert exc_info.value.errors() == [
+ {
+ "loc": ("admin_email",),
+ "msg": "Field required",
+ "type": "missing",
+ "input": {},
+ "url": IsAnyStr,
+ }
+ ]
+
+
+def test_app(client: TestClient):
+ response = client.get("/info")
+ data = response.json()
+ assert data == {
+ "app_name": "Awesome API",
+ "admin_email": "admin@example.com",
+ "items_per_user": 50,
+ }
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {
+ "/info": {
+ "get": {
+ "operationId": "info_info_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ "summary": "Info",
+ }
+ }
+ },
+ }
--- /dev/null
+import os
+from pathlib import Path
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+@pytest.fixture(scope="module")
+def client():
+ static_dir: Path = Path(os.getcwd()) / "static"
+ static_dir.mkdir(exist_ok=True)
+ sample_file = static_dir / "sample.txt"
+ sample_file.write_text("This is a sample static file.")
+ from docs_src.static_files.tutorial001_py39 import app
+
+ with TestClient(app) as client:
+ yield client
+ sample_file.unlink()
+ static_dir.rmdir()
+
+
+def test_static_files(client: TestClient):
+ response = client.get("/static/sample.txt")
+ assert response.status_code == 200, response.text
+ assert response.text == "This is a sample static file."
+
+
+def test_static_files_not_found(client: TestClient):
+ response = client.get("/static/non_existent_file.txt")
+ assert response.status_code == 404, response.text
+
+
+def test_openapi_schema(client: TestClient):
+ response = client.get("/openapi.json")
+ assert response.status_code == 200, response.text
+ assert response.json() == {
+ "openapi": "3.1.0",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "paths": {},
+ }