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()