gabriel / muse public

test_identity_domain_snapshot.py file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 πŸ’₯ blast risk
sha256:4 Merge branch 'dev' into main · gabriel · Jun 17, 2026
1 """TDD β€” IdentityPlugin.snapshot().
2
3 Covers:
4 - Snapshot from a working-tree directory (pathlib.Path path)
5 - Snapshot from an in-memory SnapshotManifest (pass-through path)
6 - Files tracked: identities/*.json and relationships/*.json only
7 - Unknown file types in .identity/ are ignored
8 - Two calls on the same tree produce identical manifests (stability)
9 - Content hashes change when file content changes
10 - domain field is always "identity"
11 """
12 from __future__ import annotations
13
14 import json
15 import pathlib
16
17 import pytest
18
19 from muse.plugins.identity.plugin import IdentityPlugin
20 from muse.plugins.identity.records import (
21 IdentityRecord,
22 RelationshipRecord,
23 SignatureRecord,
24 identity_path,
25 record_to_bytes,
26 relationship_path,
27 )
28
29
30 @pytest.fixture
31 def plugin() -> IdentityPlugin:
32 return IdentityPlugin()
33
34
35 def write_identity(root: pathlib.Path, record: IdentityRecord) -> None:
36 path = root / identity_path(record["handle"])
37 path.parent.mkdir(parents=True, exist_ok=True)
38 path.write_bytes(record_to_bytes(record))
39
40
41 def write_relationship(root: pathlib.Path, record: RelationshipRecord) -> None:
42 path = root / relationship_path(
43 record["from_handle"], record["edge_type"], record["to_handle"]
44 )
45 path.parent.mkdir(parents=True, exist_ok=True)
46 path.write_bytes(record_to_bytes(record))
47
48
49 def make_human(handle: str) -> IdentityRecord:
50 return IdentityRecord(
51 handle=handle, type="human", pubkey="ed25519:AAAA",
52 quorum=None, registered_at="2026-04-21T00:00:00Z", metadata={},
53 )
54
55
56 def make_org(handle: str, quorum: int = 1) -> IdentityRecord:
57 return IdentityRecord(
58 handle=handle, type="org", pubkey=None,
59 quorum=quorum, registered_at="2026-04-21T00:00:00Z", metadata={},
60 )
61
62
63 def make_spawns(frm: str, to: str) -> RelationshipRecord:
64 return RelationshipRecord(
65 from_handle=frm, to_handle=to, edge_type="spawns",
66 weight=None, authorized_by=[],
67 )
68
69
70 def make_member_of(frm: str, to: str, weight: str = "1") -> RelationshipRecord:
71 return RelationshipRecord(
72 from_handle=frm, to_handle=to, edge_type="member_of",
73 weight=weight, authorized_by=[],
74 )
75
76
77 # ── directory (Path) input ────────────────────────────────────────────────────
78
79 class TestSnapshotFromDirectory:
80 def test_empty_tree_produces_empty_manifest(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
81 snap = plugin.snapshot(tmp_path)
82 assert snap["files"] == {}
83 assert snap["domain"] == "identity"
84
85 def test_single_identity_file_appears(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
86 write_identity(tmp_path, make_human("gabriel"))
87 snap = plugin.snapshot(tmp_path)
88 assert "identities/gabriel.json" in snap["files"]
89
90 def test_multiple_identities_all_appear(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
91 for h in ("gabriel", "alice", "musehub-org"):
92 write_identity(tmp_path, make_human(h))
93 snap = plugin.snapshot(tmp_path)
94 assert "identities/gabriel.json" in snap["files"]
95 assert "identities/alice.json" in snap["files"]
96 assert "identities/musehub-org.json" in snap["files"]
97
98 def test_relationship_file_appears(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
99 write_relationship(tmp_path, make_spawns("gabriel", "claude-code"))
100 snap = plugin.snapshot(tmp_path)
101 assert "relationships/gabriel--spawns--claude-code.json" in snap["files"]
102
103 def test_unknown_extension_ignored(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
104 (tmp_path / "README.md").write_text("ignored")
105 (tmp_path / "identities").mkdir()
106 (tmp_path / "identities" / "stray.txt").write_text("ignored")
107 snap = plugin.snapshot(tmp_path)
108 assert snap["files"] == {}
109
110 def test_stability_two_calls(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
111 write_identity(tmp_path, make_human("gabriel"))
112 write_relationship(tmp_path, make_spawns("gabriel", "bot"))
113 assert plugin.snapshot(tmp_path) == plugin.snapshot(tmp_path)
114
115 def test_content_hash_changes_on_edit(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
116 rec = make_human("gabriel")
117 write_identity(tmp_path, rec)
118 h1 = plugin.snapshot(tmp_path)["files"]["identities/gabriel.json"]
119 rec2 = IdentityRecord(**{**rec, "pubkey": "ed25519:ZZZZ"})
120 write_identity(tmp_path, rec2)
121 h2 = plugin.snapshot(tmp_path)["files"]["identities/gabriel.json"]
122 assert h1 != h2
123
124 def test_domain_field_is_identity(self, plugin: IdentityPlugin, tmp_path: pathlib.Path) -> None:
125 assert plugin.snapshot(tmp_path)["domain"] == "identity"
126
127
128 # ── in-memory (SnapshotManifest) input ───────────────────────────────────────
129
130 class TestSnapshotFromManifest:
131 def test_pass_through_manifest(self, plugin: IdentityPlugin) -> None:
132 from muse.domain import SnapshotManifest
133 manifest = SnapshotManifest(
134 files={"identities/alice.json": "sha256:abc"},
135 domain="identity",
136 directories=[],
137 )
138 result = plugin.snapshot(manifest)
139 assert result is manifest
140
141 def test_pass_through_preserves_all_keys(self, plugin: IdentityPlugin) -> None:
142 from muse.domain import SnapshotManifest
143 manifest = SnapshotManifest(
144 files={"relationships/a--spawns--b.json": "sha256:def"},
145 domain="identity",
146 directories=[],
147 )
148 result = plugin.snapshot(manifest)
149 assert result["files"] == manifest["files"]
150 assert result["domain"] == "identity"