from __future__ import annotations from types import SimpleNamespace from unittest.mock import AsyncMock from adapter.matrix.bot import MatrixBot, build_runtime from adapter.matrix.handlers.auth import handle_invite from adapter.matrix.store import get_room_meta from core.protocol import IncomingCallback, IncomingCommand, OutgoingMessage from sdk.mock import MockPlatformClient async def test_matrix_dispatcher_registers_custom_handlers(): runtime = build_runtime(platform=MockPlatformClient()) start = IncomingCommand(user_id="u1", platform="matrix", chat_id="C1", command="start") await runtime.dispatcher.dispatch(start) new = IncomingCommand( user_id="u1", platform="matrix", chat_id="C1", command="new", args=["Research"] ) result = await runtime.dispatcher.dispatch(new) assert any(isinstance(r, OutgoingMessage) and "Research" in r.text for r in result) chats = await runtime.chat_mgr.list_active("u1") assert [c.chat_id for c in chats] == ["C1"] new2 = IncomingCommand( user_id="u1", platform="matrix", chat_id="C1", command="new", args=["Ops"] ) await runtime.dispatcher.dispatch(new2) chats = await runtime.chat_mgr.list_active("u1") assert [c.chat_id for c in chats] == ["C1", "C2"] skills = IncomingCommand( user_id="u1", platform="matrix", chat_id="C1", command="settings_skills" ) result = await runtime.dispatcher.dispatch(skills) assert any(isinstance(r, OutgoingMessage) and "Реакции 1️⃣-9️⃣" in r.text for r in result) toggle = IncomingCallback( user_id="u1", platform="matrix", chat_id="C1", action="toggle_skill", payload={"skill_index": 2}, ) result = await runtime.dispatcher.dispatch(toggle) assert any(isinstance(r, OutgoingMessage) and "fetch-url" in r.text for r in result) async def test_new_chat_creates_real_matrix_room_when_client_available(): client = SimpleNamespace(room_create=AsyncMock(return_value=SimpleNamespace(room_id="!r2:example"))) runtime = build_runtime(platform=MockPlatformClient(), client=client) start = IncomingCommand(user_id="u1", platform="matrix", chat_id="C1", command="start") await runtime.dispatcher.dispatch(start) new = IncomingCommand( user_id="u1", platform="matrix", chat_id="C1", command="new", args=["Research"], ) result = await runtime.dispatcher.dispatch(new) client.room_create.assert_awaited_once_with(name="Research", invite=["u1"], is_direct=False) chats = await runtime.chat_mgr.list_active("u1") assert [c.surface_ref for c in chats] == ["!r2:example"] assert any(isinstance(r, OutgoingMessage) and "!r2:example" in r.text for r in result) async def test_invite_event_creates_dm_room_and_sends_welcome(): runtime = build_runtime(platform=MockPlatformClient()) client = SimpleNamespace(join=AsyncMock(), room_send=AsyncMock()) room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice DM") event = SimpleNamespace(sender="@alice:example.org", membership="invite") await handle_invite(client, room, event, runtime.platform, runtime.store, runtime.auth_mgr) client.join.assert_awaited_once_with("!dm:example.org") client.room_send.assert_awaited_once() meta = await get_room_meta(runtime.store, "!dm:example.org") assert meta is not None assert meta["chat_id"] == "C1" assert meta["matrix_user_id"] == "@alice:example.org" assert await runtime.auth_mgr.is_authenticated("@alice:example.org") is True async def test_invite_event_is_idempotent_per_room(): runtime = build_runtime(platform=MockPlatformClient()) client = SimpleNamespace(join=AsyncMock(), room_send=AsyncMock()) room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice DM") event = SimpleNamespace(sender="@alice:example.org", membership="invite") await handle_invite(client, room, event, runtime.platform, runtime.store, runtime.auth_mgr) await handle_invite(client, room, event, runtime.platform, runtime.store, runtime.auth_mgr) client.join.assert_awaited_once_with("!dm:example.org") client.room_send.assert_awaited_once() async def test_bot_ignores_its_own_messages(): runtime = build_runtime(platform=MockPlatformClient()) client = SimpleNamespace(user_id="@bot:example.org") bot = MatrixBot(client, runtime) bot._send_all = AsyncMock() runtime.dispatcher.dispatch = AsyncMock() room = SimpleNamespace(room_id="!dm:example.org") event = SimpleNamespace(sender="@bot:example.org", body="hello") await bot.on_room_message(room, event) runtime.dispatcher.dispatch.assert_not_awaited() bot._send_all.assert_not_awaited()