fix: separate bot vs viewer WebSocket connections, add client identification
The dashboard's own WS connection was being counted as a bot subscriber,
causing "1 bot connected" with no bots actually present. Now WS clients
send a role ("bot" or "viewer") in the subscribe message. Only bots count
toward the subscriber total. Bot plugins also send a configurable client_id
so the dashboard shows which specific bots are connected.
Made-with: Cursor
This commit is contained in:
@@ -162,12 +162,23 @@ def test_announce_invalid_position(client, db):
|
||||
|
||||
# --- WebSocket tests ---
|
||||
|
||||
def test_ws_subscribe_with_valid_token(app):
|
||||
def test_ws_subscribe_bot_with_valid_token(app):
|
||||
with TestClient(app) as c:
|
||||
with c.websocket_connect("/ws/announce") as ws:
|
||||
ws.send_json({"type": "subscribe", "token": "test-token"})
|
||||
ws.send_json({"type": "subscribe", "token": "test-token", "role": "bot", "client_id": "test-bot"})
|
||||
data = ws.receive_json()
|
||||
assert data["type"] == "status"
|
||||
assert data["subscribers"] == 1
|
||||
assert data["clients"][0]["client_id"] == "test-bot"
|
||||
|
||||
|
||||
def test_ws_subscribe_viewer_not_counted(app):
|
||||
with TestClient(app) as c:
|
||||
with c.websocket_connect("/ws/announce") as ws:
|
||||
ws.send_json({"type": "subscribe", "token": "test-token", "role": "viewer"})
|
||||
data = ws.receive_json()
|
||||
assert data["type"] == "status"
|
||||
assert data["subscribers"] == 0
|
||||
|
||||
|
||||
def test_ws_subscribe_with_invalid_token(app):
|
||||
|
||||
@@ -7,12 +7,12 @@ def manager():
|
||||
return AnnounceManager()
|
||||
|
||||
|
||||
def test_no_subscribers_initially(manager):
|
||||
assert manager.subscriber_count == 0
|
||||
def test_no_bots_initially(manager):
|
||||
assert manager.bot_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_subscribe_and_broadcast(manager):
|
||||
async def test_bot_subscribe_and_broadcast(manager):
|
||||
received = []
|
||||
|
||||
class FakeWS:
|
||||
@@ -20,15 +20,34 @@ async def test_subscribe_and_broadcast(manager):
|
||||
received.append(data)
|
||||
|
||||
ws = FakeWS()
|
||||
manager.add_subscriber(ws)
|
||||
assert manager.subscriber_count == 1
|
||||
manager.add_client(ws, role="bot", client_id="test-bot", remote_addr="127.0.0.1")
|
||||
assert manager.bot_count == 1
|
||||
|
||||
await manager.broadcast({"type": "announce", "message": "Now Playing: Song #1"})
|
||||
assert len(received) == 1
|
||||
assert received[0]["message"] == "Now Playing: Song #1"
|
||||
|
||||
manager.remove_subscriber(ws)
|
||||
assert manager.subscriber_count == 0
|
||||
manager.remove_client(ws)
|
||||
assert manager.bot_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_viewer_not_counted_as_bot(manager):
|
||||
received = []
|
||||
|
||||
class FakeWS:
|
||||
async def send_json(self, data):
|
||||
received.append(data)
|
||||
|
||||
ws = FakeWS()
|
||||
manager.add_client(ws, role="viewer")
|
||||
assert manager.bot_count == 0
|
||||
|
||||
await manager.broadcast({"type": "announce", "message": "test"})
|
||||
assert len(received) == 1
|
||||
|
||||
manager.remove_client(ws)
|
||||
assert manager.bot_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -38,8 +57,44 @@ async def test_broadcast_skips_dead_connections(manager):
|
||||
raise Exception("connection closed")
|
||||
|
||||
ws = DeadWS()
|
||||
manager.add_subscriber(ws)
|
||||
assert manager.subscriber_count == 1
|
||||
manager.add_client(ws, role="bot", client_id="dead-bot")
|
||||
assert manager.bot_count == 1
|
||||
|
||||
await manager.broadcast({"type": "announce", "message": "test"})
|
||||
assert manager.subscriber_count == 0
|
||||
assert manager.bot_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_clients_returns_metadata(manager):
|
||||
class FakeWS:
|
||||
async def send_json(self, data):
|
||||
pass
|
||||
|
||||
ws = FakeWS()
|
||||
manager.add_client(ws, role="bot", client_id="limnoria-prod", remote_addr="10.0.0.5")
|
||||
clients = manager.bot_clients
|
||||
assert len(clients) == 1
|
||||
assert clients[0]["client_id"] == "limnoria-prod"
|
||||
assert clients[0]["remote_addr"] == "10.0.0.5"
|
||||
assert "connected_at" in clients[0]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_status_broadcast_includes_clients(manager):
|
||||
received = []
|
||||
|
||||
class FakeWS:
|
||||
async def send_json(self, data):
|
||||
received.append(data)
|
||||
|
||||
bot = FakeWS()
|
||||
viewer = FakeWS()
|
||||
manager.add_client(bot, role="bot", client_id="my-bot", remote_addr="1.2.3.4")
|
||||
manager.add_client(viewer, role="viewer")
|
||||
|
||||
await manager.broadcast_status()
|
||||
for msg in received:
|
||||
assert msg["type"] == "status"
|
||||
assert msg["subscribers"] == 1
|
||||
assert len(msg["clients"]) == 1
|
||||
assert msg["clients"][0]["client_id"] == "my-bot"
|
||||
|
||||
Reference in New Issue
Block a user