main
py 124 lines 5.05 KB
Raw
1 import json
2 import tempfile
3 import unittest
4 from datetime import date
5 from pathlib import Path
6
7 import yaml
8
9 import scripts.check_copilot_pricing_review as pricing_review
10
11
12 class CopilotPricingReviewTests(unittest.TestCase):
13 def test_review_not_due_before_two_month_interval(self) -> None:
14 status = pricing_review.pricing_status(date(2026, 8, 5))
15 self.assertFalse(status["needs_review"])
16 self.assertFalse(status["review_due"])
17 self.assertEqual(status["due_date"], "2026-08-06")
18
19 def test_review_due_at_two_month_interval(self) -> None:
20 status = pricing_review.pricing_status(date(2026, 8, 6))
21 self.assertTrue(status["needs_review"])
22 self.assertTrue(status["review_due"])
23
24 def test_source_url_mismatch_requires_review(self) -> None:
25 status = pricing_review.pricing_status(
26 date(2026, 7, 1), source_url="https://example.invalid/pricing"
27 )
28 self.assertTrue(status["needs_review"])
29 self.assertFalse(status["source_url_matches"])
30
31 def test_source_headers_are_parsed_for_report_metadata(self) -> None:
32 tests_root = Path(__file__).resolve().parent
33 with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
34 headers = Path(tmpdir) / "headers.txt"
35 headers.write_text(
36 'HTTP/2 200\netag: "abc123"\nlast-modified: Sat, 06 Jun 2026 00:00:00 GMT\n',
37 encoding="utf-8",
38 )
39 status = pricing_review.pricing_status(
40 date(2026, 7, 1), source_headers=pricing_review.parse_source_headers(headers)
41 )
42 report = pricing_review.render_report(status)
43 self.assertEqual(status["source_headers"]["etag"], '"abc123"')
44 self.assertIn("last-modified", report)
45
46 def test_main_writes_report_json_and_github_outputs(self) -> None:
47 tests_root = Path(__file__).resolve().parent
48 with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
49 base = Path(tmpdir)
50 report_path = base / "report.md"
51 json_path = base / "status.json"
52 github_output = base / "github-output.txt"
53
54 rc = pricing_review.main(
55 [
56 "--current-date",
57 "2026-08-06",
58 "--output",
59 str(report_path),
60 "--json-output",
61 str(json_path),
62 "--github-output",
63 str(github_output),
64 ]
65 )
66
67 self.assertEqual(rc, 0)
68 self.assertIn(
69 "does not change pricing automatically", report_path.read_text(encoding="utf-8")
70 )
71 status = json.loads(json_path.read_text(encoding="utf-8"))
72 self.assertTrue(status["needs_review"])
73 self.assertIn("needs_review=true", github_output.read_text(encoding="utf-8"))
74
75
76 class CopilotPricingReviewWorkflowTests(unittest.TestCase):
77 def test_workflow_is_scheduled_and_opens_issue_without_changing_pricing(self) -> None:
78 workflow_path = Path(".github/workflows/copilot-pricing-review.yml")
79 workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8"))
80
81 self.assertEqual(workflow["name"], "Copilot Pricing Review")
82 trigger = workflow.get("on", workflow.get(True))
83 self.assertIsNotNone(trigger)
84 self.assertEqual(trigger["schedule"][0]["cron"], "23 9 6 2,4,6,8,10,12 *")
85 self.assertIn("workflow_dispatch", trigger)
86 self.assertEqual(workflow["permissions"], {"contents": "read"})
87
88 job = workflow["jobs"]["review-pricing"]
89 # issues:write is scoped to the job (least-privilege) rather than the workflow.
90 self.assertEqual(job["permissions"], {"contents": "read", "issues": "write"})
91 pricing_step = next((step for step in job["steps"] if step.get("id") == "pricing"), None)
92 self.assertIsNotNone(pricing_step)
93 self.assertIn("scripts/check_copilot_pricing_review.py", pricing_step["run"])
94 self.assertIn("--source-headers", pricing_step["run"])
95 self.assertIn("--github-output", pricing_step["run"])
96 metadata_step = next(
97 (
98 step
99 for step in job["steps"]
100 if step.get("name") == "Capture Copilot pricing source metadata"
101 ),
102 None,
103 )
104 self.assertIsNotNone(metadata_step)
105 self.assertIn("curl -fsSLI", metadata_step["run"])
106
107 issue_step = next(
108 (
109 step
110 for step in job["steps"]
111 if step.get("name") == "Create or update pricing review issue"
112 ),
113 None,
114 )
115 self.assertIsNotNone(issue_step)
116 self.assertEqual(issue_step["if"], "steps.pricing.outputs.needs_review == 'true'")
117 self.assertIn("gh issue create", issue_step["run"])
118 self.assertIn("gh issue comment", issue_step["run"])
119 self.assertNotIn("git commit", issue_step["run"])
120 self.assertNotIn("git push", issue_step["run"])
121
122
123 if __name__ == "__main__":
124 unittest.main()