gabriel / muse public
test_core_shallow.py python
245 lines 9.2 KB
Raw
sha256:f8e686793bb93114c2923d0d294162d13b4e6f4d57ae0f6cbc1e0d493e80f965 fix: ls-remote signing identity uses resolved remote URL Sonnet 4.6 patch 11 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 from muse.core.paths import muse_dir
31
32
33 # ---------------------------------------------------------------------------
34 # Helpers
35 # ---------------------------------------------------------------------------
36
37 def _init_repo(path: pathlib.Path) -> pathlib.Path:
38 muse = muse_dir(path)
39 for d in ("commits", "snapshots", "objects", "refs/heads"):
40 (muse / d).mkdir(parents=True, exist_ok=True)
41 (muse / "HEAD").write_text("ref: refs/heads/main")
42 (muse / "repo.json").write_text(json.dumps({"repo_id": "shallow-test", "domain": "code"}))
43 return path
44
45
46 _CID_A = long_id("a" * 64)
47 _CID_B = long_id("b" * 64)
48 _CID_C = long_id("c" * 64)
49
50
51 # ---------------------------------------------------------------------------
52 # shallow_path
53 # ---------------------------------------------------------------------------
54
55 class TestShallowPath:
56 def test_returns_path_under_muse(self, tmp_path: pathlib.Path) -> None:
57 p = shallow_path(tmp_path)
58 assert p == muse_dir(tmp_path) / "shallow"
59
60 def test_does_not_create_file(self, tmp_path: pathlib.Path) -> None:
61 shallow_path(tmp_path)
62 assert not (muse_dir(tmp_path) / "shallow").exists()
63
64
65 # ---------------------------------------------------------------------------
66 # read_shallow
67 # ---------------------------------------------------------------------------
68
69 class TestReadShallow:
70 def test_returns_empty_frozenset_when_no_file(self, tmp_path: pathlib.Path) -> None:
71 repo = _init_repo(tmp_path)
72 result = read_shallow(repo)
73 assert result == frozenset()
74
75 def test_returns_empty_frozenset_for_empty_file(self, tmp_path: pathlib.Path) -> None:
76 repo = _init_repo(tmp_path)
77 (muse_dir(repo) / "shallow").write_text("")
78 assert read_shallow(repo) == frozenset()
79
80 def test_reads_single_commit(self, tmp_path: pathlib.Path) -> None:
81 repo = _init_repo(tmp_path)
82 (muse_dir(repo) / "shallow").write_text(_CID_A + "\n")
83 assert read_shallow(repo) == frozenset({_CID_A})
84
85 def test_reads_multiple_commits(self, tmp_path: pathlib.Path) -> None:
86 repo = _init_repo(tmp_path)
87 (muse_dir(repo) / "shallow").write_text(f"{_CID_A}\n{_CID_B}\n{_CID_C}\n")
88 assert read_shallow(repo) == frozenset({_CID_A, _CID_B, _CID_C})
89
90 def test_ignores_blank_lines(self, tmp_path: pathlib.Path) -> None:
91 repo = _init_repo(tmp_path)
92 (muse_dir(repo) / "shallow").write_text(f"{_CID_A}\n\n{_CID_B}\n")
93 assert read_shallow(repo) == frozenset({_CID_A, _CID_B})
94
95 def test_strips_whitespace(self, tmp_path: pathlib.Path) -> None:
96 repo = _init_repo(tmp_path)
97 (muse_dir(repo) / "shallow").write_text(f" {_CID_A} \n")
98 assert _CID_A in read_shallow(repo)
99
100 def test_ignores_comment_lines(self, tmp_path: pathlib.Path) -> None:
101 repo = _init_repo(tmp_path)
102 (muse_dir(repo) / "shallow").write_text(f"# generated by muse\n{_CID_A}\n")
103 assert read_shallow(repo) == frozenset({_CID_A})
104
105 def test_returns_frozenset_not_set(self, tmp_path: pathlib.Path) -> None:
106 repo = _init_repo(tmp_path)
107 (muse_dir(repo) / "shallow").write_text(_CID_A + "\n")
108 result = read_shallow(repo)
109 assert isinstance(result, frozenset)
110
111 def test_deduplicates(self, tmp_path: pathlib.Path) -> None:
112 repo = _init_repo(tmp_path)
113 (muse_dir(repo) / "shallow").write_text(f"{_CID_A}\n{_CID_A}\n")
114 assert read_shallow(repo) == frozenset({_CID_A})
115
116
117 # ---------------------------------------------------------------------------
118 # write_shallow
119 # ---------------------------------------------------------------------------
120
121 class TestWriteShallow:
122 def test_writes_empty_set_creates_empty_file(self, tmp_path: pathlib.Path) -> None:
123 repo = _init_repo(tmp_path)
124 write_shallow(repo, set())
125 p = muse_dir(repo) / "shallow"
126 assert p.exists()
127 assert p.read_text().strip() == ""
128
129 def test_writes_single_commit(self, tmp_path: pathlib.Path) -> None:
130 repo = _init_repo(tmp_path)
131 write_shallow(repo, {_CID_A})
132 result = read_shallow(repo)
133 assert result == frozenset({_CID_A})
134
135 def test_writes_multiple_commits_sorted(self, tmp_path: pathlib.Path) -> None:
136 repo = _init_repo(tmp_path)
137 write_shallow(repo, {_CID_C, _CID_A, _CID_B})
138 lines = [l for l in (muse_dir(repo) / "shallow").read_text().splitlines() if l.strip()]
139 assert lines == sorted(lines)
140
141 def test_round_trips(self, tmp_path: pathlib.Path) -> None:
142 repo = _init_repo(tmp_path)
143 original = {_CID_A, _CID_B, _CID_C}
144 write_shallow(repo, original)
145 assert read_shallow(repo) == frozenset(original)
146
147 def test_overwrites_existing(self, tmp_path: pathlib.Path) -> None:
148 repo = _init_repo(tmp_path)
149 write_shallow(repo, {_CID_A, _CID_B})
150 write_shallow(repo, {_CID_C})
151 assert read_shallow(repo) == frozenset({_CID_C})
152
153 def test_creates_muse_dir_if_absent(self, tmp_path: pathlib.Path) -> None:
154 # .muse dir may not exist yet
155 write_shallow(tmp_path, {_CID_A})
156 assert (muse_dir(tmp_path) / "shallow").exists()
157
158
159 # ---------------------------------------------------------------------------
160 # add_shallow
161 # ---------------------------------------------------------------------------
162
163 class TestAddShallow:
164 def test_adds_to_empty(self, tmp_path: pathlib.Path) -> None:
165 repo = _init_repo(tmp_path)
166 add_shallow(repo, _CID_A)
167 assert read_shallow(repo) == frozenset({_CID_A})
168
169 def test_adds_to_existing(self, tmp_path: pathlib.Path) -> None:
170 repo = _init_repo(tmp_path)
171 write_shallow(repo, {_CID_A})
172 add_shallow(repo, _CID_B)
173 assert read_shallow(repo) == frozenset({_CID_A, _CID_B})
174
175 def test_add_is_idempotent(self, tmp_path: pathlib.Path) -> None:
176 repo = _init_repo(tmp_path)
177 add_shallow(repo, _CID_A)
178 add_shallow(repo, _CID_A)
179 assert read_shallow(repo) == frozenset({_CID_A})
180
181 def test_add_when_no_file_exists(self, tmp_path: pathlib.Path) -> None:
182 repo = _init_repo(tmp_path)
183 assert not (muse_dir(repo) / "shallow").exists()
184 add_shallow(repo, _CID_A)
185 assert _CID_A in read_shallow(repo)
186
187
188 # ---------------------------------------------------------------------------
189 # remove_shallow
190 # ---------------------------------------------------------------------------
191
192 class TestRemoveShallow:
193 def test_removes_existing_entry(self, tmp_path: pathlib.Path) -> None:
194 repo = _init_repo(tmp_path)
195 write_shallow(repo, {_CID_A, _CID_B})
196 remove_shallow(repo, _CID_A)
197 assert read_shallow(repo) == frozenset({_CID_B})
198
199 def test_remove_nonexistent_is_noop(self, tmp_path: pathlib.Path) -> None:
200 repo = _init_repo(tmp_path)
201 write_shallow(repo, {_CID_A})
202 remove_shallow(repo, _CID_C) # not present
203 assert read_shallow(repo) == frozenset({_CID_A})
204
205 def test_remove_from_empty_is_noop(self, tmp_path: pathlib.Path) -> None:
206 repo = _init_repo(tmp_path)
207 remove_shallow(repo, _CID_A) # no file
208 assert read_shallow(repo) == frozenset()
209
210 def test_remove_last_entry_leaves_empty(self, tmp_path: pathlib.Path) -> None:
211 repo = _init_repo(tmp_path)
212 write_shallow(repo, {_CID_A})
213 remove_shallow(repo, _CID_A)
214 assert read_shallow(repo) == frozenset()
215
216
217 # ---------------------------------------------------------------------------
218 # is_shallow
219 # ---------------------------------------------------------------------------
220
221 class TestIsShallow:
222 def test_false_when_no_file(self, tmp_path: pathlib.Path) -> None:
223 repo = _init_repo(tmp_path)
224 assert is_shallow(repo) is False
225
226 def test_false_when_empty_file(self, tmp_path: pathlib.Path) -> None:
227 repo = _init_repo(tmp_path)
228 (muse_dir(repo) / "shallow").write_text("")
229 assert is_shallow(repo) is False
230
231 def test_true_when_has_entry(self, tmp_path: pathlib.Path) -> None:
232 repo = _init_repo(tmp_path)
233 write_shallow(repo, {_CID_A})
234 assert is_shallow(repo) is True
235
236 def test_true_with_multiple_entries(self, tmp_path: pathlib.Path) -> None:
237 repo = _init_repo(tmp_path)
238 write_shallow(repo, {_CID_A, _CID_B})
239 assert is_shallow(repo) is True
240
241 def test_false_after_removing_all(self, tmp_path: pathlib.Path) -> None:
242 repo = _init_repo(tmp_path)
243 write_shallow(repo, {_CID_A})
244 remove_shallow(repo, _CID_A)
245 assert is_shallow(repo) is False
File History 1 commit
sha256:f8e686793bb93114c2923d0d294162d13b4e6f4d57ae0f6cbc1e0d493e80f965 fix: ls-remote signing identity uses resolved remote URL Sonnet 4.6 patch 11 days ago