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

383 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 pydantic import Field
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
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",
)
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 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)
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
return "localhost", s.getsockname()[1]
def launch_server(ip, port):
print(f"port2={port}")
init_classes()
uvicorn.run(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True)
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()
print(f"ip1={ip}")
print(f"port1={port}")
proc = Process(
target=launch_server,
args=(
ip,
port,
),
)
proc.start()
sleep(1)
s = requests.Session()
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"],
)
# Login in
"""
response = s.post(
f"http://{ip}:{port}/login",
params={"username": "test", "password": "test"},
)
self.assertEqual(response.status_code, 200)
"""
# 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:
proc.terminate()
s.close()
@unittest.skip
def test_perf_dict(self):
print(f"SOCKET PERF TEST")
n_loop = 10000
ip, port = find_free_port()
print(f"ip1={ip}")
print(f"port1={port}")
proc = Process(
target=launch_server,
args=(
ip,
port,
),
)
proc.start()
sleep(1)
s = requests.Session()
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:
proc.terminate()
s.close()