main
py 94 lines 3.1 KB
Raw
1 """Tests for scripts/budget_alerts.py."""
2
3 from __future__ import annotations
4
5 import json
6 from datetime import UTC, datetime
7 from pathlib import Path
8
9 import pytest
10
11 from scripts.budget_alerts import (
12 evaluate,
13 load_monthly_spend,
14 main,
15 )
16
17
18 @pytest.fixture
19 def metrics_file(tmp_path: Path) -> Path:
20 return tmp_path / "token-usage.jsonl"
21
22
23 class TestLoadMonthlySpend:
24 def test_missing_file(self, tmp_path: Path):
25 assert load_monthly_spend(tmp_path / "nonexistent.jsonl") == 0.0
26
27 def test_empty_file(self, metrics_file: Path):
28 metrics_file.write_text("")
29 assert load_monthly_spend(metrics_file) == 0.0
30
31 def test_sums_current_month(self, metrics_file: Path):
32 now = datetime(2026, 5, 19, tzinfo=UTC)
33 entries = [
34 {"timestamp": "2026-05-01T10:00:00Z", "estimated_cost": 0.30},
35 {"timestamp": "2026-05-15T10:00:00Z", "estimated_cost": 0.50},
36 {"timestamp": "2026-04-28T10:00:00Z", "estimated_cost": 1.00}, # prev month
37 ]
38 metrics_file.write_text("\n".join(json.dumps(e) for e in entries))
39 assert load_monthly_spend(metrics_file, now=now) == pytest.approx(0.80)
40
41 def test_handles_malformed_lines(self, metrics_file: Path):
42 now = datetime(2026, 5, 19, tzinfo=UTC)
43 metrics_file.write_text(
44 "not json\n" + json.dumps({"timestamp": "2026-05-01T10:00:00Z", "estimated_cost": 0.25})
45 )
46 assert load_monthly_spend(metrics_file, now=now) == pytest.approx(0.25)
47
48
49 class TestEvaluate:
50 def test_no_alerts_under_thresholds(self):
51 annotations, code = evaluate(0.10, 2.00)
52 assert annotations == []
53 assert code == 0
54
55 def test_single_run_warning(self):
56 annotations, code = evaluate(0.60, 2.00)
57 assert any("::warning::" in a and "Single run" in a for a in annotations)
58 assert code == 0
59
60 def test_single_run_fail(self):
61 annotations, code = evaluate(1.50, 2.00)
62 assert any("::error::" in a for a in annotations)
63 assert code == 1
64
65 def test_monthly_warning(self):
66 annotations, code = evaluate(None, 6.00)
67 assert any("::warning::" in a and "cumulative" in a for a in annotations)
68 assert code == 0
69
70 def test_monthly_recommend_switch(self):
71 annotations, code = evaluate(None, 11.00)
72 assert any("cheaper model" in a for a in annotations)
73 assert code == 0
74
75 def test_both_single_and_monthly(self):
76 annotations, code = evaluate(0.60, 6.00)
77 assert len(annotations) == 2
78 assert code == 0
79
80
81 class TestMain:
82 def test_exit_0_no_issues(self, metrics_file: Path):
83 metrics_file.write_text("")
84 code = main(["--run-cost", "0.10", "--metrics", str(metrics_file)])
85 assert code == 0
86
87 def test_exit_1_over_cap(self, metrics_file: Path):
88 metrics_file.write_text("")
89 code = main(["--run-cost", "1.50", "--metrics", str(metrics_file)])
90 assert code == 1
91
92 def test_missing_metrics_file(self, tmp_path: Path):
93 code = main(["--run-cost", "0.10", "--metrics", str(tmp_path / "missing.jsonl")])
94 assert code == 0