Files
kefu/deploy/review-visibility-20260916/test_protocol_review_visibility.py
T
2026-09-21 10:34:06 +08:00

154 lines
7.3 KiB
Python

"""Synthetic protocol review workflow: local temp state, no network or WeCom I/O."""
import json
from pathlib import Path
import tempfile
import unittest
from unittest import mock
from protocol_engine import ProtocolBot, session_key, MANUAL_MEDIA_REASON, MANUAL_BATCH_REASON
from test_native_protocol_pipeline import Database, Sender, message, ACCOUNT, CONV
from wecom_native_sender import DeliveryUnknown
class ProtocolReviewVisibilityTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.db = Database()
self.sender = Sender(self.db)
self.generate = mock.Mock(return_value='合成回复草稿')
for patcher in (
mock.patch('protocol_engine.application_data_dir', return_value=self.root),
mock.patch('queue_log.application_data_dir', return_value=self.root),
mock.patch('socket.create_connection', side_effect=AssertionError('network forbidden')),
mock.patch('ai_config.AI_REVIEW_RULES', []),
):
patcher.start()
self.addCleanup(patcher.stop)
self.bot = self.make_bot()
self.key = session_key(ACCOUNT, CONV)
def make_bot(self):
bot = ProtocolBot(db=self.db, sender=self.sender, identity={'accountId': ACCOUNT, 'pid': 1},
path=self.root / 'protocol.json', generate=self.generate, log=lambda _: None)
bot.message_batch_window_seconds = 0
bot.send_delay_seconds = 0
return bot
def enqueue(self, item=None):
self.db.add(item or message())
self.bot.poll_once()
return self.bot._pending.get(self.key)
def saved(self):
return json.loads(self.bot.path.read_text(encoding='utf-8'))['pending'][self.key]
def test_review_draft_reason_persists_then_explicit_approval_sends_once(self):
self.bot.send_mode = 'review'
state = self.enqueue()
self.assertTrue(state['awaiting_review'])
self.assertIn('人工审核', state['review_reason'])
self.assertEqual(state['staged_reply_text'], '合成回复草稿')
self.assertEqual(self.saved()['review_reason'], state['review_reason'])
self.assertFalse(self.sender.calls)
self.assertEqual(self.bot.approve_pending_replies([self.key])['approved'], [self.key])
self.assertEqual(self.saved()['stage'], 'ready_to_send')
self.assertFalse(self.saved()['awaiting_review'])
self.bot.poll_once()
self.bot.poll_once()
self.assertEqual(len(self.sender.calls), 1)
def test_high_risk_reason_available_in_automatic_mode(self):
self.bot.generate = lambda _: (self.bot._pending[self.key].update(judge_risk='high') or '高风险草稿')
state = self.enqueue()
self.assertTrue(state['awaiting_review'])
self.assertEqual(state['review_reason'], '模型裁判评估为高风险')
self.assertFalse(self.sender.calls)
def test_media_batch_is_visible_manual_work_without_approvable_draft(self):
self.db.add({**message(2, text='合成图片'), 'content_type': 3})
state = self.enqueue(message(3, text='请看上面的图'))
self.assertEqual(state['stage'], 'error')
self.assertTrue(state['manual_required'])
self.assertEqual(state['manual_reason'], MANUAL_MEDIA_REASON)
self.assertIn('合成图片', state['last_lines'])
self.assertIn('请看上面的图', state['chat_text'])
self.assertFalse(state['awaiting_review'])
self.assertEqual(self.bot.approve_pending_replies([self.key])['not_pending'], [self.key])
self.assertFalse(self.sender.calls)
self.generate.assert_not_called()
def test_overlong_batch_cannot_be_approved_as_a_draft(self):
self.db.messages = [message(i) for i in range(1, 102)]
self.db.events = [self.db.messages[-1]]
self.bot.poll_once()
state = self.saved()
self.assertTrue(state['manual_required'])
self.assertEqual(state['manual_reason'], MANUAL_BATCH_REASON)
self.assertEqual(self.bot.approve_pending_replies([self.key])['not_pending'], [self.key])
self.assertFalse(self.sender.calls)
def test_retry_clears_old_metadata_then_rechecks_media_without_sending(self):
self.enqueue({**message(2), 'content_type': 3})
self.bot._pending[self.key].update(staged_reply_text='旧草稿', review_reason='旧审核原因')
self.assertEqual(self.bot.retry_pending_replies([self.key])['retried'], [self.key])
queued = self.saved()
for field in ('manual_required', 'manual_reason', 'review_reason', 'staged_reply_text', 'awaiting_review'):
self.assertFalse(queued[field], field)
self.bot.poll_once()
self.assertTrue(self.saved()['manual_required'])
self.assertFalse(self.sender.calls)
def test_next_valid_batch_discards_old_manual_requirement(self):
self.enqueue({**message(2), 'content_type': 3})
self.db.add(message(3, self=True, text='人工已处理图片'))
self.bot.send_mode = 'review'
state = self.enqueue(message(4, text='新的文本问题'))
self.assertFalse(state['manual_required'])
self.assertFalse(state['manual_reason'])
self.assertTrue(state['awaiting_review'])
self.assertFalse(self.sender.calls)
def test_old_saved_review_and_manual_tasks_restore_metadata_only(self):
self.bot.send_mode = 'review'
state = self.enqueue()
state.pop('review_reason')
self.bot._save()
restored = self.make_bot()
self.assertIn('人工审核', restored._pending[self.key]['review_reason'])
self.assertFalse(restored._pending[self.key].get('approved'))
state = restored._pending[self.key]
state.update(stage='error', last_error=MANUAL_MEDIA_REASON, awaiting_review=False, reply_text='')
state.pop('manual_required', None)
state.pop('manual_reason', None)
restored._save()
restored = self.make_bot()
self.assertEqual(restored._pending[self.key]['manual_reason'], MANUAL_MEDIA_REASON)
self.assertTrue(restored._pending[self.key]['manual_required'])
self.assertFalse(self.sender.calls)
def test_unknown_receipt_cannot_be_approved_or_retried(self):
self.sender.error = DeliveryUnknown('合成回执超时')
self.enqueue()
self.assertTrue(self.bot.needs_attention)
self.assertEqual(self.bot.approve_pending_replies([self.key])['protected'], [self.key])
self.assertEqual(self.bot.retry_pending_replies([self.key])['protected'], [self.key])
self.bot.poll_once()
self.assertEqual(len(self.sender.calls), 1)
def test_foreign_account_task_cannot_be_approved_deleted_or_retried(self):
self.bot.send_mode = 'review'
state = self.enqueue()
state['account'] = 'different-account'
self.bot._save()
for method in (self.bot.approve_pending_replies, self.bot.cancel_pending_replies, self.bot.retry_pending_replies):
self.assertEqual(method([self.key])['protected'], [self.key])
self.assertTrue(self.saved()['awaiting_review'])
self.assertFalse(self.saved().get('approved'))
self.assertFalse(self.sender.calls)
if __name__ == '__main__':
unittest.main()