File size: 5,413 Bytes
fcf8749 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | """
Unit tests for fairness metrics calculations.
Tests Gini index and fairness score functions.
"""
import pytest
from app.services.fairness import (
gini_index,
calculate_fairness_score,
calculate_global_fairness,
)
class TestGiniIndex:
"""Tests for the Gini index calculation."""
def test_gini_perfect_equality(self):
"""All equal workloads should give Gini = 0."""
workloads = [50.0, 50.0, 50.0, 50.0]
result = gini_index(workloads)
assert result == 0.0
def test_gini_maximum_inequality(self):
"""One person has all work, Gini should be close to 1."""
workloads = [100.0, 0.0, 0.0, 0.0]
result = gini_index(workloads)
# For n=4, maximum Gini is (n-1)/n = 0.75
assert result > 0.7
def test_gini_moderate_inequality(self):
"""Moderate spread should give moderate Gini."""
workloads = [20.0, 40.0, 60.0, 80.0]
result = gini_index(workloads)
assert 0.1 < result < 0.4
def test_gini_empty_list(self):
"""Empty list should return 0."""
result = gini_index([])
assert result == 0.0
def test_gini_single_element(self):
"""Single element should return 0 (no inequality possible)."""
result = gini_index([100.0])
assert result == 0.0
def test_gini_two_elements_equal(self):
"""Two equal elements should return 0."""
result = gini_index([50.0, 50.0])
assert result == 0.0
def test_gini_two_elements_unequal(self):
"""Two unequal elements should give positive Gini."""
result = gini_index([30.0, 70.0])
assert result > 0.0
assert result < 1.0
def test_gini_zero_sum(self):
"""All zeros should return 0."""
result = gini_index([0.0, 0.0, 0.0])
assert result == 0.0
def test_gini_realistic_workloads(self):
"""Realistic workload distribution."""
# Simulating a fairly balanced day
workloads = [62.5, 58.3, 71.2, 65.8, 55.4]
result = gini_index(workloads)
assert 0.0 < result < 0.15 # Should be well balanced
class TestFairnessScore:
"""Tests for individual fairness score calculation."""
def test_fairness_exactly_average(self):
"""Workload exactly at average should give score of 1.0."""
result = calculate_fairness_score(50.0, 50.0)
assert result == 1.0
def test_fairness_above_average(self):
"""Workload above average should give score < 1.0."""
result = calculate_fairness_score(75.0, 50.0)
assert result < 1.0
assert result >= 0.0
def test_fairness_below_average(self):
"""Workload below average should give score < 1.0."""
result = calculate_fairness_score(25.0, 50.0)
assert result < 1.0
assert result >= 0.0
def test_fairness_symmetric(self):
"""Same deviation above and below should give same score."""
score_above = calculate_fairness_score(60.0, 50.0)
score_below = calculate_fairness_score(40.0, 50.0)
assert abs(score_above - score_below) < 0.01
def test_fairness_zero_average(self):
"""Zero average should use 1.0 as denominator."""
result = calculate_fairness_score(5.0, 0.0)
assert result >= 0.0
assert result <= 1.0
def test_fairness_double_average(self):
"""Workload at double average."""
result = calculate_fairness_score(100.0, 50.0)
# |100 - 50| / 50 = 1.0, so fairness = 1 - 1 = 0
assert result == 0.0
class TestGlobalFairness:
"""Tests for global fairness metrics calculation."""
def test_global_fairness_empty(self):
"""Empty workloads should return zero metrics."""
result = calculate_global_fairness([])
assert result.avg_workload == 0.0
assert result.std_dev == 0.0
assert result.gini_index == 0.0
def test_global_fairness_single(self):
"""Single workload should have zero std dev and gini."""
result = calculate_global_fairness([50.0])
assert result.avg_workload == 50.0
assert result.std_dev == 0.0
assert result.gini_index == 0.0
def test_global_fairness_equal(self):
"""Equal workloads should have zero std dev and gini."""
result = calculate_global_fairness([50.0, 50.0, 50.0])
assert result.avg_workload == 50.0
assert result.std_dev == 0.0
assert result.gini_index == 0.0
def test_global_fairness_varied(self):
"""Varied workloads should have positive std dev and gini."""
result = calculate_global_fairness([30.0, 50.0, 70.0])
assert result.avg_workload == 50.0
assert result.std_dev > 0.0
assert result.gini_index > 0.0
def test_global_fairness_realistic(self):
"""Test with realistic workload values."""
workloads = [62.5, 58.3, 71.2, 65.8, 55.4]
result = calculate_global_fairness(workloads)
# Check average
expected_avg = sum(workloads) / len(workloads)
assert abs(result.avg_workload - expected_avg) < 0.1
# Check that metrics are reasonable
assert result.std_dev > 0
assert result.gini_index > 0
assert result.gini_index < 0.2 # Should be fairly balanced
|