"""Tests for voting and consensus logic.""" import pytest from discussions.voting import ( VotingConfig, ConsensusResult, calculate_consensus, is_human_participant, format_vote_summary, ) class TestIsHumanParticipant: def test_human_names(self): assert is_human_participant("Rob") is True assert is_human_participant("Alice") is True assert is_human_participant("bob_smith") is True def test_ai_names(self): assert is_human_participant("AI-Architect") is False assert is_human_participant("AI_Security") is False assert is_human_participant("ai-moderator") is False def test_empty(self): assert is_human_participant("") is False assert is_human_participant(None) is False class TestVotingConfig: def test_defaults(self): config = VotingConfig() assert config.threshold_ready == 0.67 assert config.threshold_reject == 0.01 assert config.human_required is True def test_custom_thresholds(self): config = VotingConfig(threshold_ready=0.5, threshold_reject=0.2) assert config.threshold_ready == 0.5 assert config.threshold_reject == 0.2 def test_invalid_threshold(self): with pytest.raises(ValueError): VotingConfig(threshold_ready=1.5) with pytest.raises(ValueError): VotingConfig(threshold_reject=-0.1) class TestCalculateConsensus: def test_consensus_reached_all_ready(self): votes = { "AI-Architect": "READY", "AI-Security": "READY", "AI-Pragmatist": "READY", "Rob": "READY", } config = VotingConfig(human_required=True) result = calculate_consensus(votes, config) assert result.reached is True assert result.outcome == "READY" assert result.ready_count == 4 def test_consensus_reached_with_changes(self): votes = { "AI-Architect": "READY", "AI-Security": "READY", "AI-Pragmatist": "CHANGES", "Rob": "READY", } config = VotingConfig(threshold_ready=0.67, human_required=True) result = calculate_consensus(votes, config) assert result.reached is True assert result.ready_count == 3 assert result.changes_count == 1 def test_blocked_by_reject(self): votes = { "AI-Architect": "READY", "AI-Security": "REJECT", "AI-Pragmatist": "READY", "Rob": "READY", } config = VotingConfig(threshold_reject=0.01) result = calculate_consensus(votes, config) assert result.reached is False assert "AI-Security" in result.blocked_by assert "REJECT" in result.reason def test_human_required_not_met(self): votes = { "AI-Architect": "READY", "AI-Security": "READY", "AI-Pragmatist": "READY", } config = VotingConfig(human_required=True) result = calculate_consensus(votes, config) assert result.reached is False assert "Human approval required" in result.reason def test_human_required_disabled(self): votes = { "AI-Architect": "READY", "AI-Security": "READY", "AI-Pragmatist": "READY", } config = VotingConfig(human_required=False) result = calculate_consensus(votes, config) assert result.reached is True def test_insufficient_votes(self): votes = {} config = VotingConfig(minimum_votes=1) result = calculate_consensus(votes, config) assert result.reached is False assert "Insufficient votes" in result.reason def test_not_enough_ready_votes(self): votes = { "AI-Architect": "CHANGES", "AI-Security": "CHANGES", "AI-Pragmatist": "READY", "Rob": "READY", } config = VotingConfig(threshold_ready=0.67, human_required=True) result = calculate_consensus(votes, config) assert result.reached is False assert "more READY votes" in result.reason class TestFormatVoteSummary: def test_format(self): votes = { "Alice": "READY", "Bob": "CHANGES", "Carol": "READY", } summary = format_vote_summary(votes) assert "READY: 2" in summary assert "CHANGES: 1" in summary assert "REJECT: 0" in summary