"""Tests for six-level nuanced vote evaluation. Levels: 0-CONTRE, 1-PAS DU TOUT, 2-PAS D'ACCORD, 3-NEUTRE, 4-D'ACCORD, 5-TOUT A FAIT Positive = levels 3 + 4 + 5 Adoption requires: positive_pct >= threshold (80%) AND total >= min_participants (59). """ import pytest from app.engine.nuanced_vote import evaluate_nuanced class TestNuancedVoteAdoption: """Cases where the vote should be adopted.""" def test_59_positive_10_negative_adopted(self): """59 positive (levels 3-5) + 10 negative = 69 total. positive_pct = 59/69 ~ 85.5% >= 80% and 69 >= 59 => adopted. """ votes = [5] * 20 + [4] * 20 + [3] * 19 + [2] * 5 + [1] * 3 + [0] * 2 result = evaluate_nuanced(votes, threshold_pct=80, min_participants=59) assert result["total"] == 69 assert result["positive_count"] == 59 assert result["positive_pct"] == pytest.approx(85.51, abs=0.1) assert result["threshold_met"] is True assert result["min_participants_met"] is True assert result["adopted"] is True def test_all_tout_a_fait_adopted(self): """All 59 voters at level 5 => 100% positive, adopted.""" votes = [5] * 59 result = evaluate_nuanced(votes, threshold_pct=80, min_participants=59) assert result["total"] == 59 assert result["positive_count"] == 59 assert result["positive_pct"] == 100.0 assert result["adopted"] is True class TestNuancedVoteRejection: """Cases where the vote should be rejected.""" def test_40_positive_30_negative_rejected(self): """40 positive + 30 negative = 70 total. positive_pct = 40/70 ~ 57.14% < 80% => threshold not met. """ votes = [5] * 15 + [4] * 15 + [3] * 10 + [2] * 10 + [1] * 10 + [0] * 10 result = evaluate_nuanced(votes, threshold_pct=80, min_participants=59) assert result["total"] == 70 assert result["positive_count"] == 40 assert result["positive_pct"] == pytest.approx(57.14, abs=0.1) assert result["threshold_met"] is False assert result["min_participants_met"] is True # 70 >= 59 assert result["adopted"] is False def test_min_participants_not_met(self): """50 positive + 5 negative = 55 total < 59 min_participants. Even though 50/55 ~ 90.9% > 80%, adoption fails on min_participants. """ votes = [5] * 30 + [4] * 10 + [3] * 10 + [1] * 3 + [0] * 2 result = evaluate_nuanced(votes, threshold_pct=80, min_participants=59) assert result["total"] == 55 assert result["positive_count"] == 50 assert result["positive_pct"] > 80 assert result["threshold_met"] is True assert result["min_participants_met"] is False assert result["adopted"] is False class TestNuancedVoteEdgeCases: """Edge cases and exact boundary conditions.""" def test_exact_threshold_80_percent(self): """Exactly 80% positive votes should pass the threshold.""" # 80 positive out of 100 = exactly 80% votes = [5] * 40 + [4] * 20 + [3] * 20 + [2] * 10 + [1] * 5 + [0] * 5 result = evaluate_nuanced(votes, threshold_pct=80, min_participants=59) assert result["total"] == 100 assert result["positive_count"] == 80 assert result["positive_pct"] == 80.0 assert result["threshold_met"] is True assert result["min_participants_met"] is True assert result["adopted"] is True def test_just_below_threshold(self): """79 positive out of 100 = 79% < 80% => rejected.""" votes = [5] * 39 + [4] * 20 + [3] * 20 + [2] * 11 + [1] * 5 + [0] * 5 result = evaluate_nuanced(votes, threshold_pct=80, min_participants=59) assert result["total"] == 100 assert result["positive_count"] == 79 assert result["positive_pct"] == 79.0 assert result["threshold_met"] is False assert result["adopted"] is False def test_empty_votes(self): """Zero votes => not adopted.""" result = evaluate_nuanced([], threshold_pct=80, min_participants=59) assert result["total"] == 0 assert result["positive_count"] == 0 assert result["positive_pct"] == 0.0 assert result["adopted"] is False def test_invalid_vote_level(self): """Vote level outside 0-5 raises ValueError.""" with pytest.raises(ValueError, match="invalide"): evaluate_nuanced([5, 3, 6]) def test_per_level_counts(self): """Verify per-level breakdown is correct.""" votes = [0, 1, 2, 3, 4, 5, 5, 4, 3] result = evaluate_nuanced(votes, threshold_pct=50, min_participants=1) assert result["per_level_counts"] == {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 2} assert result["positive_count"] == 6 # 2+2+2 assert result["total"] == 9