"""F14 Rebuttal 单元测试 按 test-plan-v2.6.md §F14: - F14 T1: 反驳权流程(P0) - F14 T2: 最大轮次限制(P0) """ import json import pytest from pathlib import Path from unittest.mock import MagicMock from src.blackboard.models import Task from src.blackboard.operations import Blackboard from src.daemon.review import RebuttalManager pytestmark = pytest.mark.unit @pytest.fixture def db_path(tmp_path): return tmp_path / "blackboard.db" @pytest.fixture def bb(db_path): return Blackboard(db_path) # --------------------------------------------------------------------------- # F14 T1: 反驳权流程 # --------------------------------------------------------------------------- class TestRebuttal: def test_submit_rebuttal_accepted(self, bb): task = Task(id="t1", title="T", status="pending", assigned_by="d") bb.create_task(task) rm = RebuttalManager(bb=bb) result = rm.submit_rebuttal("t1", "agent-1", "I disagree with the review") assert result["status"] == "accepted" assert result["round"] == 1 assert result["escalation_target"] == "simayi-challenger" def test_second_round_escalates_to_pangtong(self, bb): task = Task(id="t1", title="T", status="pending", assigned_by="d") bb.create_task(task) rm = RebuttalManager(bb=bb) rm.submit_rebuttal("t1", "agent-1", "Round 1") result = rm.submit_rebuttal("t1", "agent-1", "Round 2") assert result["status"] == "accepted" assert result["round"] == 2 assert result["escalation_target"] == "pangtong-fujunshi" # --------------------------------------------------------------------------- # F14 T2: 最大轮次限制 # --------------------------------------------------------------------------- class TestRebuttalLimits: def test_max_rounds_rejected(self, bb): task = Task(id="t1", title="T", status="pending", assigned_by="d") bb.create_task(task) rm = RebuttalManager(bb=bb) rm.submit_rebuttal("t1", "a", "R1") rm.submit_rebuttal("t1", "a", "R2") result = rm.submit_rebuttal("t1", "a", "R3") assert result["status"] == "rejected" assert "Max" in result["reason"] def test_rebuttal_without_bb(self): rm = RebuttalManager(bb=None) result = rm.submit_rebuttal("t1", "a", "reason") assert result["status"] == "accepted" assert result["round"] == 1 def test_rebuttal_observation_recorded(self, bb): task = Task(id="t1", title="T", status="pending", assigned_by="d") bb.create_task(task) rm = RebuttalManager(bb=bb) rm.submit_rebuttal("t1", "agent-1", "test reason", evidence="file.txt") obs = bb.get_observations(task_id="t1") rebuttals = [o for o in obs if "Rebuttal round" in (o.body or "")] assert len(rebuttals) == 1