Phase 1-4: Audio stack, mixer engine, MIDI, and network API
Lint & Validate / lint (push) Has been cancelled
Lint & Validate / lint (push) Has been cancelled
P2-R1: ALSA + JACK2 low-latency config (scripts, quirks, tuning) P2-R2: Carla integration (build scripts, 8ch rack config, NAM LV2 support) P2-R3: Plugin manager, categories, blacklist, NAM model support P3-R1: Mixer DSP engine (channel strip, routing matrix, bus mgr, automation) P4-R1: MIDI engine (learn mode, clock sync, HID discovery, mapping store) P4-R2: Network API (OSC server, FastAPI REST, WebSocket, auth, rate limiter) P5-R1: Touchscreen UI evaluation + main entry point docs: Audio stack, Carla integration, MIDI support, UI evaluation tests: Full test suite (292 passing)
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user