hermes-agent/tests/gateway/test_telegram_mention_boundaries.py
Teknium 39975613b1
test: prune wave 2 + speed fixes — 28,106 → 19,757 test functions, suite wall 315s → 294s
Second, deeper pass over tools/gateway/hermes_cli plus first pass over
the trees wave 1 missed (acp, acp_adapter, skills, computer_use, docker,
dashboard, conformance, monitoring, secret_sources, hermes_state,
providers). Same rubric as wave 1 (AGENTS.md test policy); security,
alternation/caching invariants, issue-number regressions, and E2E kept.

Real test-quality fixes found and rooted out along the way:
- tests/tools/test_command_guards.py made real auxiliary-LLM HTTPS calls
  (DEFAULT_CONFIG smart-approval leaked in) — pinned approval
  mode=manual via autouse fixture: 17.4s → 0.4s.
- test_model_switch_custom_providers.py / test_user_providers_model_switch.py
  silently probed live provider catalogs (~2s/test) — stubbed
  cached_provider_model_ids/provider_model_ids/fetch_api_models.
- test_telegram_noise_filter.py: 15-platform copy-paste matrix over
  shared gateway.run logic → 3 representative platforms (55s → 3.9s).
- test_gateway_shutdown.py: stop()'s 5s interrupt-deadline loop spun on
  MagicMock agents — interrupt.side_effect now clears _running_agents
  (22s → 1.0s).
- test_gateway_inactivity_timeout.py poll-harness timings shrunk 3-5x
  (24s → 1.1s); test_mcp_stability.py backoff/SIGTERM-grace sleeps
  patched (15.4s → 2.5s); test_async_delegation.py negative-drain wait
  5s → 0.5s.
- test_telegram_init_deadline.py: loop-block margin restored to 1.0s
  with rationale comment — the watchdog-dump assertion needs the loop
  blocked well past deadline+grace under parallel load (flaked once in
  the 40-worker verification run at a 0.2s margin).

Verification: full hermetic suite via scripts/run_tests.sh —
2,438 files, 21,718 tests passed, 0 failed, 293.9s wall.
Suite totals vs original baseline: 46,820 → 19,757 test functions
(−57.8%), wall 583.5s → 293.9s (−50%), subprocess CPU 13,564s → 11,623s.
2026-07-29 13:39:40 -07:00

121 lines
4.5 KiB
Python

"""Tests for Telegram bot mention detection (bug #12545).
The old implementation used a naive substring check
(`f"@{bot_username}" in text.lower()`), which incorrectly matched partial
substrings like 'foo@hermes_bot.example'.
Detection now relies entirely on the MessageEntity objects Telegram's server
emits for real mentions. A bare `@username` substring in message text without
a corresponding `MENTION` entity is NOT a mention — this correctly ignores
@handles that appear inside URLs, code blocks, email-like strings, or quoted
text, because Telegram's parser does not emit mention entities for any of
those contexts.
"""
from types import SimpleNamespace
from gateway.config import Platform, PlatformConfig
from plugins.platforms.telegram.adapter import TelegramAdapter
def _make_adapter():
adapter = object.__new__(TelegramAdapter)
adapter.platform = Platform.TELEGRAM
adapter.config = PlatformConfig(enabled=True, token="***", extra={})
adapter._bot = SimpleNamespace(id=999, username="hermes_bot")
return adapter
def _mention_entity(text, mention="@hermes_bot"):
"""Build a MENTION entity pointing at a literal `@username` in `text`."""
offset = text.index(mention)
return SimpleNamespace(type="mention", offset=offset, length=len(mention))
def _text_mention_entity(offset, length, user_id):
"""Build a TEXT_MENTION entity (used when the target user has no public @handle)."""
return SimpleNamespace(
type="text_mention",
offset=offset,
length=length,
user=SimpleNamespace(id=user_id),
)
def _message(text=None, caption=None, entities=None, caption_entities=None):
return SimpleNamespace(
text=text,
caption=caption,
entities=entities or [],
caption_entities=caption_entities or [],
message_thread_id=None,
chat=SimpleNamespace(id=-100, type="group"),
reply_to_message=None,
)
class TestRealMentionsAreDetected:
"""A real Telegram mention always comes with a MENTION entity — detect those."""
def test_mention_at_start_of_message(self):
adapter = _make_adapter()
text = "@hermes_bot hello world"
msg = _message(text=text, entities=[_mention_entity(text)])
assert adapter._message_mentions_bot(msg) is True
def test_text_mention_entity_targets_bot(self):
"""TEXT_MENTION is Telegram's entity type for @FirstName -> user without a public handle."""
adapter = _make_adapter()
msg = _message(text="hey you", entities=[_text_mention_entity(4, 3, user_id=999)])
assert adapter._message_mentions_bot(msg) is True
class TestSubstringFalsePositivesAreRejected:
"""Bare `@bot_username` substrings without a MENTION entity must NOT match.
These are all inputs where the OLD substring check returned True incorrectly.
A word-boundary regex would still over-match some of these (code blocks,
URLs). Entity-based detection handles them all correctly because Telegram's
parser does not emit mention entities for non-mention contexts.
"""
def test_email_like_substring(self):
"""bug #12545 exact repro: 'foo@hermes_bot.example'."""
adapter = _make_adapter()
msg = _message(text="email me at foo@hermes_bot.example")
assert adapter._message_mentions_bot(msg) is False
def test_substring_inside_url_without_entity(self):
"""@handle inside a URL produces a URL entity, not a MENTION entity."""
adapter = _make_adapter()
msg = _message(text="see https://example.com/@hermes_bot for details")
assert adapter._message_mentions_bot(msg) is False
def test_email_substring_in_caption(self):
adapter = _make_adapter()
msg = _message(caption="foo@hermes_bot.example")
assert adapter._message_mentions_bot(msg) is False
class TestEntityEdgeCases:
"""Malformed or mismatched entities should not crash or over-match."""
def test_malformed_entity_with_negative_offset(self):
adapter = _make_adapter()
msg = _message(text="@hermes_bot hi",
entities=[SimpleNamespace(type="mention", offset=-1, length=11)])
assert adapter._message_mentions_bot(msg) is False
class TestCaseInsensitivity:
"""Telegram usernames are case-insensitive; the slice-compare normalizes both sides."""
def test_uppercase_mention(self):
adapter = _make_adapter()
text = "hi @HERMES_BOT"
msg = _message(text=text, entities=[_mention_entity(text, mention="@HERMES_BOT")])
assert adapter._message_mentions_bot(msg) is True