diff --git a/tests/gateway/test_stream_consumer_thread_routing.py b/tests/gateway/test_stream_consumer_thread_routing.py index 62e44f459e..01d20dc389 100644 --- a/tests/gateway/test_stream_consumer_thread_routing.py +++ b/tests/gateway/test_stream_consumer_thread_routing.py @@ -172,3 +172,27 @@ class TestFeishuFallbackThreadRouting: f"Expected receive_id_type='thread_id', got '{receive_id_type}'" ) + +class TestFallbackResendThreading: + """#103068: a full fallback resend replaces the preview, so it must land in + the originating thread; tail continuations keep their unthreaded delivery.""" + + @pytest.mark.asyncio + async def test_full_fallback_resend_threads_first_chunk_only(self): + adapter = _make_adapter(max_length=700) + adapter.send.side_effect = [ + SimpleNamespace(success=True, message_id=f"full_{i}") for i in range(10) + ] + consumer = GatewayStreamConsumer(adapter, "chat_123", initial_reply_to_id="om_user_1") + consumer._message_id = "om_preview" + consumer._last_sent_text = "truncated snapshot" + consumer._already_sent = True + consumer._fallback_final_send = True + + final = " ".join(["word"] * 400) # not prefixed by the snapshot -> full resend + await consumer._send_fallback_final(final) + + calls = adapter.send.await_args_list + assert len(calls) > 1 + assert calls[0].kwargs["reply_to"] == "om_user_1" + assert all("reply_to" not in c.kwargs for c in calls[1:]) diff --git a/tests/gateway/test_telegram_final_delivery.py b/tests/gateway/test_telegram_final_delivery.py index 82b8c54be9..b5c85e73c3 100644 --- a/tests/gateway/test_telegram_final_delivery.py +++ b/tests/gateway/test_telegram_final_delivery.py @@ -149,28 +149,6 @@ async def test_complete_preview_survives_long_flood_fallback_failure(monkeypatch assert consumer.delivered_final_matches("Final answer") is True -@pytest.mark.asyncio -async def test_full_fallback_resend_threads_and_deletes_truncated_preview(): - """When the visible prefix is not a prefix of the final (Feishu post vs - cleaned text), fallback resends the whole answer. Thread it to the user - message and delete the truncated edit bubble (#103068).""" - adapter = _adapter() - adapter.send.return_value = SendResult(success=True, message_id="full-1") - - consumer = GatewayStreamConsumer( - adapter, "chat-1", initial_reply_to_id="user-1", - ) - consumer._message_id = "preview-1" - consumer._last_sent_text = "truncated snapshot" - consumer._already_sent = True - consumer._fallback_final_send = True - - await consumer._send_fallback_final("full completed answer that does not start with the snapshot") - - assert adapter.send.await_args.kwargs["reply_to"] == "user-1" - adapter.delete_message.assert_awaited() - - @pytest.mark.asyncio async def test_telegram_long_flood_result_keeps_retry_after(): """The real adapter contract preserves the server delay for consumers."""