feat: finalize matrix platform audit and docs

This commit is contained in:
Mikhail Putilovskij 2026-04-21 15:35:03 +03:00
parent 6422c7db58
commit 4524a6abc8
30 changed files with 3093 additions and 176 deletions

View file

@ -6,7 +6,11 @@ from unittest.mock import AsyncMock
from nio.api import RoomVisibility
from nio.responses import RoomCreateError
from adapter.matrix.handlers.chat import make_handle_archive, make_handle_new_chat, make_handle_rename
from adapter.matrix.handlers.chat import (
make_handle_archive,
make_handle_new_chat,
make_handle_rename,
)
from adapter.matrix.store import get_room_meta, set_user_meta
from core.auth import AuthManager
from core.chat import ChatManager
@ -28,7 +32,9 @@ async def _setup():
async def test_mat04_new_chat_calls_room_put_state_with_space_id():
platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()
await set_user_meta(store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2})
await set_user_meta(
store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2}
)
client = SimpleNamespace(
room_create=AsyncMock(return_value=SimpleNamespace(room_id="!newroom:ex")),
@ -59,7 +65,7 @@ async def test_mat04_new_chat_calls_room_put_state_with_space_id():
assert kwargs.get("state_key") == "!newroom:ex"
room_meta = await get_room_meta(store, "!newroom:ex")
assert room_meta is not None
assert room_meta["platform_chat_id"] == "matrix:!newroom:ex"
assert room_meta["platform_chat_id"] == "1"
assert any(isinstance(item, OutgoingMessage) and "Test" in item.text for item in result)
@ -169,10 +175,14 @@ async def test_mat11b_rename_from_unregistered_room_returns_error_message():
async def test_mat12_room_create_error_returns_user_message():
platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()
await set_user_meta(store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2})
await set_user_meta(
store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2}
)
client = SimpleNamespace(
room_create=AsyncMock(return_value=RoomCreateError(message="rate limited", status_code="429")),
room_create=AsyncMock(
return_value=RoomCreateError(message="rate limited", status_code="429")
),
room_put_state=AsyncMock(),
room_invite=AsyncMock(),
)

View file

