test_gfg_d2_realign_plan.py python
157 lines 5.2 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Integration: realign plan/verify under Muse content-hash tips (§D2F.9)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6 from unittest.mock import MagicMock
7
8 from adapters.types import AnchorResult, HeadResult, RealignResult
9 from tools.governance_hygiene.realign import execute_realign_guard, plan_realign
10 from tools.governance_hygiene.types import DriftReport, VerifiedReads
11
12
13 def _config(tmp_path: Path):
14 from tests.support import load_fixture_config, write_config
15
16 write_config(tmp_path, "config-muse-git-mirror.yaml")
17 return load_fixture_config(tmp_path, "config-muse-git-mirror.yaml")
18
19
20 def _reads(*, r2_muse: str, r1: str = "b" * 40) -> VerifiedReads:
21 return VerifiedReads(
22 regime="muse+git-mirror",
23 r1_github_main_sha=r1,
24 r1_command="git rev-parse origin/main",
25 r2_anchor_sha=r2_muse.lower(),
26 r2_source=".muse/git-bridge.toml:last_export.muse_commit_id",
27 r3_canonical_main_sha=r2_muse.lower(),
28 r3_command="muse rev-parse main",
29 r4_merged_prs=(),
30 r5_branch="main",
31 r5_dirty=False,
32 r5_regime="muse+git-mirror",
33 )
34
35
36 def test_plan_realign_skips_when_d2_aligned_despite_git_sha_mismatch(tmp_path: Path) -> None:
37 tip = "sha256:" + ("a" * 64)
38 git_sha = "1e734a922a8de5dcac248007b8dfb706c4a0f84e"
39 muse = tmp_path / ".muse"
40 muse.mkdir()
41 (muse / "git-bridge.toml").write_text(
42 f'[last_export]\nmuse_commit_id = "{tip}"\ngit_sha = "{git_sha}"\n',
43 encoding="utf-8",
44 )
45 assert tip != git_sha
46
47 config = _config(tmp_path)
48 adapter = MagicMock()
49 adapter.repo_root = tmp_path
50 drift = DriftReport(
51 d1_handover_vs_git="aligned",
52 d2_anchor_vs_canonical="aligned",
53 d3_queue_vs_merged="aligned",
54 )
55 should, reason = plan_realign(config, adapter, _reads(r2_muse=tip), drift)
56 assert should is False
57 assert "skip" in reason.lower()
58 adapter.runner.run.assert_not_called()
59
60
61 def test_plan_realign_uses_git_sha_for_ancestry_not_muse_id(tmp_path: Path) -> None:
62 tip = "sha256:" + ("c" * 64)
63 git_sha = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
64 main = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
65 muse = tmp_path / ".muse"
66 muse.mkdir()
67 (muse / "git-bridge.toml").write_text(
68 f'[last_export]\nmuse_commit_id = "{tip}"\ngit_sha = "{git_sha}"\n',
69 encoding="utf-8",
70 )
71 config = _config(tmp_path)
72 adapter = MagicMock()
73 adapter.repo_root = tmp_path
74 runner = MagicMock()
75 runner.run.return_value = MagicMock(ok=True)
76 adapter.runner = runner
77
78 drift = DriftReport(
79 d1_handover_vs_git="aligned",
80 d2_anchor_vs_canonical="drifted",
81 d3_queue_vs_merged="aligned",
82 )
83 reads = _reads(r2_muse=tip, r1=main)
84 # Force r3 differ so D2 drifted context is honest
85 reads = VerifiedReads(
86 regime=reads.regime,
87 r1_github_main_sha=main,
88 r1_command=reads.r1_command,
89 r2_anchor_sha=tip.lower(),
90 r2_source=reads.r2_source,
91 r3_canonical_main_sha=("sha256:" + ("d" * 64)).lower(),
92 r3_command=reads.r3_command,
93 r4_merged_prs=(),
94 r5_branch="main",
95 r5_dirty=False,
96 r5_regime="muse+git-mirror",
97 )
98 should, reason = plan_realign(config, adapter, reads, drift)
99 assert should is True
100 assert "superset" in reason
101 cmd = runner.run.call_args[0][0]
102 assert git_sha in cmd
103 assert "sha256:" not in cmd
104
105
106 def test_execute_realign_verify_compares_muse_ids(tmp_path: Path) -> None:
107 tip = "sha256:" + ("e" * 64)
108 git_sha = "ffffffffffffffffffffffffffffffffffffffff"
109 muse = tmp_path / ".muse"
110 muse.mkdir()
111 (muse / "git-bridge.toml").write_text(
112 f'[last_export]\nmuse_commit_id = "{tip}"\ngit_sha = "{git_sha}"\n',
113 encoding="utf-8",
114 )
115 config = _config(tmp_path)
116 adapter = MagicMock()
117 adapter.repo_root = tmp_path
118 runner = MagicMock()
119 runner.run.return_value = MagicMock(ok=True)
120 adapter.runner = runner
121 adapter.realign.return_value = RealignResult(
122 would_import=2,
123 applied=True,
124 from_ref=git_sha,
125 to_ref="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
126 )
127 adapter.read_canonical_anchor.return_value = AnchorResult(
128 anchor_sha=tip, source=".muse/git-bridge.toml:last_export.muse_commit_id"
129 )
130 adapter.read_head.return_value = HeadResult(sha=tip, kind="muse")
131
132 drift = DriftReport(
133 d1_handover_vs_git="aligned",
134 d2_anchor_vs_canonical="drifted",
135 d3_queue_vs_merged="aligned",
136 )
137 reads = VerifiedReads(
138 regime="muse+git-mirror",
139 r1_github_main_sha="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
140 r1_command="git rev-parse origin/main",
141 r2_anchor_sha=("sha256:" + ("0" * 64)).lower(),
142 r2_source=".muse/git-bridge.toml:last_export.muse_commit_id",
143 r3_canonical_main_sha=tip.lower(),
144 r3_command="muse rev-parse main",
145 r4_merged_prs=(),
146 r5_branch="main",
147 r5_dirty=False,
148 r5_regime="muse+git-mirror",
149 )
150 summary, err = execute_realign_guard(
151 config, adapter, reads, drift, dry_run=False
152 )
153 assert err is None
154 assert summary is not None
155 assert "imported" in summary
156 # Verify path must not require git_sha == tip
157 assert git_sha != tip
File History 1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago