- ConnectionManager: Add redis_factory constructor parameter - GameService: Add engine_factory constructor parameter - AuthHandler: New class replacing standalone functions with token_verifier and conn_manager injection - Update all tests to use constructor DI instead of patch() - Update CLAUDE.md with factory injection patterns - Update services README with new patterns - Add socketio README documenting AuthHandler and events Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
121 lines
3.4 KiB
Markdown
121 lines
3.4 KiB
Markdown
# 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
|