From d5196ed727048d3682dc3a27a9093a25354890e5 Mon Sep 17 00:00:00 2001 From: kedod <35638715+kedod@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:58:25 +0200 Subject: [PATCH] test: Add tests for dto factory doc examples (#3453) * test: Add tests for dto factory doc examples --------- Co-authored-by: kedod --- .../factory/dto_data_problem_statement.py | 21 +++++++++--------- .../factory/dto_data_usage.py | 19 ++++++++-------- .../factory/leading_underscore_private.py | 6 ++--- .../leading_underscore_private_override.py | 6 ++--- .../overriding_implicit_return_dto.py | 22 ++++++++++++++++--- docs/usage/dto/1-abstract-dto.rst | 2 +- .../test_factory/__init__.py | 0 .../test_dto_data_problem_statement.py | 14 ++++++++++++ .../test_factory/test_dto_data_usage.py | 13 +++++++++++ .../test_leading_underscore_private.py | 12 ++++++++++ ...est_leading_underscore_private_override.py | 12 ++++++++++ .../test_factory/test_type_checking.py | 8 +++++++ .../test_overriding_implicit_return_dto.py | 12 ++++++++++ tests/examples/test_dto/test_example_apps.py | 19 ---------------- 14 files changed, 117 insertions(+), 49 deletions(-) create mode 100644 tests/examples/test_data_transfer_objects/test_factory/__init__.py create mode 100644 tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py create mode 100644 tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py create mode 100644 tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py create mode 100644 tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py create mode 100644 tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py create mode 100644 tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py diff --git a/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py b/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py index f9ac24769f..411caa3cb6 100644 --- a/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py +++ b/docs/examples/data_transfer_objects/factory/dto_data_problem_statement.py @@ -1,20 +1,21 @@ from __future__ import annotations -from dataclasses import dataclass -from uuid import UUID +from dataclasses import dataclass, field +from uuid import UUID, uuid4 from litestar import Litestar, post from litestar.dto import DataclassDTO, DTOConfig @dataclass -class Person: - id: UUID +class User: name: str + email: str age: int + id: UUID = field(default_factory=uuid4) -class WriteDTO(DataclassDTO[Person]): +class UserWriteDTO(DataclassDTO[User]): """Don't allow client to set the id.""" config = DTOConfig(exclude={"id"}) @@ -23,12 +24,12 @@ class WriteDTO(DataclassDTO[Person]): # We need a dto for the handler to parse the request data per the configuration, however, # we don't need a return DTO as we are returning a dataclass, and Litestar already knows # how to serialize dataclasses. -@post("/person", dto=WriteDTO, return_dto=None, sync_to_thread=False) -def create_person(data: Person) -> Person: - """Create a person.""" +@post("/users", dto=UserWriteDTO, return_dto=None, sync_to_thread=False) +def create_user(data: User) -> User: + """Create an user.""" return data -app = Litestar(route_handlers=[create_person]) +app = Litestar(route_handlers=[create_user]) -# run: /person -H "Content-Type: application/json" -d '{"name":"Peter","age":41}' +# run: /users -H "Content-Type: application/json" -d '{"name":"Peter","email": "peter@example.com", "age":41}' diff --git a/docs/examples/data_transfer_objects/factory/dto_data_usage.py b/docs/examples/data_transfer_objects/factory/dto_data_usage.py index 3ebfcd90db..6b4db20325 100644 --- a/docs/examples/data_transfer_objects/factory/dto_data_usage.py +++ b/docs/examples/data_transfer_objects/factory/dto_data_usage.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from dataclasses import dataclass from uuid import UUID, uuid4 @@ -8,24 +6,25 @@ @dataclass -class Person: - id: UUID +class User: name: str + email: str age: int + id: UUID -class WriteDTO(DataclassDTO[Person]): +class UserWriteDTO(DataclassDTO[User]): """Don't allow client to set the id.""" config = DTOConfig(exclude={"id"}) -@post("/person", dto=WriteDTO, return_dto=None, sync_to_thread=False) -def create_person(data: DTOData[Person]) -> Person: - """Create a person.""" +@post("/users", dto=UserWriteDTO, return_dto=None, sync_to_thread=False) +def create_user(data: DTOData[User]) -> User: + """Create an user.""" return data.create_instance(id=uuid4()) -app = Litestar(route_handlers=[create_person]) +app = Litestar(route_handlers=[create_user]) -# run: /person -H "Content-Type: application/json" -d '{"name":"Peter","age":41}' +# run: /users -H "Content-Type: application/json" -d '{"name":"Peter", "email": "peter@example.com", "age":41}' diff --git a/docs/examples/data_transfer_objects/factory/leading_underscore_private.py b/docs/examples/data_transfer_objects/factory/leading_underscore_private.py index ade7395fd6..70dfc6770f 100644 --- a/docs/examples/data_transfer_objects/factory/leading_underscore_private.py +++ b/docs/examples/data_transfer_objects/factory/leading_underscore_private.py @@ -6,8 +6,8 @@ @dataclass class Foo: - bar: str - _baz: str = "Mars" + this_will: str + _this_will: str = "Mars" @post("/", dto=DataclassDTO[Foo], sync_to_thread=False) @@ -17,4 +17,4 @@ def handler(data: Foo) -> Foo: app = Litestar(route_handlers=[handler]) -# run: / -H "Content-Type: application/json" -d '{"bar":"Hello","_baz":"World!"}' +# run: / -H "Content-Type: application/json" -d '{"bar":"stay","_baz":"go_away!"}' diff --git a/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py b/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py index 038d5b963d..f3705aaae3 100644 --- a/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py +++ b/docs/examples/data_transfer_objects/factory/leading_underscore_private_override.py @@ -6,8 +6,8 @@ @dataclass class Foo: - bar: str - _baz: str = "Mars" + this_will: str + _this_will: str = "not_go_away!" class DTO(DataclassDTO[Foo]): @@ -21,4 +21,4 @@ def handler(data: Foo) -> Foo: app = Litestar(route_handlers=[handler]) -# run: / -H "Content-Type: application/json" -d '{"bar":"Hello","_baz":"World!"}' +# run: / -H "Content-Type: application/json" -d '{"this_will":"stay","_this_will":"not_go_away!"}' diff --git a/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py b/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py index 9469626275..7c8f53fd40 100644 --- a/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py +++ b/docs/examples/data_transfer_objects/overriding_implicit_return_dto.py @@ -1,8 +1,24 @@ -from litestar import post +from dataclasses import dataclass, field +from uuid import UUID, uuid4 -from .models import User, UserDTO +from litestar import Litestar, post +from litestar.dto import DataclassDTO -@post(dto=UserDTO, return_dto=None) +@dataclass +class User: + name: str + email: str + age: int + id: UUID = field(default_factory=uuid4) + + +UserDTO = DataclassDTO[User] + + +@post(dto=UserDTO, return_dto=None, sync_to_thread=False) def create_user(data: User) -> bytes: return data.name.encode(encoding="utf-8") + + +app = Litestar([create_user]) diff --git a/docs/usage/dto/1-abstract-dto.rst b/docs/usage/dto/1-abstract-dto.rst index 346141a573..4ae382d9b5 100644 --- a/docs/usage/dto/1-abstract-dto.rst +++ b/docs/usage/dto/1-abstract-dto.rst @@ -176,7 +176,7 @@ handler. .. literalinclude:: /examples/data_transfer_objects/factory/dto_data_problem_statement.py :language: python - :emphasize-lines: 18,19,20,21,27 + :emphasize-lines: 19,20,21,22,28 :linenos: Notice that we get a ``500`` response from the handler - this is because the DTO has attempted to convert the request diff --git a/tests/examples/test_data_transfer_objects/test_factory/__init__.py b/tests/examples/test_data_transfer_objects/test_factory/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py new file mode 100644 index 0000000000..209ca901c3 --- /dev/null +++ b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_problem_statement.py @@ -0,0 +1,14 @@ +from unittest.mock import ANY + +from litestar.status_codes import HTTP_201_CREATED +from litestar.testing.client import TestClient + + +def test_create_user(user_data: dict) -> None: + from docs.examples.data_transfer_objects.factory.dto_data_problem_statement import app + + with TestClient(app=app) as client: + response = client.post("/users", json=user_data) + + assert response.status_code == HTTP_201_CREATED + assert response.json() == {"id": ANY, "name": "Mr Sunglass", "email": "mr.sunglass@example.com", "age": 30} diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py new file mode 100644 index 0000000000..02b0bcf992 --- /dev/null +++ b/tests/examples/test_data_transfer_objects/test_factory/test_dto_data_usage.py @@ -0,0 +1,13 @@ +from unittest.mock import ANY + +from litestar.testing import TestClient + + +def test_create_user(user_data) -> None: + from docs.examples.data_transfer_objects.factory.dto_data_usage import app + + with TestClient(app) as client: + response = client.post("/users", json=user_data) + + assert response.status_code == 201 + assert response.json() == {"id": ANY, "name": "Mr Sunglass", "email": "mr.sunglass@example.com", "age": 30} diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py new file mode 100644 index 0000000000..d6803d220a --- /dev/null +++ b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private.py @@ -0,0 +1,12 @@ +from litestar.status_codes import HTTP_201_CREATED +from litestar.testing.client import TestClient + + +def test_create_underscored_value() -> None: + from docs.examples.data_transfer_objects.factory.leading_underscore_private import app + + with TestClient(app=app) as client: + response = client.post("/", json={"this_will": "stay", "_this_will": "go_away!"}) + + assert response.status_code == HTTP_201_CREATED + assert response.json() == {"this_will": "stay"} diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py new file mode 100644 index 0000000000..8b98033920 --- /dev/null +++ b/tests/examples/test_data_transfer_objects/test_factory/test_leading_underscore_private_override.py @@ -0,0 +1,12 @@ +from litestar.status_codes import HTTP_201_CREATED +from litestar.testing.client import TestClient + + +def test_create_underscored_field() -> None: + from docs.examples.data_transfer_objects.factory.leading_underscore_private_override import app + + with TestClient(app=app) as client: + response = client.post("/", json={"this_will": "stay", "_this_will": "not_go_away!"}) + + assert response.status_code == HTTP_201_CREATED + assert response.json() == {"this_will": "stay", "_this_will": "not_go_away!"} diff --git a/tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py b/tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py new file mode 100644 index 0000000000..36e80c95d8 --- /dev/null +++ b/tests/examples/test_data_transfer_objects/test_factory/test_type_checking.py @@ -0,0 +1,8 @@ +import pytest + +from litestar.exceptions.dto_exceptions import InvalidAnnotationException + + +def test_should_raise_error_on_route_registration() -> None: + with pytest.raises(InvalidAnnotationException): + from docs.examples.data_transfer_objects.factory.type_checking import app # noqa: F401 diff --git a/tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py b/tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py new file mode 100644 index 0000000000..921707c621 --- /dev/null +++ b/tests/examples/test_data_transfer_objects/test_overriding_implicit_return_dto.py @@ -0,0 +1,12 @@ +from litestar.status_codes import HTTP_201_CREATED +from litestar.testing.client import TestClient + + +def test_create_user(user_data: dict) -> None: + from docs.examples.data_transfer_objects.overriding_implicit_return_dto import app + + with TestClient(app=app) as client: + response = client.post("/", json=user_data) + + assert response.status_code == HTTP_201_CREATED + assert response.content == b"Mr Sunglass" diff --git a/tests/examples/test_dto/test_example_apps.py b/tests/examples/test_dto/test_example_apps.py index 3ad9573279..353c976590 100644 --- a/tests/examples/test_dto/test_example_apps.py +++ b/tests/examples/test_dto/test_example_apps.py @@ -1,27 +1,8 @@ from __future__ import annotations -from unittest.mock import ANY - from litestar.testing import TestClient -def test_dto_data_problem_statement_app() -> None: - from docs.examples.data_transfer_objects.factory.dto_data_problem_statement import app - - with TestClient(app) as client: - response = client.post("/person", json={"name": "John", "age": 30}) - assert response.status_code == 500 - - -def test_dto_data_usage_app() -> None: - from docs.examples.data_transfer_objects.factory.dto_data_usage import app - - with TestClient(app) as client: - response = client.post("/person", json={"name": "John", "age": 30}) - assert response.status_code == 201 - assert response.json() == {"id": ANY, "name": "John", "age": 30} - - def test_dto_data_nested_data_create_instance_app() -> None: from docs.examples.data_transfer_objects.factory.providing_values_for_nested_data import app