107 lines
5.5 KiB
Python
107 lines
5.5 KiB
Python
"""Use actual production classes, including when imported from the frozen EXE."""
|
|
import unittest
|
|
from unittest import mock
|
|
import test_visual_review_visibility as visual
|
|
|
|
|
|
class VisualPolicyIntegration(unittest.TestCase):
|
|
setUp = visual.VisualReviewVisibilityTest.setUp
|
|
restart = visual.VisualReviewVisibilityTest.restart
|
|
|
|
def test_shadow_mode_survives_restart_and_releases_obsolete_review(self):
|
|
self.state.update(judge_mode='shadow', review_reason='模型裁判评估为高风险')
|
|
restored = self.restart()
|
|
with mock.patch('ai_config.AI_REVIEW_RULES', []):
|
|
self.bot._drop_stale_review_flags()
|
|
self.assertEqual(restored['judge_mode'], 'shadow')
|
|
self.assertFalse(restored.get('awaiting_review'))
|
|
self.assertEqual(restored['stage'], 'queued')
|
|
|
|
def test_disabling_keyword_rule_releases_existing_draft(self):
|
|
self.state.update(judge_risk='', staged_user_text='合成关键词')
|
|
restored = self.restart()
|
|
with mock.patch('ai_config.AI_REVIEW_RULES', [{'label':'测试规则','keywords':['合成关键词'],'enabled':False}]):
|
|
self.bot._drop_stale_review_flags()
|
|
self.assertFalse(restored.get('awaiting_review'))
|
|
self.assertFalse(restored.get('review_reason'))
|
|
|
|
def test_shadow_never_clears_unknown_receipt(self):
|
|
self.state.update(judge_mode='shadow', stage='receipt_check', send_state='uncertain')
|
|
with mock.patch('ai_config.AI_REVIEW_RULES', []):
|
|
self.bot._drop_stale_review_flags()
|
|
self.assertTrue(self.state['awaiting_review'])
|
|
self.assertEqual(self.state['send_state'], 'uncertain')
|
|
|
|
def test_new_strict_verdict_overrides_old_shadow_config(self):
|
|
self.state.update(judge_mode='score_only', judge_risk='high')
|
|
with mock.patch('review_policy.current_judge_mode',return_value='shadow'), \
|
|
mock.patch('ai_config.AI_REVIEW_RULES',[]):
|
|
self.assertTrue(self.bot._reply_needs_review(self.state,'合成问题','合成草稿')[0])
|
|
|
|
def test_old_judge_callback_cannot_modify_new_turn(self):
|
|
self.state.update(model_task_id='new-task', judge_risk='low', judge_mode='score_only')
|
|
outcome={'judge':{'risk':'high','participated':False},'judge_mode':'shadow','reply':'旧回答'}
|
|
self.bot._record_model_call(self.fp,outcome,task_id='old-task',
|
|
expected_task_id='old-task',expected_state=self.state)
|
|
self.assertEqual(self.state['model_task_id'],'new-task')
|
|
self.assertEqual(self.state['judge_risk'],'low')
|
|
|
|
|
|
try:
|
|
import test_protocol_review_visibility as protocol
|
|
except ImportError:
|
|
protocol = None
|
|
|
|
|
|
@unittest.skipIf(protocol is None, 'No protocol transport in this checkout')
|
|
class ProtocolPolicyIntegration(unittest.TestCase):
|
|
if protocol:
|
|
setUp = protocol.ProtocolReviewVisibilityTests.setUp
|
|
make_bot = protocol.ProtocolReviewVisibilityTests.make_bot
|
|
enqueue = protocol.ProtocolReviewVisibilityTests.enqueue
|
|
|
|
def test_gateway_shadow_verdict_is_recorded_and_sent_without_approval(self):
|
|
self.bot.generate=self.bot._generate
|
|
self.bot.send_delay_seconds=600
|
|
trace={'judge':{'risk':'high'},'judge_mode':'shadow','task_id':'gateway-task'}
|
|
with mock.patch('ai_config.AI_ENABLED',True), \
|
|
mock.patch('ai_chat.gateway_provider',return_value=object()), \
|
|
mock.patch('ai_chat.get_ai_reply',return_value='合成自动回复'), \
|
|
mock.patch('ai_chat.take_last_gateway_trace',side_effect=[{},trace]):
|
|
state=self.enqueue()
|
|
self.assertEqual(state['judge_mode'],'shadow')
|
|
self.assertEqual(state['judge_risk'],'high')
|
|
self.assertFalse(state['awaiting_review'])
|
|
state['send_ready_at']=0
|
|
self.bot.poll_once(); self.bot.poll_once()
|
|
self.assertEqual(len(self.sender.calls),1)
|
|
|
|
def test_current_shadow_plan_releases_legacy_high_risk_once(self):
|
|
self.bot.send_mode='review'; state=self.enqueue()
|
|
state.update(judge_risk='high',review_reason='模型裁判评估为高风险')
|
|
state.pop('judge_mode',None)
|
|
self.bot.send_mode='auto'
|
|
with mock.patch('review_policy.current_judge_mode',return_value='shadow'):
|
|
self.bot.poll_once(); self.bot.poll_once()
|
|
self.assertEqual(len(self.sender.calls),1)
|
|
|
|
def test_score_only_high_risk_still_requires_approval(self):
|
|
self.bot.generate=lambda _: (self.bot._pending[self.key].update(judge_risk='high',judge_mode='score_only') or '合成草稿')
|
|
with mock.patch('review_policy.current_judge_mode',return_value='score_only'):
|
|
state=self.enqueue()
|
|
self.assertTrue(state['awaiting_review'])
|
|
self.assertFalse(self.sender.calls)
|
|
|
|
def test_gateway_strict_verdict_overrides_stale_shadow_cache(self):
|
|
self.bot.generate=self.bot._generate
|
|
trace={'judge':{'risk':'high'},'judge_mode':'score_only','task_id':'new-gateway-task'}
|
|
with mock.patch('ai_config.AI_ENABLED',True), \
|
|
mock.patch('review_policy.current_judge_mode',return_value='shadow'), \
|
|
mock.patch('ai_chat.gateway_provider',return_value=object()), \
|
|
mock.patch('ai_chat.get_ai_reply',return_value='合成严格审核草稿'), \
|
|
mock.patch('ai_chat.take_last_gateway_trace',side_effect=[{},trace]):
|
|
state=self.enqueue()
|
|
self.assertEqual(state['judge_mode'],'score_only')
|
|
self.assertTrue(state['awaiting_review'])
|
|
self.assertFalse(self.sender.calls)
|