gabriel / muse public
test_core_shallow.py python
244 lines 9.2 KB
Raw
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 133 days ago
1 """Tests for muse/core/shallow.py — shallow graft management.
2
3 A shallow repo is one whose local history is intentionally incomplete.
4 `.muse/shallow` lists the boundary commits: their snapshots and objects
5 are expected locally, but their parents are not.
6
7 Coverage:
8 Unit: read/write/add/remove/is_shallow/shallow_path
9 Edge cases: empty file, nonexistent file, duplicate IDs, bare hex IDs,
10 sha256:-prefixed IDs, invalid lines silently skipped
11 Integration: BFS walk stops at shallow boundary (tested via run_verify)
12 """
13
14 from __future__ import annotations
15
16 import json
17 import pathlib
18
19 import pytest
20
21 from muse.core.shallow import (
22 add_shallow,
23 is_shallow,
24 read_shallow,
25 remove_shallow,
26 shallow_path,
27 write_shallow,
28 )
29 from muse.core._types import long_id
30
31
32 # ---------------------------------------------------------------------------
33 # Helpers
34 # ---------------------------------------------------------------------------
35
36 def _init_repo(path: pathlib.Path) -> pathlib.Path:
37 muse = path / ".muse"
38 for d in ("commits", "snapshots", "objects", "refs/heads"):
39 (muse / d).mkdir(parents=True, exist_ok=True)
40 (muse / "HEAD").write_text("ref: refs/heads/main")
41 (muse / "repo.json").write_text(json.dumps({"repo_id": "shallow-test", "domain": "code"}))
42 return path
43
44
45 _CID_A = long_id("a" * 64)
46 _CID_B = long_id("b" * 64)
47 _CID_C = long_id("c" * 64)
48
49
50 # ---------------------------------------------------------------------------
51 # shallow_path
52 # ---------------------------------------------------------------------------
53
54 class TestShallowPath:
55 def test_returns_path_under_muse(self, tmp_path: pathlib.Path) -> None:
56 p = shallow_path(tmp_path)
57 assert p == tmp_path / ".muse" / "shallow"
58
59 def test_does_not_create_file(self, tmp_path: pathlib.Path) -> None:
60 shallow_path(tmp_path)
61 assert not (tmp_path / ".muse" / "shallow").exists()
62
63
64 # ---------------------------------------------------------------------------
65 # read_shallow
66 # ---------------------------------------------------------------------------
67
68 class TestReadShallow:
69 def test_returns_empty_frozenset_when_no_file(self, tmp_path: pathlib.Path) -> None:
70 repo = _init_repo(tmp_path)
71 result = read_shallow(repo)
72 assert result == frozenset()
73
74 def test_returns_empty_frozenset_for_empty_file(self, tmp_path: pathlib.Path) -> None:
75 repo = _init_repo(tmp_path)
76 (repo / ".muse" / "shallow").write_text("")
77 assert read_shallow(repo) == frozenset()
78
79 def test_reads_single_commit(self, tmp_path: pathlib.Path) -> None:
80 repo = _init_repo(tmp_path)
81 (repo / ".muse" / "shallow").write_text(_CID_A + "\n")
82 assert read_shallow(repo) == frozenset({_CID_A})
83
84 def test_reads_multiple_commits(self, tmp_path: pathlib.Path) -> None:
85 repo = _init_repo(tmp_path)
86 (repo / ".muse" / "shallow").write_text(f"{_CID_A}\n{_CID_B}\n{_CID_C}\n")
87 assert read_shallow(repo) == frozenset({_CID_A, _CID_B, _CID_C})
88
89 def test_ignores_blank_lines(self, tmp_path: pathlib.Path) -> None:
90 repo = _init_repo(tmp_path)
91 (repo / ".muse" / "shallow").write_text(f"{_CID_A}\n\n{_CID_B}\n")
92 assert read_shallow(repo) == frozenset({_CID_A, _CID_B})
93
94 def test_strips_whitespace(self, tmp_path: pathlib.Path) -> None:
95 repo = _init_repo(tmp_path)
96 (repo / ".muse" / "shallow").write_text(f" {_CID_A} \n")
97 assert _CID_A in read_shallow(repo)
98
99 def test_ignores_comment_lines(self, tmp_path: pathlib.Path) -> None:
100 repo = _init_repo(tmp_path)
101 (repo / ".muse" / "shallow").write_text(f"# generated by muse\n{_CID_A}\n")
102 assert read_shallow(repo) == frozenset({_CID_A})
103
104 def test_returns_frozenset_not_set(self, tmp_path: pathlib.Path) -> None:
105 repo = _init_repo(tmp_path)
106 (repo / ".muse" / "shallow").write_text(_CID_A + "\n")
107 result = read_shallow(repo)
108 assert isinstance(result, frozenset)
109
110 def test_deduplicates(self, tmp_path: pathlib.Path) -> None:
111 repo = _init_repo(tmp_path)
112 (repo / ".muse" / "shallow").write_text(f"{_CID_A}\n{_CID_A}\n")
113 assert read_shallow(repo) == frozenset({_CID_A})
114
115
116 # ---------------------------------------------------------------------------
117 # write_shallow
118 # ---------------------------------------------------------------------------
119
120 class TestWriteShallow:
121 def test_writes_empty_set_creates_empty_file(self, tmp_path: pathlib.Path) -> None:
122 repo = _init_repo(tmp_path)
123 write_shallow(repo, set())
124 p = repo / ".muse" / "shallow"
125 assert p.exists()
126 assert p.read_text().strip() == ""
127
128 def test_writes_single_commit(self, tmp_path: pathlib.Path) -> None:
129 repo = _init_repo(tmp_path)
130 write_shallow(repo, {_CID_A})
131 result = read_shallow(repo)
132 assert result == frozenset({_CID_A})
133
134 def test_writes_multiple_commits_sorted(self, tmp_path: pathlib.Path) -> None:
135 repo = _init_repo(tmp_path)
136 write_shallow(repo, {_CID_C, _CID_A, _CID_B})
137 lines = [l for l in (repo / ".muse" / "shallow").read_text().splitlines() if l.strip()]
138 assert lines == sorted(lines)
139
140 def test_round_trips(self, tmp_path: pathlib.Path) -> None:
141 repo = _init_repo(tmp_path)
142 original = {_CID_A, _CID_B, _CID_C}
143 write_shallow(repo, original)
144 assert read_shallow(repo) == frozenset(original)
145
146 def test_overwrites_existing(self, tmp_path: pathlib.Path) -> None:
147 repo = _init_repo(tmp_path)
148 write_shallow(repo, {_CID_A, _CID_B})
149 write_shallow(repo, {_CID_C})
150 assert read_shallow(repo) == frozenset({_CID_C})
151
152 def test_creates_muse_dir_if_absent(self, tmp_path: pathlib.Path) -> None:
153 # .muse dir may not exist yet
154 write_shallow(tmp_path, {_CID_A})
155 assert (tmp_path / ".muse" / "shallow").exists()
156
157
158 # ---------------------------------------------------------------------------
159 # add_shallow
160 # ---------------------------------------------------------------------------
161
162 class TestAddShallow:
163 def test_adds_to_empty(self, tmp_path: pathlib.Path) -> None:
164 repo = _init_repo(tmp_path)
165 add_shallow(repo, _CID_A)
166 assert read_shallow(repo) == frozenset({_CID_A})
167
168 def test_adds_to_existing(self, tmp_path: pathlib.Path) -> None:
169 repo = _init_repo(tmp_path)
170 write_shallow(repo, {_CID_A})
171 add_shallow(repo, _CID_B)
172 assert read_shallow(repo) == frozenset({_CID_A, _CID_B})
173
174 def test_add_is_idempotent(self, tmp_path: pathlib.Path) -> None:
175 repo = _init_repo(tmp_path)
176 add_shallow(repo, _CID_A)
177 add_shallow(repo, _CID_A)
178 assert read_shallow(repo) == frozenset({_CID_A})
179
180 def test_add_when_no_file_exists(self, tmp_path: pathlib.Path) -> None:
181 repo = _init_repo(tmp_path)
182 assert not (repo / ".muse" / "shallow").exists()
183 add_shallow(repo, _CID_A)
184 assert _CID_A in read_shallow(repo)
185
186
187 # ---------------------------------------------------------------------------
188 # remove_shallow
189 # ---------------------------------------------------------------------------
190
191 class TestRemoveShallow:
192 def test_removes_existing_entry(self, tmp_path: pathlib.Path) -> None:
193 repo = _init_repo(tmp_path)
194 write_shallow(repo, {_CID_A, _CID_B})
195 remove_shallow(repo, _CID_A)
196 assert read_shallow(repo) == frozenset({_CID_B})
197
198 def test_remove_nonexistent_is_noop(self, tmp_path: pathlib.Path) -> None:
199 repo = _init_repo(tmp_path)
200 write_shallow(repo, {_CID_A})
201 remove_shallow(repo, _CID_C) # not present
202 assert read_shallow(repo) == frozenset({_CID_A})
203
204 def test_remove_from_empty_is_noop(self, tmp_path: pathlib.Path) -> None:
205 repo = _init_repo(tmp_path)
206 remove_shallow(repo, _CID_A) # no file
207 assert read_shallow(repo) == frozenset()
208
209 def test_remove_last_entry_leaves_empty(self, tmp_path: pathlib.Path) -> None:
210 repo = _init_repo(tmp_path)
211 write_shallow(repo, {_CID_A})
212 remove_shallow(repo, _CID_A)
213 assert read_shallow(repo) == frozenset()
214
215
216 # ---------------------------------------------------------------------------
217 # is_shallow
218 # ---------------------------------------------------------------------------
219
220 class TestIsShallow:
221 def test_false_when_no_file(self, tmp_path: pathlib.Path) -> None:
222 repo = _init_repo(tmp_path)
223 assert is_shallow(repo) is False
224
225 def test_false_when_empty_file(self, tmp_path: pathlib.Path) -> None:
226 repo = _init_repo(tmp_path)
227 (repo / ".muse" / "shallow").write_text("")
228 assert is_shallow(repo) is False
229
230 def test_true_when_has_entry(self, tmp_path: pathlib.Path) -> None:
231 repo = _init_repo(tmp_path)
232 write_shallow(repo, {_CID_A})
233 assert is_shallow(repo) is True
234
235 def test_true_with_multiple_entries(self, tmp_path: pathlib.Path) -> None:
236 repo = _init_repo(tmp_path)
237 write_shallow(repo, {_CID_A, _CID_B})
238 assert is_shallow(repo) is True
239
240 def test_false_after_removing_all(self, tmp_path: pathlib.Path) -> None:
241 repo = _init_repo(tmp_path)
242 write_shallow(repo, {_CID_A})
243 remove_shallow(repo, _CID_A)
244 assert is_shallow(repo) is False
File History 2 commits
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 133 days ago
sha256:7f9e2ef5286aedad9c1e6011b4c46ca27f39dbdad6e3409357e36b26e46b3b7c docs: docstring sprint for-each-ref→hotspots — idiomatic ru… Sonnet 4.6 patch 139 days ago