test_muse_sync.py
python
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
3 days ago
| 1 | """Unit tests for the Muse-sync hard gate (§KH2.4).""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | from dataclasses import fields |
| 6 | |
| 7 | from adapters.types import StatusResult |
| 8 | from tests.support import FIXTURES, load_fixture_config |
| 9 | from tools.muse_sync import check_muse_sync |
| 10 | |
| 11 | |
| 12 | def _status(regime: str, *, muse_dirty: bool | None, git_dirty: bool | None) -> StatusResult: |
| 13 | return StatusResult( |
| 14 | regime=regime, |
| 15 | dirty=bool(muse_dirty) or bool(git_dirty), |
| 16 | branch="main", |
| 17 | muse_dirty=muse_dirty, |
| 18 | git_dirty=git_dirty, |
| 19 | ) |
| 20 | |
| 21 | |
| 22 | def test_status_result_new_fields_default_to_none() -> None: |
| 23 | """Adding muse_dirty/git_dirty must not break existing positional/keyword construction.""" |
| 24 | result = StatusResult(regime="git-only", dirty=False, branch="main") |
| 25 | assert result.muse_dirty is None |
| 26 | assert result.git_dirty is None |
| 27 | field_names = {f.name for f in fields(StatusResult)} |
| 28 | assert {"muse_dirty", "git_dirty"}.issubset(field_names) |
| 29 | |
| 30 | |
| 31 | def test_git_only_is_not_applicable(tmp_path) -> None: |
| 32 | config = load_fixture_config(tmp_path, "config-git-only.yaml") |
| 33 | report = check_muse_sync(config, _status("git-only", muse_dirty=None, git_dirty=True)) |
| 34 | assert report.ok |
| 35 | assert report.state == "not_applicable" |
| 36 | assert report.remediation is None |
| 37 | |
| 38 | |
| 39 | def test_muse_only_is_not_applicable(tmp_path) -> None: |
| 40 | config = load_fixture_config(tmp_path, "config-muse-only.yaml") |
| 41 | report = check_muse_sync(config, _status("muse-only", muse_dirty=True, git_dirty=None)) |
| 42 | assert report.ok |
| 43 | assert report.state == "not_applicable" |
| 44 | |
| 45 | |
| 46 | def test_mirror_pending_when_git_clean_and_muse_dirty(tmp_path) -> None: |
| 47 | """The exact frozen trigger: Git already committed; Muse has not.""" |
| 48 | config = load_fixture_config(tmp_path, "config-muse-git-mirror.yaml") |
| 49 | report = check_muse_sync( |
| 50 | config, _status("muse+git-mirror", muse_dirty=True, git_dirty=False) |
| 51 | ) |
| 52 | assert not report.ok |
| 53 | assert report.state == "pending" |
| 54 | assert "muse commit" in report.remediation |
| 55 | |
| 56 | |
| 57 | def test_mirror_synced_when_both_clean(tmp_path) -> None: |
| 58 | config = load_fixture_config(tmp_path, "config-muse-git-mirror.yaml") |
| 59 | report = check_muse_sync( |
| 60 | config, _status("muse+git-mirror", muse_dirty=False, git_dirty=False) |
| 61 | ) |
| 62 | assert report.ok |
| 63 | assert report.state == "synced" |
| 64 | assert report.remediation is None |
| 65 | |
| 66 | |
| 67 | def test_mirror_synced_when_both_dirty_mid_edit_non_trigger(tmp_path) -> None: |
| 68 | """Frozen non-trigger: git_dirty=True must never yield 'pending', regardless of muse_dirty.""" |
| 69 | config = load_fixture_config(tmp_path, "config-muse-git-mirror.yaml") |
| 70 | report = check_muse_sync( |
| 71 | config, _status("muse+git-mirror", muse_dirty=True, git_dirty=True) |
| 72 | ) |
| 73 | assert report.ok |
| 74 | assert report.state == "synced" |
| 75 | |
| 76 | |
| 77 | def test_mirror_synced_when_git_dirty_muse_clean_non_trigger(tmp_path) -> None: |
| 78 | """Frozen non-trigger: normal mid-edit state (git dirty, muse already caught up).""" |
| 79 | config = load_fixture_config(tmp_path, "config-muse-git-mirror.yaml") |
| 80 | report = check_muse_sync( |
| 81 | config, _status("muse+git-mirror", muse_dirty=False, git_dirty=True) |
| 82 | ) |
| 83 | assert report.ok |
| 84 | assert report.state == "synced" |
| 85 | |
| 86 | |
| 87 | def test_mirror_unreadable_when_muse_dirty_unknown(tmp_path) -> None: |
| 88 | config = load_fixture_config(tmp_path, "config-muse-git-mirror.yaml") |
| 89 | report = check_muse_sync( |
| 90 | config, _status("muse+git-mirror", muse_dirty=None, git_dirty=False) |
| 91 | ) |
| 92 | assert not report.ok |
| 93 | assert report.state == "unreadable" |
| 94 | |
| 95 | |
| 96 | def test_mirror_unreadable_when_git_dirty_unknown(tmp_path) -> None: |
| 97 | config = load_fixture_config(tmp_path, "config-muse-git-mirror.yaml") |
| 98 | report = check_muse_sync( |
| 99 | config, _status("muse+git-mirror", muse_dirty=True, git_dirty=None) |
| 100 | ) |
| 101 | assert not report.ok |
| 102 | assert report.state == "unreadable" |
| 103 | |
| 104 | |
| 105 | def test_fixtures_dir_sane() -> None: |
| 106 | """Guard the fixture path this module's other tests rely on.""" |
| 107 | assert (FIXTURES / "config-muse-git-mirror.yaml").is_file() |
File History
1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
3 days ago