Skip to content

Commit 929c3cb

Browse files
committed
Add tests for fetch_schedule.py
1 parent f5ae433 commit 929c3cb

1 file changed

Lines changed: 201 additions & 0 deletions

File tree

tests/unit/test_fetch_schedule.py

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"""Unit tests for skills/rhdh-test-plan-review/scripts/fetch_schedule.py pure helpers."""
2+
3+
import datetime as dt_module
4+
import sys
5+
from pathlib import Path
6+
7+
import pytest
8+
9+
PROJECT_ROOT = Path(__file__).resolve().parents[2]
10+
_FETCH_SCHEDULE_SCRIPTS = PROJECT_ROOT / "skills" / "rhdh-test-plan-review" / "scripts"
11+
if str(_FETCH_SCHEDULE_SCRIPTS) not in sys.path:
12+
sys.path.insert(0, str(_FETCH_SCHEDULE_SCRIPTS))
13+
14+
import fetch_schedule as fs # noqa: E402
15+
16+
17+
class TestNormalizeVersion:
18+
def test_plain_semver(self):
19+
assert fs.normalize_version("1.6") == "1.6"
20+
21+
def test_rhdh_prefix_variants(self):
22+
assert fs.normalize_version("RHDH 1.6") == "1.6"
23+
assert fs.normalize_version("rhdh-1.6") == "1.6"
24+
25+
def test_v_prefix(self):
26+
assert fs.normalize_version("v1.10") == "1.10"
27+
28+
def test_double_digit_minor(self):
29+
assert fs.normalize_version("release-2.12-rc1") == "2.12"
30+
31+
def test_no_digits_returns_stripped(self):
32+
assert fs.normalize_version(" upcoming ") == "upcoming"
33+
34+
35+
class TestParseDate:
36+
def test_iso(self):
37+
assert fs.parse_date("2025-03-15") == "2025-03-15"
38+
39+
def test_us_slash(self):
40+
assert fs.parse_date("3/15/2025") == "2025-03-15"
41+
42+
def test_long_month(self):
43+
assert fs.parse_date("March 15, 2025") == "2025-03-15"
44+
45+
def test_short_month(self):
46+
assert fs.parse_date("Mar 15, 2025") == "2025-03-15"
47+
48+
def test_two_digit_year(self):
49+
assert fs.parse_date("3/15/25") == "2025-03-15"
50+
51+
def test_unparseable(self):
52+
assert fs.parse_date("TBD") is None
53+
54+
def test_strips_whitespace(self):
55+
assert fs.parse_date(" 2025-01-01 ") == "2025-01-01"
56+
57+
58+
class TestRowDate:
59+
def test_first_parseable_wins(self):
60+
assert fs.row_date(["n/a", "2025-04-01", "2025-05-01"]) == "2025-04-01"
61+
62+
def test_none_when_empty(self):
63+
assert fs.row_date([]) is None
64+
65+
66+
def _fixed_clock(monkeypatch, year: int, month: int = 6, day: int = 1):
67+
"""Replace fetch_schedule.datetime (Python 3.14+ blocks patching datetime.now on the class)."""
68+
69+
class _Clock:
70+
strptime = staticmethod(dt_module.datetime.strptime)
71+
72+
@staticmethod
73+
def now(tz=None):
74+
return dt_module.datetime(year, month, day)
75+
76+
monkeypatch.setattr(fs, "datetime", _Clock)
77+
78+
79+
class TestFindScheduleTab:
80+
def test_prefers_tab_matching_current_year(self, monkeypatch):
81+
_fixed_clock(monkeypatch, 2026)
82+
tabs = ["Notes", "2025 Schedule", "2026 Schedule Q1", "Other"]
83+
assert fs.find_schedule_tab(tabs) == "2026 Schedule Q1"
84+
85+
def test_falls_forward_to_next_year(self, monkeypatch):
86+
_fixed_clock(monkeypatch, 2026)
87+
tabs = ["Notes", "2027 Release Schedule"]
88+
assert fs.find_schedule_tab(tabs) == "2027 Release Schedule"
89+
90+
def test_then_previous_year(self, monkeypatch):
91+
_fixed_clock(monkeypatch, 2026)
92+
tabs = ["2025 schedule overview"]
93+
assert fs.find_schedule_tab(tabs) == "2025 schedule overview"
94+
95+
def test_fallback_any_schedule_without_year_match(self, monkeypatch):
96+
_fixed_clock(monkeypatch, 2026)
97+
tabs = ["Foo", "Master Schedule"]
98+
assert fs.find_schedule_tab(tabs) == "Master Schedule"
99+
100+
def test_returns_none_when_no_schedule(self, monkeypatch):
101+
_fixed_clock(monkeypatch, 2026)
102+
assert fs.find_schedule_tab(["Overview", "Archive"]) is None
103+
104+
105+
class TestFindMilestones:
106+
def test_empty_rows(self):
107+
assert fs.find_milestones([], "1.6") == {}
108+
109+
def test_no_ga_row(self):
110+
rows = [["Feature Freeze"], ["Code Freeze"], ["1.6 RC"]]
111+
assert fs.find_milestones(rows, "1.6") == {}
112+
113+
def test_ga_only_with_date(self):
114+
rows = [["RHDH 1.6", "GA announce", "2025-08-01"]]
115+
assert fs.find_milestones(rows, "1.6") == {"ga_date": "2025-08-01"}
116+
117+
def test_walks_backwards_for_freezes_before_ga(self):
118+
rows = [
119+
["Feature Freeze", "2025-05-01"],
120+
["Code Freeze", "2025-06-01"],
121+
["RHDH 1.6", "GA date", "2025-08-15"],
122+
]
123+
assert fs.find_milestones(rows, "1.6") == {
124+
"feature_freeze": "2025-05-01",
125+
"code_freeze": "2025-06-01",
126+
"ga_date": "2025-08-15",
127+
}
128+
129+
def test_stops_at_prior_ga_row(self):
130+
"""Walking upward stops at an earlier GA row so 1.5 freezes are not merged into 1.6."""
131+
rows = [
132+
["Feature Freeze", "2024-12-01"],
133+
["Code Freeze", "2024-11-01"],
134+
["1.5", "GA", "2024-10-01"],
135+
["Feature Freeze", "2025-05-01"],
136+
["Code Freeze", "2025-06-01"],
137+
["1.6", "GA announce", "2025-08-01"],
138+
]
139+
out = fs.find_milestones(rows, "1.6")
140+
assert out == {
141+
"feature_freeze": "2025-05-01",
142+
"code_freeze": "2025-06-01",
143+
"ga_date": "2025-08-01",
144+
}
145+
146+
def test_general_availability_keyword(self):
147+
rows = [
148+
["Code freeze", "2025-02-10"],
149+
["RHDH 1.7", "General Availability", "2025-04-20"],
150+
]
151+
assert fs.find_milestones(rows, "1.7") == {
152+
"code_freeze": "2025-02-10",
153+
"ga_date": "2025-04-20",
154+
}
155+
156+
def test_code_freeze_hyphenated(self):
157+
rows = [
158+
["Code-freeze", "1/15/2025"],
159+
["1.6", "GA ", "2025-03-01"],
160+
]
161+
assert fs.find_milestones(rows, "1.6")["code_freeze"] == "2025-01-15"
162+
163+
def test_feature_freeze_hyphenated(self):
164+
rows = [
165+
["Feature-freeze", "Jan 10, 2025"],
166+
["v1.6", "ga date", "2025-03-01"],
167+
]
168+
assert fs.find_milestones(rows, "1.6")["feature_freeze"] == "2025-01-10"
169+
170+
def test_ff_abbreviation_with_spaces(self):
171+
"""Row text is space-padded; ' ff ' matches abbreviated freeze lines."""
172+
rows = [
173+
["Milestone", " FF ", "2025-02-01"],
174+
["1.6 GA", "2025-03-01"],
175+
]
176+
assert fs.find_milestones(rows, "1.6")["feature_freeze"] == "2025-02-01"
177+
178+
def test_first_ga_row_wins(self):
179+
rows = [
180+
["1.6", "GA", "2025-01-01"],
181+
["1.6", "GA", "2025-12-01"],
182+
]
183+
assert fs.find_milestones(rows, "1.6")["ga_date"] == "2025-01-01"
184+
185+
def test_ga_without_date_still_collects_freezes(self):
186+
rows = [
187+
["Feature freeze", "2025-05-20"],
188+
["Code freeze", "2025-06-20"],
189+
["RHDH 1.6", "GA", "TBD"],
190+
]
191+
out = fs.find_milestones(rows, "1.6")
192+
assert "ga_date" not in out
193+
assert out["feature_freeze"] == "2025-05-20"
194+
assert out["code_freeze"] == "2025-06-20"
195+
196+
def test_version_match_via_stripped_rhdh(self):
197+
rows = [
198+
["Code freeze", "2025-01-05"],
199+
["RHDH 2.0", "GA ", "2025-02-01"],
200+
]
201+
assert fs.find_milestones(rows, "RHDH 2.0")["code_freeze"] == "2025-01-05"

0 commit comments

Comments
 (0)