generated from coulomb/repo-seed
feat(adapters): GitShardAdapter history adopt + cross-substrate integration (WP-0012 T3)
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>
This commit is contained in:
116
tests/test_git_adapter_integration.py
Normal file
116
tests/test_git_adapter_integration.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user