generated from coulomb/repo-seed
Adopt git-native history (TSD §A.5): a VERSION-gated history(key) surfaces the commit list for a path (newest-first sha + subject) — declared by every git-IS-store shard, read-only or not. Integration proves the union/overlay/edit machinery works unchanged across folder + git substrates: resolve/chorus span both, edit through a git shard fast-forwards as a commit, apply-under-drift refuses on an external commit (sha drift) without clobbering, and a read-only git target keeps the overlay as a draft. SCOPE updated; WP-0012 done. 196 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
117 lines
4.6 KiB
Python
117 lines
4.6 KiB
Python
"""GitShardAdapter history adopt + cross-substrate integration (SHARD-WP-0012 T3)."""
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
import pytest
|
|
|
|
from shard_wiki.adapters import FolderAdapter, GitShardAdapter
|
|
from shard_wiki.coordination import ApplyStatus
|
|
from shard_wiki.space import InformationSpace
|
|
|
|
_ENV = {
|
|
"GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@t",
|
|
"GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@t",
|
|
"PATH": os.environ.get("PATH", ""),
|
|
}
|
|
|
|
|
|
def _git(repo, *args):
|
|
return subprocess.run(
|
|
["git", "-C", str(repo), *args], check=True, capture_output=True, text=True, env=_ENV
|
|
).stdout.strip()
|
|
|
|
|
|
def _git_repo(tmp_path, files, name="git"):
|
|
repo = tmp_path / name
|
|
repo.mkdir()
|
|
_git(repo, "init", "--quiet")
|
|
for rel, text in files.items():
|
|
(repo / rel).parent.mkdir(parents=True, exist_ok=True)
|
|
(repo / rel).write_text(text, encoding="utf-8")
|
|
_git(repo, "add", rel)
|
|
_git(repo, "commit", "-m", "seed")
|
|
return repo
|
|
|
|
|
|
def _folder(tmp_path, name, files, writable=False):
|
|
root = tmp_path / name
|
|
for rel, text in files.items():
|
|
p = root / rel
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
p.write_text(text, encoding="utf-8")
|
|
return FolderAdapter(name, root, writable=writable)
|
|
|
|
|
|
# -- history adopt -------------------------------------------------------------
|
|
|
|
|
|
def test_history_lists_commits_newest_first(tmp_path):
|
|
repo = _git_repo(tmp_path, {"Home.md": "v1"})
|
|
adapter = GitShardAdapter("git", repo, writable=True)
|
|
adapter.write("Home", "v2")
|
|
history = adapter.history("Home")
|
|
assert len(history) == 2
|
|
assert history[0].message == "write Home.md" # newest first
|
|
assert history[-1].message == "seed"
|
|
assert all(rev.sha for rev in history)
|
|
|
|
|
|
def test_history_unknown_key_raises(tmp_path):
|
|
adapter = GitShardAdapter("git", _git_repo(tmp_path, {"Home.md": "h"}))
|
|
with pytest.raises(KeyError):
|
|
adapter.history("Nope")
|
|
|
|
|
|
# -- cross-substrate integration ----------------------------------------------
|
|
|
|
|
|
def test_resolve_across_git_and_folder(tmp_path):
|
|
space = InformationSpace("space")
|
|
space.attach(GitShardAdapter("git", _git_repo(tmp_path, {"Home.md": "git home"})))
|
|
space.attach(_folder(tmp_path, "notes", {"Daily.md": "folder daily"}))
|
|
assert space.read("Home").body == "git home" # resolved from the git shard
|
|
assert space.read("Daily").body == "folder daily" # resolved from the folder shard
|
|
|
|
|
|
def test_chorus_spans_substrates_with_divergence(tmp_path):
|
|
space = InformationSpace("space")
|
|
space.attach(GitShardAdapter("git", _git_repo(tmp_path, {"Shared.md": "from git"})))
|
|
space.attach(_folder(tmp_path, "notes", {"Shared.md": "from folder"}))
|
|
res = space.resolve("Shared")
|
|
assert {p.body for p in res.pages} == {"from git", "from folder"} # chorus across substrates
|
|
git_page = next(p for p in res.pages if p.identity.shard == "git")
|
|
assert git_page.envelope.divergence # divergence recorded, not erased
|
|
|
|
|
|
def test_edit_through_git_shard_commits(tmp_path):
|
|
repo = _git_repo(tmp_path, {"Home.md": "original"})
|
|
space = InformationSpace("space")
|
|
space.attach(GitShardAdapter("git", repo, writable=True))
|
|
result = space.edit("Home", "edited via overlay")
|
|
assert result.status is ApplyStatus.APPLIED # write-through fast-forward on a git shard
|
|
assert space.read("Home").body == "edited via overlay"
|
|
assert int(_git(repo, "rev-list", "--count", "HEAD")) == 2 # the edit became a commit
|
|
|
|
|
|
def test_apply_under_drift_refuses_on_external_commit(tmp_path):
|
|
repo = _git_repo(tmp_path, {"Home.md": "original"})
|
|
space = InformationSpace("space")
|
|
space.attach(GitShardAdapter("git", repo, writable=True))
|
|
overlay = space.overlay("Home", "my draft") # base_rev = current git sha
|
|
# Another writer commits to the same path → the sha moves underneath the draft.
|
|
(repo / "Home.md").write_text("someone else", encoding="utf-8")
|
|
_git(repo, "add", "Home.md")
|
|
_git(repo, "commit", "-m", "external")
|
|
result = space.apply_overlay(overlay.overlay_id)
|
|
assert result.status is ApplyStatus.REFUSED_DRIFT # never clobber (sha drift detected)
|
|
# The shard itself is untouched — the external commit stands; the draft remains a draft.
|
|
assert space.union.shard("git").read("Home").body == "someone else"
|
|
|
|
|
|
def test_overlay_on_read_only_git_shard_kept_as_draft(tmp_path):
|
|
space = InformationSpace("space")
|
|
space.attach(GitShardAdapter("git", _git_repo(tmp_path, {"Home.md": "ro"}), writable=False))
|
|
result = space.edit("Home", "wanted change")
|
|
assert result.status is ApplyStatus.KEPT_DRAFT # read-only target → overlay retained
|