surfaces/tests/adapter/matrix/test_dispatcher.py
Mikhail Putilovskij 974935c880 test(01-06): add failing matrix command-only regressions
- Assert skills text no longer includes reaction-era labels
- Require converter to drop reaction callback support
- Lock !settings dashboard to read-only snapshot copy
2026-04-03 12:32:21 +03:00

173 lines
6.9 KiB
Python

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, get_user_meta, set_user_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 "!skill on/off" 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")),
room_put_state=AsyncMock(),
room_invite=AsyncMock(),
)
runtime = build_runtime(platform=MockPlatformClient(), client=client)
await set_user_meta(runtime.store, "u1", {"space_id": "!space:example", "next_chat_index": 1})
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", visibility="private", is_direct=False)
client.room_put_state.assert_awaited_once()
put_call = client.room_put_state.call_args
assert put_call.kwargs.get("room_id") == "!space:example" or put_call.args[0] == "!space:example"
chats = await runtime.chat_mgr.list_active("u1")
assert [c.surface_ref for c in chats] == ["!r2:example"]
assert any(isinstance(r, OutgoingMessage) and "Research" in r.text for r in result)
async def test_invite_event_creates_space_and_chat_room():
runtime = build_runtime(platform=MockPlatformClient())
space_resp = SimpleNamespace(room_id="!space:example.org")
chat_resp = SimpleNamespace(room_id="!chat1:example.org")
client = SimpleNamespace(
join=AsyncMock(),
room_create=AsyncMock(side_effect=[space_resp, chat_resp]),
room_put_state=AsyncMock(),
room_invite=AsyncMock(),
room_send=AsyncMock(),
)
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)
assert client.room_create.await_count == 2
first_call = client.room_create.call_args_list[0]
assert first_call.kwargs.get("space") is True or (
len(first_call.args) > 0 and first_call.kwargs.get("space") is True
)
client.room_put_state.assert_awaited_once()
put_state_call = client.room_put_state.call_args
assert put_state_call.kwargs.get("event_type") == "m.space.child" or put_state_call.args[1] == "m.space.child"
user_meta = await get_user_meta(runtime.store, "@alice:example.org")
assert user_meta is not None
assert user_meta.get("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"] == "C1"
assert room_meta["space_id"] == "!space:example.org"
assert await runtime.auth_mgr.is_authenticated("@alice:example.org") is True
client.room_send.assert_awaited_once()
async def test_invite_event_is_idempotent_per_user():
runtime = build_runtime(platform=MockPlatformClient())
space_resp = SimpleNamespace(room_id="!space:example.org")
chat_resp = SimpleNamespace(room_id="!chat1:example.org")
client = SimpleNamespace(
join=AsyncMock(),
room_create=AsyncMock(side_effect=[space_resp, chat_resp]),
room_put_state=AsyncMock(),
room_invite=AsyncMock(),
room_send=AsyncMock(),
)
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)
await handle_invite(client, room, event, runtime.platform, runtime.store, runtime.auth_mgr)
assert client.room_create.await_count == 2
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()
async def test_mat11_settings_returns_dashboard():
runtime = build_runtime(platform=MockPlatformClient())
start = IncomingCommand(user_id="u1", platform="matrix", chat_id="C1", command="start")
await runtime.dispatcher.dispatch(start)
settings_cmd = IncomingCommand(user_id="u1", platform="matrix", chat_id="C1", command="settings")
result = await runtime.dispatcher.dispatch(settings_cmd)
assert len(result) >= 1
text = result[0].text
assert "Скиллы" in text or "скиллы" in text.lower()
assert "Личность" in text
assert "Безопасность" in text
assert "Активные чаты" in text
assert "Изменить" not in text
assert "!connectors" not in text
assert "!whoami" not in text