Skip to content

Commit 765108c

Browse files
committed
added more test for route signature setup
1 parent ac2d7bf commit 765108c

17 files changed

+1477
-56
lines changed

ellar/core/middleware/schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
from starlette.middleware import Middleware
44
from starlette.middleware.base import BaseHTTPMiddleware
55

6-
from ellar.core.schema import PydanticSchema
6+
from ellar.core.schema import Schema
77

88

9-
class MiddlewareSchema(PydanticSchema):
9+
class MiddlewareSchema(Schema):
1010
middleware_class: t.Type[BaseHTTPMiddleware]
1111
dispatch: t.Callable[[t.Any, t.Callable], t.Any]
1212
options: t.Dict

tests/main.py

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from typing import Optional
22
from uuid import UUID
33

4+
from pydantic import Field
5+
46
from ellar.common import Path, Query
57
from ellar.core.factory import AppFactory
8+
from ellar.core.schema import Schema
69

710
app = AppFactory.create_app()
811

@@ -201,39 +204,15 @@ def get_query_param_required_type(query: int = Query(...)):
201204
return f"foo bar {query}"
202205

203206

204-
# class AliasedSchema(PydanticSchema):
205-
# query: str = Field(..., alias="aliased.-_~name")
206-
#
207-
#
208-
# @app.Get("/query/aliased-name")
209-
# def get_query_aliased_name(query: AliasedSchema = Query(..., alias="aliased.-_~name")):
210-
# return f"foo bar {query.query}"
211-
212-
213-
# @router.get("/query")
214-
# def get_query(request, query):
215-
# return f"foo bar {query}"
216-
#
217-
#
218-
# @router.get("/query/optional")
219-
# def get_query_optional(request, query=None):
220-
# if query is None:
221-
# return "foo bar"
222-
# return f"foo bar {query}"
223-
#
224-
#
225-
# @router.get("/query/int")
226-
# def get_query_type(request, query: int):
227-
# return f"foo bar {query}"
228-
#
229-
#
230-
# @router.get("/query/int/optional")
231-
# def get_query_type_optional(request, query: int = None):
232-
# if query is None:
233-
# return "foo bar"
234-
# return f"foo bar {query}"
235-
#
236-
#
207+
class AliasedSchema(Schema):
208+
query: str = Field(..., alias="aliased.-_~name")
209+
210+
211+
@app.Get("/query/aliased-name")
212+
def get_query_aliased_name(query: AliasedSchema = Query(..., alias="aliased.-_~name")):
213+
return f"foo bar {query.query}"
214+
215+
237216
@app.Get("/query/param")
238217
def get_query_param(request, query=Query(None)):
239218
if query is None:

tests/test_routing/sample.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from datetime import datetime
2+
from enum import IntEnum
3+
from typing import Optional
4+
5+
from pydantic import Field
6+
7+
from ellar.core.schema import Schema
8+
9+
10+
class Product(Schema):
11+
name: str
12+
description: str = None # type: ignore
13+
price: float
14+
15+
16+
class Item(Schema):
17+
name: Optional[str] = None
18+
19+
20+
class OtherItem(Schema):
21+
price: int
22+
23+
24+
class Range(IntEnum):
25+
TWENTY = 20
26+
FIFTY = 50
27+
TWO_HUNDRED = 200
28+
29+
30+
class Filter(Schema):
31+
to_datetime: datetime = Field(alias="to")
32+
from_datetime: datetime = Field(alias="from")
33+
range: Range = Range.TWENTY
34+
35+
36+
class Data(Schema):
37+
an_int: int = Field(alias="int", default=0)
38+
a_float: float = Field(alias="float", default=1.5)

tests/test_routing/test_get_request_body.py renamed to tests/test_routing/test_body_schema.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
from pydantic import BaseModel
2-
31
from ellar.core.factory import AppFactory
42
from ellar.openapi import OpenAPIDocumentBuilder
53
from ellar.serializer import serialize_object
64

7-
app = AppFactory.create_app()
8-
5+
from .sample import Product
96

10-
class Product(BaseModel):
11-
name: str
12-
description: str = None # type: ignore
13-
price: float
7+
app = AppFactory.create_app()
148

159

