mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-21 05:11:26 +00:00
test(telegram): regression coverage for edit overflow split-and-deliver
Two new tests: - tests/gateway/test_telegram_format.py test_message_too_long_splits_into_continuations_not_silent_truncation: asserts edit_message returns success=True with continuation_message_ids populated and message_id pointing at the last continuation when content exceeds MAX_MESSAGE_LENGTH (#19537). Replaces the original fail-on-overflow assertion with the split-and-deliver contract. - tests/gateway/test_stream_consumer.py TestEditOverflowSplitAndDeliver.test_consumer_advances_message_id_on_split_and_deliver: asserts the consumer side updates _message_id to the latest continuation, clears _last_sent_text, and fires on_new_message when the adapter reports a split-and-deliver result.
This commit is contained in:
parent
bf1f40996f
commit
82352e54c4
2 changed files with 87 additions and 0 deletions
|
|
@ -939,6 +939,56 @@ class TestFinalResponseDeliveryGuard:
|
|||
assert consumer._final_response_sent is True
|
||||
|
||||
|
||||
class TestEditOverflowSplitAndDeliver:
|
||||
"""When edit_message split-and-delivers an oversized payload across the
|
||||
original message + N continuations (Telegram >4096 UTF-16), the consumer
|
||||
must update _message_id to the latest continuation, reset _last_sent_text,
|
||||
and fire on_new_message so subsequent tool-progress bubbles linearize
|
||||
below the new visible message."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_consumer_advances_message_id_on_split_and_deliver(self):
|
||||
adapter = MagicMock()
|
||||
# Simulate edit_message split-and-deliver: success=True with the
|
||||
# final continuation's id and a populated continuation_message_ids
|
||||
# tuple (the new SendResult contract).
|
||||
adapter.edit_message = AsyncMock(return_value=SimpleNamespace(
|
||||
success=True,
|
||||
message_id="msg_continuation_2",
|
||||
continuation_message_ids=("msg_continuation_1", "msg_continuation_2"),
|
||||
))
|
||||
adapter.send = AsyncMock(
|
||||
return_value=SimpleNamespace(success=True, message_id="msg_initial"),
|
||||
)
|
||||
adapter.MAX_MESSAGE_LENGTH = 4096
|
||||
|
||||
config = StreamConsumerConfig(
|
||||
edit_interval=0.01, buffer_threshold=5, cursor="",
|
||||
)
|
||||
consumer = GatewayStreamConsumer(adapter, "chat_999", config)
|
||||
|
||||
# Track on_new_message firings.
|
||||
new_msg_count = [0]
|
||||
consumer._on_new_message = lambda: new_msg_count.__setitem__(0, new_msg_count[0] + 1)
|
||||
|
||||
# Seed the consumer as if a first send succeeded already.
|
||||
consumer._message_id = "msg_initial"
|
||||
consumer._last_sent_text = "old"
|
||||
consumer._already_sent = True
|
||||
|
||||
# Drive an edit that the adapter "split and delivers".
|
||||
ok = await consumer._send_or_edit("new full text after overflow")
|
||||
|
||||
assert ok is True
|
||||
# Consumer advanced to the latest continuation id.
|
||||
assert consumer._message_id == "msg_continuation_2"
|
||||
# Skip-if-same cache reset so the next edit doesn't false-positive.
|
||||
assert consumer._last_sent_text == ""
|
||||
# on_new_message fired so the tool-progress bubble breaks below
|
||||
# the new continuation (per the openclaw #32535 lesson).
|
||||
assert new_msg_count[0] == 1
|
||||
|
||||
|
||||
class TestInterimCommentaryMessages:
|
||||
@pytest.mark.asyncio
|
||||
async def test_commentary_message_stays_separate_from_final_stream(self):
|
||||
|
|
|
|||
|
|
@ -759,6 +759,43 @@ class TestEditMessageStreamingSafety:
|
|||
"text": "final **bold**",
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_message_too_long_splits_into_continuations_not_silent_truncation(self):
|
||||
"""When edit_message_text exceeds Telegram's 4096 UTF-16 limit, the
|
||||
adapter must split the content across the existing message + new
|
||||
continuation messages so the user gets the full reply. Previously
|
||||
the adapter best-effort truncated the content with '…' and returned
|
||||
success=True, dropping everything past the truncation boundary
|
||||
(#19537)."""
|
||||
adapter = TelegramAdapter(PlatformConfig(enabled=True, token="fake-token"))
|
||||
adapter._bot = MagicMock()
|
||||
adapter._bot.edit_message_text = AsyncMock()
|
||||
# Continuation sends return monotonically increasing message ids.
|
||||
_next_id = [1000]
|
||||
async def _fake_send(**kwargs):
|
||||
_next_id[0] += 1
|
||||
return SimpleNamespace(message_id=_next_id[0])
|
||||
adapter._bot.send_message = AsyncMock(side_effect=_fake_send)
|
||||
|
||||
# 6000-char content well over the 4096 UTF-16 limit.
|
||||
oversized = "x" * 6000
|
||||
result = await adapter.edit_message("123", "456", oversized, finalize=False)
|
||||
|
||||
# Adapter reports success with continuations populated.
|
||||
assert result.success is True
|
||||
assert result.error is None
|
||||
assert len(result.continuation_message_ids) >= 1, (
|
||||
"expected at least one continuation message"
|
||||
)
|
||||
# The reported message_id is the LAST visible message (the final
|
||||
# continuation), so subsequent edits target the most recent.
|
||||
assert result.message_id == result.continuation_message_ids[-1]
|
||||
# Original message_id (456) was edited with chunk 1.
|
||||
first_edit = adapter._bot.edit_message_text.call_args
|
||||
assert first_edit.kwargs["message_id"] == 456
|
||||
# Continuations were sent threaded as replies for visual grouping.
|
||||
assert adapter._bot.send_message.await_count == len(result.continuation_message_ids)
|
||||
|
||||
# =========================================================================
|
||||
# Telegram guest mention gating
|
||||
# =========================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue