orchestrated-discussions/tests/test_voting.py

148 lines
4.4 KiB
Python

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