Files
pyrestresource/test/test_rest_resource.py
2023-10-31 23:58:51 +00:00

558 lines
23 KiB
Python

from __future__ import annotations
import unittest
from unittest.mock import patch
from os import chdir
from pathlib import Path
from typing import Optional
from pydantic import Field
from uuid import UUID, uuid4
from time import time
import json
print(__name__)
print(__package__)
from src.pyrestresource import (
register_rest_rootpoint,
RestResourceBase,
rsrc_verb,
RestRequestParams_GET,
RestRequestParams_POST,
RestRequestParams_Dict_GET,
T_SupportedRESTFields,
)
from pprint import pprint
testdir_path = Path(__file__).parent.resolve()
chdir(testdir_path.parent.resolve())
# to allow mock-ing, all the tested classes are in a function
def init_classes():
class Info(RestResourceBase):
version: str
api_version: str
class Patch(RestResourceBase):
uuid: UUID = Field(default_factory=uuid4, primary_key=True)
shortname: str
name: Optional[str] = None
description: Optional[str] = None
class Profile(RestResourceBase):
uuid: UUID = Field(default_factory=uuid4, primary_key=True)
shortname: str
name: Optional[str] = None
description: Optional[str] = None
class Game(RestResourceBase):
uuid: UUID = Field(default_factory=uuid4, primary_key=True)
shortname: str
name: Optional[str] = None
description: Optional[str] = None
profiles: dict[UUID, Profile] = {}
patchs: dict[UUID, Patch] = {}
Patch_1 = Patch(uuid="cee1e870-65fa-11ee-8c99-0242ac120002", shortname="testPatch1")
Patch_2 = Patch(uuid="d385a1d2-65fa-11ee-8c99-0242ac120002", shortname="testPatch2")
class User(RestResourceBase):
uuid: UUID = Field(default_factory=uuid4, primary_key=True)
name: str
secret: str = Field(..., exclude=True)
User1 = User(
uuid="8da57a3c-661f-11ee-8c99-0242ac120002",
name="chacha",
secret="la blanquette est bonne",
)
ext_patchs: dict[UUID, Patch] = {}
class Patch2(RestResourceBase):
uuid: UUID = Field(default_factory=uuid4, primary_key=True)
shortname: str
name: Optional[str] = None
description: Optional[str] = None
@register_rest_rootpoint
class RootApp(RestResourceBase):
testValueRoot: float = 3.14
info: Info = Info(version="0.0.1", api_version="0.0.2")
games: dict[UUID, Game] = {
UUID("9b0381d4-65f6-11ee-8c99-0242ac120002"): Game(
uuid="9b0381d4-65f6-11ee-8c99-0242ac120002",
shortname="testGame",
patchs={Patch_1.uuid: Patch_1},
profiles={
UUID("aee1e870-65fa-11ee-8c99-0242ac120002"): Profile(
uuid="aee1e870-65fa-11ee-8c99-0242ac120002",
shortname="testprofile",
)
},
)
}
patchs: dict[UUID, Patch] = {Patch_1.uuid: Patch_1, Patch_2.uuid: Patch_2}
users: dict[UUID, User] = {User1.uuid: User1}
patchs2: dict[UUID, Patch2] = {}
# this add the classes to globals to allow using them later on
# => this is only for uinit-testing purpose and is not needed in real use
globals()[Info.__name__] = Info
globals()[Game.__name__] = Game
globals()[User.__name__] = User
globals()[Profile.__name__] = Profile
globals()[Patch.__name__] = Patch
globals()[Patch2.__name__] = Patch2
globals()[RootApp.__name__] = RootApp
class Test_RestAPI_GET(unittest.TestCase):
def setUp(self) -> None:
chdir(testdir_path.parent.resolve())
init_classes()
self.testapp = RootApp()
def test_get_root(self):
result = self.testapp.process_request("/", rsrc_verb.GET)
self.assertEqual(result, '{"testValueRoot": 3.14}')
def test_get_root__multiple_slash(self):
result = self.testapp.process_request("/////", rsrc_verb.GET)
self.assertEqual(result, '{"testValueRoot": 3.14}')
result = self.testapp.process_request("////", rsrc_verb.GET)
self.assertEqual(result, '{"testValueRoot": 3.14}')
def test_get_root__nested_value(self):
result = self.testapp.process_request("/testValueRoot", rsrc_verb.GET)
self.assertEqual(result, "3.14")
def test_get_root__nested_value__trailing_slash(self):
result = self.testapp.process_request("/testValueRoot/", rsrc_verb.GET)
self.assertEqual(result, "3.14")
result = self.testapp.process_request("/testValueRoot//", rsrc_verb.GET)
self.assertEqual(result, "3.14")
result = self.testapp.process_request("/testValueRoot///", rsrc_verb.GET)
self.assertEqual(result, "3.14")
def test_get_root__nested_value__multiple_slash(self):
result = self.testapp.process_request("//testValueRoot", rsrc_verb.GET)
self.assertEqual(result, "3.14")
result = self.testapp.process_request("///testValueRoot", rsrc_verb.GET)
self.assertEqual(result, "3.14")
def test_get_version(self):
result = self.testapp.process_request("/info", rsrc_verb.GET)
self.assertEqual(result, '{"version": "0.0.1", "api_version": "0.0.2"}')
def test_get_version__trailing_slash(self):
result = self.testapp.process_request("/info/", rsrc_verb.GET)
self.assertEqual(result, '{"version": "0.0.1", "api_version": "0.0.2"}')
result = self.testapp.process_request("/info//", rsrc_verb.GET)
self.assertEqual(result, '{"version": "0.0.1", "api_version": "0.0.2"}')
result = self.testapp.process_request("/info///", rsrc_verb.GET)
self.assertEqual(result, '{"version": "0.0.1", "api_version": "0.0.2"}')
def test_get_version__multiple_slash(self):
result = self.testapp.process_request("//info", rsrc_verb.GET)
self.assertEqual(result, '{"version": "0.0.1", "api_version": "0.0.2"}')
result = self.testapp.process_request("///info", rsrc_verb.GET)
self.assertEqual(result, '{"version": "0.0.1", "api_version": "0.0.2"}')
def test_get_version__nested_value(self):
result = self.testapp.process_request("/info/api_version", rsrc_verb.GET)
self.assertEqual(result, '"0.0.2"')
result = self.testapp.process_request("/info/version", rsrc_verb.GET)
self.assertEqual(result, '"0.0.1"')
def test_get_dict_games(self):
result = self.testapp.process_request("/games", rsrc_verb.GET)
self.assertEqual(result, '["9b0381d4-65f6-11ee-8c99-0242ac120002"]')
def test_get_dict_patchs(self):
result = self.testapp.process_request("/patchs", rsrc_verb.GET)
self.assertEqual(
result,
'["cee1e870-65fa-11ee-8c99-0242ac120002", "d385a1d2-65fa-11ee-8c99-0242ac120002"]',
)
def test_get_dict_patch_element(self):
result = self.testapp.process_request("/patchs/cee1e870-65fa-11ee-8c99-0242ac120002", rsrc_verb.GET)
self.assertEqual(
result,
'{"uuid": "cee1e870-65fa-11ee-8c99-0242ac120002", "shortname": "testPatch1", "name": null, "description": null}',
)
def test_get_dict_game_element(self):
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002", rsrc_verb.GET)
expected = '{"uuid": "9b0381d4-65f6-11ee-8c99-0242ac120002", "shortname": "testGame", "name": null, "description": null}'
self.assertEqual(result, expected)
def test_get_dict_game_element__nested_value(self):
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/shortname", rsrc_verb.GET)
expected = '"testGame"'
self.assertEqual(result, expected)
def test_get_dict_game_element__nested_value2(self):
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/uuid", rsrc_verb.GET)
expected = '"9b0381d4-65f6-11ee-8c99-0242ac120002"'
self.assertEqual(result, expected)
def test_get_nested_dict_games_patchs(self):
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs", rsrc_verb.GET)
self.assertEqual(result, '["cee1e870-65fa-11ee-8c99-0242ac120002"]')
def test_get_nested_dict_games_patch_element(self):
result = self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002",
rsrc_verb.GET,
)
expected = '{"uuid": "cee1e870-65fa-11ee-8c99-0242ac120002", "shortname": "testPatch1", "name": null, "description": null}'
self.assertEqual(result, expected)
def test_get_nested_dict_games_patch_element__nested_value(self):
result = self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/uuid",
rsrc_verb.GET,
)
self.assertEqual(result, '"cee1e870-65fa-11ee-8c99-0242ac120002"')
def test_get_dict_game_element__API_nested(self):
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002?API_nested=True", rsrc_verb.GET)
expected = '{"uuid": "9b0381d4-65f6-11ee-8c99-0242ac120002", "shortname": "testGame", "name": null, "description": null}'
self.assertEqual(result, expected)
def test_get_dict_users(self):
result = self.testapp.process_request("/users", rsrc_verb.GET)
self.assertEqual(result, '["8da57a3c-661f-11ee-8c99-0242ac120002"]')
def test_get_dict_user_element(self):
result = self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002", rsrc_verb.GET)
self.assertEqual(
result,
'{"uuid": "8da57a3c-661f-11ee-8c99-0242ac120002", "name": "chacha"}',
"no secret seen",
)
def test_get_dict_user_element2(self):
result = self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002?API_nested=True", rsrc_verb.GET)
self.assertEqual(
result,
'{"uuid": "8da57a3c-661f-11ee-8c99-0242ac120002", "name": "chacha"}',
"no secret seen",
)
def test_get_dict_user_element__nested_value(self):
result = self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002/name", rsrc_verb.GET)
self.assertEqual(result, '"chacha"')
def test_get_dict_user_element__nested_value__forbiden(self):
with self.assertRaises(RuntimeError): # TODO: custom exception
self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002/secret", rsrc_verb.GET)
def test_get_dict_user_element__nested_value__forbiden2(self):
with self.assertRaises(RuntimeError): # TODO: custom exception
self.testapp.process_request(
"/users/8da57a3c-661f-11ee-8c99-0242ac120002/secret?API_nested=True",
rsrc_verb.GET,
)
class Test_RestAPI_PUT(unittest.TestCase):
def setUp(self) -> None:
chdir(testdir_path.parent.resolve())
init_classes()
self.testapp = RootApp()
def test_put_info(self):
self.testapp.process_request("/info", rsrc_verb.PUT, '{"version": "1.2.3", "api_version": "3.2.1"}')
result = self.testapp.process_request("/info", rsrc_verb.GET)
self.assertEqual(result, '{"version": "1.2.3", "api_version": "3.2.1"}')
def test_put_dict_user_nested_value(self):
self.testapp.process_request(
"/users/8da57a3c-661f-11ee-8c99-0242ac120002/name",
rsrc_verb.PUT,
'"chacha2"',
)
result = self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002/name", rsrc_verb.GET)
self.assertEqual(result, '"chacha2"')
def test_put_user_nested_value__forbiden(self):
with self.assertRaises(RuntimeError): # TODO: custom exception
self.testapp.process_request(
"/users/8da57a3c-661f-11ee-8c99-0242ac120002/secret",
rsrc_verb.PUT,
'"test"',
)
def test_put_dict_user_element(self):
self.testapp.process_request(
"/users/8da57a3c-661f-11ee-8c99-0242ac120002",
rsrc_verb.PUT,
'{"name": "testUser4", "secret": "test5"}',
)
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = '["8da57a3c-661f-11ee-8c99-0242ac120002"]'
self.assertEqual(result, expected)
result = self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002", rsrc_verb.GET)
expected = '{"uuid": "8da57a3c-661f-11ee-8c99-0242ac120002", "name": "testUser4"}'
self.assertEqual(result, expected)
def test_put_dict_patch__nested(self):
self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002",
rsrc_verb.PUT,
'{"shortname": "testPatch998", "name": "MyPatch", "description": "MyDescription123"}',
)
result = self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002",
rsrc_verb.GET,
)
expected = '{"uuid": "cee1e870-65fa-11ee-8c99-0242ac120002", "shortname": "testPatch998", "name": "MyPatch", "description": "MyDescription123"}'
self.assertEqual(result, expected)
class Test_RestAPI_POST(unittest.TestCase):
def setUp(self) -> None:
chdir(testdir_path.parent.resolve())
init_classes()
self.testapp = RootApp()
def test_post_dict_user__API_key(self):
result = self.testapp.process_request(
"/users?API_key=e5e87d32-662b-11ee-8c99-0242ac120002",
rsrc_verb.POST,
'{"name": "testUser", "secret": "test"}',
)
self.assertEqual(result, '"e5e87d32-662b-11ee-8c99-0242ac120002"')
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = '["8da57a3c-661f-11ee-8c99-0242ac120002", "e5e87d32-662b-11ee-8c99-0242ac120002"]'
self.assertEqual(result, expected)
result = self.testapp.process_request("/users/e5e87d32-662b-11ee-8c99-0242ac120002", rsrc_verb.GET)
expected = '{"uuid": "e5e87d32-662b-11ee-8c99-0242ac120002", "name": "testUser"}'
self.assertEqual(result, expected)
def test_post_dict_user__nested_key(self):
result = self.testapp.process_request(
"/users",
rsrc_verb.POST,
'{"name": "testUser2", "secret": "test", "uuid":"e7e86d32-662b-11ee-8c99-0242ac120002"}',
)
self.assertEqual(result, '"e7e86d32-662b-11ee-8c99-0242ac120002"')
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = '["8da57a3c-661f-11ee-8c99-0242ac120002", "e7e86d32-662b-11ee-8c99-0242ac120002"]'
self.assertEqual(result, expected)
result = self.testapp.process_request("/users/e7e86d32-662b-11ee-8c99-0242ac120002", rsrc_verb.GET)
expected = '{"uuid": "e7e86d32-662b-11ee-8c99-0242ac120002", "name": "testUser2"}'
self.assertEqual(result, expected)
@patch(f"{__loader__.name }.uuid4")
def test_post_dict_user__auto_key(self, mock_uuid4):
mock_uuid4.return_value = UUID("5faccb2e-69aa-11ee-8c99-0242ac120002")
# recreating classes & objects to force using the Mock-ed uuid4
init_classes()
self.testapp = RootApp()
result = self.testapp.process_request("/users", rsrc_verb.POST, '{"name": "testUser3", "secret": "test"}')
self.assertEqual(result, '"5faccb2e-69aa-11ee-8c99-0242ac120002"')
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = '["8da57a3c-661f-11ee-8c99-0242ac120002", "5faccb2e-69aa-11ee-8c99-0242ac120002"]'
self.assertEqual(result, expected)
result = self.testapp.process_request("/users/5faccb2e-69aa-11ee-8c99-0242ac120002", rsrc_verb.GET)
expected = '{"uuid": "5faccb2e-69aa-11ee-8c99-0242ac120002", "name": "testUser3"}'
self.assertEqual(result, expected)
def test_post_dict_patch__nested_API_key(self):
self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs?API_key=cee1e971-65fa-11ee-8c99-0242ac120002",
rsrc_verb.POST,
'{"shortname": "testPatch99", "name": "MyPatch", "description": "MyDescription"}',
)
result = self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e971-65fa-11ee-8c99-0242ac120002",
rsrc_verb.GET,
)
expected = '{"uuid": "cee1e971-65fa-11ee-8c99-0242ac120002", "shortname": "testPatch99", "name": "MyPatch", "description": "MyDescription"}'
self.assertEqual(result, expected)
class Test_RestAPI_DELETE(unittest.TestCase):
def setUp(self) -> None:
chdir(testdir_path.parent.resolve())
init_classes()
self.testapp = RootApp()
def test_delete_dict_user__API_key(self):
self.testapp.process_request("/users?API_key=8da57a3c-661f-11ee-8c99-0242ac120002", rsrc_verb.DELETE)
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = "[]"
self.assertEqual(result, expected)
def test_delete_dict_user__All(self):
result = self.testapp.process_request(
"/users?API_key=e5e87d32-662b-11ee-8c99-0242ac120002",
rsrc_verb.POST,
'{"name": "testUser", "secret": "test"}',
)
self.assertEqual(result, '"e5e87d32-662b-11ee-8c99-0242ac120002"')
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = '["8da57a3c-661f-11ee-8c99-0242ac120002", "e5e87d32-662b-11ee-8c99-0242ac120002"]'
self.assertEqual(result, expected)
self.testapp.process_request("/users", rsrc_verb.DELETE)
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = "[]"
self.assertEqual(result, expected)
def test_delete_dict_user_element(self):
self.testapp.process_request("/users/8da57a3c-661f-11ee-8c99-0242ac120002", rsrc_verb.DELETE)
result = self.testapp.process_request("/users", rsrc_verb.GET)
expected = "[]"
self.assertEqual(result, expected)
def test_delete_nested_dict_games_patch_element(self):
self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002",
rsrc_verb.DELETE,
)
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs", rsrc_verb.GET)
expected = "[]"
self.assertEqual(result, expected)
def test_delete_nested_dict_games_patch_API_key(self):
self.testapp.process_request(
"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs?API_key=cee1e870-65fa-11ee-8c99-0242ac120002",
rsrc_verb.DELETE,
)
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs", rsrc_verb.GET)
expected = "[]"
self.assertEqual(result, expected)
def test_delete_nested_dict_games_patch_All(self):
self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs", rsrc_verb.DELETE)
result = self.testapp.process_request("/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs", rsrc_verb.GET)
expected = "[]"
self.assertEqual(result, expected)
class Test_RestAPI_PERFO(unittest.TestCase):
def setUp(self) -> None:
chdir(testdir_path.parent.resolve())
init_classes()
self.testapp = RootApp()
@unittest.skip
def test_perf_dict(self):
print(f"LIB INTERNAL PERF TEST")
n_loop = 10000
start = time()
for i in range(n_loop):
self.testapp.process_request(f"/users/8da57a3c-661f-11ee-8c99-0242ac120002", rsrc_verb.GET)
end = time()
print(f"GET 1st level dict: {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
newUUID = uuid4()
self.testapp.process_request(
f"/users?API_key={newUUID}",
rsrc_verb.POST,
'{"name": "testUser", "secret": "test"}',
)
end = time()
print(f"POST 1st level dict (API_key): {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
newUUID = uuid4()
self.testapp.process_request(
f"/users?API_key={newUUID}",
rsrc_verb.POST,
'{"name": "testUser", "secret": "test"}',
)
self.testapp.process_request(f"/users/{newUUID}", rsrc_verb.GET)
end = time()
print(f"POST/GET 1st level dict (API_key): {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
result = self.testapp.process_request(f"/users", rsrc_verb.POST, '{"name": "testUser", "secret": "test"}')
self.testapp.process_request(f"/users/{json.loads(result)}", rsrc_verb.GET)
end = time()
print(f"POST/GET 1st level dict (autokey): {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
self.testapp.process_request(
f"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/shortname",
rsrc_verb.PUT,
'"TestValue!!"',
)
self.testapp.process_request(f"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/shortname", rsrc_verb.GET)
end = time()
print(f"PUT/GET 1st level (value) dict: {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
self.testapp.process_request(
f"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002",
rsrc_verb.GET,
)
end = time()
print(f"GET 2nd level dict: {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
self.testapp.process_request(
f"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/shortname",
rsrc_verb.GET,
)
end = time()
print(f"GET 2nd level (value) dict: {int(n_loop/(end-start))} Req/s")
start = time()
for i in range(n_loop):
self.testapp.process_request(
f"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/shortname",
rsrc_verb.PUT,
'"TestValue!!"',
)
self.testapp.process_request(
f"/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/shortname",
rsrc_verb.GET,
)
end = time()
print(f"PUT/GET 2nd level (value) dict: {int(n_loop/(end-start))} Req/s")