test_upgrade_regime_integrity.py file-level

at main · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:6 fix(ISR): default require_independent_second_reviewer to require Opera… · aaronrene · Sep 2, 2026
1 """Data-integrity — Track O / O3 upgrade-regime (§O2.9 data-integrity)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 from adapters.config import load_config
8 from cli.digest import sha256_hex
9 from cli.footprint import MUSE_BRIDGE_DEPLOY_DEST
10 from cli.kit_root import kit_root
11 from cli.version_lock import read_version_lock
12 from tests.support import (
13 FIXTURES,
14 make_runner,
15 muse_mirror_status_runner,
16 muse_status_runner,
17 ok,
18 run_cli,
19 seed_muse_substrate,
20 )
21
22
23 def _init_muse_only(tmp_path: Path) -> None:
24 seed_muse_substrate(tmp_path)
25 assert (
26 run_cli(
27 [
28 "init",
29 "--from-config",
30 str(FIXTURES / "config-muse-only.yaml"),
31 "--non-interactive",
32 ],
33 cwd=tmp_path,
34 kit=kit_root(),
35 runner=muse_status_runner(tmp_path),
36 )
37 == 0
38 )
39
40
41 def _runner(tmp_path: Path):
42 base = muse_mirror_status_runner(tmp_path)
43 responses = dict(base.responses)
44 responses["git remote get-url origin"] = ok("[email protected]:o/r.git")
45 return make_runner(responses)
46
47
48 def test_dry_run_leaves_tree_unchanged(tmp_path: Path) -> None:
49 _init_muse_only(tmp_path)
50 before_cfg = (tmp_path / ".overseer" / "config.yaml").read_bytes()
51 before_lock = (tmp_path / ".overseer" / "version.lock").read_bytes()
52 code = run_cli(
53 ["upgrade-regime", "--from", "muse-only", "--to", "muse+git-mirror", "--dry-run"],
54 cwd=tmp_path,
55 kit=kit_root(),
56 runner=_runner(tmp_path),
57 )
58 assert code == 0
59 assert (tmp_path / ".overseer" / "config.yaml").read_bytes() == before_cfg
60 assert (tmp_path / ".overseer" / "version.lock").read_bytes() == before_lock
61 assert not (tmp_path / MUSE_BRIDGE_DEPLOY_DEST).exists()
62
63
64 def test_conflict_preserves_pre_conflict_bytes(tmp_path: Path) -> None:
65 _init_muse_only(tmp_path)
66 scripts = tmp_path / "scripts"
67 scripts.mkdir(parents=True)
68 hand = b"#!/bin/bash\necho preserved-hand-tuned\n"
69 (scripts / "muse-bridge-deploy.sh").write_bytes(hand)
70 code = run_cli(
71 ["upgrade-regime", "--from", "muse-only", "--to", "muse+git-mirror", "--apply"],
72 cwd=tmp_path,
73 kit=kit_root(),
74 runner=_runner(tmp_path),
75 )
76 assert code == 4
77 assert (scripts / "muse-bridge-deploy.sh").read_bytes() == hand
78
79
80 def test_successful_apply_twice_stable_lock_digest(tmp_path: Path) -> None:
81 _init_muse_only(tmp_path)
82 runner = _runner(tmp_path)
83 assert (
84 run_cli(
85 ["upgrade-regime", "--from", "muse-only", "--to", "muse+git-mirror", "--apply"],
86 cwd=tmp_path,
87 kit=kit_root(),
88 runner=runner,
89 )
90 == 0
91 )
92 lock1 = read_version_lock(tmp_path / ".overseer" / "version.lock")
93 digest1 = lock1.footprint_digest
94 assert (
95 run_cli(
96 ["upgrade-regime", "--from", "muse-only", "--to", "muse+git-mirror", "--apply"],
97 cwd=tmp_path,
98 kit=kit_root(),
99 runner=runner,
100 )
101 == 0
102 )
103 lock2 = read_version_lock(tmp_path / ".overseer" / "version.lock")
104 assert lock2.footprint_digest == digest1
105 # Config still muse+git-mirror
106 assert load_config(tmp_path / ".overseer" / "config.yaml").vcs.regime == "muse+git-mirror"
107 # Bridge script digest stable
108 script = (tmp_path / MUSE_BRIDGE_DEPLOY_DEST).read_bytes()
109 assert sha256_hex(script)
110
111
112 def test_induced_mid_write_failure_no_partial_lock_advance(tmp_path: Path) -> None:
113 """§O2.9: no partial lock advance on induced mid-write failure during C3 seed."""
114 from unittest.mock import patch
115
116 from cli.atomic import WriteFailure
117 from cli.footprint_writes import write_footprint_bytes as real_write
118
119 _init_muse_only(tmp_path)
120 # Advance to muse+git-mirror config only (C2) via a normal apply first would seed;
121 # instead: apply once, then delete one bridge file + induce sync write failure on repair.
122 assert (
123 run_cli(
124 ["upgrade-regime", "--from", "muse-only", "--to", "muse+git-mirror", "--apply"],
125 cwd=tmp_path,
126 kit=kit_root(),
127 runner=_runner(tmp_path),
128 )
129 == 0
130 )
131 before = read_version_lock(tmp_path / ".overseer" / "version.lock")
132 (tmp_path / MUSE_BRIDGE_DEPLOY_DEST).unlink()
133 calls = {"n": 0}
134
135 def flaky_write(path: Path, data: bytes, *, destination: str | None = None) -> None:
136 calls["n"] += 1
137 if destination == MUSE_BRIDGE_DEPLOY_DEST or path.name == "muse-bridge-deploy.sh":
138 raise WriteFailure(path, OSError("simulated mid-write failure"))
139 if destination is not None:
140 real_write(path, data, destination=destination)
141 else:
142 real_write(path, data, destination=str(path))
143
144 with patch("cli.commands.sync.write_footprint_bytes", side_effect=flaky_write):
145 code = run_cli(
146 ["upgrade-regime", "--from", "muse-only", "--to", "muse+git-mirror", "--apply"],
147 cwd=tmp_path,
148 kit=kit_root(),
149 runner=_runner(tmp_path),
150 )
151 assert code == 5
152 after = read_version_lock(tmp_path / ".overseer" / "version.lock")
153 assert after.footprint_digest == before.footprint_digest
154 assert not (tmp_path / MUSE_BRIDGE_DEPLOY_DEST).exists()