test_upgrade_regime_unit.py
python
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
2 days ago
| 1 | """Unit tests — Track O / O3 upgrade-regime classifiers and gates (§O2.9 unit).""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | from pathlib import Path |
| 6 | |
| 7 | from adapters.config import load_config |
| 8 | from adapters.runner import CommandResult |
| 9 | from adapters.templating import render_template |
| 10 | from cli.config_gen import load_config_from_dict |
| 11 | from cli.kit_root import kit_root |
| 12 | from cli.main import build_parser |
| 13 | from tests.support import FIXTURES, make_runner, ok, write_config |
| 14 | from tools.upgrade_regime.ceremony import ( |
| 15 | StartState, |
| 16 | build_upgraded_config_dict, |
| 17 | check_g3_deploy_script, |
| 18 | check_g4_deploy_script, |
| 19 | check_g5_deploy_script, |
| 20 | check_g6_deploy_script, |
| 21 | check_g8_git_remote, |
| 22 | classify_start_state, |
| 23 | docs_preserved, |
| 24 | is_silent_regime_only_patch, |
| 25 | required_vcs_complete, |
| 26 | ) |
| 27 | |
| 28 | |
| 29 | def _load_muse_only(): |
| 30 | return load_config(FIXTURES / "config-muse-only.yaml") |
| 31 | |
| 32 | |
| 33 | def test_argparse_frozen_flags() -> None: |
| 34 | parser = build_parser() |
| 35 | args = parser.parse_args( |
| 36 | [ |
| 37 | "upgrade-regime", |
| 38 | "--from", |
| 39 | "muse-only", |
| 40 | "--to", |
| 41 | "muse+git-mirror", |
| 42 | "--dry-run", |
| 43 | "--apply", |
| 44 | "--live-bridge", |
| 45 | "--force", |
| 46 | "-y", |
| 47 | ] |
| 48 | ) |
| 49 | assert args.command == "upgrade-regime" |
| 50 | assert args.from_regime == "muse-only" |
| 51 | assert args.to_regime == "muse+git-mirror" |
| 52 | assert args.dry_run is True |
| 53 | assert args.apply is True |
| 54 | assert args.live_bridge is True |
| 55 | assert args.force is True |
| 56 | assert args.yes is True |
| 57 | |
| 58 | |
| 59 | def test_argparse_rejects_unsupported_from_pair() -> None: |
| 60 | parser = build_parser() |
| 61 | try: |
| 62 | parser.parse_args(["upgrade-regime", "--from", "git-only", "--to", "muse+git-mirror"]) |
| 63 | raise AssertionError("expected SystemExit") |
| 64 | except SystemExit as exc: |
| 65 | assert exc.code == 2 |
| 66 | |
| 67 | |
| 68 | def test_classify_muse_only(tmp_path: Path) -> None: |
| 69 | write_config(tmp_path, "config-muse-only.yaml") |
| 70 | state = classify_start_state(tmp_path, tmp_path / ".overseer" / "config.yaml") |
| 71 | assert state == StartState.MUSE_ONLY |
| 72 | |
| 73 | |
| 74 | def test_classify_missing_config(tmp_path: Path) -> None: |
| 75 | state = classify_start_state(tmp_path, tmp_path / ".overseer" / "config.yaml") |
| 76 | assert state == StartState.MISSING_CONFIG |
| 77 | |
| 78 | |
| 79 | def test_classify_wrong_regime_git_only(tmp_path: Path) -> None: |
| 80 | write_config(tmp_path, "config-git-only.yaml") |
| 81 | state = classify_start_state(tmp_path, tmp_path / ".overseer" / "config.yaml") |
| 82 | assert state == StartState.WRONG_REGIME |
| 83 | |
| 84 | |
| 85 | def test_classify_incomplete_upgrade_without_bridge(tmp_path: Path) -> None: |
| 86 | write_config(tmp_path, "config-muse-git-mirror.yaml") |
| 87 | state = classify_start_state(tmp_path, tmp_path / ".overseer" / "config.yaml") |
| 88 | assert state == StartState.INCOMPLETE_UPGRADE |
| 89 | |
| 90 | |
| 91 | def test_classify_complete_upgrade(tmp_path: Path) -> None: |
| 92 | from tests.support import muse_mirror_status_runner, run_cli, seed_muse_substrate |
| 93 | from cli.kit_root import kit_root |
| 94 | |
| 95 | seed_muse_substrate(tmp_path) |
| 96 | assert ( |
| 97 | run_cli( |
| 98 | [ |
| 99 | "init", |
| 100 | "--from-config", |
| 101 | str(FIXTURES / "config-muse-git-mirror.yaml"), |
| 102 | "--non-interactive", |
| 103 | ], |
| 104 | cwd=tmp_path, |
| 105 | kit=kit_root(), |
| 106 | runner=muse_mirror_status_runner(tmp_path), |
| 107 | ) |
| 108 | == 0 |
| 109 | ) |
| 110 | state = classify_start_state(tmp_path, tmp_path / ".overseer" / "config.yaml") |
| 111 | assert state == StartState.COMPLETE_UPGRADE |
| 112 | |
| 113 | |
| 114 | def test_required_vcs_complete_and_build_preserves_docs() -> None: |
| 115 | before = _load_muse_only() |
| 116 | assert not required_vcs_complete(before) |
| 117 | data = build_upgraded_config_dict(before) |
| 118 | after = load_config_from_dict(data, "proj.yaml") |
| 119 | assert required_vcs_complete(after) |
| 120 | assert docs_preserved(before, after) |
| 121 | assert after.docs.handover == before.docs.handover |
| 122 | assert after.docs.roadmap == before.docs.roadmap |
| 123 | assert after.vcs.git.mirror_branch == "muse-mirror" |
| 124 | assert after.vcs.muse.staging_remote == "staging" |
| 125 | |
| 126 | |
| 127 | def test_silent_regime_only_patch_detector() -> None: |
| 128 | before = _load_muse_only() |
| 129 | silent = { |
| 130 | "vcs": { |
| 131 | "regime": "muse+git-mirror", |
| 132 | "canonical": "muse", |
| 133 | "git": {"remote": "origin", "main_branch": "main", "mirror_branch": None}, |
| 134 | "muse": {"staging_remote": None, "main_branch": "main"}, |
| 135 | } |
| 136 | } |
| 137 | assert is_silent_regime_only_patch(before, silent) is True |
| 138 | good = build_upgraded_config_dict(before) |
| 139 | assert is_silent_regime_only_patch(before, good) is False |
| 140 | |
| 141 | |
| 142 | def test_g3_g6_helpers_on_fixture_script() -> None: |
| 143 | config = load_config(FIXTURES / "config-muse-git-mirror.yaml") |
| 144 | script = render_template( |
| 145 | kit_root() / "templates" / "scripts" / "muse-bridge-deploy.sh.template", |
| 146 | config, |
| 147 | ) |
| 148 | assert check_g3_deploy_script(script).ok |
| 149 | assert check_g4_deploy_script(script).ok |
| 150 | assert check_g5_deploy_script(script).ok |
| 151 | assert check_g6_deploy_script(script).ok |
| 152 | |
| 153 | bad_push = script.replace( |
| 154 | 'push "${GIT_REMOTE}" "${MIRROR_BRANCH}"', |
| 155 | "git push origin main", |
| 156 | ) |
| 157 | assert not check_g4_deploy_script(bad_push).ok |
| 158 | |
| 159 | home = script + "\n# /Users/operator/secret\n" |
| 160 | assert not check_g6_deploy_script(home).ok |
| 161 | |
| 162 | |
| 163 | def test_g8_remote_helper() -> None: |
| 164 | runner = make_runner({"git remote get-url origin": ok("[email protected]:owner/repo.git")}) |
| 165 | assert check_g8_git_remote(Path("/tmp"), "origin", runner).ok |
| 166 | |
| 167 | runner_empty = make_runner( |
| 168 | {"git remote get-url origin": CommandResult(stdout="", stderr="", exit_code=0)} |
| 169 | ) |
| 170 | assert not check_g8_git_remote(Path("/tmp"), "origin", runner_empty).ok |
File History
1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
2 days ago