gabriel / musehub public

test_clones_unit.py file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 πŸ’₯ blast risk
sha256:0 fix: fall back to any indexed mpack in read_object_bytes when push mpac… · gabriel · Jun 17, 2026
1 """Tier 1 β€” Unit tests for clone browser helper functions (issue #17).
2
3 Tests are strictly pure: no DB, no HTTP, no I/O. Every test exercises a
4 single helper from ``musehub.api.routes.musehub.ui_intel`` in isolation so
5 failures point exactly to the broken function.
6
7 Helpers under test:
8 _cl_tier_class β€” tier name β†’ CSS modifier
9 _cl_language_set β€” members_json β†’ sorted distinct languages
10 _cl_file_count β€” members_json β†’ distinct file count
11 _cl_is_cross_file β€” members_json β†’ bool (files > 1)
12 _cl_parse_members β€” members_json β†’ normalised list with ``file`` key
13 _cl_files_breakdown β€” normalised members β†’ grouped-by-file breakdown
14
15 Coverage target: 13 primary cases + edge cases for every parse-error path.
16 """
17 from __future__ import annotations
18
19 import json
20
21 import pytest
22
23 from musehub.api.routes.musehub.ui_intel import (
24 _cl_file_count,
25 _cl_files_breakdown,
26 _cl_is_cross_file,
27 _cl_language_set,
28 _cl_parse_members,
29 _cl_tier_class,
30 )
31
32 # ─────────────────────────────────────────────────────────────────────────────
33 # Fixtures
34 # ─────────────────────────────────────────────────────────────────────────────
35
36 _ONE_FILE = json.dumps(
37 [
38 {"address": "src/a.py::foo", "kind": "function", "language": "Python"},
39 {"address": "src/a.py::bar", "kind": "function", "language": "Python"},
40 ]
41 )
42
43 _TWO_FILES = json.dumps(
44 [
45 {"address": "src/a.py::foo", "kind": "function", "language": "Python"},
46 {"address": "src/b.py::foo", "kind": "function", "language": "Python"},
47 {"address": "docs/README.md::heading", "kind": "section", "language": "Markdown"},
48 ]
49 )
50
51 _EMPTY_ARRAY = "[]"
52 _INVALID_JSON = "not json"
53 _BLANK = ""
54
55
56 # ─────────────────────────────────────────────────────────────────────────────
57 # _cl_tier_class
58 # ─────────────────────────────────────────────────────────────────────────────
59
60
61 class TestClTierClass:
62 """U01–U03: tier name maps to correct CSS modifier class."""
63
64 def test_U01_exact_returns_exact_class(self) -> None:
65 assert _cl_tier_class("exact") == "cl-badge--exact"
66
67 def test_U02_near_returns_near_class(self) -> None:
68 assert _cl_tier_class("near") == "cl-badge--near"
69
70 def test_U03_unknown_tier_returns_near_as_safe_default(self) -> None:
71 """Unknown tier must never raise β€” falls back to near (less alarming)."""
72 assert _cl_tier_class("unknown") == "cl-badge--near"
73 assert _cl_tier_class("") == "cl-badge--near"
74
75
76 # ─────────────────────────────────────────────────────────────────────────────
77 # _cl_language_set
78 # ─────────────────────────────────────────────────────────────────────────────
79
80
81 class TestClLanguageSet:
82 """U04–U07: members_json β†’ sorted distinct language list."""
83
84 def test_U04_deduplicates_same_language(self) -> None:
85 blob = json.dumps([
86 {"language": "Python"},
87 {"language": "Python"},
88 ])
89 assert _cl_language_set(blob) == ["Python"]
90
91 def test_U05_multiple_languages_sorted(self) -> None:
92 blob = json.dumps([
93 {"language": "Python"},
94 {"language": "Markdown"},
95 ])
96 assert _cl_language_set(blob) == ["Markdown", "Python"]
97
98 def test_U06_invalid_json_returns_placeholder(self) -> None:
99 assert _cl_language_set(_INVALID_JSON) == ["β€”"]
100
101 def test_U07_empty_array_returns_placeholder(self) -> None:
102 assert _cl_language_set(_EMPTY_ARRAY) == ["β€”"]
103
104 def test_blank_string_returns_placeholder(self) -> None:
105 assert _cl_language_set(_BLANK) == ["β€”"]
106
107 def test_members_missing_language_key_skipped(self) -> None:
108 blob = json.dumps([{"address": "a.py::fn", "kind": "function"}])
109 assert _cl_language_set(blob) == ["β€”"]
110
111
112 # ─────────────────────────────────────────────────────────────────────────────
113 # _cl_file_count
114 # ─────────────────────────────────────────────────────────────────────────────
115
116
117 class TestClFileCount:
118 """U08–U10: members_json β†’ count of distinct source files."""
119
120 def test_U08_cross_file_fixture_returns_correct_count(self) -> None:
121 # _TWO_FILES contains 3 distinct files: src/a.py, src/b.py, docs/README.md
122 assert _cl_file_count(_TWO_FILES) == 3
123
124 def test_U09_one_file_returns_one(self) -> None:
125 assert _cl_file_count(_ONE_FILE) == 1
126
127 def test_U10_blank_input_returns_zero(self) -> None:
128 assert _cl_file_count(_BLANK) == 0
129
130 def test_invalid_json_returns_zero(self) -> None:
131 assert _cl_file_count(_INVALID_JSON) == 0
132
133 def test_empty_array_returns_zero(self) -> None:
134 assert _cl_file_count(_EMPTY_ARRAY) == 0
135
136 def test_no_double_colon_uses_whole_address_as_file(self) -> None:
137 blob = json.dumps([{"address": "plain_path.py", "language": "Python"}])
138 assert _cl_file_count(blob) == 1
139
140 def test_three_files(self) -> None:
141 blob = json.dumps([
142 {"address": "a.py::x", "language": "Python"},
143 {"address": "b.py::x", "language": "Python"},
144 {"address": "c.py::x", "language": "Python"},
145 ])
146 assert _cl_file_count(blob) == 3
147
148
149 # ─────────────────────────────────────────────────────────────────────────────
150 # _cl_is_cross_file
151 # ─────────────────────────────────────────────────────────────────────────────
152
153
154 class TestClIsCrossFile:
155 """U11–U13: members span multiple files β†’ True."""
156
157 def test_U11_two_files_is_cross_file(self) -> None:
158 assert _cl_is_cross_file(_TWO_FILES) is True
159
160 def test_U12_one_file_is_not_cross_file(self) -> None:
161 assert _cl_is_cross_file(_ONE_FILE) is False
162
163 def test_U13_malformed_json_returns_false(self) -> None:
164 """Parse error β†’ assume same-file; no false positive cross-file alarm."""
165 assert _cl_is_cross_file(_INVALID_JSON) is False
166
167 def test_blank_input_returns_false(self) -> None:
168 assert _cl_is_cross_file(_BLANK) is False
169
170
171 # ─────────────────────────────────────────────────────────────────────────────
172 # _cl_parse_members
173 # ─────────────────────────────────────────────────────────────────────────────
174
175
176 class TestClParseMembers:
177 """Members JSON is normalised with a synthetic ``file`` key."""
178
179 def test_file_key_added_from_address(self) -> None:
180 blob = json.dumps([{"address": "src/a.py::fn", "kind": "function", "language": "Python"}])
181 members = _cl_parse_members(blob)
182 assert len(members) == 1
183 assert members[0]["file"] == "src/a.py"
184
185 def test_address_without_double_colon_uses_whole_address_as_file(self) -> None:
186 blob = json.dumps([{"address": "plain.py", "kind": "variable", "language": "Python"}])
187 members = _cl_parse_members(blob)
188 assert members[0]["file"] == "plain.py"
189
190 def test_missing_address_key_defaults_to_empty(self) -> None:
191 blob = json.dumps([{"kind": "variable", "language": "Python"}])
192 members = _cl_parse_members(blob)
193 assert members[0]["address"] == ""
194 assert members[0]["file"] == ""
195
196 def test_invalid_json_returns_empty_list(self) -> None:
197 assert _cl_parse_members(_INVALID_JSON) == []
198
199 def test_blank_input_returns_empty_list(self) -> None:
200 assert _cl_parse_members(_BLANK) == []
201
202 def test_preserves_kind_and_language(self) -> None:
203 blob = json.dumps([{"address": "a.py::fn", "kind": "class", "language": "Python"}])
204 m = _cl_parse_members(blob)[0]
205 assert m["kind"] == "class"
206 assert m["language"] == "Python"
207
208
209 # ─────────────────────────────────────────────────────────────────────────────
210 # _cl_files_breakdown
211 # ─────────────────────────────────────────────────────────────────────────────
212
213
214 class TestClFilesBreakdown:
215 """Parsed members are grouped by file, sorted by count desc, with pct."""
216
217 def test_groups_by_file(self) -> None:
218 members = _cl_parse_members(_TWO_FILES)
219 breakdown = _cl_files_breakdown(members)
220 files = [b["file"] for b in breakdown]
221 assert "src/a.py" in files
222 assert "src/b.py" in files
223
224 def test_sorted_by_count_descending(self) -> None:
225 blob = json.dumps([
226 {"address": "src/a.py::x", "kind": "function", "language": "Python"},
227 {"address": "src/a.py::y", "kind": "function", "language": "Python"},
228 {"address": "src/b.py::x", "kind": "function", "language": "Python"},
229 ])
230 members = _cl_parse_members(blob)
231 breakdown = _cl_files_breakdown(members)
232 assert breakdown[0]["file"] == "src/a.py"
233 assert breakdown[0]["count"] == 2
234 assert breakdown[1]["count"] == 1
235
236 def test_pct_100_for_largest_file(self) -> None:
237 blob = json.dumps([
238 {"address": "src/a.py::x", "kind": "function", "language": "Python"},
239 {"address": "src/a.py::y", "kind": "function", "language": "Python"},
240 {"address": "src/b.py::x", "kind": "function", "language": "Python"},
241 ])
242 breakdown = _cl_files_breakdown(_cl_parse_members(blob))
243 assert breakdown[0]["pct"] == 100
244
245 def test_empty_members_returns_empty_list(self) -> None:
246 assert _cl_files_breakdown([]) == []
247
248 def test_single_file_100_pct(self) -> None:
249 members = _cl_parse_members(_ONE_FILE)
250 breakdown = _cl_files_breakdown(members)
251 assert len(breakdown) == 1
252 assert breakdown[0]["pct"] == 100