increase pydantic integration with headers, query string and url path
This commit is contained in:
@@ -30,6 +30,7 @@ def test_show_oad_of_app(cmd_line, capfd):
|
||||
"name": "a",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "a",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
@@ -44,6 +45,7 @@ def test_show_oad_of_app(cmd_line, capfd):
|
||||
"name": "b",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "b",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
@@ -75,6 +77,7 @@ def test_show_oad_of_sub_app(cmd_line, capfd):
|
||||
"name": "b",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "b",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
@@ -106,6 +109,7 @@ def test_show_oad_of_a_callable(cmd_line, capfd):
|
||||
"name": "a",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "a",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,19 +65,19 @@ async def test_pets_route_should_have_get_method(generated_oas):
|
||||
"in": "query",
|
||||
"name": "format",
|
||||
"required": True,
|
||||
"schema": {"type": "string"},
|
||||
"schema": {"title": "format", "type": "string"},
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "name",
|
||||
"required": False,
|
||||
"schema": {"type": "string"},
|
||||
"schema": {"title": "name", "type": "string"},
|
||||
},
|
||||
{
|
||||
"in": "header",
|
||||
"name": "promo",
|
||||
"required": False,
|
||||
"schema": {"format": "uuid", "type": "string"},
|
||||
"schema": {"title": "promo", "format": "uuid", "type": "string"},
|
||||
},
|
||||
],
|
||||
"responses": {
|
||||
@@ -152,7 +152,7 @@ async def test_pets_id_route_should_have_delete_method(generated_oas):
|
||||
"required": True,
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"schema": {"type": "integer"},
|
||||
"schema": {"title": "id", "type": "integer"},
|
||||
}
|
||||
],
|
||||
"responses": {"204": {"content": {}}},
|
||||
@@ -166,7 +166,7 @@ async def test_pets_id_route_should_have_get_method(generated_oas):
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": True,
|
||||
"schema": {"type": "integer"},
|
||||
"schema": {"title": "id", "type": "integer"},
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -197,7 +197,7 @@ async def test_pets_id_route_should_have_put_method(generated_oas):
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": True,
|
||||
"schema": {"type": "integer"},
|
||||
"schema": {"title": "id", "type": "integer"},
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
|
||||
@@ -38,32 +38,42 @@ def test_parse_func_signature():
|
||||
def path_body_qs_and_header(self, id: str, /, user: User, page: int, *, auth: UUID):
|
||||
pass
|
||||
|
||||
assert _parse_func_signature(body_only) == ({}, {"user": User}, {}, {})
|
||||
assert _parse_func_signature(path_only) == ({"id": str}, {}, {}, {})
|
||||
assert _parse_func_signature(qs_only) == ({}, {}, {"page": int}, {})
|
||||
assert _parse_func_signature(header_only) == ({}, {}, {}, {"auth": UUID})
|
||||
assert _parse_func_signature(path_and_qs) == ({"id": str}, {}, {"page": int}, {})
|
||||
assert _parse_func_signature(body_only) == ({}, {"user": User}, {}, {}, {})
|
||||
assert _parse_func_signature(path_only) == ({"id": str}, {}, {}, {}, {})
|
||||
assert _parse_func_signature(qs_only) == ({}, {}, {"page": int}, {}, {})
|
||||
assert _parse_func_signature(header_only) == ({}, {}, {}, {"auth": UUID}, {})
|
||||
assert _parse_func_signature(path_and_qs) == (
|
||||
{"id": str},
|
||||
{},
|
||||
{"page": int},
|
||||
{},
|
||||
{},
|
||||
)
|
||||
assert _parse_func_signature(path_and_header) == (
|
||||
{"id": str},
|
||||
{},
|
||||
{},
|
||||
{"auth": UUID},
|
||||
{},
|
||||
)
|
||||
assert _parse_func_signature(qs_and_header) == (
|
||||
{},
|
||||
{},
|
||||
{"page": int},
|
||||
{"auth": UUID},
|
||||
{},
|
||||
)
|
||||
assert _parse_func_signature(path_qs_and_header) == (
|
||||
{"id": str},
|
||||
{},
|
||||
{"page": int},
|
||||
{"auth": UUID},
|
||||
{},
|
||||
)
|
||||
assert _parse_func_signature(path_body_qs_and_header) == (
|
||||
{"id": str},
|
||||
{"user": User},
|
||||
{"page": int},
|
||||
{"auth": UUID},
|
||||
{},
|
||||
)
|
||||
|
||||
@@ -27,7 +27,12 @@ async def test_post_an_article_without_required_field_should_return_an_error_mes
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
{"loc": ["name"], "msg": "field required", "type": "value_error.missing"}
|
||||
{
|
||||
"in": "body",
|
||||
"loc": ["name"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -43,6 +48,7 @@ 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",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
@@ -21,6 +22,16 @@ class ArticleView(PydanticView):
|
||||
)
|
||||
|
||||
|
||||
class FormatEnum(str, Enum):
|
||||
UTM = "UMT"
|
||||
MGRS = "MGRS"
|
||||
|
||||
|
||||
class ViewWithEnumType(PydanticView):
|
||||
async def get(self, *, format: FormatEnum):
|
||||
return web.json_response({"format": format}, dumps=JSONEncoder().encode)
|
||||
|
||||
|
||||
async def test_get_article_without_required_header_should_return_an_error_message(
|
||||
aiohttp_client, loop
|
||||
):
|
||||
@@ -33,6 +44,7 @@ async def test_get_article_without_required_header_should_return_an_error_messag
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "headers",
|
||||
"loc": ["signature_expired"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
@@ -52,6 +64,7 @@ async def test_get_article_with_wrong_header_type_should_return_an_error_message
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "headers",
|
||||
"loc": ["signature_expired"],
|
||||
"msg": "invalid datetime format",
|
||||
"type": "value_error.datetime",
|
||||
@@ -87,3 +100,37 @@ async def test_get_article_with_valid_header_containing_hyphen_should_be_returne
|
||||
assert resp.status == 200
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == {"signature": "2020-10-04T18:01:00"}
|
||||
|
||||
|
||||
async def test_wrong_value_to_header_defined_with_str_enum(aiohttp_client, loop):
|
||||
app = web.Application()
|
||||
app.router.add_view("/coord", ViewWithEnumType)
|
||||
|
||||
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"}
|
||||
)
|
||||
assert resp.status == 400
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
|
||||
async def test_correct_value_to_header_defined_with_str_enum(aiohttp_client, loop):
|
||||
app = web.Application()
|
||||
app.router.add_view("/coord", ViewWithEnumType)
|
||||
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get("/coord", headers={"format": "UMT"})
|
||||
assert await resp.json() == {"format": "UMT"}
|
||||
assert resp.status == 200
|
||||
assert resp.content_type == "application/json"
|
||||
|
||||
@@ -8,7 +8,7 @@ class ArticleView(PydanticView):
|
||||
return web.json_response({"path": [author_id, tag, date]})
|
||||
|
||||
|
||||
async def test_get_article_without_required_qs_should_return_an_error_message(
|
||||
async def test_get_article_with_correct_path_parameters_should_return_parameters_in_path(
|
||||
aiohttp_client, loop
|
||||
):
|
||||
app = web.Application()
|
||||
@@ -19,3 +19,23 @@ async def test_get_article_without_required_qs_should_return_an_error_message(
|
||||
assert resp.status == 200
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == {"path": ["1234", "music", 1980]}
|
||||
|
||||
|
||||
async def test_get_article_with_wrong_path_parameters_should_return_error(
|
||||
aiohttp_client, loop
|
||||
):
|
||||
app = web.Application()
|
||||
app.router.add_view("/article/{author_id}/tag/{tag}/before/{date}", ArticleView)
|
||||
|
||||
client = await aiohttp_client(app)
|
||||
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",
|
||||
}
|
||||
]
|
||||
|
||||
@@ -6,8 +6,12 @@ from aiohttp_pydantic import PydanticView
|
||||
|
||||
|
||||
class ArticleView(PydanticView):
|
||||
async def get(self, with_comments: bool, age: Optional[int] = None):
|
||||
return web.json_response({"with_comments": with_comments, "age": age})
|
||||
async def get(
|
||||
self, with_comments: bool, age: Optional[int] = None, nb_items: int = 7
|
||||
):
|
||||
return web.json_response(
|
||||
{"with_comments": with_comments, "age": age, "nb_items": nb_items}
|
||||
)
|
||||
|
||||
|
||||
async def test_get_article_without_required_qs_should_return_an_error_message(
|
||||
@@ -22,6 +26,7 @@ async def test_get_article_without_required_qs_should_return_an_error_message(
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == [
|
||||
{
|
||||
"in": "query string",
|
||||
"loc": ["with_comments"],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing",
|
||||
@@ -41,6 +46,7 @@ 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",
|
||||
@@ -59,10 +65,10 @@ async def test_get_article_with_valid_qs_should_return_the_parsed_type(
|
||||
resp = await client.get("/article", params={"with_comments": "yes", "age": 3})
|
||||
assert resp.status == 200
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == {"with_comments": True, "age": 3}
|
||||
assert await resp.json() == {"with_comments": True, "age": 3, "nb_items": 7}
|
||||
|
||||
|
||||
async def test_get_article_with_valid_qs_and_omitted_optional_should_return_none(
|
||||
async def test_get_article_with_valid_qs_and_omitted_optional_should_return_default_value(
|
||||
aiohttp_client, loop
|
||||
):
|
||||
app = web.Application()
|
||||
@@ -71,6 +77,6 @@ async def test_get_article_with_valid_qs_and_omitted_optional_should_return_none
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.get("/article", params={"with_comments": "yes"})
|
||||
assert await resp.json() == {"with_comments": True, "age": None, "nb_items": 7}
|
||||
assert resp.status == 200
|
||||
assert resp.content_type == "application/json"
|
||||
assert await resp.json() == {"with_comments": True, "age": None}
|
||||
|
||||
Reference in New Issue
Block a user