361 lines
13 KiB
Python
361 lines
13 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 uuid import UUID, uuid4
|
|
from time import time, sleep
|
|
import json
|
|
import uvicorn
|
|
import socket
|
|
import requests
|
|
from contextlib import closing
|
|
from multiprocessing import Process
|
|
from requests.adapters import HTTPAdapter
|
|
import coverage
|
|
|
|
print(__name__)
|
|
print(__package__)
|
|
|
|
from src.pyrestresource import (
|
|
RestField,
|
|
register_rest_rootpoint,
|
|
RestResourceBase,
|
|
rsrc_verb,
|
|
RestRequestParams_GET,
|
|
RestRequestParams_POST,
|
|
RestRequestParams_Dict_GET,
|
|
T_SupportedRESTFields,
|
|
)
|
|
from pprint import pprint
|
|
|
|
from test import ThreadedUvicorn
|
|
|
|
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 = RestField(default_factory=uuid4, primary_key=True)
|
|
shortname: str
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
|
|
class Profile(RestResourceBase):
|
|
uuid: UUID = RestField(default_factory=uuid4, primary_key=True)
|
|
shortname: str
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
|
|
class Game(RestResourceBase):
|
|
uuid: UUID = RestField(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 = RestField(default_factory=uuid4, primary_key=True)
|
|
name: str
|
|
secret: str = RestField(..., exclude=True)
|
|
|
|
User1 = User(
|
|
uuid="8da57a3c-661f-11ee-8c99-0242ac120002",
|
|
name="chacha",
|
|
secret="la blanquette est bonne",
|
|
)
|
|
|
|
class Patch2(RestResourceBase):
|
|
uuid: UUID = RestField(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 Origin",
|
|
description="test Game Desc Origin",
|
|
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
|
|
|
|
|
|
def find_free_port():
|
|
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
|
s.bind(("", 0))
|
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
return "localhost", s.getsockname()[1]
|
|
|
|
|
|
class Test_RestAPI_WebServer(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
chdir(testdir_path.parent.resolve())
|
|
|
|
def test_nomal_AllCmd_games(self):
|
|
ip, port = find_free_port()
|
|
init_classes()
|
|
|
|
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
|
server.start()
|
|
sleep(1)
|
|
s = requests.Session()
|
|
s.mount("http://", HTTPAdapter(max_retries=0))
|
|
|
|
try:
|
|
# Fetching games
|
|
response = s.get(f"http://{ip}:{port}/games")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIsInstance(data, list)
|
|
self.assertEqual(
|
|
data,
|
|
["9b0381d4-65f6-11ee-8c99-0242ac120002"],
|
|
)
|
|
|
|
# Add a new one (with all values setted)
|
|
response = s.post(
|
|
f"http://{ip}:{port}/games",
|
|
json={
|
|
"shortname": "test",
|
|
"name": "nametest",
|
|
"description": "test Game Desc",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 201)
|
|
data = response.json()
|
|
NEW_GAME_UUID = UUID(data)
|
|
|
|
# Fetching games again
|
|
response = s.get(f"http://{ip}:{port}/games")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIsInstance(data, list)
|
|
self.assertEqual(
|
|
data,
|
|
["9b0381d4-65f6-11ee-8c99-0242ac120002", str(NEW_GAME_UUID)],
|
|
)
|
|
|
|
# Getting accurate values of created element
|
|
response = s.get(f"http://{ip}:{port}/games/{str(NEW_GAME_UUID)}")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIsInstance(data, dict)
|
|
self.assertIsInstance(data, dict)
|
|
self.assertIn("shortname", data)
|
|
self.assertIn("name", data)
|
|
self.assertIn("description", data)
|
|
self.assertIn("uuid", data)
|
|
NEW_GAME_UUID = UUID(data["uuid"])
|
|
del data["uuid"]
|
|
self.assertDictEqual(
|
|
data,
|
|
{
|
|
"name": "nametest",
|
|
"shortname": "test",
|
|
"description": "test Game Desc",
|
|
},
|
|
)
|
|
|
|
# removing the new one
|
|
response = s.delete(f"http://{ip}:{port}/games/{str(NEW_GAME_UUID)}")
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Fetching games again
|
|
response = s.get(f"http://{ip}:{port}/games")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertEqual(
|
|
data,
|
|
["9b0381d4-65f6-11ee-8c99-0242ac120002"],
|
|
)
|
|
|
|
# Getting accurate values
|
|
response = s.get(f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIsInstance(data, dict)
|
|
self.assertIsInstance(data, dict)
|
|
self.assertIn("shortname", data)
|
|
self.assertIn("name", data)
|
|
self.assertIn("description", data)
|
|
self.assertIn("uuid", data)
|
|
NEW_GAME_UUID = UUID(data["uuid"])
|
|
del data["uuid"]
|
|
self.assertDictEqual(
|
|
data,
|
|
{
|
|
"name": None,
|
|
"shortname": "testGame Origin",
|
|
"description": "test Game Desc Origin",
|
|
},
|
|
)
|
|
|
|
# Update values
|
|
response = s.put(
|
|
f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002",
|
|
json={
|
|
"name": "MyName",
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 201)
|
|
|
|
# Getting accurate values
|
|
response = s.get(f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIsInstance(data, dict)
|
|
self.assertIsInstance(data, dict)
|
|
self.assertIn("shortname", data)
|
|
self.assertIn("name", data)
|
|
self.assertIn("description", data)
|
|
self.assertIn("uuid", data)
|
|
NEW_GAME_UUID = UUID(data["uuid"])
|
|
del data["uuid"]
|
|
self.assertDictEqual(
|
|
data,
|
|
{
|
|
"name": "MyName",
|
|
"shortname": "testGame Origin",
|
|
"description": "test Game Desc Origin",
|
|
},
|
|
)
|
|
|
|
# removing original element
|
|
response = s.delete(f"http://{ip}:{port}/games?API_key={str(NEW_GAME_UUID)}")
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Fetching games again
|
|
response = s.get(f"http://{ip}:{port}/games")
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertTrue(len(data) == 0)
|
|
finally:
|
|
s.close()
|
|
server.stop()
|
|
|
|
@unittest.skip
|
|
def test_perf_dict(self):
|
|
print(f"SOCKET PERF TEST")
|
|
n_loop = 10000
|
|
|
|
ip, port = find_free_port()
|
|
init_classes()
|
|
|
|
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
|
server.start()
|
|
sleep(1)
|
|
s = requests.Session()
|
|
s.mount("http://", HTTPAdapter(max_retries=0))
|
|
|
|
try:
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
s.get(f"http://{ip}:{port}/users/8da57a3c-661f-11ee-8c99-0242ac120002")
|
|
end = time()
|
|
print(f"GET 1st level dict: {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
newUUID = uuid4()
|
|
s.post(
|
|
f"http://{ip}:{port}/users?API_key={newUUID}",
|
|
json={"name": "testUser", "secret": "test"},
|
|
)
|
|
end = time()
|
|
|
|
print(f"POST 1st level dict (API_key): {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
newUUID = uuid4()
|
|
s.post(
|
|
f"http://{ip}:{port}/users?API_key={str(newUUID)}",
|
|
json={"name": "testUser", "secret": "test"},
|
|
)
|
|
s.get(f"http://{ip}:{port}/users/{newUUID}")
|
|
end = time()
|
|
print(f"POST/GET 1st level dict (API_key): {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
response = s.post(f"http://{ip}:{port}/users", '{"name": "testUser", "secret": "test"}')
|
|
s.get(f"http://{ip}:{port}/users/{response.json()}")
|
|
end = time()
|
|
print(f"POST/GET 1st level dict (autokey): {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
s.put(
|
|
f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002/shortname",
|
|
json="TestValue!!",
|
|
)
|
|
s.get(f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002/shortname")
|
|
end = time()
|
|
print(f"PUT/GET 1st level (value) dict: {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
s.get(f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002")
|
|
end = time()
|
|
print(f"GET 2nd level dict: {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
s.get(
|
|
f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/shortname",
|
|
)
|
|
end = time()
|
|
print(f"GET 2nd level (value) dict: {int(n_loop/(end-start))} Req/s")
|
|
|
|
start = time()
|
|
for _ in range(n_loop):
|
|
s.put(
|
|
f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/shortname",
|
|
json="TestValue!!",
|
|
)
|
|
s.get(
|
|
f"http://{ip}:{port}/games/9b0381d4-65f6-11ee-8c99-0242ac120002/patchs/cee1e870-65fa-11ee-8c99-0242ac120002/shortname",
|
|
)
|
|
end = time()
|
|
print(f"PUT/GET 2nd level (value) dict: {int(n_loop/(end-start))} Req/s")
|
|
|
|
finally:
|
|
s.close()
|
|
server.stop()
|