1610
@app.Get("/product")
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from typing import Dict
2+
3+
from pydantic import BaseModel
4+
5+
from ellar.core import TestClientFactory
6+
from ellar.openapi import OpenAPIDocumentBuilder
7+
from ellar.serializer import serialize_object
8+
9+
tm = TestClientFactory.create_test_module()
10+
11+
12+
class Items_(BaseModel):
13+
items: Dict[str, int]
14+
15+
16+
@tm.app.Post("/foo")
17+
def foo(items: Items_):
18+
return items.items
19+
20+
21+
client = tm.get_client()
22+
23+
24+
item_openapi_schema = {
25+
"openapi": "3.0.2",
26+
"info": {"title": "Ellar API Docs", "version": "1.0.0"},
27+
"paths": {
28+
"/foo": {
29+
"post": {
30+
"operationId": "foo_foo_post",
31+
"requestBody": {
32+
"content": {
33+
"application/json": {
34+
"schema": {"$ref": "#/components/schemas/Items_"}
35+
}
36+
},
37+
"required": True,
38+
},
39+
"responses": {
40+
"200": {
41+
"description": "Successful Response",
42+
"content": {
43+
"application/json": {
44+
"schema": {"title": "Response Model", "type": "object"}
45+
}
46+
},
47+
},
48+
"422": {
49+
"description": "Validation Error",
50+
"content": {
51+
"application/json": {
52+
"schema": {
53+
"$ref": "#/components/schemas/HTTPValidationError"
54+
}
55+
}
56+
},
57+
},
58+
},
59+
}
60+
}
61+
},
62+
"components": {
63+
"schemas": {
64+
"HTTPValidationError": {
65+
"title": "HTTPValidationError",
66+
"required": ["detail"],
67+
"type": "object",
68+
"properties": {
69+
"detail": {
70+
"title": "Details",
71+
"type": "array",
72+
"items": {"$ref": "#/components/schemas/ValidationError"},
73+
}
74+
},
75+
},
76+
"Items_": {
77+
"title": "Items_",
78+
"required": ["items"],
79+
"type": "object",
80+
"properties": {
81+
"items": {
82+
"title": "Items",
83+
"type": "object",
84+
"additionalProperties": {"type": "integer"},
85+
}
86+
},
87+
},
88+
"ValidationError": {
89+
"title": "ValidationError",
90+
"required": ["loc", "msg", "type"],
91+
"type": "object",
92+
"properties": {
93+
"loc": {
94+
"title": "Location",
95+
"type": "array",
96+
"items": {"type": "string"},
97+
},
98+
"msg": {"title": "Message", "type": "string"},
99+
"type": {"title": "Error Type", "type": "string"},
100+
},
101+
},
102+
}
103+
},
104+
"tags": [],
105+
}
106+
107+
108+
def test_body_extra_schema():
109+
document = serialize_object(OpenAPIDocumentBuilder().build_document(tm.app))
110+
assert document == item_openapi_schema
111+
112+
113+
def test_additional_properties_post():
114+
response = client.post("/foo", json={"items": {"foo": 1, "bar": 2}})
115+
assert response.status_code == 200, response.text
116+
assert response.json() == {"foo": 1, "bar": 2}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
from typing import Union
2+
3+
from ellar.core import TestClientFactory
4+
from ellar.openapi import OpenAPIDocumentBuilder
5+
from ellar.serializer import serialize_object
6+
7+
from .sample import Item, OtherItem
8+
9+
tm = TestClientFactory.create_test_module()
10+
11+
12+
@tm.app.Post("/items/")
13+
def save_union_body(item: Union[OtherItem, Item]):
14+
return {"item": item}
15+
16+
17+
client = tm.get_client()
18+
19+
20+
item_openapi_schema = {
21+
"openapi": "3.0.2",
22+
"info": {"title": "Ellar API Docs", "version": "1.0.0"},
23+
"paths": {
24+
"/items/": {
25+
"post": {
26+
"operationId": "save_union_body_items__post",
27+
"requestBody": {
28+
"content": {
29+
"application/json": {
30+
"schema": {
31+
"title": "Item",
32+
"anyOf": [
33+
{"$ref": "#/components/schemas/OtherItem"},
34+
{"$ref": "#/components/schemas/Item"},
35+
],
36+
}
37+
}
38+
},
39+
"required": True,
40+
},
41+
"responses": {
42+
"200": {
43+
"description": "Successful Response",
44+
"content": {
45+
"application/json": {
46+
"schema": {"title": "Response Model", "type": "object"}
47+
}
48+
},
49+
},
50+
"422": {
51+
"description": "Validation Error",
52+
"content": {
53+
"application/json": {
54+
"schema": {
55+
"$ref": "#/components/schemas/HTTPValidationError"
56+
}
57+
}
58+
},
59+
},
60+
},
61+
}
62+
}
63+
},
64+
"components": {
65+
"schemas": {
66+
"HTTPValidationError": {
67+
"title": "HTTPValidationError",
68+
"required": ["detail"],
69+
"type": "object",
70+
"properties": {
71+
"detail": {
72+
"title": "Details",
73+
"type": "array",
74+
"items": {"$ref": "#/components/schemas/ValidationError"},
75+
}
76+
},
77+
},
78+
"Item": {
79+
"title": "Item",
80+
"type": "object",
81+
"properties": {"name": {"title": "Name", "type": "string"}},
82+
},
83+
"OtherItem": {
84+
"title": "OtherItem",
85+
"required": ["price"],
86+
"type": "object",
87+
"properties": {"price": {"title": "Price", "type": "integer"}},
88+
},
89+
"ValidationError": {
90+
"title": "ValidationError",
91+
"required": ["loc", "msg", "type"],
92+
"type": "object",
93+
"properties": {
94+
"loc": {
95+
"title": "Location",
96+
"type": "array",
97+
"items": {"type": "string"},
98+
},
99+
"msg": {"title": "Message", "type": "string"},
100+
"type": {"title": "Error Type", "type": "string"},
101+
},
102+
},
103+
}
104+
},
105+
"tags": [],
106+
}
107+
108+
109+
def test_item_openapi_schema():
110+
document = serialize_object(OpenAPIDocumentBuilder().build_document(tm.app))
111+
assert document == item_openapi_schema
112+
113+
114+
def test_post_other_item():
115+
response = client.post("/items/", json={"price": 100})
116+
assert response.status_code == 200, response.text
117+
assert response.json() == {"item": {"price": 100}}
118+
119+
120+
def test_post_item():
121+
response = client.post("/items/", json={"name": "Foo"})
122+
assert response.status_code == 200, response.text
123+
assert response.json() == {"item": {"name": "Foo"}}

0 commit comments

Comments
 (0)