From ccc811dd246d0229960389ddc11a1b863b887281 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Mon, 14 Sep 2026 17:50:14 -0700 Subject: [PATCH] test(matrix): trim the reply-pill salvage to two invariant tests Keep the two invariants that were red on main: a reply to the bot under require_mention keeps reply_to_author_id, and a typed @bot in the reply text is still stripped. Drop the plain-mention regression (covered by test_matrix_mention.py) and the _split_reply_fallback table (change-detector). --- .../test_matrix_message_event_metadata.py | 73 ++----------------- 1 file changed, 7 insertions(+), 66 deletions(-) diff --git a/tests/gateway/test_matrix_message_event_metadata.py b/tests/gateway/test_matrix_message_event_metadata.py index 17e65ea4a4..ee61090916 100644 --- a/tests/gateway/test_matrix_message_event_metadata.py +++ b/tests/gateway/test_matrix_message_event_metadata.py @@ -249,53 +249,36 @@ async def test_media_message_carries_sender_and_reply_context(monkeypatch): # --------------------------------------------------------------------------- -# Reply fallback vs. the mention strip (#111233) -# -# A reply to the bot names the bot in the ``> <@bot:srv> ...`` pill, which is -# what makes the message count as a mention under the default -# MATRIX_REQUIRE_MENTION=true. The mention strip therefore runs on exactly the -# messages that carry a reply pill, and it must not rewrite the pill before -# _extract_reply_context parses it. +# Reply fallback vs. the mention strip (#111233): under the default +# MATRIX_REQUIRE_MENTION=true a reply to the bot is a mention *because of* the +# ``> <@bot:srv> ...`` pill, so the strip runs on exactly the messages that carry +# a pill — and must not rewrite it before _extract_reply_context parses it. # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_reply_to_bot_under_require_mention_keeps_reply_author(monkeypatch): - """Replying to the bot must keep the replied-to author under the default mention gate. - - The pill is the mention, so the strip runs and used to rewrite ``> <@bot>`` to - ``> <>`` before the fallback parse — losing reply_to_author_id and mangling - reply_to_text, so the prompt rendered "[Replying to: "<> did you check the logs?"]" - instead of "[Replying to your previous message: ...]". - """ + """The pill is the only mention: the strip used to turn ``> <@bot>`` into ``> <>`` + before the fallback parse, losing reply_to_author_id and mangling reply_to_text.""" adapter = _make_adapter(require_mention=True, monkeypatch=monkeypatch) adapter._startup_ts = time.time() - 10 - # No explicit "@hermes" in the reply text: the pill is the only mention. - assert adapter._user_id not in "hello there" body = "> <@hermes:example.org> did you check the logs?\n\nhello there" event = _make_event(body, in_reply_to_event_id="$bot_msg", event_id="$evt_reply_bot") await adapter._on_room_message(event) adapter.handle_message.assert_awaited_once() msg = adapter.handle_message.await_args.args[0] - assert msg.reply_to_message_id == "$bot_msg" assert msg.reply_to_author_id == "@hermes:example.org" assert msg.reply_to_author_name == "hermes" - # The quoted text must be the reply target's text, never the mangled "<> ..." pill remnant. assert msg.reply_to_text == "did you check the logs?" - # The user's actual reply is still delivered, with the quote block stripped. assert msg.text == "hello there" @pytest.mark.asyncio async def test_reply_with_explicit_mention_still_strips_it_from_reply_text(monkeypatch): - """The narrower strip must still remove an explicit @bot from the reply text. - - Only the quote block is exempt; a typed mention in the reply itself is stripped as - before, so the model does not see the addressing token. - """ + """Only the quote block is exempt: a typed @bot in the reply text is still stripped.""" adapter = _make_adapter(require_mention=True, monkeypatch=monkeypatch) adapter._startup_ts = time.time() - 10 @@ -307,45 +290,3 @@ async def test_reply_with_explicit_mention_still_strips_it_from_reply_text(monke assert msg.reply_to_author_id == "@carol:example.org" assert msg.reply_to_text == "original question" assert msg.text == "because reasons" - - -@pytest.mark.asyncio -async def test_plain_mention_without_quote_is_still_stripped(monkeypatch): - """A non-quote mention keeps the previous whole-body strip behaviour.""" - adapter = _make_adapter(require_mention=True, monkeypatch=monkeypatch) - adapter._startup_ts = time.time() - 10 - - event = _make_event("@hermes:example.org hello there", event_id="$evt_plain_mention") - await adapter._on_room_message(event) - - msg = adapter.handle_message.await_args.args[0] - assert msg.text == "hello there" - assert msg.reply_to_author_id is None - - -def test_split_reply_fallback_reassembles_body(): - """The split is lossless: the two halves rebuild the original body byte for byte. - - Callers transform one half and rebuild the body, so any drift between the split and - the original text would silently rewrite the message. - """ - from plugins.platforms.matrix.adapter import _split_reply_fallback - - cases = [ - ("> <@a:ex.org> q\n\nreply", ("> <@a:ex.org> q\n\n", "reply")), - ("> <@a:ex.org> q\nreply", ("> <@a:ex.org> q\n", "reply")), - ("> <@a:ex.org> q", ("> <@a:ex.org> q", "")), - ("> <@a:ex.org> q\n", ("> <@a:ex.org> q\n", "")), - ("> q\n> more\n\nreply", ("> q\n> more\n\n", "reply")), - ("> q\n>\n\nreply", ("> q\n>\n\n", "reply")), - # CRLF: the "\r" line is not the empty separator, so the quote block keeps "\r\n" - # and the reply text keeps the leading break — which the strip's .strip() removes. - ("> <@a:ex.org> q\r\n\r\nreply", ("> <@a:ex.org> q\r\n", "\r\nreply")), - ("no quote here", ("", "no quote here")), - ("", ("", "")), - ] - for body, expected in cases: - quote, reply = _split_reply_fallback(body) - assert (quote, reply) == expected - # The split must be lossless: callers rebuild the body from the two halves. - assert quote + reply == body