392 lines
12 KiB
Python
392 lines
12 KiB
Python
"""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
|