from __future__ import annotations from types import SimpleNamespace from unittest.mock import AsyncMock from nio.api import RoomVisibility from nio.responses import RoomCreateError from adapter.matrix.handlers.chat import ( make_handle_archive, make_handle_new_chat, make_handle_rename, ) from adapter.matrix.store import get_room_meta, set_user_meta from core.auth import AuthManager from core.chat import ChatManager from core.protocol import IncomingCommand, OutgoingMessage from core.settings import SettingsManager from core.store import InMemoryStore from sdk.mock import MockPlatformClient async def _setup(): platform = MockPlatformClient() store = InMemoryStore() chat_mgr = ChatManager(platform, store) auth_mgr = AuthManager(platform, store) settings_mgr = SettingsManager(platform, store) await auth_mgr.confirm("@alice:example.org") return platform, store, chat_mgr, auth_mgr, settings_mgr async def test_mat04_new_chat_calls_room_put_state_with_space_id(): platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup() await set_user_meta( store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2} ) client = SimpleNamespace( room_create=AsyncMock(return_value=SimpleNamespace(room_id="!newroom:ex")), room_put_state=AsyncMock(), room_invite=AsyncMock(), ) handler = make_handle_new_chat(client, store) event = IncomingCommand( user_id="@alice:example.org", platform="matrix", chat_id="C1", command="new", args=["Test"], ) result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr) client.room_create.assert_awaited_once_with( name="Test", visibility=RoomVisibility.private, is_direct=False, invite=["@alice:example.org"], ) client.room_put_state.assert_awaited_once() client.room_invite.assert_not_awaited() kwargs = client.room_put_state.call_args.kwargs assert kwargs.get("room_id") == "!space:ex" assert kwargs.get("event_type") == "m.space.child" assert kwargs.get("state_key") == "!newroom:ex" room_meta = await get_room_meta(store, "!newroom:ex") assert room_meta is not None assert room_meta["platform_chat_id"] == "1" assert any(isinstance(item, OutgoingMessage) and "Test" in item.text for item in result) async def test_mat05_new_chat_without_space_id_returns_error(): platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup() await set_user_meta(store, "@alice:example.org", {"next_chat_index": 1}) client = SimpleNamespace( room_create=AsyncMock(), room_put_state=AsyncMock(), room_invite=AsyncMock(), ) handler = make_handle_new_chat(client, store) event = IncomingCommand( user_id="@alice:example.org", platform="matrix", chat_id="C1", command="new", ) result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr) assert len(result) == 1 assert isinstance(result[0], OutgoingMessage) assert "Space" in result[0].text or "ошибка" in result[0].text.lower() client.room_create.assert_not_awaited() async def test_mat10_archive_calls_chat_mgr_archive(): platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup() client = SimpleNamespace(room_leave=AsyncMock()) handler = make_handle_archive(client, store) event = IncomingCommand( user_id="@alice:example.org", platform="matrix", chat_id="C1", command="archive", ) await chat_mgr.get_or_create( user_id="@alice:example.org", chat_id="C1", platform="matrix", surface_ref="!room:ex", name="Test", ) result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr) assert len(result) == 1 assert "архивирован" in result[0].text client.room_leave.assert_awaited_once_with("!room:ex") chats = await chat_mgr.list_active("@alice:example.org") assert chats == [] async def test_mat11_rename_updates_matrix_room_name_via_state_event(): platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup() await chat_mgr.get_or_create( user_id="@alice:example.org", chat_id="C1", platform="matrix", surface_ref="!room:ex", name="Old", ) client = SimpleNamespace(room_put_state=AsyncMock()) handler = make_handle_rename(client, store) event = IncomingCommand( user_id="@alice:example.org", platform="matrix", chat_id="C1", command="rename", args=["New", "Name"], ) result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr) client.room_put_state.assert_awaited_once_with( room_id="!room:ex", event_type="m.room.name", content={"name": "New Name"}, state_key="", ) assert len(result) == 1 assert "Переименован" in result[0].text async def test_mat11b_rename_from_unregistered_room_returns_error_message(): platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup() client = SimpleNamespace(room_put_state=AsyncMock()) handler = make_handle_rename(client, store) event = IncomingCommand( user_id="@alice:example.org", platform="matrix", chat_id="unregistered:!old:example.org", command="rename", args=["New"], ) result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr) client.room_put_state.assert_not_awaited() assert len(result) == 1 assert "не найден" in result[0].text.lower() or "примите приглашение" in result[0].text.lower() async def test_mat12_room_create_error_returns_user_message(): platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup() await set_user_meta( store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2} ) client = SimpleNamespace( room_create=AsyncMock( return_value=RoomCreateError(message="rate limited", status_code="429") ), room_put_state=AsyncMock(), room_invite=AsyncMock(), ) handler = make_handle_new_chat(client, store) event = IncomingCommand( user_id="@alice:example.org", platform="matrix", chat_id="C1", command="new", args=["Fail"], ) result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr) assert len(result) == 1 assert isinstance(result[0], OutgoingMessage) assert "Не удалось" in result[0].text or "не удалось" in result[0].text client.room_put_state.assert_not_awaited()