surfaces/tests/adapter/matrix/test_send_outgoing.py

194 lines
6.8 KiB
Python

from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import AsyncMock
from adapter.matrix.bot import send_outgoing
from adapter.matrix.converter import from_room_event
from adapter.matrix.handlers.confirm import make_handle_cancel, make_handle_confirm
from adapter.matrix.store import get_pending_confirm, set_room_meta
from core.auth import AuthManager
from core.chat import ChatManager
from core.protocol import Attachment, OutgoingMessage, OutgoingUI, UIButton
from core.settings import SettingsManager
from core.store import InMemoryStore
from sdk.mock import MockPlatformClient
async def test_mat06_outgoing_ui_renders_text_with_yes_no():
client = SimpleNamespace(room_send=AsyncMock())
store = InMemoryStore()
await set_room_meta(store, "!confirm:example.org", {"matrix_user_id": "@alice:example.org"})
event = OutgoingUI(
chat_id="C7",
text="Удалить файл?",
buttons=[UIButton(label="Подтвердить", action="confirm")],
)
await send_outgoing(client, "!confirm:example.org", event, store=store)
client.room_send.assert_awaited_once()
body = client.room_send.call_args.args[2]["body"]
assert "Удалить файл?" in body
assert "!yes" in body
assert "!no" in body
assert "Подтвердить" in body
async def test_mat07_outgoing_ui_no_reaction_sent():
client = SimpleNamespace(room_send=AsyncMock())
store = InMemoryStore()
await set_room_meta(store, "!confirm:example.org", {"matrix_user_id": "@alice:example.org"})
event = OutgoingUI(
chat_id="C7",
text="Confirm action?",
buttons=[UIButton(label="OK", action="confirm", payload={"id": 1})],
)
await send_outgoing(client, "!confirm:example.org", event, store=store)
assert client.room_send.await_count == 1
assert client.room_send.call_args.args[1] == "m.room.message"
for call in client.room_send.call_args_list:
assert call.args[1] != "m.reaction"
pending = await get_pending_confirm(store, "@alice:example.org", "!confirm:example.org")
assert pending == {
"action_id": "confirm",
"description": "Confirm action?",
"payload": {"id": 1},
}
async def test_outgoing_ui_yes_round_trip_uses_user_and_room_scope():
client = SimpleNamespace(room_send=AsyncMock())
store = InMemoryStore()
platform = MockPlatformClient()
chat_mgr = ChatManager(platform, store)
auth_mgr = AuthManager(platform, store)
settings_mgr = SettingsManager(platform, store)
await set_room_meta(store, "!confirm:example.org", {"matrix_user_id": "@alice:example.org"})
await set_room_meta(store, "!other:example.org", {"matrix_user_id": "@bob:example.org"})
await send_outgoing(
client,
"!confirm:example.org",
OutgoingUI(
chat_id="C7",
text="Archive room",
buttons=[UIButton(label="Confirm", action="archive", payload={"id": 7})],
),
store=store,
)
await send_outgoing(
client,
"!other:example.org",
OutgoingUI(
chat_id="C8",
text="Keep other room",
buttons=[UIButton(label="Confirm", action="archive", payload={"id": 8})],
),
store=store,
)
callback = from_room_event(
SimpleNamespace(
sender="@alice:example.org",
body="!yes",
event_id="$yes",
msgtype="m.text",
replyto_event_id=None,
),
room_id="!confirm:example.org",
chat_id="C7",
)
result = await make_handle_confirm(store)(callback, auth_mgr, platform, chat_mgr, settings_mgr)
assert "Archive room" in result[0].text
assert await get_pending_confirm(store, "@alice:example.org", "!confirm:example.org") is None
assert await get_pending_confirm(store, "@bob:example.org", "!other:example.org") is not None
async def test_outgoing_ui_no_round_trip_uses_user_and_room_scope():
client = SimpleNamespace(room_send=AsyncMock())
store = InMemoryStore()
platform = MockPlatformClient()
chat_mgr = ChatManager(platform, store)
auth_mgr = AuthManager(platform, store)
settings_mgr = SettingsManager(platform, store)
await set_room_meta(store, "!confirm:example.org", {"matrix_user_id": "@alice:example.org"})
await set_room_meta(store, "!other:example.org", {"matrix_user_id": "@bob:example.org"})
await send_outgoing(
client,
"!confirm:example.org",
OutgoingUI(
chat_id="C7",
text="Delete room",
buttons=[UIButton(label="Confirm", action="delete", payload={"id": 7})],
),
store=store,
)
await send_outgoing(
client,
"!other:example.org",
OutgoingUI(
chat_id="C8",
text="Keep other room",
buttons=[UIButton(label="Confirm", action="archive", payload={"id": 8})],
),
store=store,
)
callback = from_room_event(
SimpleNamespace(
sender="@alice:example.org",
body="!no",
event_id="$no",
msgtype="m.text",
replyto_event_id=None,
),
room_id="!confirm:example.org",
chat_id="C7",
)
result = await make_handle_cancel(store)(callback, auth_mgr, platform, chat_mgr, settings_mgr)
assert "отменено" in result[0].text.lower()
assert await get_pending_confirm(store, "@alice:example.org", "!confirm:example.org") is None
assert await get_pending_confirm(store, "@bob:example.org", "!other:example.org") is not None
async def test_send_outgoing_uploads_workspace_file_attachment(tmp_path, monkeypatch):
workspace_file = tmp_path / "surfaces" / "matrix" / "alice" / "room" / "inbox" / "result.txt"
workspace_file.parent.mkdir(parents=True, exist_ok=True)
workspace_file.write_text("ready")
monkeypatch.setenv("SURFACES_WORKSPACE_DIR", str(tmp_path))
client = SimpleNamespace(
upload=AsyncMock(return_value=(SimpleNamespace(content_uri="mxc://server/file"), {})),
room_send=AsyncMock(),
)
await send_outgoing(
client,
"!room:example.org",
OutgoingMessage(
chat_id="!room:example.org",
text="Файл готов",
attachments=[
Attachment(
type="document",
filename="result.txt",
mime_type="text/plain",
workspace_path="surfaces/matrix/alice/room/inbox/result.txt",
)
],
),
)
client.upload.assert_awaited_once()
client.room_send.assert_awaited()
assert client.room_send.await_args_list[0].args[2]["body"] == "Файл готов"
file_call = client.room_send.await_args_list[1]
assert file_call.args[2]["msgtype"] == "m.file"
assert file_call.args[2]["url"] == "mxc://server/file"