from __future__ import annotations import pytest from unittest.mock import AsyncMock, MagicMock from adapter.matrix.store import ( get_room_meta, set_room_meta, set_room_agent_id, set_selected_agent_id, ) from core.protocol import IncomingCommand, OutgoingMessage from core.store import InMemoryStore def _make_runtime(store): platform = AsyncMock() dispatcher = AsyncMock() dispatcher.dispatch.return_value = [OutgoingMessage(chat_id="!r:s", text="ok")] runtime = MagicMock() runtime.store = store runtime.dispatcher = dispatcher runtime.platform = platform runtime.agent_routing_enabled = True return runtime def _make_bot(store): from adapter.matrix.bot import MatrixBot client = MagicMock() client.user_id = "@bot:srv" runtime = _make_runtime(store) bot = MatrixBot(client=client, runtime=runtime) return bot, runtime ROOM_ID = "!room:srv" USER_ID = "@alice:srv" async def _send_message(bot, body): from nio import RoomMessageText, MatrixRoom room = MagicMock(spec=MatrixRoom) room.room_id = ROOM_ID event = MagicMock(spec=RoomMessageText) event.sender = USER_ID event.body = body event.source = {} bot._send_all = AsyncMock() await bot.on_room_message(room, event) return bot._send_all async def test_stale_room_blocks_normal_message(): store = InMemoryStore() await set_room_meta(store, ROOM_ID, {"room_type": "chat", "matrix_user_id": USER_ID, "platform_chat_id": "1", "agent_id": "agent-1"}) await set_selected_agent_id(store, USER_ID, "agent-2") bot, runtime = _make_bot(store) send_all = await _send_message(bot, "hello") runtime.dispatcher.dispatch.assert_not_called() args = send_all.call_args[0] assert any("agent-1" in m.text and "!new" in m.text for m in args[1]) async def test_stale_room_allows_commands(): store = InMemoryStore() await set_room_meta(store, ROOM_ID, {"room_type": "chat", "matrix_user_id": USER_ID, "platform_chat_id": "1", "agent_id": "agent-1"}) await set_selected_agent_id(store, USER_ID, "agent-2") bot, runtime = _make_bot(store) await _send_message(bot, "!help") runtime.dispatcher.dispatch.assert_called_once() async def test_no_selected_agent_blocks_normal_message(): store = InMemoryStore() await set_room_meta(store, ROOM_ID, {"room_type": "chat", "matrix_user_id": USER_ID, "platform_chat_id": "1"}) bot, runtime = _make_bot(store) send_all = await _send_message(bot, "hello") runtime.dispatcher.dispatch.assert_not_called() args = send_all.call_args[0] assert any("!agent" in m.text for m in args[1]) async def test_no_selected_agent_allows_commands(): store = InMemoryStore() await set_room_meta(store, ROOM_ID, {"room_type": "chat", "matrix_user_id": USER_ID, "platform_chat_id": "1"}) bot, runtime = _make_bot(store) await _send_message(bot, "!agent") runtime.dispatcher.dispatch.assert_called_once() async def test_unbound_room_binds_on_message_when_agent_selected(): store = InMemoryStore() await set_room_meta(store, ROOM_ID, {"room_type": "chat", "matrix_user_id": USER_ID, "platform_chat_id": "1"}) await set_selected_agent_id(store, USER_ID, "agent-1") bot, runtime = _make_bot(store) await _send_message(bot, "hello") meta = await get_room_meta(store, ROOM_ID) assert meta["agent_id"] == "agent-1" runtime.dispatcher.dispatch.assert_called_once()