feat: update pydantic
This commit is contained in:
parent
26fd6fa19f
commit
a94c9d4863
@ -10,6 +10,8 @@ from aiohttp.web_exceptions import HTTPMethodNotAllowed
|
||||
from aiohttp.web_response import StreamResponse
|
||||
from pydantic import ValidationError
|
||||
|
||||
from pydantic_core import ErrorDetails
|
||||
|
||||
from .injectors import (
|
||||
AbstractInjector,
|
||||
BodyGetter,
|
||||
@ -22,6 +24,10 @@ from .injectors import (
|
||||
)
|
||||
|
||||
|
||||
class PydanticValidationError(ErrorDetails):
|
||||
loc_in: CONTEXT
|
||||
|
||||
|
||||
class PydanticView(AbstractView):
|
||||
"""
|
||||
An AIOHTTP View that validate request using function annotations.
|
||||
@ -91,7 +97,7 @@ class PydanticView(AbstractView):
|
||||
return injectors
|
||||
|
||||
async def on_validation_error(
|
||||
self, exception: ValidationError, context: CONTEXT
|
||||
self, exception: ValidationError, context: CONTEXT
|
||||
) -> StreamResponse:
|
||||
"""
|
||||
This method is a hook to intercept ValidationError.
|
||||
@ -101,14 +107,13 @@ class PydanticView(AbstractView):
|
||||
"headers", "path" or "query string"
|
||||
"""
|
||||
errors = exception.errors()
|
||||
for error in errors:
|
||||
error["in"] = context
|
||||
own_errors = [PydanticValidationError(**x, loc_in=context) for x in errors]
|
||||
|
||||
return json_response(data=errors, status=400)
|
||||
return json_response(data=own_errors, status=400)
|
||||
|
||||
|
||||
def inject_params(
|
||||
handler, parse_func_signature: Callable[[Callable], Iterable[AbstractInjector]]
|
||||
handler, parse_func_signature: Callable[[Callable], Iterable[AbstractInjector]]
|
||||
):
|
||||
"""
|
||||
Decorator to unpack the query string, route path, body and http header in
|
||||
@ -146,6 +151,7 @@ def is_pydantic_view(obj) -> bool:
|
||||
|
||||
|
||||
__all__ = (
|
||||
"PydanticValidationError",
|
||||
"AbstractInjector",
|
||||
"BodyGetter",
|
||||
"HeadersGetter",
|
||||
|
@ -1,28 +1,8 @@
|
||||
aiohttp==3.8.1
|
||||
aiosignal==1.2.0
|
||||
async-timeout==4.0.2
|
||||
atomicwrites==1.4.1
|
||||
attrs==21.4.0
|
||||
bleach==5.0.1
|
||||
charset-normalizer==2.1.0
|
||||
colorama==0.4.5
|
||||
coverage==6.4.2
|
||||
docutils==0.19
|
||||
frozenlist==1.3.0
|
||||
idna==3.3
|
||||
iniconfig==1.1.1
|
||||
multidict==6.0.2
|
||||
packaging==21.3
|
||||
pluggy==1.0.0
|
||||
py==1.11.0
|
||||
Pygments==2.12.0
|
||||
pyparsing==3.0.9
|
||||
pytest==7.1.2
|
||||
aiohttp==3.8.4
|
||||
pydantic==2.0.2
|
||||
jinja2==3.1.2
|
||||
swagger-4-ui-bundle==0.0.4
|
||||
pytest==7.4.0
|
||||
pytest-aiohttp==1.0.4
|
||||
pytest-asyncio==0.19.0
|
||||
pytest-cov==3.0.0
|
||||
readme-renderer==35.0
|
||||
six==1.16.0
|
||||
tomli==2.0.1
|
||||
webencodings==0.5.1
|
||||
yarl==1.7.2
|
||||
pytest-asyncio==0.21.1
|
||||
pytest-cov==4.1.0
|
||||
|
@ -18,8 +18,8 @@ classifiers =
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
Topic :: Software Development :: Libraries :: Application Frameworks
|
||||
Framework :: aiohttp
|
||||
License :: OSI Approved :: MIT License
|
||||
@ -28,10 +28,10 @@ classifiers =
|
||||
zip_safe = False
|
||||
include_package_data = True
|
||||
packages = find:
|
||||
python_requires = >=3.8
|
||||
python_requires = >=3.10
|
||||
install_requires =
|
||||
aiohttp
|
||||
pydantic>=1.7
|
||||
pydantic>=2.0.0
|
||||
swagger-4-ui-bundle
|
||||
|
||||
[options.extras_require]
|
||||
|
@ -4,9 +4,10 @@ from typing import Iterator, List, Optional
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp.web_response import json_response
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, RootModel
|
||||
|
||||
from aiohttp_pydantic import PydanticView
|
||||
from aiohttp_pydantic.view import PydanticValidationError
|
||||
|
||||
|
||||
class ArticleModel(BaseModel):
|
||||
@ -14,11 +15,11 @@ class ArticleModel(BaseModel):
|
||||
nb_page: Optional[int]
|
||||
|
||||
|
||||
class ArticleModels(BaseModel):
|
||||
__root__: List[ArticleModel]
|
||||
class ArticleModels(RootModel):
|
||||
root: List[ArticleModel]
|
||||
|
||||
def __iter__(self) -> Iterator[ArticleModel]:
|
||||
return iter(self.__root__)
|
||||
return iter(self.root)
|
||||
|
||||
|
||||
class ArticleView(PydanticView):
|
||||
@ -30,14 +31,12 @@ class ArticleView(PydanticView):
|
||||
|
||||
async def on_validation_error(self, exception, context):
|
||||
errors = exception.errors()
|
||||
for error in errors:
|
||||
error["in"] = context
|
||||
error["custom"] = "custom"
|
||||
return json_response(data=errors, status=400)
|
||||
own_errors = [PydanticValidationError(**x, loc_in=context) for x in errors]
|
||||
return json_response(data=own_errors, status=400)
|
||||
|
||||
|
||||
async def test_post_an_article_with_wrong_type_field_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -47,12 +46,14 @@ async def test_post_an_article_with_wrong_type_field_should_return_an_error_mess
|
||||
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "body",
|
||||
"loc": ["nb_page"],
|
||||
"msg": "value is not a valid integer",
|
||||
"custom": "custom",
|
||||
"type": "type_error.integer",
|
||||
'loc_in': 'body',
|
||||
'input': 'foo',
|
||||
'loc': ['nb_page'],
|
||||
'msg': 'Input should be a valid integer, unable to parse string as an integer',
|
||||
'type': 'int_parsing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/int_parsing'
|
||||
}
|
||||
]
|
||||
|
@ -6,7 +6,7 @@ from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from aiohttp import web
|
||||
from pydantic import Field
|
||||
from pydantic import Field, RootModel
|
||||
from pydantic.main import BaseModel
|
||||
|
||||
from aiohttp_pydantic import PydanticView, oas
|
||||
@ -52,8 +52,7 @@ class Dog(BaseModel):
|
||||
barks: float
|
||||
|
||||
|
||||
class Animal(BaseModel):
|
||||
__root__: Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]
|
||||
Animal = RootModel[Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]]
|
||||
|
||||
|
||||
class PetCollectionView(PydanticView):
|
||||
|
@ -3,21 +3,21 @@ from __future__ import annotations
|
||||
from typing import Iterator, List, Optional
|
||||
|
||||
from aiohttp import web
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, RootModel
|
||||
|
||||
from aiohttp_pydantic import PydanticView
|
||||
|
||||
|
||||
class ArticleModel(BaseModel):
|
||||
name: str
|
||||
nb_page: Optional[int]
|
||||
nb_page: Optional[int] = None
|
||||
|
||||
|
||||
class ArticleModels(BaseModel):
|
||||
__root__: List[ArticleModel]
|
||||
class ArticleModels(RootModel):
|
||||
root: List[ArticleModel]
|
||||
|
||||
def __iter__(self) -> Iterator[ArticleModel]:
|
||||
return iter(self.__root__)
|
||||
return iter(self.root)
|
||||
|
||||
|
||||
class ArticleView(PydanticView):
|
||||
@ -29,7 +29,7 @@ class ArticleView(PydanticView):
|
||||
|
||||
|
||||
async def test_post_an_article_without_required_field_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -38,18 +38,21 @@ async def test_post_an_article_without_required_field_should_return_an_error_mes
|
||||
resp = await client.post("/article", json={})
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "body",
|
||||
"loc": ["name"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
'input': {},
|
||||
'loc': ['name'],
|
||||
'loc_in': 'body',
|
||||
'msg': 'Field required',
|
||||
'type': 'missing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/missing'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_post_an_article_with_wrong_type_field_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -60,10 +63,12 @@ async def test_post_an_article_with_wrong_type_field_should_return_an_error_mess
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "body",
|
||||
"loc": ["nb_page"],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer",
|
||||
'input': 'foo',
|
||||
'loc': ['nb_page'],
|
||||
'loc_in': 'body',
|
||||
'msg': 'Input should be a valid integer, unable to parse string as an integer',
|
||||
'type': 'int_parsing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/int_parsing'
|
||||
}
|
||||
]
|
||||
|
||||
@ -81,7 +86,7 @@ async def test_post_an_array_json_is_supported(aiohttp_client, event_loop):
|
||||
|
||||
|
||||
async def test_post_an_array_json_to_an_object_model_should_return_an_error(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -101,7 +106,7 @@ async def test_post_an_array_json_to_an_object_model_should_return_an_error(
|
||||
|
||||
|
||||
async def test_post_an_object_json_to_a_list_model_should_return_an_error(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -110,12 +115,15 @@ async def test_post_an_object_json_to_a_list_model_should_return_an_error(
|
||||
resp = await client.put("/article", json={"name": "foo", "nb_page": 3})
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "body",
|
||||
"loc": ["__root__"],
|
||||
"msg": "value is not a valid list",
|
||||
"type": "type_error.list",
|
||||
'input': {'name': 'foo', 'nb_page': 3},
|
||||
'loc': [],
|
||||
'loc_in': 'body',
|
||||
'msg': 'Input should be a valid list',
|
||||
'type': 'list_type',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/list_type'
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -50,9 +50,9 @@ class Signature(Group):
|
||||
|
||||
class ArticleViewWithSignatureGroup(PydanticView):
|
||||
async def get(
|
||||
self,
|
||||
*,
|
||||
signature: Signature,
|
||||
self,
|
||||
*,
|
||||
signature: Signature,
|
||||
):
|
||||
return web.json_response(
|
||||
{"expired": signature.expired, "scope": signature.scope},
|
||||
@ -61,7 +61,7 @@ class ArticleViewWithSignatureGroup(PydanticView):
|
||||
|
||||
|
||||
async def test_get_article_without_required_header_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -70,18 +70,24 @@ async def test_get_article_without_required_header_should_return_an_error_messag
|
||||
resp = await client.get("/article", headers={})
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
|
||||
result = await resp.json()
|
||||
assert len(result) == 1
|
||||
result[0].pop('input')
|
||||
|
||||
assert result == [
|
||||
{
|
||||
"in": "headers",
|
||||
"loc": ["signature_expired"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
'type': 'missing',
|
||||
'loc': ['signature_expired'],
|
||||
'msg': 'Field required',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/missing',
|
||||
'loc_in': 'headers'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_get_article_with_wrong_header_type_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -90,18 +96,22 @@ async def test_get_article_with_wrong_header_type_should_return_an_error_message
|
||||
resp = await client.get("/article", headers={"signature_expired": "foo"})
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "headers",
|
||||
"loc": ["signature_expired"],
|
||||
"msg": "invalid datetime format",
|
||||
"type": "value_error.datetime",
|
||||
'type': 'datetime_parsing',
|
||||
'loc': ['signature_expired'],
|
||||
'msg': 'Input should be a valid datetime, input is too short',
|
||||
'input': 'foo',
|
||||
'ctx': {'error': 'input is too short'},
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/datetime_parsing',
|
||||
'loc_in': 'headers'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_get_article_with_valid_header_should_return_the_parsed_type(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -116,7 +126,7 @@ async def test_get_article_with_valid_header_should_return_the_parsed_type(
|
||||
|
||||
|
||||
async def test_get_article_with_valid_header_containing_hyphen_should_be_returned(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -136,18 +146,20 @@ async def test_wrong_value_to_header_defined_with_str_enum(aiohttp_client, event
|
||||
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get("/coord", headers={"format": "WGS84"})
|
||||
|
||||
assert (
|
||||
await resp.json()
|
||||
== [
|
||||
{
|
||||
"ctx": {"enum_values": ["UMT", "MGRS"]},
|
||||
"in": "headers",
|
||||
"loc": ["format"],
|
||||
"msg": "value is not a valid enumeration member; permitted: 'UMT', 'MGRS'",
|
||||
"type": "type_error.enum",
|
||||
}
|
||||
]
|
||||
!= {"signature": "2020-10-04T18:01:00"}
|
||||
await resp.json()
|
||||
== [
|
||||
{
|
||||
'ctx': {'expected': "'UMT' or 'MGRS'"},
|
||||
'input': 'WGS84',
|
||||
'loc': ['format'],
|
||||
'loc_in': 'headers',
|
||||
'msg': "Input should be 'UMT' or 'MGRS'",
|
||||
'type': 'enum'
|
||||
}
|
||||
]
|
||||
!= {"signature": "2020-10-04T18:01:00"}
|
||||
)
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
@ -33,11 +33,14 @@ async def test_get_article_with_wrong_path_parameters_should_return_error(
|
||||
resp = await client.get("/article/1234/tag/music/before/now")
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "path",
|
||||
"loc": ["date"],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer",
|
||||
'input': 'now',
|
||||
'loc': ['date'],
|
||||
'loc_in': 'path',
|
||||
'msg': 'Input should be a valid integer, unable to parse string as an integer',
|
||||
'type': 'int_parsing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/int_parsing'
|
||||
}
|
||||
]
|
||||
|
@ -11,11 +11,11 @@ from aiohttp_pydantic.injectors import Group
|
||||
|
||||
class ArticleView(PydanticView):
|
||||
async def get(
|
||||
self,
|
||||
with_comments: bool,
|
||||
age: Optional[int] = None,
|
||||
nb_items: int = 7,
|
||||
tags: List[str] = Field(default_factory=list),
|
||||
self,
|
||||
with_comments: bool,
|
||||
age: Optional[int] = None,
|
||||
nb_items: int = 7,
|
||||
tags: List[str] = Field(default_factory=list),
|
||||
):
|
||||
return web.json_response(
|
||||
{
|
||||
@ -42,9 +42,9 @@ class Pagination(Group):
|
||||
|
||||
class ArticleViewWithPaginationGroup(PydanticView):
|
||||
async def get(
|
||||
self,
|
||||
with_comments: bool,
|
||||
page: Pagination,
|
||||
self,
|
||||
with_comments: bool,
|
||||
page: Pagination,
|
||||
):
|
||||
return web.json_response(
|
||||
{
|
||||
@ -70,7 +70,7 @@ class ArticleViewWithEnumInQuery(PydanticView):
|
||||
|
||||
|
||||
async def test_get_article_without_required_qs_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -79,18 +79,21 @@ async def test_get_article_without_required_qs_should_return_an_error_message(
|
||||
resp = await client.get("/article")
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "query string",
|
||||
"loc": ["with_comments"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
'input': {},
|
||||
'loc': ['with_comments'],
|
||||
'loc_in': 'query string',
|
||||
'msg': 'Field required',
|
||||
'type': 'missing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/missing'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_get_article_with_wrong_qs_type_should_return_an_error_message(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -101,16 +104,18 @@ async def test_get_article_with_wrong_qs_type_should_return_an_error_message(
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "query string",
|
||||
"loc": ["with_comments"],
|
||||
"msg": "value could not be parsed to a boolean",
|
||||
"type": "type_error.bool",
|
||||
'input': 'foo',
|
||||
'loc': ['with_comments'],
|
||||
'loc_in': 'query string',
|
||||
'msg': 'Input should be a valid boolean, unable to interpret input',
|
||||
'type': 'bool_parsing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/bool_parsing'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_get_article_with_valid_qs_should_return_the_parsed_type(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -129,7 +134,7 @@ async def test_get_article_with_valid_qs_should_return_the_parsed_type(
|
||||
|
||||
|
||||
async def test_get_article_with_valid_qs_and_omitted_optional_should_return_default_value(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -148,7 +153,7 @@ async def test_get_article_with_valid_qs_and_omitted_optional_should_return_defa
|
||||
|
||||
|
||||
async def test_get_article_with_multiple_value_for_qs_age_must_failed(
|
||||
aiohttp_client, event_loop
|
||||
aiohttp_client, event_loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article", ArticleView)
|
||||
@ -158,10 +163,12 @@ async def test_get_article_with_multiple_value_for_qs_age_must_failed(
|
||||
resp = await client.get("/article", params={"age": ["2", "3"], "with_comments": 1})
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "query string",
|
||||
"loc": ["age"],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer",
|
||||
'input': ['2', '3'],
|
||||
'loc': ['age'],
|
||||
'loc_in': 'query string',
|
||||
'msg': 'Input should be a valid integer',
|
||||
'type': 'int_type',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/int_type'
|
||||
}
|
||||
]
|
||||
assert resp.status == 400
|
||||
@ -215,10 +222,12 @@ async def test_get_article_without_required_field_page(aiohttp_client, event_loo
|
||||
resp = await client.get("/article", params={"with_comments": 1})
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "query string",
|
||||
"loc": ["page_num"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
'input': {'with_comments': '1'},
|
||||
'loc': ['page_num'],
|
||||
'loc_in': 'query string',
|
||||
'msg': 'Field required',
|
||||
'type': 'missing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/missing'
|
||||
}
|
||||
]
|
||||
assert resp.status == 400
|
||||
@ -276,10 +285,13 @@ async def test_get_article_with_page_and_wrong_page_size(aiohttp_client, event_l
|
||||
)
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "query string",
|
||||
"loc": ["page_size"],
|
||||
"msg": "value is not a valid integer",
|
||||
"type": "type_error.integer",
|
||||
'input': 'large',
|
||||
'loc': ['page_size'],
|
||||
'loc_in': 'query string',
|
||||
'msg': 'Input should be a valid integer, unable to parse string as an '
|
||||
'integer',
|
||||
'type': 'int_parsing',
|
||||
'url': 'https://errors.pydantic.dev/2.1.2/v/int_parsing'
|
||||
}
|
||||
]
|
||||
assert resp.status == 400
|
||||
|
Loading…
x
Reference in New Issue
Block a user