test_muse_sync_status.py python
137 lines 4.8 KB
Raw
sha256:0e9549ec7b463911bc08b7d586dc320b1ac9b1f5c943ee7e3865dcc6cb0f6f83 chore(governance): sync handover+roadmap to 84db8c8 (drift:… Human 1 day ago
1 """Integration tests: adapters surface muse_dirty/git_dirty; status wires the gate (§KH2.5)."""
2
3 from __future__ import annotations
4
5 import json
6 from pathlib import Path
7
8 from adapters.errors import ReadError
9 from tests.support import (
10 FIXTURES,
11 adapter_for,
12 make_runner,
13 ok,
14 run_cli,
15 seed_governance_freshness,
16 seed_muse_substrate,
17 )
18
19
20 def _init(tmp_path: Path, runner, config_name: str) -> None:
21 assert (
22 run_cli(
23 ["init", "--from-config", str(FIXTURES / config_name), "--non-interactive"],
24 cwd=tmp_path,
25 runner=runner,
26 )
27 == 0
28 )
29 seed_governance_freshness(tmp_path)
30
31
32 def test_mirror_adapter_populates_both_dirty_fields(muse_git_mirror_config, repo_root: Path) -> None:
33 root = str(repo_root.resolve())
34 runner = make_runner(
35 {
36 f"muse -C {root} rev-parse --abbrev-ref HEAD": ok("main"),
37 f"muse -C {root} status --json": ok('{"dirty": true}'),
38 "git rev-parse --abbrev-ref HEAD": ok("main"),
39 "git status --porcelain": ok(""),
40 }
41 )
42 adapter = adapter_for(muse_git_mirror_config, repo_root, runner)
43 status = adapter.status()
44 assert not isinstance(status, ReadError)
45 assert status.muse_dirty is True
46 assert status.git_dirty is False
47 assert status.dirty is True # combined flag unchanged in meaning
48
49
50 def test_muse_only_adapter_leaves_git_dirty_none(muse_only_config, repo_root: Path) -> None:
51 root = str(repo_root.resolve())
52 runner = make_runner(
53 {
54 f"muse -C {root} branch --show-current": ok("main"),
55 f"muse -C {root} status --porcelain": ok(""),
56 }
57 )
58 adapter = adapter_for(muse_only_config, repo_root, runner)
59 status = adapter.status()
60 assert not isinstance(status, ReadError)
61 assert status.muse_dirty is False
62 assert status.git_dirty is None
63
64
65 def test_git_only_adapter_leaves_muse_dirty_none(git_only_config, repo_root: Path) -> None:
66 runner = make_runner(
67 {
68 "git rev-parse --abbrev-ref HEAD": ok("main"),
69 "git status --porcelain": ok(" M file"),
70 }
71 )
72 adapter = adapter_for(git_only_config, repo_root, runner)
73 status = adapter.status()
74 assert not isinstance(status, ReadError)
75 assert status.muse_dirty is None
76 assert status.git_dirty is True
77
78
79 def test_status_exit_code_2_when_muse_pending(tmp_path: Path) -> None:
80 root = str(tmp_path.resolve())
81 tip = "cafebabe"
82 runner = make_runner(
83 {
84 f"muse -C {root} rev-parse --abbrev-ref HEAD": ok("main"),
85 f"muse -C {root} status --json": ok('{"dirty": true}'),
86 "git rev-parse --abbrev-ref HEAD": ok("main"),
87 "git status --porcelain": ok(""),
88 "git rev-parse origin/main": ok(tip),
89 f"muse -C {root} rev-parse main": ok(tip),
90 }
91 )
92 _init(tmp_path, runner, "config-muse-git-mirror.yaml")
93 seed_muse_substrate(tmp_path)
94 code = run_cli(["status", "--json", "--exit-code"], cwd=tmp_path, runner=runner, json_mode=True)
95 assert code == 2
96
97
98 def test_status_exit_code_0_when_muse_synced(tmp_path: Path) -> None:
99 root = str(tmp_path.resolve())
100 tip = "cafebabe"
101 runner = make_runner(
102 {
103 f"muse -C {root} rev-parse --abbrev-ref HEAD": ok("main"),
104 f"muse -C {root} status --json": ok('{"dirty": false}'),
105 "git rev-parse --abbrev-ref HEAD": ok("main"),
106 "git status --porcelain": ok(""),
107 "git rev-parse origin/main": ok(tip),
108 f"muse -C {root} rev-parse main": ok(tip),
109 }
110 )
111 _init(tmp_path, runner, "config-muse-git-mirror.yaml")
112 seed_muse_substrate(tmp_path)
113 code = run_cli(["status", "--json", "--exit-code"], cwd=tmp_path, runner=runner, json_mode=True)
114 assert code == 0
115
116
117 def test_status_json_payload_reports_muse_sync_state(tmp_path: Path, capsys) -> None:
118 root = str(tmp_path.resolve())
119 tip = "cafebabe"
120 runner = make_runner(
121 {
122 f"muse -C {root} rev-parse --abbrev-ref HEAD": ok("main"),
123 f"muse -C {root} status --json": ok('{"dirty": true}'),
124 "git rev-parse --abbrev-ref HEAD": ok("main"),
125 "git status --porcelain": ok(""),
126 "git rev-parse origin/main": ok(tip),
127 f"muse -C {root} rev-parse main": ok(tip),
128 }
129 )
130 _init(tmp_path, runner, "config-muse-git-mirror.yaml")
131 seed_muse_substrate(tmp_path)
132 capsys.readouterr() # discard `init` output before capturing the status payload
133 run_cli(["status", "--json"], cwd=tmp_path, runner=runner, json_mode=True)
134 payload = json.loads(capsys.readouterr().out)
135 assert payload["muse_sync"]["state"] == "pending"
136 assert payload["muse_sync"]["ok"] is False
137 assert "muse commit" in payload["muse_sync"]["remediation"]
File History 1 commit
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor 1 day ago