hermes-agent/tests/gateway/test_clarify_active_session_bypass.py

121 lines
4.1 KiB
Python

"""Regression tests for clarify replies while a gateway session is busy."""
import asyncio
from unittest.mock import AsyncMock, patch
import pytest
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (
BasePlatformAdapter,
MessageEvent,
MessageType,
SendResult,
)
from gateway.session import SessionSource, build_session_key
class _ClarifyBypassAdapter(BasePlatformAdapter):
def __init__(self):
super().__init__(PlatformConfig(enabled=True, token="test"), Platform.TELEGRAM)
async def connect(self):
return True
async def disconnect(self):
pass
async def send(self, chat_id, content, reply_to=None, metadata=None):
return SendResult(success=True, message_id="text")
async def get_chat_info(self, chat_id):
return {"id": chat_id, "type": "private"}
def _event(text="custom answer"):
return MessageEvent(
text=text,
message_type=MessageType.TEXT,
source=SessionSource(
platform=Platform.TELEGRAM,
chat_id="12345",
chat_type="private",
user_id="user1",
),
message_id="msg1",
)
def _clear_clarify_state():
from tools import clarify_gateway as cm
with cm._lock:
cm._entries.clear()
cm._session_index.clear()
cm._notify_cbs.clear()
@pytest.mark.asyncio
async def test_active_session_routes_typed_choice_clarify_reply_to_runner_not_busy_queue():
"""Typed text must resolve a pending choice clarify even while the agent is busy.
Telegram button clarifies keep the adapter session active while the agent
thread blocks on ``wait_for_response``. If the adapter only bypasses for
entries already marked ``awaiting_text``, typed replies to the visible
multi-choice prompt are handled as busy follow-ups and the clarify wait is
never resolved.
"""
_clear_clarify_state()
from tools import clarify_gateway as cm
adapter = _ClarifyBypassAdapter()
adapter._message_handler = AsyncMock(return_value="")
adapter._busy_session_handler = AsyncMock(return_value=True)
event = _event("None of those are valid options")
session_key = build_session_key(
event.source,
group_sessions_per_user=adapter.config.extra.get("group_sessions_per_user", True),
thread_sessions_per_user=adapter.config.extra.get("thread_sessions_per_user", False),
)
adapter._active_sessions[session_key] = asyncio.Event()
cm.register("clarify-1", session_key, "Pick one", ["A", "B"])
await adapter.handle_message(event)
adapter._message_handler.assert_awaited_once_with(event)
adapter._busy_session_handler.assert_not_awaited()
assert adapter._pending_messages == {}
@pytest.mark.asyncio
async def test_gateway_clarify_reply_resumes_typing_before_returning_empty_ack():
"""A clarify answer must re-enable the active run's typing indicator.
Clarify pauses typing while waiting so Slack's Assistant API does not
disable the compose box. The typed answer is intercepted by the gateway
and returns an empty acknowledgment instead of starting a second run; that
interception path must therefore resume the original run's indicator.
"""
_clear_clarify_state()
from gateway.run import GatewayRunner
from tools import clarify_gateway as cm
adapter = _ClarifyBypassAdapter()
adapter.pause_typing_for_chat("12345")
event = _event("the missing details")
runner = GatewayRunner.__new__(GatewayRunner)
runner._startup_restore_in_progress = False
runner._scale_to_zero_note_real_inbound = lambda: None
runner._is_user_authorized = lambda source: True
runner._session_key_for_source = lambda source: "clarify-session"
runner._adapter_for_source = lambda source: adapter
runner._update_prompt_pending = {}
cm.register("clarify-2", "clarify-session", "What is missing?", None)
with patch("hermes_cli.plugins.invoke_hook", return_value=[]):
result = await runner._handle_message(event)
assert result == ""
assert "12345" not in adapter._typing_paused