"""Unit tests for debate framework. Tests the DebateFramework, Argument, Rebuttal, and DebateResult classes. """ import pytest from openclaw.debate.debate_framework import ( Argument, ArgumentStrength, ArgumentType, DebateConfig, DebateFramework, DebateResult, DebateRound, Rebuttal, ) class TestArgument: """Test Argument dataclass.""" def test_argument_creation(self): """Test creating an argument.""" arg = Argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Revenue is growing rapidly", evidence="20% YoY growth in Q3", strength=ArgumentStrength.STRONG, target_factors=["revenue", "growth"], ) assert arg.agent_id == "bull-1" assert arg.argument_type == ArgumentType.BULLISH assert arg.claim == "Revenue is growing rapidly" assert arg.strength == ArgumentStrength.STRONG assert "revenue" in arg.target_factors def test_argument_to_dict(self): """Test converting argument to dictionary.""" arg = Argument( agent_id="bear-1", argument_type=ArgumentType.BEARISH, claim="Competition is increasing", evidence="Market share declining 5%", strength=ArgumentStrength.MODERATE, ) d = arg.to_dict() assert d["agent_id"] == "bear-1" assert d["argument_type"] == "bearish" assert "timestamp" in d class TestRebuttal: """Test Rebuttal dataclass.""" @pytest.fixture def target_argument(self): """Create a target argument for rebuttal.""" return Argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="PE ratio is reasonable", evidence="PE is 15 vs industry 20", strength=ArgumentStrength.MODERATE, ) def test_rebuttal_creation(self, target_argument): """Test creating a rebuttal.""" rebuttal = Rebuttal( agent_id="bear-1", target_argument=target_argument, counter_claim="PE doesn't account for debt", reasoning="High debt load makes PE misleading", effectiveness=0.7, ) assert rebuttal.agent_id == "bear-1" assert rebuttal.effectiveness == 0.7 assert rebuttal.target_argument == target_argument def test_rebuttal_effectiveness_clamping(self, target_argument): """Test that effectiveness is clamped to 0-1 range.""" rebuttal_high = Rebuttal( agent_id="bear-1", target_argument=target_argument, counter_claim="Test", reasoning="Test", effectiveness=1.5, ) assert rebuttal_high.effectiveness == 1.0 rebuttal_low = Rebuttal( agent_id="bear-1", target_argument=target_argument, counter_claim="Test", reasoning="Test", effectiveness=-0.5, ) assert rebuttal_low.effectiveness == 0.0 class TestDebateRound: """Test DebateRound class.""" @pytest.fixture def round_data(self): """Create a debate round.""" return DebateRound(round_number=1) def test_add_argument(self, round_data): """Test adding arguments to a round.""" arg = Argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Growth is strong", evidence="20% YoY", strength=ArgumentStrength.STRONG, ) round_data.add_argument(arg) assert len(round_data.arguments) == 1 assert round_data.arguments[0] == arg def test_get_bullish_arguments(self, round_data): """Test filtering bullish arguments.""" bull_arg = Argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Buy", evidence="Growth", strength=ArgumentStrength.STRONG, ) bear_arg = Argument( agent_id="bear-1", argument_type=ArgumentType.BEARISH, claim="Sell", evidence="Risk", strength=ArgumentStrength.MODERATE, ) round_data.add_argument(bull_arg) round_data.add_argument(bear_arg) bullish = round_data.get_bullish_arguments() assert len(bullish) == 1 assert bullish[0].argument_type == ArgumentType.BULLISH class TestDebateConfig: """Test DebateConfig validation.""" def test_valid_config(self): """Test valid configuration.""" config = DebateConfig(max_rounds=5, min_rounds=2) assert config.max_rounds == 5 assert config.min_rounds == 2 def test_invalid_max_rounds(self): """Test that max_rounds < min_rounds raises error.""" with pytest.raises(ValueError): DebateConfig(max_rounds=1, min_rounds=2) def test_invalid_consensus_threshold(self): """Test that invalid consensus threshold raises error.""" with pytest.raises(ValueError): DebateConfig(consensus_threshold=1.5) class TestDebateFramework: """Test DebateFramework functionality.""" @pytest.fixture def framework(self): """Create a debate framework.""" config = DebateConfig(max_rounds=3, min_rounds=1) return DebateFramework(config) def test_start_debate(self, framework): """Test starting a debate.""" framework.start_debate("AAPL") assert framework.symbol == "AAPL" assert len(framework.rounds) == 0 def test_add_round(self, framework): """Test adding debate rounds.""" framework.start_debate("AAPL") round1 = framework.add_round() round2 = framework.add_round() assert round1.round_number == 1 assert round2.round_number == 2 assert len(framework.rounds) == 2 def test_submit_argument(self, framework): """Test submitting arguments.""" framework.start_debate("AAPL") argument = framework.submit_argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Strong growth", evidence="20% YoY", strength=ArgumentStrength.STRONG, ) assert argument.agent_id == "bull-1" assert len(framework.rounds) == 1 assert len(framework.rounds[0].arguments) == 1 def test_calculate_scores(self, framework): """Test score calculation.""" framework.start_debate("AAPL") framework.add_round() # Add bullish argument framework.submit_argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Growth", evidence="Data", strength=ArgumentStrength.STRONG, ) bull_score, bear_score = framework._calculate_scores() assert bull_score > 0 assert bear_score == 0 def test_conclude_debate_bull_wins(self, framework): """Test concluding debate with bull win.""" framework.start_debate("AAPL") framework.add_round() # Strong bullish argument framework.submit_argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Strong growth", evidence="20% YoY", strength=ArgumentStrength.COMPELLING, ) result = framework.conclude_debate() assert result.symbol == "AAPL" assert result.winner == "bull" assert result.recommendation == "buy" assert result.bull_score > result.bear_score def test_conclude_debate_bear_wins(self, framework): """Test concluding debate with bear win.""" framework.start_debate("AAPL") framework.add_round() # Strong bearish argument framework.submit_argument( agent_id="bear-1", argument_type=ArgumentType.BEARISH, claim="High risk", evidence="Debt increasing", strength=ArgumentStrength.COMPELLING, ) result = framework.conclude_debate() assert result.winner == "bear" assert result.recommendation == "sell" def test_should_continue_max_rounds(self, framework): """Test should_continue respects max_rounds.""" framework.start_debate("AAPL") framework.add_round() framework.add_round() framework.add_round() assert not framework.should_continue() def test_rebuttal_reduces_score(self, framework): """Test that rebuttals reduce target argument scores.""" framework.start_debate("AAPL") framework.add_round() # Submit bullish argument argument = framework.submit_argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="Strong moat", evidence="Market leader", strength=ArgumentStrength.STRONG, ) # Rebut it framework.submit_rebuttal( agent_id="bear-1", target_argument=argument, counter_claim="Moat is eroding", reasoning="New competitors emerging", effectiveness=0.8, ) bull_score, _ = framework._calculate_scores() # Score should be reduced by rebuttal assert bull_score < 40 # Strong argument is 40, reduced by 80% class TestDebateResult: """Test DebateResult dataclass.""" def test_result_creation(self): """Test creating a debate result.""" result = DebateResult( symbol="AAPL", winner="bull", bull_score=100.0, bear_score=50.0, consensus_level=0.7, recommendation="buy", confidence=0.8, ) assert result.symbol == "AAPL" assert result.winner == "bull" assert result.confidence == 0.8 def test_result_to_dict(self): """Test converting result to dictionary.""" result = DebateResult( symbol="AAPL", winner="bull", bull_score=100.0, bear_score=50.0, consensus_level=0.7, key_points=["Growth is strong"], disagreements=["Valuation debate"], ) d = result.to_dict() assert d["symbol"] == "AAPL" assert d["winner"] == "bull" assert "timestamp" in d class TestDebateIntegration: """Integration tests for full debate flow.""" def test_full_debate_flow(self): """Test a complete multi-round debate.""" config = DebateConfig(max_rounds=2, min_rounds=1) framework = DebateFramework(config) framework.start_debate("TSLA") # Round 1: Bull presents strong case round1 = framework.add_round() framework.submit_argument( agent_id="bull-1", argument_type=ArgumentType.BULLISH, claim="EV market leadership", evidence="50% market share", strength=ArgumentStrength.STRONG, target_factors=["market_share", "growth"], ) # Bear rebuts bull_arg = round1.arguments[0] framework.submit_rebuttal( agent_id="bear-1", target_argument=bull_arg, counter_claim="Competition increasing", reasoning="Legacy automakers entering", effectiveness=0.6, ) # Round 2: Bear presents case framework.add_round() framework.submit_argument( agent_id="bear-1", argument_type=ArgumentType.BEARISH, claim="Valuation too high", evidence="PE ratio 100x", strength=ArgumentStrength.MODERATE, target_factors=["valuation"], ) result = framework.conclude_debate() assert result.rounds_completed == 2 assert result.symbol == "TSLA" assert len(result.key_points) >= 0 assert len(result.disagreements) >= 0