Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Kludex committed Jul 26, 2023
1 parent 3a0dd6f commit e8aa98f
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/integrations/plugins.md
Expand Up @@ -50,7 +50,7 @@ Let's see an example of a plugin that _wraps_ the `validate_python` method of th

```py
from pprint import pprint
from typing import Any, Optional, Dict
from typing import Any, Dict, Optional

from pydantic_core import ValidationError

Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Expand Up @@ -13,7 +13,7 @@
from _pytest.assertion.rewrite import AssertionRewritingHook


def pytest_addoption(parser):
def pytest_addoption(parser: pytest.Parser):
parser.addoption('--test-mypy', action='store_true', help='run mypy tests')
parser.addoption('--update-mypy', action='store_true', help='update mypy tests')

Expand Down
155 changes: 155 additions & 0 deletions tests/test_plugins.py
@@ -0,0 +1,155 @@
from __future__ import annotations

import contextlib
from typing import Any, Generator

from pydantic_core import ValidationError

from pydantic import BaseModel
from pydantic.plugin import OnValidateJson, OnValidatePython, Plugin
from pydantic.plugin._loader import plugins


@contextlib.contextmanager
def install_plugin(plugin: Plugin) -> Generator[None, None, None]:
plugins.append(plugin)
yield
plugins.pop()


def test_on_validate_json_on_success() -> None:
class CustomOnValidateJson(OnValidateJson):
def enter(
self,
input: str | bytes | bytearray,
*,
strict: bool | None = None,
context: dict[str, Any] | None = None,
self_instance: Any | None = None,
) -> None:
assert input == '{"a": 1}'
assert strict is None
assert context is None
assert self_instance is None
assert self.config == {'title': 'Model'}
assert self.plugin_settings == {'observe': 'all'}

def on_success(self, result: Any) -> None:
assert isinstance(result, Model)

plugin = Plugin(on_validate_json=CustomOnValidateJson)
with install_plugin(plugin):

class Model(BaseModel, plugin_settings={'observe': 'all'}):
a: int

Model.model_validate_json('{"a": 1}')


def test_on_validate_json_on_error() -> None:
class CustomOnValidateJson(OnValidateJson):
def enter(
self,
input: str | bytes | bytearray,
*,
strict: bool | None = None,
context: dict[str, Any] | None = None,
self_instance: Any | None = None,
) -> None:
assert input == '{"a": "potato"}'
assert strict is None
assert context is None
assert self_instance is None
assert self.config == {'title': 'Model'}
assert self.plugin_settings == {'observe': 'all'}

def on_error(self, error: ValidationError) -> None:
assert error.title == 'Model'
assert error.errors() == [
{
'input': 'potato',
'loc': ('a',),
'msg': 'Input should be a valid integer, unable to parse string as an ' 'integer',
'type': 'int_parsing',
'url': 'https://errors.pydantic.dev/2.1/v/int_parsing',
},
]

plugin = Plugin(on_validate_json=CustomOnValidateJson)
with install_plugin(plugin):

class Model(BaseModel, plugin_settings={'observe': 'all'}):
a: int

with contextlib.suppress(ValidationError):
Model.model_validate_json('{"a": "potato"}')


def test_on_validate_python_on_success() -> None:
class CustomOnValidatePython(OnValidatePython):
def enter(
self,
input: Any,
*,
strict: bool | None = None,
from_attributes: bool | None = None,
context: dict[str, Any] | None = None,
self_instance: Any | None = None,
) -> None:
assert input == {'a': 1}
assert strict is None
assert context is None
assert self_instance is None
assert self.config == {'title': 'Model'}
assert self.plugin_settings == {'observe': 'all'}

def on_success(self, result: Any) -> None:
assert isinstance(result, Model)

plugin = Plugin(on_validate_python=CustomOnValidatePython)
with install_plugin(plugin):

class Model(BaseModel, plugin_settings={'observe': 'all'}):
a: int

Model.model_validate({'a': 1})


def test_on_validate_python_on_error() -> None:
class CustomOnValidatePython(OnValidatePython):
def enter(
self,
input: Any,
*,
strict: bool | None = None,
from_attributes: bool | None = None,
context: dict[str, Any] | None = None,
self_instance: Any | None = None,
) -> None:
assert input == {'a': 'potato'}
assert strict is None
assert context is None
assert self_instance is None
assert self.config == {'title': 'Model'}
assert self.plugin_settings == {'observe': 'all'}

def on_error(self, error: ValidationError) -> None:
assert error.title == 'Model'
assert error.errors() == [
{
'input': 'potato',
'loc': ('a',),
'msg': 'Input should be a valid integer, unable to parse string as an ' 'integer',
'type': 'int_parsing',
'url': 'https://errors.pydantic.dev/2.1/v/int_parsing',
},
]

plugin = Plugin(on_validate_python=CustomOnValidatePython)
with install_plugin(plugin):

class Model(BaseModel, plugin_settings={'observe': 'all'}):
a: int

with contextlib.suppress(ValidationError):
Model.model_validate({'a': 'potato'})

0 comments on commit e8aa98f

Please sign in to comment.