# Socket.IO Module Real-time WebSocket communication for active game sessions. ## Architecture ``` Client <--WebSocket--> Socket.IO Server <--> GameService <--> GameEngine | v ConnectionManager <--> Redis (presence tracking) ``` ## Components | Component | Responsibility | DI Pattern | |-----------|---------------|------------| | `server.py` | Socket.IO server setup, event handlers | Uses `auth_handler` | | `auth.py` | `AuthHandler` class for JWT authentication | `token_verifier`, `conn_manager` | ## AuthHandler Handles JWT-based authentication with injectable dependencies: ```python class AuthHandler: def __init__( self, token_verifier: TokenVerifier | None = None, # Callable[[str], UUID | None] conn_manager: ConnectionManager | None = None, ) -> None: self._token_verifier = token_verifier or verify_access_token self._connection_manager = conn_manager or connection_manager # Global singleton for production auth_handler = AuthHandler() # Tests inject mocks via constructor handler = AuthHandler( token_verifier=lambda token: user_id, conn_manager=mock_connection_manager, ) ``` ### Methods | Method | Purpose | |--------|---------| | `authenticate_connection(sid, auth)` | Validate JWT, return `AuthResult` | | `setup_authenticated_session(sio, sid, user_id)` | Save session, register connection | | `cleanup_authenticated_session(sid)` | Unregister connection, return user_id | ## Event Handlers All handlers are in the `/game` namespace: | Event | Direction | Purpose | |-------|-----------|---------| | `connect` | Client→Server | Authenticate and establish session | | `disconnect` | Client→Server | Clean up session and notify opponent | | `game:join` | Client→Server | Join/rejoin a game session | | `game:action` | Client→Server | Execute a game action | | `game:resign` | Client→Server | Resign from game | | `game:heartbeat` | Client→Server | Keep connection alive | ## Client Connection ```javascript const socket = io("wss://api.example.com", { auth: { token: "JWT_ACCESS_TOKEN" } }); socket.on("connect", () => { socket.emit("game:join", { game_id: "uuid" }); }); socket.on("game:state", (state) => { // Full game state update }); socket.on("auth_error", (error) => { // { code: "invalid_token", message: "..." } }); ``` ## Testing Use dependency injection - no monkey patching: ```python @pytest.fixture def mock_token_verifier(): return MagicMock(return_value=uuid4()) @pytest.fixture def mock_connection_manager(): cm = AsyncMock() cm.register_connection = AsyncMock() return cm @pytest.fixture def auth_handler(mock_token_verifier, mock_connection_manager): return AuthHandler( token_verifier=mock_token_verifier, conn_manager=mock_connection_manager, ) async def test_authenticate_success(auth_handler, mock_token_verifier): """Test successful authentication with valid JWT.""" result = await auth_handler.authenticate_connection("sid", {"token": "valid"}) assert result.success mock_token_verifier.assert_called_once_with("valid") ``` ## See Also - `app/services/connection_manager.py` - Connection presence tracking - `app/services/game_service.py` - Game orchestration - `app/schemas/ws_messages.py` - WebSocket message schemas - `CLAUDE.md` - Architecture guidelines