test(01-05): cover matrix confirm flow round trip
- assert room_id is preserved on !yes and !no callbacks - exercise send_outgoing to confirm and cancel with user+room scope
This commit is contained in:
parent
35695e043f
commit
716dec5dfd
3 changed files with 160 additions and 16 deletions
|
|
@ -19,7 +19,8 @@ async def test_mat09_yes_reads_pending_confirm():
|
||||||
|
|
||||||
await set_pending_confirm(
|
await set_pending_confirm(
|
||||||
store,
|
store,
|
||||||
"C1",
|
"@alice:example.org",
|
||||||
|
"!confirm:example.org",
|
||||||
{
|
{
|
||||||
"action_id": "delete_file",
|
"action_id": "delete_file",
|
||||||
"description": "Удалить файл config.yaml",
|
"description": "Удалить файл config.yaml",
|
||||||
|
|
@ -31,16 +32,16 @@ async def test_mat09_yes_reads_pending_confirm():
|
||||||
event = IncomingCallback(
|
event = IncomingCallback(
|
||||||
user_id="@alice:example.org",
|
user_id="@alice:example.org",
|
||||||
platform="matrix",
|
platform="matrix",
|
||||||
chat_id="C1",
|
chat_id="C7",
|
||||||
action="confirm",
|
action="confirm",
|
||||||
payload={"source": "command", "command": "yes"},
|
payload={"source": "command", "command": "yes", "room_id": "!confirm:example.org"},
|
||||||
)
|
)
|
||||||
result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)
|
result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)
|
||||||
|
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
assert isinstance(result[0], OutgoingMessage)
|
assert isinstance(result[0], OutgoingMessage)
|
||||||
assert "Удалить файл config.yaml" in result[0].text
|
assert "Удалить файл config.yaml" in result[0].text
|
||||||
assert await get_pending_confirm(store, "C1") is None
|
assert await get_pending_confirm(store, "@alice:example.org", "!confirm:example.org") is None
|
||||||
|
|
||||||
|
|
||||||
async def test_no_clears_pending_confirm():
|
async def test_no_clears_pending_confirm():
|
||||||
|
|
@ -52,7 +53,8 @@ async def test_no_clears_pending_confirm():
|
||||||
|
|
||||||
await set_pending_confirm(
|
await set_pending_confirm(
|
||||||
store,
|
store,
|
||||||
"C1",
|
"@alice:example.org",
|
||||||
|
"!confirm:example.org",
|
||||||
{
|
{
|
||||||
"action_id": "delete_file",
|
"action_id": "delete_file",
|
||||||
"description": "Удалить файл",
|
"description": "Удалить файл",
|
||||||
|
|
@ -64,15 +66,15 @@ async def test_no_clears_pending_confirm():
|
||||||
event = IncomingCallback(
|
event = IncomingCallback(
|
||||||
user_id="@alice:example.org",
|
user_id="@alice:example.org",
|
||||||
platform="matrix",
|
platform="matrix",
|
||||||
chat_id="C1",
|
chat_id="C7",
|
||||||
action="cancel",
|
action="cancel",
|
||||||
payload={"source": "command", "command": "no"},
|
payload={"source": "command", "command": "no", "room_id": "!confirm:example.org"},
|
||||||
)
|
)
|
||||||
result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)
|
result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)
|
||||||
|
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
assert "отменено" in result[0].text.lower()
|
assert "отменено" in result[0].text.lower()
|
||||||
assert await get_pending_confirm(store, "C1") is None
|
assert await get_pending_confirm(store, "@alice:example.org", "!confirm:example.org") is None
|
||||||
|
|
||||||
|
|
||||||
async def test_yes_without_pending_returns_no_pending():
|
async def test_yes_without_pending_returns_no_pending():
|
||||||
|
|
@ -94,3 +96,35 @@ async def test_yes_without_pending_returns_no_pending():
|
||||||
|
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
assert "Нет ожидающих" in result[0].text
|
assert "Нет ожидающих" in result[0].text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_yes_falls_back_to_legacy_chat_key_without_room_payload():
|
||||||
|
store = InMemoryStore()
|
||||||
|
platform = MockPlatformClient()
|
||||||
|
chat_mgr = ChatManager(platform, store)
|
||||||
|
auth_mgr = AuthManager(platform, store)
|
||||||
|
settings_mgr = SettingsManager(platform, store)
|
||||||
|
|
||||||
|
await set_pending_confirm(
|
||||||
|
store,
|
||||||
|
"legacy-chat",
|
||||||
|
{
|
||||||
|
"action_id": "delete_file",
|
||||||
|
"description": "Legacy confirm",
|
||||||
|
"payload": {},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = make_handle_confirm(store)
|
||||||
|
event = IncomingCallback(
|
||||||
|
user_id="@alice:example.org",
|
||||||
|
platform="matrix",
|
||||||
|
chat_id="legacy-chat",
|
||||||
|
action="confirm",
|
||||||
|
payload={"source": "command", "command": "yes"},
|
||||||
|
)
|
||||||
|
result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert "Legacy confirm" in result[0].text
|
||||||
|
assert await get_pending_confirm(store, "legacy-chat") is None
|
||||||
|
|
|
||||||
|
|
@ -68,15 +68,19 @@ async def test_skills_alias_to_settings_command():
|
||||||
|
|
||||||
|
|
||||||
async def test_yes_to_callback():
|
async def test_yes_to_callback():
|
||||||
result = from_room_event(text_event("!yes"), room_id="!r:m.org", chat_id="C1")
|
result = from_room_event(text_event("!yes"), room_id="!room:example.org", chat_id="C7")
|
||||||
assert isinstance(result, IncomingCallback)
|
assert isinstance(result, IncomingCallback)
|
||||||
assert result.action == "confirm"
|
assert result.action == "confirm"
|
||||||
|
assert result.chat_id == "C7"
|
||||||
|
assert result.payload["room_id"] == "!room:example.org"
|
||||||
|
|
||||||
|
|
||||||
async def test_no_to_callback():
|
async def test_no_to_callback():
|
||||||
result = from_room_event(text_event("!no"), room_id="!r:m.org", chat_id="C1")
|
result = from_room_event(text_event("!no"), room_id="!room:example.org", chat_id="C7")
|
||||||
assert isinstance(result, IncomingCallback)
|
assert isinstance(result, IncomingCallback)
|
||||||
assert result.action == "cancel"
|
assert result.action == "cancel"
|
||||||
|
assert result.chat_id == "C7"
|
||||||
|
assert result.payload["room_id"] == "!room:example.org"
|
||||||
|
|
||||||
|
|
||||||
async def test_file_attachment():
|
async def test_file_attachment():
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,28 @@ from types import SimpleNamespace
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from adapter.matrix.bot import send_outgoing
|
from adapter.matrix.bot import send_outgoing
|
||||||
from adapter.matrix.store import get_pending_confirm
|
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 OutgoingUI, UIButton
|
from core.protocol import OutgoingUI, UIButton
|
||||||
|
from core.settings import SettingsManager
|
||||||
from core.store import InMemoryStore
|
from core.store import InMemoryStore
|
||||||
|
from sdk.mock import MockPlatformClient
|
||||||
|
|
||||||
|
|
||||||
async def test_mat06_outgoing_ui_renders_text_with_yes_no():
|
async def test_mat06_outgoing_ui_renders_text_with_yes_no():
|
||||||
client = SimpleNamespace(room_send=AsyncMock())
|
client = SimpleNamespace(room_send=AsyncMock())
|
||||||
store = InMemoryStore()
|
store = InMemoryStore()
|
||||||
|
await set_room_meta(store, "!confirm:example.org", {"matrix_user_id": "@alice:example.org"})
|
||||||
event = OutgoingUI(
|
event = OutgoingUI(
|
||||||
chat_id="C1",
|
chat_id="C7",
|
||||||
text="Удалить файл?",
|
text="Удалить файл?",
|
||||||
buttons=[UIButton(label="Подтвердить", action="confirm")],
|
buttons=[UIButton(label="Подтвердить", action="confirm")],
|
||||||
)
|
)
|
||||||
|
|
||||||
await send_outgoing(client, "!room:ex", event, store=store)
|
await send_outgoing(client, "!confirm:example.org", event, store=store)
|
||||||
|
|
||||||
client.room_send.assert_awaited_once()
|
client.room_send.assert_awaited_once()
|
||||||
body = client.room_send.call_args.args[2]["body"]
|
body = client.room_send.call_args.args[2]["body"]
|
||||||
|
|
@ -31,22 +38,121 @@ async def test_mat06_outgoing_ui_renders_text_with_yes_no():
|
||||||
async def test_mat07_outgoing_ui_no_reaction_sent():
|
async def test_mat07_outgoing_ui_no_reaction_sent():
|
||||||
client = SimpleNamespace(room_send=AsyncMock())
|
client = SimpleNamespace(room_send=AsyncMock())
|
||||||
store = InMemoryStore()
|
store = InMemoryStore()
|
||||||
|
await set_room_meta(store, "!confirm:example.org", {"matrix_user_id": "@alice:example.org"})
|
||||||
event = OutgoingUI(
|
event = OutgoingUI(
|
||||||
chat_id="C1",
|
chat_id="C7",
|
||||||
text="Confirm action?",
|
text="Confirm action?",
|
||||||
buttons=[UIButton(label="OK", action="confirm", payload={"id": 1})],
|
buttons=[UIButton(label="OK", action="confirm", payload={"id": 1})],
|
||||||
)
|
)
|
||||||
|
|
||||||
await send_outgoing(client, "!room:ex", event, store=store)
|
await send_outgoing(client, "!confirm:example.org", event, store=store)
|
||||||
|
|
||||||
assert client.room_send.await_count == 1
|
assert client.room_send.await_count == 1
|
||||||
assert client.room_send.call_args.args[1] == "m.room.message"
|
assert client.room_send.call_args.args[1] == "m.room.message"
|
||||||
for call in client.room_send.call_args_list:
|
for call in client.room_send.call_args_list:
|
||||||
assert call.args[1] != "m.reaction"
|
assert call.args[1] != "m.reaction"
|
||||||
|
|
||||||
pending = await get_pending_confirm(store, "!room:ex")
|
pending = await get_pending_confirm(store, "@alice:example.org", "!confirm:example.org")
|
||||||
assert pending == {
|
assert pending == {
|
||||||
"action_id": "confirm",
|
"action_id": "confirm",
|
||||||
"description": "Confirm action?",
|
"description": "Confirm action?",
|
||||||
"payload": {"id": 1},
|
"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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue