import logging from app.pipeline.timings import FanTimings, _avg, _p95 class TestAvg: def test_returns_zero_for_empty(self) -> None: assert _avg([]) == 0 def test_returns_value_for_single(self) -> None: assert _avg([42]) == 42 def test_rounds_to_nearest_int(self) -> None: assert _avg([1, 2]) == 2 # 1.5 → 2 def test_computes_mean(self) -> None: assert _avg([10, 20, 30]) == 20 class TestP95: def test_returns_zero_for_empty(self) -> None: assert _p95([]) == 0 def test_returns_value_for_single(self) -> None: assert _p95([100]) == 100 def test_returns_95th_percentile(self) -> None: vals = list(range(1, 101)) # 1..100, index 95 → value 96 assert _p95(vals) == 96 def test_p95_ignores_top_4_percent(self) -> None: vals = [1] * 96 + [9999] * 4 # index 95 still lands on 1 assert _p95(vals) == 1 class TestFanTimings: def test_all_lists_empty_by_default(self) -> None: t = FanTimings() assert t.token_ms == [] assert t.profile_ms == [] assert t.top_artists_ms == [] assert t.recently_played_ms == [] def test_log_summary_emits_counts(self, caplog) -> None: t = FanTimings( token_ms=[10, 20, 30], profile_ms=[5, 15], top_artists_ms=[50], recently_played_ms=[], ) with caplog.at_level(logging.INFO): t.log_summary(processed=3, errors=1) assert "3 ok" in caplog.text assert "1 err" in caplog.text def test_log_summary_empty_timings_does_not_raise(self) -> None: FanTimings().log_summary(processed=0, errors=0)