"""Tests for the GitShardAdapter read path + profile (SHARD-WP-0012 T1).""" import subprocess import pytest from shard_wiki.adapters import GitShardAdapter, run_conformance from shard_wiki.model import ( AttachmentMode, History, NotSupported, ProfileError, Substrate, Verb, ) def _git(repo, *args): subprocess.run( ["git", "-C", str(repo), *args], check=True, capture_output=True, env={"GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@t", "GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@t", "PATH": __import__("os").environ.get("PATH", "")}, ) def _repo(tmp_path, files, name="repo"): repo = tmp_path / name repo.mkdir() _git(repo, "init", "--quiet") for rel, text in files.items(): p = repo / rel p.parent.mkdir(parents=True, exist_ok=True) p.write_text(text, encoding="utf-8") _git(repo, "add", rel) _git(repo, "commit", "-m", "seed") return repo def test_keys_are_tracked_md_paths(tmp_path): repo = _repo(tmp_path, {"Home.md": "h", "docs/Guide.md": "g", "ignore.txt": "x"}) adapter = GitShardAdapter("git", repo) assert set(adapter.keys()) == {"Home", "docs/Guide"} # only tracked *.md def test_read_returns_page_with_commit_sha_rev(tmp_path): repo = _repo(tmp_path, {"Home.md": "welcome"}) adapter = GitShardAdapter("git", repo) page = adapter.read("Home") assert page.identity.shard == "git" assert page.body == "welcome" head = subprocess.run( ["git", "-C", str(repo), "rev-parse", "HEAD"], capture_output=True, text=True, check=True ).stdout.strip() assert page.envelope.source_rev == head # source_rev is the commit sha assert page.envelope.lineage == "git-native" def test_read_missing_key_raises(tmp_path): adapter = GitShardAdapter("git", _repo(tmp_path, {"Home.md": "h"})) with pytest.raises(KeyError): adapter.read("Nope") def test_profile_validates_implication_rules(tmp_path): profile = GitShardAdapter("git", _repo(tmp_path, {"Home.md": "h"})).profile() assert profile.substrate is Substrate.GIT assert profile.attachment_mode is AttachmentMode.GIT_IS_STORE assert profile.history is History.GIT_NATIVE # git-is-store ⟹ git-native profile.validate() # raises if the implication rule were violated def test_profile_is_read_only_in_t1(tmp_path): profile = GitShardAdapter("git", _repo(tmp_path, {"Home.md": "h"})).profile() assert profile.supports(Verb.READ) assert not profile.supports(Verb.WRITE) def test_conformance_read_path_passes(tmp_path): adapter = GitShardAdapter("git", _repo(tmp_path, {"Home.md": "h", "Other.md": "o"})) report = run_conformance(adapter) assert report.ok, report.diff() def test_unclaimed_write_raises_not_supported(tmp_path): adapter = GitShardAdapter("git", _repo(tmp_path, {"Home.md": "h"})) with pytest.raises(NotSupported): adapter.write("Home", "new") # read-only: honest absence def test_empty_repo_has_no_keys(tmp_path): repo = tmp_path / "empty" repo.mkdir() _git(repo, "init", "--quiet") adapter = GitShardAdapter("git", repo) assert list(adapter.keys()) == [] def test_bad_profile_combo_is_rejected(): # Sanity: the implication rule that backs the git profile actually bites when violated. from shard_wiki.model import ( AccessGrant, Addressing, CapabilityProfile, ContentOpacity, MergeModel, NativeQuery, OperationalEnvelope, Translation, WriteGranularity, ) from shard_wiki.provenance import Liveness with pytest.raises(ProfileError): CapabilityProfile( substrate=Substrate.FILES, # not git, but claims git-is-store attachment_mode=AttachmentMode.GIT_IS_STORE, write_granularity=WriteGranularity.NONE, content_opacity=ContentOpacity.TRANSPARENT, operational_envelope=OperationalEnvelope.LOCAL_UNBOUNDED, access_grant=AccessGrant.OPEN, liveness=Liveness.STATIC, history=History.NONE, merge_model=MergeModel.NONE, addressing=Addressing.PATH, native_query=NativeQuery.NONE, translation=Translation.NATIVE, supported_verbs=frozenset({Verb.READ}), ).validate()