main
py 196 lines 6.92 KB
Raw
1 """Tests for hype_risk scoring model."""
2
3 import json
4 import sys
5 from pathlib import Path
6
7 sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
8 from hype_risk import classify_repo, extract_week, score_hype_risk # noqa: E402
9
10
11 class TestClassifyRepo:
12 """Test individual repo classification paths."""
13
14 def test_no_press_correlation(self):
15 """Not press_correlated → risk = 'none'."""
16 result = classify_repo("org/repo", press_correlated=False)
17 assert result["hype_risk"] == "none"
18 assert result["label"] == "No press signal"
19 assert result["press_correlated"] is False
20
21 def test_press_correlated_no_previous_data(self):
22 """Press correlated but no previous data → risk = 'medium'."""
23 result = classify_repo(
24 "org/repo",
25 press_correlated=True,
26 current_stars=1000,
27 current_stars_gained=200,
28 previous_stars=None,
29 previous_stars_gained=None,
30 )
31 assert result["hype_risk"] == "medium"
32 assert result["press_correlated"] is True
33 assert result["confidence"] < 0.6
34
35 def test_organic_growth_before_press(self):
36 """Stars already growing before press → risk = 'very_low'."""
37 result = classify_repo(
38 "org/repo",
39 press_correlated=True,
40 current_stars=2000,
41 current_stars_gained=100,
42 previous_stars=1900,
43 previous_stars_gained=80, # Already had strong growth
44 )
45 assert result["hype_risk"] == "very_low"
46 assert result["label"] == "Organic growth"
47 assert result["stars_trend"] == "organic"
48
49 def test_sustained_growth_after_press(self):
50 """Stars spike after article and sustain → risk = 'low'."""
51 result = classify_repo(
52 "org/repo",
53 press_correlated=True,
54 current_stars=5000,
55 current_stars_gained=500,
56 previous_stars=4500,
57 previous_stars_gained=100, # Small growth before, big growth now
58 )
59 assert result["hype_risk"] == "low"
60 assert result["label"] == "Press-validated, community-sustained"
61 assert result["stars_trend"] == "sustained"
62
63 def test_decaying_growth_after_press(self):
64 """Stars spiked but now fading → risk = 'high'."""
65 result = classify_repo(
66 "org/repo",
67 press_correlated=True,
68 current_stars=5000,
69 current_stars_gained=50, # Much lower than previous
70 previous_stars=4950,
71 previous_stars_gained=500, # Big spike last week
72 )
73 assert result["hype_risk"] == "high"
74 assert result["label"] == "Press-driven hype, fading"
75 assert result["stars_trend"] == "decaying"
76
77 def test_press_correlated_no_activity_spike(self):
78 """Press coverage but no GitHub activity spike → risk = 'medium'."""
79 result = classify_repo(
80 "org/repo",
81 press_correlated=True,
82 current_stars=100,
83 current_stars_gained=0,
84 previous_stars=100,
85 previous_stars_gained=0,
86 )
87 assert result["hype_risk"] == "medium"
88 assert result["label"] == "Announced but unbuilt"
89
90 def test_assessment_has_all_fields(self):
91 """Every assessment should have all required fields."""
92 result = classify_repo("org/repo", press_correlated=False)
93 assert "repo" in result
94 assert "hype_risk" in result
95 assert "label" in result
96 assert "press_correlated" in result
97 assert "stars_trend" in result
98 assert "confidence" in result
99 assert "reasoning" in result
100
101
102 class TestScoreHypeRisk:
103 """Test the batch scoring function."""
104
105 def test_empty_correlations(self):
106 result = score_hype_risk({"correlations": []}, None, None)
107 assert result == []
108
109 def test_scores_correlated_repos(self):
110 correlations = {
111 "correlations": [
112 {"repo": "org/alpha", "press_correlated": True},
113 {"repo": "org/beta", "press_correlated": False},
114 ]
115 }
116 raw_data = [
117 {"full_name": "org/alpha", "stars": 1000, "stars_gained": 200},
118 {"full_name": "org/beta", "stars": 500, "stars_gained": 10},
119 ]
120 result = score_hype_risk(correlations, raw_data, None)
121 assert len(result) == 2
122
123 alpha = next(a for a in result if a["repo"] == "org/alpha")
124 beta = next(a for a in result if a["repo"] == "org/beta")
125
126 assert alpha["hype_risk"] == "medium" # correlated, no previous
127 assert beta["hype_risk"] == "none" # not correlated
128
129 def test_with_previous_data(self):
130 correlations = {
131 "correlations": [
132 {"repo": "org/sustained", "press_correlated": True},
133 ]
134 }
135 raw_data = [
136 {"full_name": "org/sustained", "stars": 3000, "stars_gained": 400},
137 ]
138 previous_data = [
139 {"full_name": "org/sustained", "stars": 2600, "stars_gained": 50},
140 ]
141 result = score_hype_risk(correlations, raw_data, previous_data)
142 sustained = next(a for a in result if a["repo"] == "org/sustained")
143 assert sustained["hype_risk"] == "low"
144
145 def test_raw_data_wrapped_in_dict(self):
146 """Raw data may be wrapped in a dict with 'repos' key."""
147 correlations = {"correlations": [{"repo": "x/y", "press_correlated": False}]}
148 raw_data = {"repos": [{"full_name": "x/y", "stars": 10, "stars_gained": 1}]}
149 result = score_hype_risk(correlations, raw_data, None)
150 assert len(result) == 1
151 assert result[0]["hype_risk"] == "none"
152
153
154 class TestExtractWeek:
155 def test_simple_week(self):
156 assert extract_week("data/raw/ai-ml/2026-W21.json") == "2026-W21"
157
158 def test_correlations_suffix(self):
159 assert extract_week("data/analyzed/ai-ml/2026-W21-correlations.json") == "2026-W21"
160
161 def test_none_path(self):
162 assert extract_week(None) == "unknown"
163
164
165 class TestCLI:
166 """Test CLI main function."""
167
168 def test_main_with_files(self, tmp_path):
169 from hype_risk import main
170
171 corr_file = tmp_path / "correlations.json"
172 raw_file = tmp_path / "2026-W21.json"
173 out_file = tmp_path / "output.json"
174
175 corr_file.write_text(
176 json.dumps({"correlations": [{"repo": "org/repo", "press_correlated": True}]})
177 )
178 raw_file.write_text(
179 json.dumps([{"full_name": "org/repo", "stars": 500, "stars_gained": 100}])
180 )
181
182 main(
183 [
184 "--correlations",
185 str(corr_file),
186 "--raw",
187 str(raw_file),
188 "--output",
189 str(out_file),
190 ]
191 )
192
193 output = json.loads(out_file.read_text())
194 assert output["week"] == "2026-W21"
195 assert len(output["assessments"]) == 1
196 assert output["assessments"][0]["repo"] == "org/repo"