Skip to content

Commit 1256c28

Browse files
committed
Added socketio test client
1 parent 82510a8 commit 1256c28

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

ellar/socket_io/testing/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .module import SocketIOTestingModule, TestGateway
2+
3+
__all__ = ["SocketIOTestingModule", "TestGateway"]

ellar/socket_io/testing/module.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import typing as t
2+
3+
import socketio
4+
5+
from ellar.compatible import asynccontextmanager
6+
from ellar.testing.module import Test, TestingModule
7+
from ellar.testing.uvicorn_server import EllarUvicornServer
8+
9+
10+
class RunWithServerContext:
11+
__slots__ = ("sio", "base_url")
12+
13+
def __init__(self, sio: socketio.AsyncClient, base_url: str) -> None:
14+
self.sio = sio
15+
self.base_url: str = base_url
16+
17+
async def connect(
18+
self,
19+
path: str = "",
20+
namespaces: str = "/",
21+
socketio_path: str = "socket.io",
22+
**kwargs: t.Any,
23+
) -> None:
24+
assert path == "" or path.startswith("/"), "Routed paths must start with '/'"
25+
await self.sio.connect(
26+
self.base_url + path,
27+
namespaces=namespaces,
28+
socketio_path=socketio_path,
29+
**kwargs,
30+
)
31+
32+
async def wait(self, seconds: float = 0.5) -> None:
33+
await self.sio.sleep(seconds)
34+
35+
def new_socket_client_context(self) -> "RunWithServerContext":
36+
sio = socketio.AsyncClient()
37+
return self.__class__(sio=sio, base_url=self.base_url)
38+
39+
40+
class SocketIOTestingModule(TestingModule):
41+
@asynccontextmanager
42+
async def run_with_server(
43+
self, host: str = "127.0.0.1", port: int = 8000
44+
) -> t.AsyncIterator[RunWithServerContext]:
45+
base_url = f"http://{host}:{port}"
46+
47+
server = EllarUvicornServer(app=self.create_application(), host=host, port=port)
48+
49+
await server.start_up()
50+
sio = socketio.AsyncClient()
51+
52+
yield RunWithServerContext(sio=sio, base_url=base_url)
53+
54+
await sio.disconnect()
55+
await server.tear_down()
56+
57+
58+
class TestGateway(Test):
59+
TESTING_MODULE = SocketIOTestingModule

ellar/testing/uvicorn_server.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import asyncio
2+
import typing as t
3+
4+
import uvicorn
5+
from starlette.types import ASGIApp
6+
7+
8+
class EllarUvicornServer(uvicorn.Server):
9+
"""
10+
Uvicorn Server for testing purpose.
11+
12+
13+
@pytest_asyncio.fixture
14+
async def start_server():
15+
server = EllarUvicornServer(asgi_app)
16+
await server.start_up()
17+
yield
18+
await server.tear_down()
19+
20+
"""
21+
22+
def __init__(self, app: ASGIApp, host: str = "127.0.0.1", port: int = 8000):
23+
self._startup_done = asyncio.Event()
24+
self._serve_task: t.Optional[t.Awaitable[t.Any]] = None
25+
super().__init__(config=uvicorn.Config(app, host=host, port=port))
26+
27+
async def startup(self, sockets: list = None) -> None:
28+
"""Override uvicorn startup"""
29+
await super().startup(sockets)
30+
self.config.setup_event_loop()
31+
self._startup_done.set()
32+
33+
async def start_up(self) -> None:
34+
"""Start up server asynchronously"""
35+
self._serve_task = asyncio.create_task(self.serve())
36+
await self._startup_done.wait()
37+
38+
async def tear_down(self) -> None:
39+
"""Shut down server asynchronously"""
40+
self.should_exit = True
41+
if self._serve_task:
42+
await self._serve_task

0 commit comments

Comments
 (0)