import asyncio import json import types from pathlib import Path from types import SimpleNamespace from sqlalchemy import select from hockey_data.agent_bridge import VmixAgentHub from hockey_data.auth_bridge import HockeyUser from hockey_data.models import VmixDevice, VmixPreparedTitle from tests.support import LocalTestDatabase ROOT = Path(__file__).resolve().parents[1] APP_JS = (ROOT / "ui_builder/static/app.js").read_text(encoding="utf-8") BRIDGE = (ROOT / "hockey_data/agent_bridge.py").read_text(encoding="utf-8") APP = (ROOT / "app.py").read_text(encoding="utf-8") def test_build99_frontend_and_routes_exist(): assert 'preparedTitleEditingId: ""' in APP_JS assert 'data-prepared-edit=' in APP_JS assert 'method: "PUT"' in APP_JS assert '@router.put("/api/hockey/prepared-titles/{prepared_id}")' in BRIDGE assert '"Function": "SetInputName"' in BRIDGE assert 'return f"Заготовка {number}"' in BRIDGE assert 'BUILD_VERSION = "2026.08.24.2"' in APP def test_blank_name_gets_number_and_existing_clone_is_editable(tmp_path): database = LocalTestDatabase(tmp_path / "prepared99.sqlite3") database.create_all() hub = VmixAgentHub(database) # type: ignore[arg-type] user = HockeyUser(id="prep99", login="prep99", display_name="Prep99") source = { "key": "source-key", "number": "12", "title": "COMPARE", "type": "GT", "fields": [{"name": "Name.Text", "type": "text", "index": "0"}], } inventory = {"inputs": [source]} with database.session() as session: session.add(VmixDevice( device_uuid="prep-device", device_secret_hash="x" * 64, name="Prep device", wfl_user_id=user.id, is_active_for_account=True, vmix_connected=True, current_match_external_id="9001", project_inventory_json=json.dumps(inventory), )) calls = [] async def fake_sequence(self, _user, commands, *, device_id="", session_token="", timeout=0, **kwargs): calls.append(commands) if commands and commands[0].get("Function") == "CreateVirtualInput": cloned = {"inputs": [source, {**source, "key": "clone-key", "number": "13", "title": "COMPARE"}]} with database.session() as session: device = session.scalar(select(VmixDevice).where(VmixDevice.device_uuid == "prep-device")) device.project_inventory_json = json.dumps(cloned) return {"ok": True, "device_id": device_id or "prep-device", "match_id": "9001"} hub.run_vmix_sequence_for_user = types.MethodType(fake_sequence, hub) created = asyncio.run(hub.create_prepared_title(user, SimpleNamespace( name="", device_id="prep-device", session_token="", source_input_key="source-key", source_input_number="12", source_input_title="COMPARE", source_kind="manual", source_ref="", field_values={"Name.Text": {"type": "text", "value": "Иванов"}}, ))) assert created["name"] == "Заготовка 1" assert created["clone_input"]["title"] == "Заготовка 1" assert any(c.get("Function") == "SetInputName" and c.get("Value") == "Заготовка 1" for c in calls[1]) updated = asyncio.run(hub.update_prepared_title(created["id"], user, SimpleNamespace( name="Сравнение вратарей", device_id="prep-device", session_token="", field_values={"Name.Text": {"type": "text", "value": "Петров"}}, ))) assert updated["name"] == "Сравнение вратарей" assert updated["field_values"]["Name.Text"]["value"] == "Петров" assert any(c.get("Function") == "SetInputName" and c.get("Value") == "Сравнение вратарей" for c in calls[-1]) with database.session() as session: row = session.get(VmixPreparedTitle, created["id"]) assert row is not None assert row.name == "Сравнение вратарей" assert row.clone_input_title == "Сравнение вратарей"