test_muse_git_mirror.py python
160 lines 5.9 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Unit tests for muse+git-mirror adapter fail-closed branches."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 from adapters.errors import ReadError
8 from tests.support import adapter_for, fail, make_runner, ok
9
10
11 def _bridge_toml(
12 from_sha: str = "aaa111",
13 export_sha: str = "bbb222",
14 *,
15 muse_commit_id: str | None = "sha256:exportmuse",
16 ) -> str:
17 export_muse = ""
18 if muse_commit_id is not None:
19 export_muse = f'muse_commit_id = "{muse_commit_id}"\n'
20 return f"""[last_import]
21 git_sha = "{from_sha}"
22
23 [last_export]
24 {export_muse}git_sha = "{export_sha}"
25 """
26
27
28 def test_status_reads_both_histories(muse_git_mirror_config, repo_root) -> None:
29 root = str(repo_root)
30 runner = make_runner(
31 {
32 f"muse -C {root} rev-parse --abbrev-ref HEAD": ok("main"),
33 f"muse -C {root} status --json": ok('{"dirty": false}'),
34 f"muse -C {root} status --porcelain": ok(""),
35 "git rev-parse": ok("main"),
36 "git status": ok(" M file"),
37 }
38 )
39 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
40 result = adapter.status()
41 assert result.dirty is True
42 assert "sd-14" in result.notes[2]
43
44
45 def test_read_canonical_anchor_from_bridge(muse_git_mirror_config, repo_root) -> None:
46 bridge = repo_root / ".muse"
47 bridge.mkdir(parents=True)
48 (bridge / "git-bridge.toml").write_text(_bridge_toml(), encoding="utf-8")
49 adapter = adapter_for(muse_git_mirror_config, repo_root, make_runner({}))
50 result = adapter.read_canonical_anchor()
51 assert result.anchor_sha == "sha256:exportmuse"
52 assert result.source.endswith("muse_commit_id")
53 assert result.anchor_sha != "bbb222"
54
55
56 def test_read_canonical_anchor_missing_muse_commit_id(muse_git_mirror_config, repo_root) -> None:
57 bridge = repo_root / ".muse"
58 bridge.mkdir(parents=True)
59 (bridge / "git-bridge.toml").write_text(
60 _bridge_toml(muse_commit_id=None), encoding="utf-8"
61 )
62 adapter = adapter_for(muse_git_mirror_config, repo_root, make_runner({}))
63 result = adapter.read_canonical_anchor()
64 assert isinstance(result, ReadError)
65 assert "muse_commit_id" in str(result)
66
67
68 def test_read_canonical_anchor_fails_without_bridge(muse_git_mirror_config, repo_root) -> None:
69 runner = make_runner({"git rev-parse": fail("unknown ref")})
70 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
71 result = adapter.read_canonical_anchor()
72 assert isinstance(result, ReadError)
73
74
75 def test_realign_dry_run_counts_commits(muse_git_mirror_config, repo_root) -> None:
76 bridge = repo_root / ".muse"
77 bridge.mkdir(parents=True)
78 (bridge / "git-bridge.toml").write_text(_bridge_toml(from_sha="aaa"), encoding="utf-8")
79 runner = make_runner(
80 {
81 "git rev-parse": ok("ccc333"),
82 "git rev-list": ok("3"),
83 }
84 )
85 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
86 result = adapter.realign(dry_run=True, max_commits=50)
87 assert result.would_import == 3
88 assert result.applied is False
89 assert result.reason == "dry-run"
90
91
92 def test_realign_refuses_over_max_commits(muse_git_mirror_config, repo_root) -> None:
93 bridge = repo_root / ".muse"
94 bridge.mkdir(parents=True)
95 (bridge / "git-bridge.toml").write_text(_bridge_toml(), encoding="utf-8")
96 runner = make_runner(
97 {
98 "git rev-parse": ok("ccc333"),
99 "git rev-list": ok("99"),
100 }
101 )
102 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
103 result = adapter.realign(dry_run=False, max_commits=50)
104 assert result.would_import == 99
105 assert result.applied is False
106 assert "max_commits" in (result.reason or "")
107
108
109 def test_realign_apply_invokes_git_import(muse_git_mirror_config, repo_root) -> None:
110 bridge = repo_root / ".muse"
111 bridge.mkdir(parents=True)
112 (bridge / "git-bridge.toml").write_text(_bridge_toml(), encoding="utf-8")
113 root = str(repo_root)
114 runner = make_runner(
115 {
116 "git rev-parse": ok("ccc333"),
117 "git rev-list": ok("2"),
118 f"muse -C {root} bridge": ok("imported"),
119 }
120 )
121 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
122 result = adapter.realign(dry_run=False, max_commits=50)
123 assert result.applied is True
124 assert any("git-import" in call[0] for call in runner.calls)
125
126
127 def test_mirror_dry_run_reports_delta(muse_git_mirror_config, repo_root) -> None:
128 root = str(repo_root)
129 runner = make_runner({f"muse -C {root} bridge": ok("2 commits ahead")})
130 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
131 result = adapter.mirror(dry_run=True)
132 assert result.diff_summary == "2 commits ahead"
133 assert result.pushed is False
134
135
136 def test_mirror_requires_operator_before_push(muse_git_mirror_config, repo_root) -> None:
137 root = str(repo_root)
138 runner = make_runner({f"muse -C {root} bridge": ok("ready")})
139 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
140 result = adapter.mirror(dry_run=False)
141 assert result.pushed is False
142 assert result.reason == "operator-authorization-required"
143
144
145 def test_commit_feature_uses_rev_parse_for_branch_probe(muse_git_mirror_config, repo_root) -> None:
146 """muse 0.2.0rc15 lacks ``muse log --format=%H``; use ``muse rev-parse`` (returns plain SHA on success)."""
147 root = str(repo_root)
148 runner = make_runner(
149 {
150 f"muse -C {root} checkout": ok(""),
151 f"muse -C {root} rev-parse --abbrev-ref HEAD": ok("feat/k7"),
152 f"muse -C {root} commit": ok(""),
153 f"muse -C {root} rev-parse HEAD": ok('sha256:abc'),
154 }
155 )
156 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
157 result = adapter.commit_feature(branch="feat/k7", message="test", paths=[])
158 assert result.committed is True
159 assert any("rev-parse --abbrev-ref HEAD" in call[0] for call in runner.calls)
160 assert not any("log -1 --format" in call[0] for call in runner.calls)
File History 2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 52 days ago