from __future__ import annotations from types import SimpleNamespace from unittest.mock import AsyncMock from nio.api import RoomVisibility from adapter.matrix.bot import build_runtime from adapter.matrix.handlers.auth import handle_invite from adapter.matrix.store import get_room_meta, get_user_meta, set_room_meta, set_user_meta from sdk.mock import MockPlatformClient def _make_client(): space_resp = SimpleNamespace(room_id="!space:example.org") chat_resp = SimpleNamespace(room_id="!chat1:example.org") return SimpleNamespace( join=AsyncMock(), room_create=AsyncMock(side_effect=[space_resp, chat_resp]), room_put_state=AsyncMock(), room_invite=AsyncMock(), room_send=AsyncMock(), ) async def test_mat01_invite_creates_space_and_chat1(): runtime = build_runtime(platform=MockPlatformClient()) await set_user_meta(runtime.store, "@alice:example.org", {"next_chat_index": 4}) client = _make_client() room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice") event = SimpleNamespace(sender="@alice:example.org", membership="invite") await handle_invite( client, room, event, runtime.platform, runtime.store, runtime.auth_mgr, runtime.chat_mgr, ) first_call = client.room_create.call_args_list[0] assert first_call.kwargs.get("space") is True assert first_call.kwargs.get("visibility") is RoomVisibility.private assert first_call.kwargs.get("invite") == ["@alice:example.org"] second_call = client.room_create.call_args_list[1] assert second_call.kwargs.get("visibility") is RoomVisibility.private assert second_call.kwargs.get("invite") == ["@alice:example.org"] assert client.room_create.await_count == 2 client.room_invite.assert_not_awaited() client.room_put_state.assert_awaited_once() kwargs = client.room_put_state.call_args.kwargs assert kwargs.get("event_type") == "m.space.child" assert kwargs.get("state_key") == "!chat1:example.org" assert kwargs.get("room_id") == "!space:example.org" user_meta = await get_user_meta(runtime.store, "@alice:example.org") assert user_meta is not None assert user_meta["space_id"] == "!space:example.org" room_meta = await get_room_meta(runtime.store, "!chat1:example.org") assert room_meta is not None assert room_meta["chat_id"] == "C4" assert room_meta["space_id"] == "!space:example.org" assert room_meta["platform_chat_id"] == "1" assert user_meta["next_chat_index"] == 5 chats = await runtime.chat_mgr.list_active("@alice:example.org") assert [chat.chat_id for chat in chats] == ["C4"] assert [chat.surface_ref for chat in chats] == ["!chat1:example.org"] async def test_mat02_invite_idempotent(): runtime = build_runtime(platform=MockPlatformClient()) client = _make_client() room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice") event = SimpleNamespace(sender="@alice:example.org", membership="invite") await handle_invite( client, room, event, runtime.platform, runtime.store, runtime.auth_mgr, runtime.chat_mgr, ) await handle_invite( client, room, event, runtime.platform, runtime.store, runtime.auth_mgr, runtime.chat_mgr, ) assert client.room_create.await_count == 2 async def test_existing_user_invite_reinvites_space_and_active_chats(): runtime = build_runtime(platform=MockPlatformClient()) await set_user_meta( runtime.store, "@alice:example.org", {"space_id": "!space:example.org", "next_chat_index": 2}, ) await set_room_meta( runtime.store, "!chat1:example.org", { "room_type": "chat", "chat_id": "C1", "display_name": "Чат 1", "matrix_user_id": "@alice:example.org", "space_id": "!space:example.org", "platform_chat_id": "1", "agent_id": "agent-1", }, ) await runtime.chat_mgr.get_or_create( user_id="@alice:example.org", chat_id="C1", platform="matrix", surface_ref="!chat1:example.org", name="Чат 1", ) client = _make_client() room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice") event = SimpleNamespace(sender="@alice:example.org", membership="invite") await handle_invite( client, room, event, runtime.platform, runtime.store, runtime.auth_mgr, runtime.chat_mgr, ) client.room_create.assert_not_awaited() client.room_invite.assert_any_await("!space:example.org", "@alice:example.org") client.room_invite.assert_any_await("!chat1:example.org", "@alice:example.org") client.room_send.assert_awaited() async def test_mat03_no_hardcoded_c1(): runtime = build_runtime(platform=MockPlatformClient()) await set_user_meta(runtime.store, "@alice:example.org", {"next_chat_index": 7}) client = _make_client() room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice") event = SimpleNamespace(sender="@alice:example.org", membership="invite") await handle_invite( client, room, event, runtime.platform, runtime.store, runtime.auth_mgr, runtime.chat_mgr, ) room_meta = await get_room_meta(runtime.store, "!chat1:example.org") assert room_meta is not None assert room_meta["chat_id"] == "C7" assert room_meta["platform_chat_id"] == "1" user_meta = await get_user_meta(runtime.store, "@alice:example.org") assert user_meta is not None assert user_meta["next_chat_index"] == 8