test(05-02): add failing regressions for clear routing

- cover room-local clear rotation and upstream disconnect behavior
- assert strict routed-platform failures on incomplete room bindings
This commit is contained in:
Mikhail Putilovskij 2026-04-28 01:13:54 +03:00
parent 8a80d004fd
commit ae37476ddf
2 changed files with 171 additions and 1 deletions

View file

@ -1,11 +1,12 @@
from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import AsyncMock
from unittest.mock import AsyncMock, Mock
import pytest
from adapter.matrix.bot import MatrixBot, build_runtime
from adapter.matrix.handlers import register_matrix_handlers
from adapter.matrix.handlers.context_commands import (
make_handle_context,
make_handle_load,
@ -29,6 +30,7 @@ class MatrixCommandPlatform(MockPlatformClient):
super().__init__()
self._prototype_state = PrototypeStateStore()
self._agent_api = object()
self.disconnect_chat = AsyncMock()
self.send_message = AsyncMock(
return_value=MessageResponse(
message_id="msg-1",
@ -39,6 +41,12 @@ class MatrixCommandPlatform(MockPlatformClient):
)
@pytest.fixture(autouse=True)
def clear_matrix_registry_env(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("MATRIX_AGENT_REGISTRY_PATH", raising=False)
monkeypatch.delenv("MATRIX_PLATFORM_BACKEND", raising=False)
@pytest.mark.asyncio
async def test_save_command_auto_name_records_session():
platform = MatrixCommandPlatform()
@ -179,6 +187,88 @@ async def test_reset_command_assigns_new_platform_chat_id():
assert "сброшен" in result[0].text.lower()
@pytest.mark.asyncio
async def test_clear_command_rotates_only_current_room_and_disconnects_old_upstream_chat():
from adapter.matrix.store import get_platform_chat_id
platform = MatrixCommandPlatform()
runtime = build_runtime(platform=platform)
await runtime.chat_mgr.get_or_create(
user_id="u1",
chat_id="C1",
platform="matrix",
surface_ref="!room-a:example.org",
name="Chat A",
)
await runtime.chat_mgr.get_or_create(
user_id="u1",
chat_id="C2",
platform="matrix",
surface_ref="!room-b:example.org",
name="Chat B",
)
await set_room_meta(
runtime.store,
"!room-a:example.org",
{"chat_id": "C1", "matrix_user_id": "u1", "platform_chat_id": "41"},
)
await set_room_meta(
runtime.store,
"!room-b:example.org",
{"chat_id": "C2", "matrix_user_id": "u1", "platform_chat_id": "99"},
)
handler = make_handle_reset(store=runtime.store, prototype_state=platform._prototype_state)
event = IncomingCommand(
user_id="u1",
platform="matrix",
chat_id="C1",
command="clear",
args=[],
)
result = await handler(
event,
runtime.auth_mgr,
platform,
runtime.chat_mgr,
runtime.settings_mgr,
)
room_a_chat_id = await get_platform_chat_id(runtime.store, "!room-a:example.org")
room_b_chat_id = await get_platform_chat_id(runtime.store, "!room-b:example.org")
assert room_a_chat_id == "1"
assert room_a_chat_id != "41"
assert room_b_chat_id == "99"
platform.disconnect_chat.assert_awaited_once_with("41")
assert "сброшен" in result[0].text.lower()
def test_register_matrix_handlers_exposes_clear_and_optional_reset_alias():
dispatcher = SimpleNamespace(register=Mock())
register_matrix_handlers(
dispatcher,
client=object(),
store=object(),
registry=None,
prototype_state=PrototypeStateStore(),
)
clear_calls = [
call
for call in dispatcher.register.call_args_list
if call.args[:2] == (IncomingCommand, "clear")
]
reset_calls = [
call
for call in dispatcher.register.call_args_list
if call.args[:2] == (IncomingCommand, "reset")
]
assert clear_calls
assert len(reset_calls) <= 1
@pytest.mark.asyncio
async def test_context_command_shows_current_snapshot():
platform = MatrixCommandPlatform()

View file

@ -14,6 +14,7 @@ from core.chat import ChatManager
from core.store import InMemoryStore
from sdk.interface import MessageChunk, MessageResponse, User, UserSettings
from sdk.mock import MockPlatformClient
from sdk.interface import PlatformError
class FakeDelegate:
@ -99,6 +100,12 @@ class FakeDelegate:
self.update_calls.append((user_id, action))
@pytest.fixture(autouse=True)
def clear_matrix_registry_env(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delenv("MATRIX_AGENT_REGISTRY_PATH", raising=False)
monkeypatch.delenv("MATRIX_PLATFORM_BACKEND", raising=False)
@pytest.mark.asyncio
async def test_send_message_routes_by_room_agent_and_platform_chat_id():
store = InMemoryStore()
@ -159,6 +166,79 @@ async def test_stream_message_routes_by_room_agent_and_platform_chat_id():
]
@pytest.mark.asyncio
async def test_send_message_fails_fast_when_platform_chat_id_is_missing():
store = InMemoryStore()
chat_mgr = ChatManager(None, store)
await chat_mgr.get_or_create("u1", "C1", "matrix", "!room:example.org")
await set_room_meta(
store,
"!room:example.org",
{"agent_id": "agent-2"},
)
platform = RoutedPlatformClient(
chat_mgr=chat_mgr,
store=store,
delegates={"agent-2": FakeDelegate(name="agent-2")},
)
with pytest.raises(PlatformError, match="routing is incomplete") as exc_info:
await platform.send_message("u1", "C1", "hello")
assert exc_info.value.code == "MATRIX_ROUTE_INCOMPLETE"
@pytest.mark.asyncio
async def test_stream_message_fails_fast_when_agent_id_is_missing():
store = InMemoryStore()
chat_mgr = ChatManager(None, store)
await chat_mgr.get_or_create("u1", "C1", "matrix", "!room:example.org")
await set_room_meta(
store,
"!room:example.org",
{"platform_chat_id": "41"},
)
platform = RoutedPlatformClient(
chat_mgr=chat_mgr,
store=store,
delegates={"agent-2": FakeDelegate(name="agent-2")},
)
with pytest.raises(PlatformError, match="routing is incomplete") as exc_info:
await anext(platform.stream_message("u1", "C1", "hello"))
assert exc_info.value.code == "MATRIX_ROUTE_INCOMPLETE"
@pytest.mark.asyncio
async def test_routing_uses_repaired_room_metadata_without_runtime_backfill():
store = InMemoryStore()
chat_mgr = ChatManager(None, store)
await chat_mgr.get_or_create("u1", "C1", "matrix", "!room:example.org")
await set_room_meta(
store,
"!room:example.org",
{"platform_chat_id": "restored-41", "agent_id": "agent-2"},
)
delegate = FakeDelegate(name="agent-2")
platform = RoutedPlatformClient(
chat_mgr=chat_mgr,
store=store,
delegates={"agent-2": delegate},
)
await platform.send_message("u1", "C1", "hello")
assert delegate.send_calls == [
{
"user_id": "u1",
"chat_id": "restored-41",
"text": "hello",
"attachments": None,
}
]
@pytest.mark.asyncio
async def test_user_and_settings_delegate_to_default_client():
store = InMemoryStore()