Files
hockey_new/tests/test_multi_agent_routing.py
2026-08-19 15:08:39 +03:00

133 lines
5.0 KiB
Python

from __future__ import annotations
import asyncio
from pathlib import Path
from types import SimpleNamespace
from hockey_data.agent_bridge import VmixAgentHub
from hockey_data.auth_bridge import HockeyUser
from hockey_data.models import OperatorSession
from tests.support import LocalTestDatabase
class FakeWebSocket:
def __init__(self) -> None:
self.client = SimpleNamespace(host="127.0.0.1")
self.sent: list[dict] = []
self.closed = False
async def send_json(self, payload: dict) -> None:
self.sent.append(payload)
async def close(self, **_kwargs) -> None:
self.closed = True
async def _ack_latest(hub: VmixAgentHub, device_id: str, ws: FakeWebSocket) -> dict:
command = None
for _ in range(100):
await asyncio.sleep(0)
commands = [item for item in ws.sent if item.get("type") == "vmix.command"]
if commands:
command = commands[-1]
break
assert command is not None
await hub.receive_command_ack(
device_id,
{"type": "command.ack", "request_id": command["request_id"], "ok": True},
)
return command
def test_multiple_agents_stay_enabled_and_runtime_routes_to_explicit_browser_agent(tmp_path: Path) -> None:
database = LocalTestDatabase(tmp_path / "multi-agent.sqlite3")
database.create_all()
hub = VmixAgentHub(database) # type: ignore[arg-type]
user = HockeyUser(id="multi-1", login="multi", display_name="Multi")
ws_test = FakeWebSocket()
ws_live = FakeWebSocket()
async def scenario() -> None:
for device_id, secret, ws in (
("GFX-MULTI-TEST", "t" * 40, ws_test),
("GFX-MULTI-LIVE", "l" * 40, ws_live),
):
await hub.register(
ws, # type: ignore[arg-type]
{
"device_id": device_id,
"device_secret": secret,
"device_name": device_id,
"hostname": device_id,
"agent_version": "1.4.0",
"vmix": {"connected": True, "url": "http://127.0.0.1:8088/api/"},
},
)
await hub.pair_device(device_id, user)
devices = await hub.list_for_user(user)
assert set(devices["active_device_ids"]) == {"GFX-MULTI-TEST", "GFX-MULTI-LIVE"}
with database.session() as session:
session.add(
OperatorSession(
session_token="multi-session",
wfl_user_id=user.id,
login_snapshot=user.login,
tournament_external_id="1437",
game_external_id="902918",
status="active",
)
)
assert await hub.assign_match(
wfl_user_id=user.id,
tournament_external_id="1437",
game_external_id="902918",
device_id="GFX-MULTI-TEST",
operator_session_token="multi-session",
)
assert await hub.assign_match(
wfl_user_id=user.id,
tournament_external_id="1437",
game_external_id="902918",
device_id="GFX-MULTI-LIVE",
operator_session_token="multi-session",
)
# Ignore pairing / assignment traffic and count only runtime vmix.command messages.
before_test = len([item for item in ws_test.sent if item.get("type") == "vmix.command"])
before_live = len([item for item in ws_live.sent if item.get("type") == "vmix.command"])
task_test = asyncio.create_task(
hub.run_vmix_sequence_for_user(
user,
[{"Function": "OverlayInput1In", "Input": "score-key"}],
device_id="GFX-MULTI-TEST",
session_token="multi-session",
)
)
command_test = await _ack_latest(hub, "GFX-MULTI-TEST", ws_test)
result_test = await task_test
assert result_test["device_id"] == "GFX-MULTI-TEST"
assert command_test["command"]["Function"] == "OverlayInput1In"
assert len([item for item in ws_test.sent if item.get("type") == "vmix.command"]) == before_test + 1
assert len([item for item in ws_live.sent if item.get("type") == "vmix.command"]) == before_live
task_live = asyncio.create_task(
hub.run_vmix_sequence_for_user(
user,
[{"Function": "OverlayInput1Out", "Input": "score-key"}],
device_id="GFX-MULTI-LIVE",
session_token="multi-session",
)
)
command_live = await _ack_latest(hub, "GFX-MULTI-LIVE", ws_live)
result_live = await task_live
assert result_live["device_id"] == "GFX-MULTI-LIVE"
assert command_live["command"]["Function"] == "OverlayInput1Out"
assert len([item for item in ws_live.sent if item.get("type") == "vmix.command"]) == before_live + 1
assert len([item for item in ws_test.sent if item.get("type") == "vmix.command"]) == before_test + 1
asyncio.run(scenario())