mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-31 06:51:29 +00:00
send_slash_confirm() sent the raw command preview with ParseMode.MARKDOWN,
skipping the format_message() conversion applied to every other dynamic
send in the adapter. Commands with underscores, dots, brackets, or other
MarkdownV2-sensitive characters raised BadRequest: Can't parse entities;
the exception was swallowed by the outer try/except, so the confirmation
prompt silently never appeared.
Fix: wrap preview through format_message() and switch to MARKDOWN_V2,
symmetric with send_update_prompt and the callback sends fixed in
a69404052.
109 lines
3.5 KiB
Python
109 lines
3.5 KiB
Python
"""Regression guard: send_slash_confirm must use format_message + MARKDOWN_V2."""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
_repo = str(Path(__file__).resolve().parents[2])
|
|
if _repo not in sys.path:
|
|
sys.path.insert(0, _repo)
|
|
|
|
|
|
def _ensure_telegram_mock():
|
|
if "telegram" in sys.modules and hasattr(sys.modules["telegram"], "__file__"):
|
|
return
|
|
mod = MagicMock()
|
|
mod.ext.ContextTypes.DEFAULT_TYPE = type(None)
|
|
mod.constants.ParseMode.MARKDOWN = "Markdown"
|
|
mod.constants.ParseMode.MARKDOWN_V2 = "MarkdownV2"
|
|
mod.constants.ParseMode.HTML = "HTML"
|
|
mod.constants.ChatType.PRIVATE = "private"
|
|
mod.constants.ChatType.GROUP = "group"
|
|
mod.constants.ChatType.SUPERGROUP = "supergroup"
|
|
mod.constants.ChatType.CHANNEL = "channel"
|
|
mod.error.NetworkError = type("NetworkError", (OSError,), {})
|
|
mod.error.TimedOut = type("TimedOut", (OSError,), {})
|
|
mod.error.BadRequest = type("BadRequest", (Exception,), {})
|
|
for name in ("telegram", "telegram.ext", "telegram.constants", "telegram.request"):
|
|
sys.modules.setdefault(name, mod)
|
|
sys.modules.setdefault("telegram.error", mod.error)
|
|
|
|
|
|
_ensure_telegram_mock()
|
|
|
|
from gateway.platforms.telegram import TelegramAdapter
|
|
from gateway.config import PlatformConfig
|
|
|
|
|
|
def _make_adapter():
|
|
config = PlatformConfig(enabled=True, token="test-token", extra={})
|
|
adapter = TelegramAdapter(config)
|
|
adapter._bot = AsyncMock()
|
|
adapter._app = MagicMock()
|
|
return adapter
|
|
|
|
|
|
class TestSendSlashConfirm:
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_uses_markdown_v2_and_escapes_special_chars(self):
|
|
"""send_slash_confirm must pass preview through format_message and use
|
|
MARKDOWN_V2 — so commands with underscores, dots, or brackets don't
|
|
raise BadRequest: Can't parse entities."""
|
|
adapter = _make_adapter()
|
|
sent = {}
|
|
|
|
async def mock_send(**kwargs):
|
|
sent.update(kwargs)
|
|
return SimpleNamespace(message_id=7)
|
|
|
|
adapter._bot.send_message = AsyncMock(side_effect=mock_send)
|
|
|
|
result = await adapter.send_slash_confirm(
|
|
chat_id="100",
|
|
title="Confirm",
|
|
message="/run script_name.sh --flag=value [option]",
|
|
session_key="sk",
|
|
confirm_id="cid1",
|
|
)
|
|
|
|
assert result.success is True
|
|
assert "MARKDOWN_V2" in repr(sent["parse_mode"])
|
|
# Underscores and dots must be escaped by format_message
|
|
assert "script\\_name" in sent["text"]
|
|
assert "\\." in sent["text"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_stores_slash_confirm_state(self):
|
|
adapter = _make_adapter()
|
|
adapter._bot.send_message = AsyncMock(
|
|
return_value=SimpleNamespace(message_id=8)
|
|
)
|
|
|
|
await adapter.send_slash_confirm(
|
|
chat_id="100",
|
|
title="Confirm",
|
|
message="reload-mcp",
|
|
session_key="my-session",
|
|
confirm_id="cid2",
|
|
)
|
|
|
|
assert adapter._slash_confirm_state["cid2"] == "my-session"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_not_connected_returns_failure(self):
|
|
adapter = _make_adapter()
|
|
adapter._bot = None
|
|
|
|
result = await adapter.send_slash_confirm(
|
|
chat_id="100",
|
|
title="Confirm",
|
|
message="reload-mcp",
|
|
session_key="sk",
|
|
confirm_id="cid3",
|
|
)
|
|
|
|
assert result.success is False
|