Skip to content

Commit 2493392

Browse files
committed
Add tests
1 parent 54a5743 commit 2493392

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

tests/socketio_app/namespaces/__init__.py

Whitespace-only changes.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from unittest.mock import AsyncMock, patch
2+
3+
import pytest
4+
5+
from socketio_app import ChatNamespace
6+
7+
8+
@pytest.fixture
9+
def chat_namespace():
10+
return ChatNamespace("/chat")
11+
12+
13+
@pytest.mark.asyncio
14+
async def test_on_connect(chat_namespace):
15+
sid = "test_session_id"
16+
environ = {}
17+
18+
# Test that connect doesn't raise any exceptions
19+
chat_namespace.on_connect(sid, environ)
20+
21+
22+
@pytest.mark.asyncio
23+
async def test_on_disconnect(chat_namespace):
24+
sid = "test_session_id"
25+
reason = "test_reason"
26+
27+
# Test that disconnect doesn't raise any exceptions
28+
chat_namespace.on_disconnect(sid, reason)
29+
30+
31+
@pytest.mark.asyncio
32+
async def test_on_echo_message(chat_namespace):
33+
sid = "test_session_id"
34+
test_data = {"message": "Hello, World!"}
35+
36+
# Mock the emit method
37+
chat_namespace.emit = AsyncMock()
38+
39+
# Mock the logging
40+
with patch("logging.info") as mock_log:
41+
await chat_namespace.on_echo_message(sid, test_data)
42+
43+
# Verify logging was called
44+
mock_log.assert_called_once_with("received message")
45+
46+
# Verify emit was called with correct arguments
47+
chat_namespace.emit.assert_called_once_with("echo_response", test_data)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from unittest.mock import patch
2+
3+
import socketio
4+
from starlette.routing import Mount, Router
5+
6+
from common import AppConfig
7+
from socketio_app import create_app
8+
9+
10+
def test_create_app_returns_router():
11+
"""Test that create_app returns a Router instance"""
12+
app = create_app()
13+
assert isinstance(app, Router)
14+
15+
16+
def test_create_app_with_custom_config():
17+
"""Test that create_app accepts custom config"""
18+
test_config = AppConfig(DEBUG=True)
19+
with patch("common.bootstrap.init_storage", return_value=None):
20+
app = create_app(test_config=test_config)
21+
22+
assert isinstance(app, Router)
23+
24+
25+
def test_create_app_routes():
26+
"""Test that create_app creates all expected routes"""
27+
with patch("common.bootstrap.init_storage", return_value=None):
28+
app = create_app()
29+
30+
# Check that we have exactly 3 routes (docs JSON, docs HTML, and socketio mount)
31+
assert len(app.routes) == 3
32+
33+
# Check routes paths and methods
34+
routes = [(route.path, getattr(route, "methods", None)) for route in app.routes]
35+
assert ("/docs/asyncapi.json", {"GET", "HEAD"}) in routes
36+
assert ("/docs", {"GET", "HEAD"}) in routes
37+
38+
# Check that one route is a Mount instance for socketio
39+
mount_routes = [route for route in app.routes if isinstance(route, Mount)]
40+
assert len(mount_routes) == 1
41+
assert mount_routes[0].name == "socketio"
42+
assert isinstance(mount_routes[0].app, socketio.ASGIApp)
43+
44+
45+
def test_create_app_socketio_namespace():
46+
"""Test that socketio server has the chat namespace registered"""
47+
with patch("common.bootstrap.init_storage", return_value=None):
48+
app = create_app()
49+
50+
# Find the socketio mount
51+
socketio_mount = next(route for route in app.routes if isinstance(route, Mount))
52+
sio_app = socketio_mount.app
53+
54+
# Check that the chat namespace is registered
55+
assert "/chat" in sio_app.engineio_server.namespace_handlers

tests/socketio_app/web_routes/__init__.py

Whitespace-only changes.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from unittest.mock import Mock, patch
2+
3+
import pytest
4+
from pydantic import BaseModel
5+
from starlette.requests import Request
6+
7+
from socketio_app.web_routes.docs import (
8+
ASYNCAPI_CSS_DEFAULT_URL,
9+
ASYNCAPI_JS_DEFAULT_URL,
10+
NORMALIZE_CSS_DEFAULT_URL,
11+
PydanticResponse,
12+
asyncapi_json,
13+
get_asyncapi_html,
14+
)
15+
16+
17+
# Test model
18+
class TestModel(BaseModel):
19+
name: str
20+
value: int
21+
22+
23+
# Fixtures
24+
@pytest.fixture
25+
def test_model():
26+
return TestModel(name="test", value=42)
27+
28+
29+
@pytest.fixture
30+
def mock_request():
31+
return Mock(spec=Request)
32+
33+
34+
@pytest.fixture
35+
def mock_app_config():
36+
with patch("socketio_app.web_routes.docs.AppConfig") as mock:
37+
mock.return_value.APP_NAME = "Test App"
38+
yield mock
39+
40+
41+
# Tests for PydanticResponse
42+
def test_pydantic_response_render(test_model):
43+
response = PydanticResponse(test_model)
44+
expected = b'{"name":"test","value":42}'
45+
assert response.render(test_model) == expected
46+
47+
48+
# Tests for asyncapi_json endpoint
49+
async def test_asyncapi_json(mock_request, test_model):
50+
with patch("socketio_app.web_routes.docs.get_schema") as mock_get_schema:
51+
mock_get_schema.return_value = test_model
52+
response = await asyncapi_json(mock_request)
53+
assert isinstance(response, PydanticResponse)
54+
assert response.body == b'{"name":"test","value":42}'
55+
56+
57+
# Tests for get_asyncapi_html endpoint
58+
async def test_get_asyncapi_html_default_params(mock_request, mock_app_config):
59+
mock_request.query_params = {}
60+
response = await get_asyncapi_html(mock_request)
61+
62+
assert response.status_code == 200
63+
assert response.headers["content-type"] == "text/html; charset=utf-8"
64+
65+
content = response.body.decode()
66+
assert "Test App AsyncAPI" in content
67+
assert ASYNCAPI_JS_DEFAULT_URL in content
68+
assert NORMALIZE_CSS_DEFAULT_URL in content
69+
assert ASYNCAPI_CSS_DEFAULT_URL in content
70+
assert '"sidebar": true' in content
71+
assert '"info": true' in content
72+
73+
74+
async def test_get_asyncapi_html_custom_params(mock_request, mock_app_config):
75+
mock_request.query_params = {
76+
"sidebar": "false",
77+
"info": "false",
78+
"servers": "false",
79+
"operations": "false",
80+
"messages": "false",
81+
"schemas": "false",
82+
"errors": "false",
83+
"expand_message_examples": "true",
84+
}
85+
86+
response = await get_asyncapi_html(mock_request)
87+
content = response.body.decode()
88+
89+
assert '"sidebar": false' in content
90+
assert '"info": false' in content
91+
assert '"servers": false' in content
92+
assert '"operations": false' in content
93+
assert '"messages": false' in content
94+
assert '"schemas": false' in content
95+
assert '"errors": false' in content
96+
assert '"messageExamples": true' in content

0 commit comments

Comments
 (0)