"""Self-reply regressions; only synthetic records and mocked desktop input.""" import tempfile import time import unittest from pathlib import Path from unittest import mock from conversation_store import ConversationStore from engine_b import DataEngine from wechat_bot import WeChatBot class ArchiveFeedbackTest(unittest.TestCase): def test_packaged_archive_generation_and_send_flow(self): from packaging_checks import check_reply_feedback check_reply_feedback() def setUp(self): self.temp = tempfile.TemporaryDirectory() self.addCleanup(self.temp.cleanup) self.path = Path(self.temp.name) / "conversations.json" self.store = ConversationStore(str(self.path)) self.bot = mock.Mock() self.bot.has_active_pending.return_value = False self.bot.enqueue_detected.return_value = (True, "queued") self.engine = DataEngine(bot=self.bot, conversations_path=str(self.path)) def test_completed_exchange_does_not_trigger_on_startup_or_repoll(self): self.store.append_exchange_once("aa", "customer question", "our answer", "exchange1") self.engine.poll_once() self.engine.poll_once() self.bot.enqueue_detected.assert_not_called() def test_saving_our_reply_does_not_create_another_customer_event(self): self.store.append("aa", "user", "customer question") self.store.save() self.engine.poll_once() self.assertEqual(self.bot.enqueue_detected.call_count, 1) self.bot.enqueue_detected.reset_mock() for i in range(8): self.store.append_exchange_once("aa", "customer question", f"our answer {i}", f"exchange{i}") self.engine.poll_once() self.bot.enqueue_detected.assert_not_called() def test_genuine_followup_after_reply_is_still_enqueued(self): self.store.append_exchange_once("aa", "customer question", "our answer", "exchange1") self.engine.poll_once() self.bot.enqueue_detected.reset_mock() self.store.append("aa", "user", "customer question") self.store.save() self.engine.poll_once() self.bot.enqueue_detected.assert_called_once() def test_meta_records_after_reply_do_not_revive_old_customer_turn(self): self.store.append_exchange_once("aa", "question", "answer", "exchange1") self.store.append("aa", "system", "archive metadata") self.store.save() self.engine.poll_once() self.bot.enqueue_detected.assert_not_called() def test_new_customer_record_with_same_timestamp_as_answer_is_processed(self): with mock.patch("conversation_store.time.time", return_value=time.time()): self.store.append_exchange_once("aa", "question", "answer", "exchange1") self.engine.poll_once() self.bot.enqueue_detected.reset_mock() self.store.append("aa", "user", "question") self.store.save() self.engine.poll_once() self.bot.enqueue_detected.assert_called_once() class OwnMessageGuardTest(unittest.TestCase): def bot(self): bot = WeChatBot.__new__(WeChatBot) bot._active_session_fp = b"a" * 40 bot._pending_reply_sessions = {bot._active_session_fp.hex(): {"confirmed_unread": True, "display_name": "customer"}} bot._known_outgoing_speakers = mock.Mock(return_value={"configured assistant"}) bot._last_visible_bubble_is_outgoing = mock.Mock(return_value=True) bot._open_chat_display_name = mock.Mock(return_value="customer") bot._clear_reply_pending = mock.Mock() bot._remember_active_surface = mock.Mock() bot._log_queue_event = mock.Mock() return bot def test_unknown_staff_name_is_not_positive_customer_proof(self): bot = self.bot() text = "different staff 14:12:01\nour answer" self.assertFalse(bot._clipboard_tail_is_explicit_customer_text(text)) self.assertTrue(bot._nothing_left_to_answer(bot._active_session_fp, text)) bot._clear_reply_pending.assert_called_once_with(bot._active_session_fp) def test_verified_customer_name_preserves_short_followup_despite_pixel_error(self): bot = self.bot() text = "different staff 14:12:01\nour answer\ncustomer 14:12:02\nthanks" self.assertTrue(bot._clipboard_tail_is_explicit_customer_text(text)) self.assertFalse(bot._nothing_left_to_answer(bot._active_session_fp, text)) bot._clear_reply_pending.assert_not_called() def test_known_staff_tail_is_not_answered_even_when_pixel_direction_is_unknown(self): bot = self.bot() bot._last_visible_bubble_is_outgoing.return_value = None text = "configured assistant 14:12:01\nour answer" self.assertTrue(bot._nothing_left_to_answer(bot._active_session_fp, text)) def test_new_uncopyable_incoming_media_is_not_hidden_by_old_staff_text(self): bot = self.bot() bot._last_visible_bubble_is_outgoing.return_value = False text = "configured assistant 14:12:01\nour older answer" self.assertFalse(bot._nothing_left_to_answer(bot._active_session_fp, text)) bot._clear_reply_pending.assert_not_called() def test_stale_unread_task_cannot_call_model_after_our_answer(self): bot = self.bot() bot._reply_task_cancelled = mock.Mock(return_value=False) bot._orchestrated_reply = mock.Mock() with mock.patch("ai_config.AI_ENABLED", True): result = bot._generate_ai_reply_impl( bot._active_session_fp, chat_text="different staff 14:12:01\nour answer", confirmed_unread=True, reliable_text_pending=True, ) self.assertIsNone(result) bot._orchestrated_reply.assert_not_called() bot._clear_reply_pending.assert_called_once_with(bot._active_session_fp) if __name__ == "__main__": unittest.main()