"""Tests for WebSocket manager.""" import asyncio import json import pytest from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.testclient import TestClient from src.network.websocket import WebSocketManager from src.network.schemas import ( MixerStateSchema, ChannelSchema, WSMessage, WSMessageType, ) from src.network.auth import APIKeyAuth from src.network.rate_limiter import RateLimiter class TestWebSocketManager: """Tests for WebSocketManager (unit tests for the manager class).""" @pytest.fixture def manager(self): return WebSocketManager(max_connections=10) def test_initial_state(self, manager): assert manager.connected_clients == 0 stats = manager.stats assert stats["connected_clients"] == 0 assert stats["total_connections"] == 0 assert stats["total_broadcasts"] == 0 def test_set_state_provider(self, manager): called = [] def get_state(): called.append(True) return MixerStateSchema( channels=[ChannelSchema(channel=0)], ) manager.set_state_provider(get_state) assert manager._state_provider is not None def test_set_parameter_handler(self, manager): calls = [] def handler(pt, v, ch): calls.append((pt, v, ch)) manager.set_parameter_handler(handler) assert manager._parameter_handler is not None def test_set_transport_handler(self, manager): calls = [] def handler(cmd): calls.append(cmd) manager.set_transport_handler(handler) assert manager._transport_handler is not None def test_max_connections_property(self, manager): assert manager._max_connections == 10 class TestWebSocketBroadcast: """Tests for WebSocket broadcasting logic.""" @pytest.fixture def manager(self): mgr = WebSocketManager(max_connections=10) return mgr @pytest.mark.asyncio async def test_broadcast_no_clients(self, manager): msg = WSMessage( type=WSMessageType.PARAMETER_UPDATE, payload={"param_type": "volume", "value": -3.0, "channel": 0}, ) count = await manager.broadcast(msg) assert count == 0 @pytest.mark.asyncio async def test_broadcast_parameter_update_no_clients(self, manager): count = await manager.broadcast_parameter_update("volume", -3.0, 0) assert count == 0 @pytest.mark.asyncio async def test_broadcast_transport_no_clients(self, manager): count = await manager.broadcast_transport_update({"command": "play"}) assert count == 0 @pytest.mark.asyncio async def test_client_add_remove(self, manager): """Test client bookkeeping (without actual WebSocket).""" assert manager.connected_clients == 0 # Simulate adding a client async with manager._lock: manager._clients["test-client"] = "mock-ws" manager._total_connections += 1 assert manager.connected_clients == 1 assert manager.stats["total_connections"] == 1 # Remove client await manager._remove_client("test-client") assert manager.connected_clients == 0 class TestWebSocketStats: """Tests for WebSocket statistics.""" def test_stats_structure(self): manager = WebSocketManager(max_connections=5) stats = manager.stats assert "connected_clients" in stats assert "total_connections" in stats assert "total_messages" in stats assert "total_broadcasts" in stats assert "uptime_seconds" in stats assert "max_connections" in stats assert stats["max_connections"] == 5 def test_uptime_increases(self): manager = WebSocketManager() stats1 = manager.stats # Stats should have a reasonable uptime assert stats1["uptime_seconds"] >= 0 class TestWebSocketMessageTypes: """Tests for WebSocket message type handling.""" def test_ws_message_serialization(self): msg = WSMessage( type=WSMessageType.FULL_STATE, payload={"channels": [], "master": {"volume_db": 0.0}}, ) data = msg.model_dump() assert data["type"] == "full_state" def test_all_message_types(self): for msg_type in WSMessageType: msg = WSMessage(type=msg_type, payload={}) assert msg.type == msg_type assert msg.payload == {} def test_parameter_update_message(self): msg = WSMessage( type=WSMessageType.PARAMETER_UPDATE, payload={ "param_type": "comp_threshold", "value": -15.0, "channel": 2, }, ) assert msg.payload["param_type"] == "comp_threshold" assert msg.payload["value"] == -15.0