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:
parent
8a80d004fd
commit
ae37476ddf
2 changed files with 171 additions and 1 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue