gabriel / musehub public

test_symbol_anchor_parser.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 """TDD: symbol anchor parser — same-repo, cross-repo, and ref-pinned anchors.
2
3 Covers musehub.services.symbol_anchor:
4 parse_symbol_anchor(raw: str) -> SymbolAnchorRef
5
6 Anchor grammar
7 --------------
8 anchor := cross_repo_anchor | same_repo_anchor
9 cross_anchor := owner '/' repo '::' file_path ('::' symbol)? ('@' ref)?
10 same_anchor := file_path ('::' symbol)? ('@' ref)?
11
12 owner/repo — exactly one '/', no dots, no sub-slashes
13 file_path — any path (may contain '/', must include extension or multiple
14 path components distinguishing it from an owner/repo slug)
15 symbol — function/class name, may contain dots (e.g. 'Cls.method')
16 ref — branch name OR 'sha256:<64hex>'
17
18 blob_url() contract
19 -------------------
20 same-repo anchor → /{default_owner}/{default_repo}/blob/{ref}/{file}#{S:sym}
21 cross-repo anchor → /{owner}/{repo}/blob/{ref}/{file}#{S:sym}
22 ref defaults to 'main' when not specified
23
24 Unit tests (pure function, no DB):
25 test_same_repo_file_only
26 test_same_repo_file_and_symbol
27 test_same_repo_with_branch_ref
28 test_same_repo_with_commit_ref
29 test_cross_repo_file_only
30 test_cross_repo_file_and_symbol
31 test_cross_repo_with_branch_ref
32 test_cross_repo_with_commit_ref
33 test_cross_repo_deeply_nested_file
34 test_symbol_with_dots_preserved
35 test_file_with_extension_not_mistaken_for_owner_repo
36 test_deeply_nested_path_not_mistaken_for_owner_repo
37 test_is_cross_repo_flag
38 blob_url tests:
39 test_blob_url_same_repo_file_only
40 test_blob_url_same_repo_with_symbol
41 test_blob_url_cross_repo_with_symbol
42 test_blob_url_uses_explicit_ref
43 test_blob_url_cross_repo_uses_own_ref
44 test_blob_url_default_ref_is_main
45 display_label tests:
46 test_display_label_same_repo
47 test_display_label_cross_repo_shows_owner_repo_prefix
48 test_display_label_cross_repo_with_ref
49 """
50 from __future__ import annotations
51
52 import pytest
53
54 from musehub.services.symbol_anchor import SymbolAnchorRef, parse_symbol_anchor
55
56
57 # ---------------------------------------------------------------------------
58 # Helpers
59 # ---------------------------------------------------------------------------
60
61 BASE = "https://staging.musehub.ai"
62 OWN_OWNER = "gabriel"
63 OWN_REPO = "musehub"
64
65
66 def _url(raw: str, ref: str = "main") -> str:
67 return parse_symbol_anchor(raw).blob_url(BASE, OWN_OWNER, OWN_REPO, default_ref=ref)
68
69
70 # ---------------------------------------------------------------------------
71 # Parsing — same-repo anchors
72 # ---------------------------------------------------------------------------
73
74
75 def test_same_repo_file_only() -> None:
76 a = parse_symbol_anchor("musehub/services/billing.py")
77 assert a.owner is None
78 assert a.repo is None
79 assert a.file_path == "musehub/services/billing.py"
80 assert a.symbol_name is None
81 assert a.ref is None
82
83
84 def test_same_repo_file_and_symbol() -> None:
85 a = parse_symbol_anchor("musehub/services/billing.py::compute_total")
86 assert a.owner is None
87 assert a.file_path == "musehub/services/billing.py"
88 assert a.symbol_name == "compute_total"
89 assert a.ref is None
90
91
92 def test_same_repo_with_branch_ref() -> None:
93 a = parse_symbol_anchor("musehub/services/billing.py::compute_total@dev")
94 assert a.owner is None
95 assert a.file_path == "musehub/services/billing.py"
96 assert a.symbol_name == "compute_total"
97 assert a.ref == "dev"
98
99
100 def test_same_repo_with_commit_ref() -> None:
101 ref = "sha256:fa0025329d55487de25035b1122b17517bf88e234f5a8f697111087fc785e81c"
102 a = parse_symbol_anchor(f"muse/cli/commands/bridge.py::GitExporter._has_shebang@{ref}")
103 assert a.owner is None
104 assert a.file_path == "muse/cli/commands/bridge.py"
105 assert a.symbol_name == "GitExporter._has_shebang"
106 assert a.ref == ref
107
108
109 def test_same_repo_file_only_with_ref() -> None:
110 a = parse_symbol_anchor("src/main.py@feat/my-branch")
111 assert a.file_path == "src/main.py"
112 assert a.symbol_name is None
113 assert a.ref == "feat/my-branch"
114
115
116 # ---------------------------------------------------------------------------
117 # Parsing — cross-repo anchors
118 # ---------------------------------------------------------------------------
119
120
121 def test_cross_repo_file_only() -> None:
122 a = parse_symbol_anchor("gabriel/muse::muse/core/snapshot.py")
123 assert a.owner == "gabriel"
124 assert a.repo == "muse"
125 assert a.file_path == "muse/core/snapshot.py"
126 assert a.symbol_name is None
127 assert a.ref is None
128
129
130 def test_cross_repo_file_and_symbol() -> None:
131 a = parse_symbol_anchor("gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes")
132 assert a.owner == "gabriel"
133 assert a.repo == "muse"
134 assert a.file_path == "muse/cli/commands/bridge.py"
135 assert a.symbol_name == "GitExporter.fix_file_modes"
136 assert a.ref is None
137
138
139 def test_cross_repo_with_branch_ref() -> None:
140 a = parse_symbol_anchor("gabriel/muse::muse/cli/commands/bridge.py::GitExporter._has_shebang@dev")
141 assert a.owner == "gabriel"
142 assert a.repo == "muse"
143 assert a.file_path == "muse/cli/commands/bridge.py"
144 assert a.symbol_name == "GitExporter._has_shebang"
145 assert a.ref == "dev"
146
147
148 def test_cross_repo_with_commit_ref() -> None:
149 sha = "sha256:fa0025329d55487de25035b1122b17517bf88e234f5a8f697111087fc785e81c"
150 a = parse_symbol_anchor(f"gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes@{sha}")
151 assert a.owner == "gabriel"
152 assert a.ref == sha
153
154
155 def test_cross_repo_deeply_nested_file() -> None:
156 a = parse_symbol_anchor("acme/infra::deploy/paperclip/scripts/install.sh::main")
157 assert a.owner == "acme"
158 assert a.repo == "infra"
159 assert a.file_path == "deploy/paperclip/scripts/install.sh"
160 assert a.symbol_name == "main"
161
162
163 # ---------------------------------------------------------------------------
164 # Disambiguation — file paths must not be confused with owner/repo slugs
165 # ---------------------------------------------------------------------------
166
167
168 def test_file_with_extension_not_mistaken_for_owner_repo() -> None:
169 """'src/billing.py' has a dot in the second segment → same-repo file."""
170 a = parse_symbol_anchor("src/billing.py::compute_total")
171 assert a.owner is None
172 assert a.file_path == "src/billing.py"
173
174
175 def test_deeply_nested_path_not_mistaken_for_owner_repo() -> None:
176 """'muse/cli/commands/bridge.py' has multiple slashes → same-repo file."""
177 a = parse_symbol_anchor("muse/cli/commands/bridge.py::GitExporter.fix_file_modes")
178 assert a.owner is None
179 assert a.file_path == "muse/cli/commands/bridge.py"
180
181
182 def test_single_segment_file_is_same_repo() -> None:
183 a = parse_symbol_anchor("README.md")
184 assert a.owner is None
185 assert a.file_path == "README.md"
186
187
188 def test_symbol_with_dots_preserved() -> None:
189 """Method names like 'GitExporter.fix_file_modes' survive the split."""
190 a = parse_symbol_anchor("gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes")
191 assert a.symbol_name == "GitExporter.fix_file_modes"
192
193
194 # ---------------------------------------------------------------------------
195 # is_cross_repo flag
196 # ---------------------------------------------------------------------------
197
198
199 def test_is_cross_repo_true_for_cross_repo_anchor() -> None:
200 a = parse_symbol_anchor("gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes")
201 assert a.is_cross_repo is True
202
203
204 def test_is_cross_repo_false_for_same_repo_anchor() -> None:
205 a = parse_symbol_anchor("musehub/services/billing.py::compute_total")
206 assert a.is_cross_repo is False
207
208
209 # ---------------------------------------------------------------------------
210 # blob_url()
211 # ---------------------------------------------------------------------------
212
213
214 def test_blob_url_same_repo_file_only() -> None:
215 url = _url("src/main.py")
216 assert url == f"{BASE}/{OWN_OWNER}/{OWN_REPO}/blob/main/src/main.py"
217
218
219 def test_blob_url_same_repo_with_symbol() -> None:
220 url = _url("musehub/services/billing.py::compute_total")
221 assert url == f"{BASE}/{OWN_OWNER}/{OWN_REPO}/blob/main/musehub/services/billing.py#S:compute_total"
222
223
224 def test_blob_url_cross_repo_with_symbol() -> None:
225 url = _url("gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes")
226 assert url == f"{BASE}/gabriel/muse/blob/main/muse/cli/commands/bridge.py#S:GitExporter.fix_file_modes"
227
228
229 def test_blob_url_uses_explicit_ref_same_repo() -> None:
230 url = _url("src/main.py::Fn@dev")
231 assert url == f"{BASE}/{OWN_OWNER}/{OWN_REPO}/blob/dev/src/main.py#S:Fn"
232
233
234 def test_blob_url_cross_repo_uses_own_ref() -> None:
235 url = _url("gabriel/muse::muse/cli/commands/bridge.py::GitExporter._has_shebang@dev")
236 assert url == f"{BASE}/gabriel/muse/blob/dev/muse/cli/commands/bridge.py#S:GitExporter._has_shebang"
237
238
239 def test_blob_url_default_ref_is_main() -> None:
240 url = parse_symbol_anchor("src/x.py::Fn").blob_url(BASE, OWN_OWNER, OWN_REPO)
241 assert "/blob/main/" in url
242
243
244 def test_blob_url_commit_ref_in_url() -> None:
245 sha = "sha256:fa0025329d55487de25035b1122b17517bf88e234f5a8f697111087fc785e81c"
246 url = _url(f"gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes@{sha}")
247 assert f"/blob/{sha}/" in url
248
249
250 # ---------------------------------------------------------------------------
251 # display_label()
252 # ---------------------------------------------------------------------------
253
254
255 def test_display_label_same_repo_file_and_symbol() -> None:
256 a = parse_symbol_anchor("musehub/services/billing.py::compute_total")
257 assert a.display_label() == "musehub/services/billing.py::compute_total"
258
259
260 def test_display_label_same_repo_file_only() -> None:
261 a = parse_symbol_anchor("src/main.py")
262 assert a.display_label() == "src/main.py"
263
264
265 def test_display_label_cross_repo_shows_prefix() -> None:
266 a = parse_symbol_anchor("gabriel/muse::muse/cli/commands/bridge.py::GitExporter.fix_file_modes")
267 label = a.display_label()
268 assert label.startswith("gabriel/muse::")
269 assert "GitExporter.fix_file_modes" in label
270
271
272 def test_display_label_cross_repo_with_ref() -> None:
273 a = parse_symbol_anchor("gabriel/muse::muse/cli/commands/bridge.py::GitExporter._has_shebang@dev")
274 label = a.display_label()
275 assert "@dev" in label