mantimon-tcg/backend/tests/socketio/test_server_setup.py
Cal Corum 0c810e5b30 Add Phase 4 WebSocket infrastructure (WS-001 through GS-001)
WebSocket Message Schemas (WS-002):
- Add Pydantic models for all client/server WebSocket messages
- Implement discriminated unions for message type parsing
- Include JoinGame, Action, Resign, Heartbeat client messages
- Include GameState, ActionResult, Error, TurnStart server messages

Connection Manager (WS-003):
- Add Redis-backed WebSocket connection tracking
- Implement user-to-sid mapping with TTL management
- Support game room association and opponent lookup
- Add heartbeat tracking for connection health

Socket.IO Authentication (WS-004):
- Add JWT-based authentication middleware
- Support token extraction from multiple formats
- Implement session setup with ConnectionManager integration
- Add require_auth helper for event handlers

Socket.IO Server Setup (WS-001):
- Configure AsyncServer with ASGI mode
- Register /game namespace with event handlers
- Integrate with FastAPI via ASGIApp wrapper
- Configure CORS from application settings

Game Service (GS-001):
- Add stateless GameService for game lifecycle orchestration
- Create engine per-operation using rules from GameState
- Implement action-based RNG seeding for deterministic replay
- Add rng_seed field to GameState for replay support

Architecture verified:
- Core module independence (no forbidden imports)
- Config from request pattern (rules in GameState)
- Dependency injection (constructor deps, method config)
- All 1090 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:21:20 -06:00

114 lines
3.5 KiB
Python

"""Tests for Socket.IO server setup and configuration.
These tests verify that the Socket.IO server is correctly configured
and can be mounted alongside FastAPI.
"""
class TestSocketIOSetup:
"""Tests for Socket.IO server initialization."""
def test_sio_server_exists(self) -> None:
"""
Verify that the Socket.IO AsyncServer is created.
The server instance should be available as a module-level export
and configured for ASGI mode.
"""
from app.socketio import sio
assert sio is not None
assert sio.async_mode == "asgi"
def test_game_namespace_handlers_registered(self) -> None:
"""
Verify that /game namespace event handlers are registered.
All required event handlers should be available on the sio instance
before any connections are made.
"""
from app.socketio import sio
# Check that handlers are registered for /game namespace
assert "/game" in sio.handlers
game_handlers = sio.handlers["/game"]
expected_events = [
"connect",
"disconnect",
"game:join",
"game:action",
"game:resign",
"game:heartbeat",
]
for event in expected_events:
assert event in game_handlers, f"Missing handler for {event}"
def test_create_socketio_app_returns_asgi_app(self) -> None:
"""
Verify that create_socketio_app returns a valid ASGI application.
The combined app should wrap the FastAPI app and handle both
HTTP and WebSocket connections.
"""
import socketio
from fastapi import FastAPI
from app.socketio import create_socketio_app
# Create a minimal FastAPI app for testing
test_app = FastAPI()
# Create combined ASGI app
combined = create_socketio_app(test_app)
# Verify it's a Socket.IO ASGI app
assert isinstance(combined, socketio.ASGIApp)
def test_cors_configured_from_settings(self) -> None:
"""
Verify that Socket.IO CORS settings match application settings.
The Socket.IO server should allow the same origins as FastAPI
to ensure consistent cross-origin behavior.
"""
from app.config import settings
from app.socketio import sio
# Socket.IO stores CORS origins in eio (Engine.IO) settings
# The cors_allowed_origins should match settings
assert sio.eio.cors_allowed_origins == settings.cors_origins
class TestMainAppIntegration:
"""Tests for Socket.IO integration with main app."""
def test_main_app_is_combined_asgi_app(self) -> None:
"""
Verify that the main app module exports the combined ASGI app.
The 'app' variable in main.py should be the Socket.IO wrapped
FastAPI application, not the raw FastAPI app.
"""
import socketio
from app.main import app
# The exported 'app' should be a Socket.IO ASGI app
assert isinstance(app, socketio.ASGIApp)
def test_fastapi_app_accessible(self) -> None:
"""
Verify that the underlying FastAPI app is still accessible.
The FastAPI app should be available as _fastapi_app in main
for testing and direct access when needed.
"""
from fastapi import FastAPI
from app.main import _fastapi_app
assert isinstance(_fastapi_app, FastAPI)
assert _fastapi_app.title == "Mantimon TCG"