"""TDD — IdentityPlugin.snapshot(). Covers: - Snapshot from a working-tree directory (pathlib.Path path) - Snapshot from an in-memory SnapshotManifest (pass-through path) - Files tracked: identities/*.json and relationships/*.json only - Unknown file types in .identity/ are ignored - Two calls on the same tree produce identical manifests (stability) - Content hashes change when file content changes - domain field is always "identity" """ from __future__ import annotations import json import pathlib import pytest from muse.plugins.identity.plugin import IdentityPlugin from muse.plugins.identity.records import ( IdentityRecord, RelationshipRecord, SignatureRecord, identity_path, record_to_bytes, relationship_path, ) @pytest.fixture def plugin() -> IdentityPlugin: return IdentityPlugin() def write_identity(root: pathlib.Path, record: IdentityRecord) -> None: path = root / identity_path(record["handle"]) path.parent.mkdir(parents=True, exist_ok=True) path.write_bytes(record_to_bytes(record)) def write_relationship(root: pathlib.Path, record: RelationshipRecord) -> None: path = root / relationship_path( record["from_handle"], record["edge_type"], record["to_handle"] ) path.parent.mkdir(parents=True, exist_ok=True) path.write_bytes(record_to_bytes(record)) def make_human(handle: str) -> IdentityRecord: return IdentityRecord( handle=handle, type="human", pubkey="ed25519:AAAA", quorum=None, registered_at="2026-04-21T00:00:00Z", metadata={}, ) def make_org(handle: str, quorum: int = 1) -> IdentityRecord: return IdentityRecord( handle=handle, type="org", pubkey=None, quorum=quorum, registered_at="2026-04-21T00:00:00Z", metadata={}, ) def make_spawns(frm: str, to: str) -> RelationshipRecord: return RelationshipRecord( from_handle=frm, to_handle=to, edge_type="spawns", weight=None, authorized_by=[], ) def make_member_of(frm: str, to: str, weight: str = "1") -> RelationshipRecord: return RelationshipRecord( from_handle=frm, to_handle=to, edge_type="member_of", weight=weight, authorized_by=[], ) # ── directory (Path) input ──────────────────────────────────────────────────── class TestSnapshotFromDirectory: def test_empty_tree_produces_empty_manifest(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: snap = plugin.snapshot(tmp_path) assert snap["files"] == {} assert snap["domain"] == "identity" def test_single_identity_file_appears(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: write_identity(tmp_path, make_human("gabriel")) snap = plugin.snapshot(tmp_path) assert "identities/gabriel.json" in snap["files"] def test_multiple_identities_all_appear(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: for h in ("gabriel", "alice", "musehub-org"): write_identity(tmp_path, make_human(h)) snap = plugin.snapshot(tmp_path) assert "identities/gabriel.json" in snap["files"] assert "identities/alice.json" in snap["files"] assert "identities/musehub-org.json" in snap["files"] def test_relationship_file_appears(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: write_relationship(tmp_path, make_spawns("gabriel", "claude-code")) snap = plugin.snapshot(tmp_path) assert "relationships/gabriel--spawns--claude-code.json" in snap["files"] def test_unknown_extension_ignored(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: (tmp_path / "README.md").write_text("ignored") (tmp_path / "identities").mkdir() (tmp_path / "identities" / "stray.txt").write_text("ignored") snap = plugin.snapshot(tmp_path) assert snap["files"] == {} def test_stability_two_calls(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: write_identity(tmp_path, make_human("gabriel")) write_relationship(tmp_path, make_spawns("gabriel", "bot")) assert plugin.snapshot(tmp_path) == plugin.snapshot(tmp_path) def test_content_hash_changes_on_edit(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: rec = make_human("gabriel") write_identity(tmp_path, rec) h1 = plugin.snapshot(tmp_path)["files"]["identities/gabriel.json"] rec2 = IdentityRecord(**{**rec, "pubkey": "ed25519:ZZZZ"}) write_identity(tmp_path, rec2) h2 = plugin.snapshot(tmp_path)["files"]["identities/gabriel.json"] assert h1 != h2 def test_domain_field_is_identity(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None: assert plugin.snapshot(tmp_path)["domain"] == "identity" # ── in-memory (SnapshotManifest) input ─────────────────────────────────────── class TestSnapshotFromManifest: def test_pass_through_manifest(self, plugin: IdentityPlugin) -> None: from muse.domain import SnapshotManifest manifest = SnapshotManifest( files={"identities/alice.json": "sha256:abc"}, domain="identity", directories=[], ) result = plugin.snapshot(manifest) assert result is manifest def test_pass_through_preserves_all_keys(self, plugin: IdentityPlugin) -> None: from muse.domain import SnapshotManifest manifest = SnapshotManifest( files={"relationships/a--spawns--b.json": "sha256:def"}, domain="identity", directories=[], ) result = plugin.snapshot(manifest) assert result["files"] == manifest["files"] assert result["domain"] == "identity"