fix(01-04): update matrix dispatcher and reaction tests

- rewrite invite/new-chat assertions for Space-based Matrix flow
- replace legacy reaction text checks with !skill on/off expectations
- validate confirmation text against !yes and !no prompts
This commit is contained in:
Mikhail Putilovskij 2026-04-02 23:00:50 +03:00
parent 0d85947a0b
commit 6f1bdb4077
2 changed files with 59 additions and 21 deletions

View file

@ -5,7 +5,7 @@ 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 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
@ -36,7 +36,7 @@ async def test_matrix_dispatcher_registers_custom_handlers():
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)
assert any(isinstance(r, OutgoingMessage) and "!skill on/off" in r.text for r in result)
toggle = IncomingCallback(
user_id="u1",
@ -50,8 +50,13 @@ async def test_matrix_dispatcher_registers_custom_handlers():
async def test_new_chat_creates_real_matrix_room_when_client_available():
client = SimpleNamespace(room_create=AsyncMock(return_value=SimpleNamespace(room_id="!r2:example")))
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)
@ -65,40 +70,72 @@ async def test_new_chat_creates_real_matrix_room_when_client_available():
)
result = await runtime.dispatcher.dispatch(new)
client.room_create.assert_awaited_once_with(name="Research", invite=["u1"], is_direct=False)
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 "!r2:example" in r.text for r in result)
assert any(isinstance(r, OutgoingMessage) and "Research" in r.text for r in result)
async def test_invite_event_creates_dm_room_and_sends_welcome():
async def test_invite_event_creates_space_and_chat_room():
runtime = build_runtime(platform=MockPlatformClient())
client = SimpleNamespace(join=AsyncMock(), room_send=AsyncMock())
room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice DM")
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)
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 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_room():
async def test_invite_event_is_idempotent_per_user():
runtime = build_runtime(platform=MockPlatformClient())
client = SimpleNamespace(join=AsyncMock(), room_send=AsyncMock())
room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice DM")
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)
client.join.assert_awaited_once_with("!dm:example.org")
client.room_send.assert_awaited_once()
assert client.room_create.await_count == 2
async def test_bot_ignores_its_own_messages():

View file

@ -19,13 +19,14 @@ def test_build_skills_text():
text = build_skills_text(settings)
assert "web-search" in text
assert "fetch-url" in text
assert "Реакции 1⃣-9" in text
assert "!skill on/off" in text
def test_build_confirmation_text():
text = build_confirmation_text("Отправить письмо?")
assert "Отправить письмо?" in text
assert "подтвердить" in text
assert "!yes" in text
assert "!no" in text
def test_reaction_to_skill_index():