@ -1,8 +1,7 @@
from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock
import pytest
@ -15,7 +14,6 @@ from adapter.matrix.handlers.context_commands import (
)
from adapter.matrix.store import (
get_load_pending,
set_load_pending,
set_room_meta,
)
@ -48,7 +46,7 @@ async def test_save_command_auto_name_records_session():
await set_room_meta(
store,
"!room:example.org",
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "matrix:room-1"},
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "41"},
)
handler = make_handle_save(
agent_api=platform._agent_api,
@ -71,7 +69,7 @@ async def test_save_command_auto_name_records_session():
sessions = await platform._prototype_state.list_saved_sessions("u1")
assert len(sessions) == 1
assert sessions[0]["name"].startswith("context-")
assert sessions[0]["source_context_id"] == "matrix:room-1"
assert sessions[0]["source_context_id"] == "41"
@pytest.mark.asyncio
@ -81,7 +79,7 @@ async def test_save_command_with_name_uses_given_name():
await set_room_meta(
store,
"!room:example.org",
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "matrix:room-1"},
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "41"},
)
handler = make_handle_save(
agent_api=platform._agent_api,
@ -119,7 +117,13 @@ async def test_load_command_shows_numbered_list_and_sets_pending():
handler = make_handle_load(store=runtime.store, prototype_state=platform._prototype_state)
event = IncomingCommand(user_id="u1", platform="matrix", chat_id="C1", command="load", args=[])
result = await handler(event, runtime.auth_mgr, platform, runtime.chat_mgr, runtime.settings_mgr)
result = await handler(
event,
runtime.auth_mgr,
platform,
runtime.chat_mgr,
runtime.settings_mgr,
)
assert "1. session-a" in result[0].text
assert "2. session-b" in result[0].text
@ -150,16 +154,28 @@ async def test_reset_command_assigns_new_platform_chat_id():
runtime = build_runtime(platform=platform)
store = runtime.store
await set_room_meta(store, "!room:example.org", {"platform_chat_id": "matrix:!room:example.org"})
await set_room_meta(store, "!room:example.org", {"platform_chat_id": "7"})
handler = make_handle_reset(store=store, prototype_state=prototype_state)
event = IncomingCommand(user_id="u1", platform="matrix", chat_id="!room:example.org", command="reset", args=[])
event = IncomingCommand(
user_id="u1",
platform="matrix",
chat_id="!room:example.org",
command="reset",
args=[],
)
result = await handler(event, runtime.auth_mgr, platform, runtime.chat_mgr, runtime.settings_mgr)
result = await handler(
event,
runtime.auth_mgr,
platform,
runtime.chat_mgr,
runtime.settings_mgr,
)
new_id = await get_platform_chat_id(store, "!room:example.org")
assert new_id != "matrix:!room:example.org"
assert new_id.startswith("matrix:!room:example.org#")
assert new_id != "7"
assert new_id == "1"
assert "сброшен" in result[0].text.lower()
@ -177,17 +193,29 @@ async def test_context_command_shows_current_snapshot():
await set_room_meta(
runtime.store,
"!room:example.org",
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "matrix:room-1"},
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "41"},
)
await platform._prototype_state.set_current_session("matrix:room-1", "session-a")
await platform._prototype_state.set_last_tokens_used("matrix:room-1", 99)
await platform._prototype_state.set_current_session("41", "session-a")
await platform._prototype_state.set_last_tokens_used("41", 99)
await platform._prototype_state.add_saved_session("u1", "session-a")
handler = make_handle_context(store=runtime.store, prototype_state=platform._prototype_state)
event = IncomingCommand(user_id="u1", platform="matrix", chat_id="C1", command="context", args=[])
event = IncomingCommand(
user_id="u1",
platform="matrix",
chat_id="C1",
command="context",
args=[],
)
result = await handler(event, runtime.auth_mgr, platform, runtime.chat_mgr, runtime.settings_mgr)
result = await handler(
event,
runtime.auth_mgr,
platform,
runtime.chat_mgr,
runtime.settings_mgr,
)
assert "Контекст чата: matrix:room-1" in result[0].text
assert "Контекст чата: 41" in result[0].text
assert "Сессия: session-a" in result[0].text
assert "Токены (последний ответ): 99" in result[0].text
assert "session-a" in result[0].text
@ -203,7 +231,7 @@ async def test_bot_intercepts_numeric_load_selection():
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:room-1",
"platform_chat_id": "41",
},
)
client = SimpleNamespace(
@ -223,7 +251,7 @@ async def test_bot_intercepts_numeric_load_selection():
await bot.on_room_message(room, event)
platform.send_message.assert_awaited_once()
assert await platform._prototype_state.get_current_session("matrix:room-1") == "session-a"
assert await platform._prototype_state.get_current_session("41") == "session-a"
assert await platform._prototype_state.get_current_session("C1") == "session-a"
client.room_send.assert_awaited_once_with(
"!room:example.org",

View file

@ -272,10 +272,7 @@ async def test_bot_assigns_platform_chat_id_for_existing_managed_room():
await bot.on_room_message(room, event)
assert (
await get_platform_chat_id(runtime.store, "!chat1:example.org")
== "matrix:!chat1:example.org"
)
assert await get_platform_chat_id(runtime.store, "!chat1:example.org") == "1"
runtime.dispatcher.dispatch.assert_awaited_once()
@ -287,7 +284,7 @@ async def test_bot_routes_plain_messages_via_platform_chat_id():
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:ctx-1",
"platform_chat_id": "41",
},
)
client = SimpleNamespace(user_id="@bot:example.org")
@ -300,7 +297,7 @@ async def test_bot_routes_plain_messages_via_platform_chat_id():
await bot.on_room_message(room, event)
dispatched = runtime.dispatcher.dispatch.await_args.args[0]
assert dispatched.chat_id == "matrix:ctx-1"
assert dispatched.chat_id == "41"
assert dispatched.text == "hello"
@ -313,7 +310,7 @@ async def test_bot_downloads_matrix_file_to_workspace_before_staging(tmp_path, m
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:ctx-1",
"platform_chat_id": "41",
},
)
client = SimpleNamespace(
@ -539,7 +536,7 @@ async def test_next_normal_message_commits_staged_attachments():
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:ctx-1",
"platform_chat_id": "41",
},
)
await add_staged_attachment(
@ -584,7 +581,7 @@ async def test_failed_commit_preserves_staged_attachments():
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:ctx-1",
"platform_chat_id": "41",
},
)
await add_staged_attachment(
@ -622,7 +619,7 @@ async def test_bot_keeps_commands_on_local_chat_id():
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:ctx-1",
"platform_chat_id": "41",
},
)
client = SimpleNamespace(user_id="@bot:example.org")
@ -647,7 +644,7 @@ async def test_bot_leaves_existing_platform_chat_id_unchanged():
{
"chat_id": "C1",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "matrix:existing",
"platform_chat_id": "99",
},
)
client = SimpleNamespace(user_id="@bot:example.org")
@ -659,7 +656,7 @@ async def test_bot_leaves_existing_platform_chat_id_unchanged():
await bot.on_room_message(room, event)
assert await get_platform_chat_id(runtime.store, "!chat1:example.org") == "matrix:existing"
assert await get_platform_chat_id(runtime.store, "!chat1:example.org") == "99"
runtime.dispatcher.dispatch.assert_awaited_once()
@ -686,10 +683,7 @@ async def test_bot_assigns_platform_chat_id_before_load_selection():
await bot.on_room_message(room, event)
assert (
await get_platform_chat_id(runtime.store, "!chat1:example.org")
== "matrix:!chat1:example.org"
)
assert await get_platform_chat_id(runtime.store, "!chat1:example.org") == "1"
client.room_send.assert_awaited_once_with(
"!chat1:example.org",
"m.room.message",

View file

@ -64,7 +64,7 @@ async def test_mat01_invite_creates_space_and_chat1():
assert room_meta is not None
assert room_meta["chat_id"] == "C4"
assert room_meta["space_id"] == "!space:example.org"
assert room_meta["platform_chat_id"] == "matrix:!chat1:example.org"
assert room_meta["platform_chat_id"] == "1"
assert user_meta["next_chat_index"] == 5
chats = await runtime.chat_mgr.list_active("@alice:example.org")
@ -120,7 +120,7 @@ async def test_mat03_no_hardcoded_c1():
room_meta = await get_room_meta(runtime.store, "!chat1:example.org")
assert room_meta is not None
assert room_meta["chat_id"] == "C7"
assert room_meta["platform_chat_id"] == "matrix:!chat1:example.org"
assert room_meta["platform_chat_id"] == "1"
user_meta = await get_user_meta(runtime.store, "@alice:example.org")
assert user_meta is not None

View file

@ -15,6 +15,7 @@ from adapter.matrix.store import (
get_staged_attachments,
get_user_meta,
next_chat_id,
next_platform_chat_id,
remove_staged_attachment_at,
set_pending_confirm,
set_platform_chat_id,
@ -107,6 +108,12 @@ async def test_next_chat_id_increments(store: InMemoryStore):
assert await next_chat_id(store, uid) == "C3"
async def test_next_platform_chat_id_increments(store: InMemoryStore):
assert await next_platform_chat_id(store) == "1"
assert await next_platform_chat_id(store) == "2"
assert await next_platform_chat_id(store) == "3"
async def test_skills_message_roundtrip(store: InMemoryStore):
await set_skills_message_id(store, "!room", "$event")
assert await get_skills_message_id(store, "!room") == "$event"
@ -151,7 +158,8 @@ async def test_staged_attachments_roundtrip(store: InMemoryStore):
],
)
async def test_staged_attachments_invalid_container_state_returns_empty_list(
store: InMemoryStore, stored_value,
store: InMemoryStore,
stored_value,
):
room_id = "!room:m.org"
user_id = "@alice:m.org"