Skip to content

Commit

Permalink
test: coverage for contrib.pydantic. (#2583)
Browse files Browse the repository at this point in the history
* test: coverage for contrib.pydantic.

This PR adds tests for coverage in the `contrib.pydantic` namespace.

For #2535

* test: coverage for contrib.pydantic. (Sourcery refactored) (#2584)

'Refactored by Sourcery'

Co-authored-by: Sourcery AI <>

* linting

* silence type errors for variable as type annotation in tests

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
  • Loading branch information
peterschutt and sourcery-ai[bot] authored Nov 1, 2023
1 parent 055607e commit a0aca4d
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 17 deletions.
5 changes: 0 additions & 5 deletions litestar/contrib/pydantic/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
get_origin_or_inner_type,
get_type_hints_with_generics_resolved,
instantiable_type_mapping,
unwrap_annotation,
wrapper_type_set,
)

# isort: off
Expand Down Expand Up @@ -116,9 +114,6 @@ def pydantic_unwrap_and_get_origin(annotation: Any) -> Any | None:
return get_origin_or_inner_type(annotation)

origin = annotation.__pydantic_generic_metadata__["origin"]
if origin in wrapper_type_set:
inner, _, _ = unwrap_annotation(annotation)
origin = get_origin_or_inner_type(inner)
return instantiable_type_mapping.get(origin, origin)


Expand Down
28 changes: 27 additions & 1 deletion tests/unit/test_contrib/test_pydantic/test_dto.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from __future__ import annotations

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional

import pytest
from pydantic import v1 as pydantic_v1

from litestar import Request, post
from litestar.contrib.pydantic import PydanticDTO, _model_dump_json
from litestar.dto import DTOConfig
from litestar.testing import create_test_client
from litestar.types import Empty
from litestar.typing import FieldDefinition

if TYPE_CHECKING:
from pydantic import BaseModel
Expand Down Expand Up @@ -36,3 +41,24 @@ def handler(data: PydanticUser, request: Request) -> dict:
)
required = next(iter(received.json()["components"]["schemas"].values()))["required"]
assert len(required) == 2


def test_field_definition_implicit_optional_default(base_model: type[BaseModel]) -> None:
class Model(base_model): # type: ignore[misc, valid-type]
a: Optional[str] # noqa: UP007

dto_type = PydanticDTO[Model]
field_defs = list(dto_type.generate_field_definitions(Model))
assert len(field_defs) == 1
assert field_defs[0].default is None


def test_detect_nested_field_pydantic_v1(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr("litestar.contrib.pydantic.pydantic_dto_factory.pydantic_v2", Empty)

class Model(pydantic_v1.BaseModel):
a: str

dto_type = PydanticDTO[Model]
assert dto_type.detect_nested_field(FieldDefinition.from_annotation(Model)) is True
assert dto_type.detect_nested_field(FieldDefinition.from_annotation(int)) is False
31 changes: 22 additions & 9 deletions tests/unit/test_contrib/test_pydantic/test_integration.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
from typing import Any, Dict, List, Type, Union

import pydantic as pydantic_v2
import pytest
from pydantic import v1 as pydantic_v1
from typing_extensions import Annotated

from litestar import post
from litestar.contrib.pydantic.pydantic_dto_factory import PydanticDTO
from litestar.params import Parameter
from litestar.enums import RequestEncodingType
from litestar.params import Body, Parameter
from litestar.status_codes import HTTP_400_BAD_REQUEST
from litestar.testing import create_test_client
from tests.unit.test_contrib.test_pydantic.models import PydanticPerson, PydanticV1Person

from . import PydanticVersion


def test_pydantic_v1_validation_error_raises_400() -> None:
@pytest.mark.parametrize(("meta",), [(None,), (Body(media_type=RequestEncodingType.URL_ENCODED),)])
def test_pydantic_v1_validation_error_raises_400(meta: Any) -> None:
class Model(pydantic_v1.BaseModel):
foo: str = pydantic_v1.Field(max_length=2)

ModelDTO = PydanticDTO[Model]

@post(dto=ModelDTO, signature_types=[Model])
def handler(data: Model) -> Model:
annotation: Any
annotation = Annotated[Model, meta] if meta is not None else Model

@post(dto=ModelDTO, signature_namespace={"annotation": annotation})
def handler(data: annotation) -> Any: # pyright: ignore
return data

model_json = {"foo": "too long"}
Expand All @@ -36,21 +43,26 @@ def handler(data: Model) -> Model:
]

with create_test_client(route_handlers=handler) as client:
response = client.post("/", json=model_json)
kws = {"data": model_json} if meta else {"json": model_json}
response = client.post("/", **kws) # type: ignore[arg-type]
extra = response.json()["extra"]

assert response.status_code == 400
assert extra == expected_errors


def test_pydantic_v2_validation_error_raises_400() -> None:
@pytest.mark.parametrize(("meta",), [(None,), (Body(media_type=RequestEncodingType.URL_ENCODED),)])
def test_pydantic_v2_validation_error_raises_400(meta: Any) -> None:
class Model(pydantic_v2.BaseModel):
foo: str = pydantic_v2.Field(max_length=2)

ModelDTO = PydanticDTO[Model]

@post(dto=ModelDTO, signature_types=[Model])
def handler(data: Model) -> Model:
annotation: Any
annotation = Annotated[Model, meta] if meta is not None else Model

@post(dto=ModelDTO, signature_namespace={"annotation": annotation})
def handler(data: annotation) -> Any: # pyright: ignore
return data

model_json = {"foo": "too long"}
Expand All @@ -67,7 +79,8 @@ def handler(data: Model) -> Model:
]

with create_test_client(route_handlers=handler) as client:
response = client.post("/", json=model_json)
kws = {"data": model_json} if meta else {"json": model_json}
response = client.post("/", **kws) # type: ignore[arg-type]

extra = response.json()["extra"]
extra[0].pop("url")
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/test_contrib/test_pydantic/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from typing import Dict, Generic, Tuple
from typing import Any, Dict, Generic, Tuple, TypeVar

import pytest
from pydantic import BaseModel
from typing_extensions import Any, TypeVar

from litestar.contrib.pydantic.utils import pydantic_get_type_hints_with_generics_resolved

Expand Down

0 comments on commit a0aca4d

Please sign in to comment.