diff --git a/requirements.txt b/requirements.txt index 3598ed159..dbae38c38 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,6 @@ apispec>=4.0.0 cerberus contextvars extendable-pydantic -extendable-pydantic>0.0.5 extendable-pydantic>=1.0.0 extendable>=0.0.4 fastapi diff --git a/test-requirements.txt b/test-requirements.txt index 5f95f750a..f7621d172 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,2 +1 @@ httpx -extendable-pydantic @ git+https://github.com/lmignon/extendable-pydantic.git@refs/pull/7/head diff --git a/test_extendable_pydantic_fastapi/__manifest__.py b/test_extendable_pydantic_fastapi/__manifest__.py index 90ef07a8a..d20d00685 100644 --- a/test_extendable_pydantic_fastapi/__manifest__.py +++ b/test_extendable_pydantic_fastapi/__manifest__.py @@ -18,7 +18,8 @@ "demo": [], "external_dependencies": { "python": [ - "extendable-pydantic>0.0.5", + "pydantic>=2.0.0", + "extendable-pydantic>=1.0.0", ] }, } diff --git a/test_extendable_pydantic_fastapi/schemas.py b/test_extendable_pydantic_fastapi/schemas.py index 096a01be5..250934b14 100644 --- a/test_extendable_pydantic_fastapi/schemas.py +++ b/test_extendable_pydantic_fastapi/schemas.py @@ -8,8 +8,8 @@ class Coordinate(BaseModel, metaclass=ExtendableModelMeta): - lat: int - lon: int + lat: float + lon: float class ExtendedCoordinate(Coordinate, extends=Coordinate): @@ -27,5 +27,5 @@ class SearchResponse(BaseModel, Generic[_T], metaclass=ExtendableModelMeta): class CoordinateSearchResponse(SearchResponse[Coordinate]): """We declare the generic type of the items of the list as Coordinate - which is the base model of the extended. We used, it should be resolved + which is the base model of the extended. When used, it should be resolved to ExtendedCoordinate""" diff --git a/test_extendable_pydantic_fastapi/tests/__init__.py b/test_extendable_pydantic_fastapi/tests/__init__.py new file mode 100644 index 000000000..50d02897b --- /dev/null +++ b/test_extendable_pydantic_fastapi/tests/__init__.py @@ -0,0 +1 @@ +from . import test_coordinate diff --git a/test_extendable_pydantic_fastapi/tests/test_coordinate.py b/test_extendable_pydantic_fastapi/tests/test_coordinate.py new file mode 100644 index 000000000..df901f276 --- /dev/null +++ b/test_extendable_pydantic_fastapi/tests/test_coordinate.py @@ -0,0 +1,71 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from requests import Response + +from odoo.tests.common import tagged + +from odoo.addons.extendable_fastapi.tests.common import FastAPITransactionCase + +from ..routers import demo_pydantic_router +from ..schemas import Coordinate + + +@tagged("post_install", "-at_install") +class TestCoordinate(FastAPITransactionCase): + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + + def test_app_components(self): + with self._create_test_client(router=demo_pydantic_router) as test_client: + to_openapi = test_client.app.openapi() + # Check post input and output types + self.assertEqual( + to_openapi["paths"]["/demo_pydantic"]["post"]["requestBody"]["content"][ + "application/json" + ]["schema"]["$ref"], + "#/components/schemas/Coordinate", + ) + self.assertEqual( + to_openapi["paths"]["/demo_pydantic"]["post"]["responses"]["200"][ + "content" + ]["application/json"]["schema"]["$ref"], + "#/components/schemas/CoordinateSearchResponse", + ) + + # Check Pydantic model extension + self.assertEqual( + set( + to_openapi["components"]["schemas"]["Coordinate"][ + "properties" + ].keys() + ), + {"lat", "lon", "name", "description"}, + ) + self.assertEqual( + to_openapi["components"]["schemas"]["CoordinateSearchResponse"][ + "properties" + ]["items"]["items"]["$ref"], + "#/components/schemas/Coordinate", + ) + + def test_post_coordinates(self): + name = "Bievre" + desc = "Small town in Belgium" + pydantic_data = Coordinate(lat=49.94, lon=5.02, name=name, description=desc) + # Assert that class was correctly extended + self.assertTrue(pydantic_data.name) + self.assertTrue(pydantic_data.description) + + with self._create_test_client(router=demo_pydantic_router) as test_client: + response: Response = test_client.post( + "/demo_pydantic", content=pydantic_data.model_dump_json() + ) + self.assertEqual(response.status_code, 200) + res = response.json() + self.assertEqual(res["total"], 1) + coordinate = res["items"][0] + self.assertEqual(coordinate["lat"], 49.94) + self.assertEqual(coordinate["lon"], 5.02) + self.assertEqual(coordinate["name"], name) + self.assertEqual(coordinate["description"], desc)