mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-18 07:52:09 +02:00
208 lines
8.2 KiB
Python
208 lines
8.2 KiB
Python
# 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 fastapi.exceptions import ResponseValidationError
|
|
|
|
from .common import FastAPITransactionCase
|
|
from .routers import demo_pydantic_router
|
|
from .schemas import PrivateCustomer, PrivateUser, User
|
|
|
|
|
|
@tagged("post_install", "-at_install")
|
|
class TestUser(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"]["/post_user"]["post"]["requestBody"]["content"][
|
|
"application/json"
|
|
]["schema"]["$ref"],
|
|
"#/components/schemas/User",
|
|
)
|
|
self.assertEqual(
|
|
to_openapi["paths"]["/post_user"]["post"]["responses"]["200"][
|
|
"content"
|
|
]["application/json"]["schema"]["$ref"],
|
|
"#/components/schemas/UserSearchResponse",
|
|
)
|
|
self.assertEqual(
|
|
to_openapi["paths"]["/post_private_user"]["post"]["requestBody"][
|
|
"content"
|
|
]["application/json"]["schema"]["$ref"],
|
|
"#/components/schemas/PrivateUser",
|
|
)
|
|
self.assertEqual(
|
|
to_openapi["paths"]["/post_private_user"]["post"]["responses"]["200"][
|
|
"content"
|
|
]["application/json"]["schema"]["$ref"],
|
|
"#/components/schemas/User",
|
|
)
|
|
self.assertEqual(
|
|
to_openapi["paths"]["/post_private_user_generic"]["post"][
|
|
"requestBody"
|
|
]["content"]["application/json"]["schema"]["$ref"],
|
|
"#/components/schemas/PrivateUser",
|
|
)
|
|
self.assertEqual(
|
|
to_openapi["paths"]["/post_private_user_generic"]["post"]["responses"][
|
|
"200"
|
|
]["content"]["application/json"]["schema"]["$ref"],
|
|
"#/components/schemas/UserSearchResponse",
|
|
)
|
|
|
|
# Check Pydantic model extension
|
|
self.assertEqual(
|
|
set(to_openapi["components"]["schemas"]["User"]["properties"].keys()),
|
|
{"name", "address"},
|
|
)
|
|
self.assertEqual(
|
|
set(
|
|
to_openapi["components"]["schemas"]["PrivateUser"][
|
|
"properties"
|
|
].keys()
|
|
),
|
|
{"name", "address", "password"},
|
|
)
|
|
self.assertEqual(
|
|
to_openapi["components"]["schemas"]["UserSearchResponse"]["properties"][
|
|
"items"
|
|
]["items"]["$ref"],
|
|
"#/components/schemas/User",
|
|
)
|
|
|
|
def test_post_user(self):
|
|
name = "Jean Dupont"
|
|
address = "Rue du Puits 12, 4000 Liège"
|
|
pydantic_data = User(name=name, address=address)
|
|
# Assert that class was correctly extended
|
|
self.assertTrue(pydantic_data.address)
|
|
|
|
with self._create_test_client(router=demo_pydantic_router) as test_client:
|
|
response: Response = test_client.post(
|
|
"/post_user", content=pydantic_data.model_dump_json()
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
res = response.json()
|
|
self.assertEqual(res["total"], 1)
|
|
user = res["items"][0]
|
|
self.assertEqual(user["name"], name)
|
|
self.assertEqual(user["address"], address)
|
|
self.assertFalse("password" in user.keys())
|
|
|
|
def test_post_private_user(self):
|
|
"""
|
|
/post_private_user return attributes from User, but not PrivateUser
|
|
|
|
Security check: this method should never return attributes from
|
|
derived type PrivateUser, even thought a PrivateUser object
|
|
is given as input.
|
|
"""
|
|
name = "Jean Dupont"
|
|
address = "Rue du Puits 12, 4000 Liège"
|
|
password = "dummy123"
|
|
pydantic_data = PrivateUser(name=name, address=address, password=password)
|
|
# Assert that class was correctly extended
|
|
self.assertTrue(pydantic_data.address)
|
|
self.assertTrue(pydantic_data.password)
|
|
|
|
with self._create_test_client(router=demo_pydantic_router) as test_client:
|
|
response: Response = test_client.post(
|
|
"/post_private_user", content=pydantic_data.model_dump_json()
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
user = response.json()
|
|
self.assertEqual(user["name"], name)
|
|
self.assertEqual(user["address"], address)
|
|
# Private attrs were not returned
|
|
self.assertFalse("password" in user.keys())
|
|
|
|
def test_post_private_user_generic(self):
|
|
"""
|
|
/post_private_user_generic return attributes from User, but not PrivateUser
|
|
|
|
Security check: this method should never return attributes from
|
|
derived type PrivateUser, even thought a PrivateUser object
|
|
is given as input.
|
|
This test is specifically made to test this assertion with generics.
|
|
"""
|
|
name = "Jean Dupont"
|
|
address = "Rue du Puits 12, 4000 Liège"
|
|
password = "dummy123"
|
|
pydantic_data = PrivateUser(name=name, address=address, password=password)
|
|
# Assert that class was correctly extended
|
|
self.assertTrue(pydantic_data.address)
|
|
self.assertTrue(pydantic_data.password)
|
|
|
|
with self._create_test_client(router=demo_pydantic_router) as test_client:
|
|
response: Response = test_client.post(
|
|
"/post_private_user_generic", content=pydantic_data.model_dump_json()
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
res = response.json()
|
|
self.assertEqual(res["total"], 1)
|
|
user = res["items"][0]
|
|
self.assertEqual(user["name"], name)
|
|
self.assertEqual(user["address"], address)
|
|
# Private attrs were not returned
|
|
self.assertFalse("password" in user.keys())
|
|
|
|
def test_get_user_failed_no_address(self):
|
|
"""
|
|
Try to get a specific user but having no address
|
|
-> Error because address is a required field on User (extended) class
|
|
:return:
|
|
"""
|
|
user = self.env["res.users"].create(
|
|
{
|
|
"name": "Michel Dupont",
|
|
"login": "michel",
|
|
}
|
|
)
|
|
with self._create_test_client(
|
|
router=demo_pydantic_router
|
|
) as test_client, self.assertRaises(ResponseValidationError):
|
|
test_client.get(f"/{user.id}")
|
|
|
|
def test_get_user_failed_no_pwd(self):
|
|
"""
|
|
Try to get a specific user having an address but no password.
|
|
-> No error because return type is User, not PrivateUser
|
|
:return:
|
|
"""
|
|
user = self.env["res.users"].create(
|
|
{
|
|
"name": "Michel Dupont",
|
|
"login": "michel",
|
|
"street": "Rue du Moulin",
|
|
}
|
|
)
|
|
self.assertFalse(user.password)
|
|
with self._create_test_client(router=demo_pydantic_router) as test_client:
|
|
response: Response = test_client.get(f"/private/{user.id}")
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_extra_forbid_response_fails(self):
|
|
"""
|
|
If adding extra="forbid" to the User model, we cannot write
|
|
a router with a response type = User and returning PrivateUser
|
|
in the code
|
|
"""
|
|
name = "Jean Dupont"
|
|
address = "Rue du Puits 12, 4000 Liège"
|
|
password = "dummy123"
|
|
pydantic_data = PrivateCustomer(name=name, address=address, password=password)
|
|
|
|
with self.assertRaises(ResponseValidationError), self._create_test_client(
|
|
router=demo_pydantic_router
|
|
) as test_client:
|
|
test_client.post(
|
|
"/post_private_customer", content=pydantic_data.model_dump_json()
|
|
)
|