"""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)