"""Comprehensive tests for ``muse update-ref``. Coverage tiers -------------- - Unit: _FORMAT_CHOICES - Integration: create ref, update ref, delete ref, --no-verify, text format - CAS: --old-value happy path, mismatch, null guard - Security: ANSI/null in branch name rejected, errors to stderr, no traceback - Stress: 200 sequential updates """ from __future__ import annotations import datetime import json import pathlib from muse.core.errors import ExitCode from muse.core.types import fake_id, long_id from muse.core.ids import hash_commit, hash_snapshot from muse.core.commits import ( CommitRecord, write_commit, ) from muse.core.snapshots import ( SnapshotRecord, write_snapshot, ) from muse.core.paths import heads_dir, muse_dir, ref_path from tests.cli_test_helper import CliRunner, InvokeResult runner = CliRunner() _SNAP_ID: str = hash_snapshot({}) _COMMITTED_AT: datetime.datetime = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _make_repo(tmp_path: pathlib.Path) -> pathlib.Path: repo = tmp_path / "repo" dot_muse = muse_dir(repo) for sub in ("objects", "commits", "snapshots", "refs/heads"): (dot_muse / sub).mkdir(parents=True) (dot_muse / "HEAD").write_text("ref: refs/heads/main") (dot_muse / "repo.json").write_text(json.dumps({"repo_id": "test-repo", "domain": "code"})) return repo def _snap(repo: pathlib.Path) -> str: """Write an empty-manifest snapshot; return its content-addressed ID.""" write_snapshot(repo, SnapshotRecord( snapshot_id=_SNAP_ID, manifest={}, created_at=_COMMITTED_AT, )) return _SNAP_ID def _commit(repo: pathlib.Path, message: str = "test") -> str: """Write a commit with a real content-addressed ID; return the commit_id.""" snap_id = _snap(repo) commit_id = hash_commit( parent_ids=[], snapshot_id=snap_id, message=message, committed_at_iso=_COMMITTED_AT.isoformat(), ) write_commit(repo, CommitRecord( commit_id=commit_id, branch="main", snapshot_id=snap_id, message=message, committed_at=_COMMITTED_AT, )) return commit_id def _write_ref(repo: pathlib.Path, branch: str, commit_id: str) -> None: ref = ref_path(repo, branch) ref.parent.mkdir(parents=True, exist_ok=True) ref.write_text(commit_id) def _ur(repo: pathlib.Path, *args: str) -> InvokeResult: from muse.cli.app import main as cli return runner.invoke( cli, ["update-ref", *args], env={"MUSE_REPO_ROOT": str(repo)}, ) # --------------------------------------------------------------------------- # Unit # --------------------------------------------------------------------------- class TestUnit: def test_json_flag_registered(self) -> None: import argparse from muse.cli.commands.update_ref import register p = argparse.ArgumentParser() sub = p.add_subparsers() register(sub) ns = p.parse_args(["update-ref", "--json", "main"]) assert ns.json_out is True # --------------------------------------------------------------------------- # Integration — create and update # --------------------------------------------------------------------------- class TestCreateUpdate: def test_creates_new_ref(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = _commit(repo, "create ref test") result = _ur(repo, "--json", "feature", cid) assert result.exit_code == 0 data = json.loads(result.output) assert data["branch"] == "feature" assert data["commit_id"] == cid assert (heads_dir(repo) / "feature").read_text() == cid def test_previous_is_null_for_new_ref(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = _commit(repo, "new ref test") data = json.loads(_ur(repo, "--json", "new-branch", cid).output) assert data["previous"] is None def test_updates_existing_ref(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) old_id = _commit(repo, "old commit") new_id = _commit(repo, "new commit") _write_ref(repo, "main", old_id) data = json.loads(_ur(repo, "--json", "main", new_id).output) assert data["previous"] == old_id assert data["commit_id"] == new_id def test_json_shorthand(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = _commit(repo, "json shorthand test") result = _ur(repo, "--json", "main", cid) assert result.exit_code == 0 assert "commit_id" in json.loads(result.output) def test_text_mode_silent_on_success(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = _commit(repo, "text format test") result = _ur(repo, "main", cid) assert result.exit_code == 0 assert result.output.strip() == "" # --------------------------------------------------------------------------- # Integration — delete # --------------------------------------------------------------------------- class TestDeleteRef: def test_delete_existing_ref(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) _write_ref(repo, "todelete", "5" * 64) result = _ur(repo, "--json", "--delete", "todelete") assert result.exit_code == 0 data = json.loads(result.output) assert data["deleted"] is True assert not (heads_dir(repo) / "todelete").exists() def test_delete_nonexistent_ref_errors(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) result = _ur(repo, "--delete", "ghost-branch") assert result.exit_code == ExitCode.USER_ERROR def test_delete_text_mode_silent(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) _write_ref(repo, "to-del", "6" * 64) result = _ur(repo, "--delete", "to-del") assert result.exit_code == 0 assert result.output.strip() == "" # --------------------------------------------------------------------------- # Integration — --no-verify # --------------------------------------------------------------------------- class TestNoVerify: def test_no_verify_accepts_unknown_commit(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = long_id("7" * 64) # valid format but not in store result = _ur(repo, "--no-verify", "staging", cid) assert result.exit_code == 0 assert (heads_dir(repo) / "staging").read_text() == cid def test_verify_rejects_unknown_commit(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = long_id("8" * 64) # valid format but not in store result = _ur(repo, "main", cid) assert result.exit_code == ExitCode.USER_ERROR # --------------------------------------------------------------------------- # CAS — compare-and-swap # --------------------------------------------------------------------------- class TestCAS: def test_cas_succeeds_when_current_matches(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) old_id = _commit(repo, "cas old commit") new_id = _commit(repo, "cas new commit") _write_ref(repo, "main", old_id) result = _ur(repo, "--json", "--old-value", old_id, "main", new_id) assert result.exit_code == 0 data = json.loads(result.output) assert data["commit_id"] == new_id def test_cas_fails_when_current_differs(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) actual = _commit(repo, "actual commit") new_id = _commit(repo, "new commit") # Use a different (non-stored) ID as the expected old value. expected = f"c1{'0' * 62}" _write_ref(repo, "main", actual) result = _ur(repo, "--old-value", expected, "main", new_id) assert result.exit_code == ExitCode.USER_ERROR def test_cas_null_succeeds_when_ref_absent(self, tmp_path: pathlib.Path) -> None: """--old-value null asserts the ref does not yet exist.""" repo = _make_repo(tmp_path) cid = _commit(repo, "cas null test") result = _ur(repo, "--no-verify", "--old-value", "null", "brand-new", cid) assert result.exit_code == 0 def test_cas_null_fails_when_ref_exists(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) existing = _commit(repo, "existing commit") new_id = _commit(repo, "new commit for null cas") _write_ref(repo, "contested", existing) result = _ur(repo, "--old-value", "null", "contested", new_id) assert result.exit_code == ExitCode.USER_ERROR def test_cas_delete_succeeds_when_matches(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = fake_id("aa") _write_ref(repo, "conditioned", cid) result = _ur(repo, "--delete", "--old-value", cid, "conditioned") assert result.exit_code == 0 def test_cas_delete_fails_when_differs(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) actual = long_id(f"bb{'0' * 62}") wrong = long_id(f"cc{'0' * 62}") _write_ref(repo, "conditioned", actual) result = _ur(repo, "--delete", "--old-value", wrong, "conditioned") assert result.exit_code == ExitCode.USER_ERROR assert (heads_dir(repo) / "conditioned").exists() # --------------------------------------------------------------------------- # Error cases # --------------------------------------------------------------------------- class TestErrors: def test_invalid_branch_name_rejected(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) result = _ur(repo, "branch\x00null", "a" * 64) assert result.exit_code == ExitCode.USER_ERROR def test_invalid_commit_id_rejected(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) result = _ur(repo, "main", "not-hex") assert result.exit_code == ExitCode.USER_ERROR def test_no_commit_id_without_delete_errors(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) result = _ur(repo, "main") assert result.exit_code == ExitCode.USER_ERROR # --------------------------------------------------------------------------- # Security # --------------------------------------------------------------------------- class TestSecurity: def test_ansi_in_branch_rejected(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) result = _ur(repo, "\x1b[31mbranch", "a" * 64) assert result.exit_code == ExitCode.USER_ERROR def test_no_traceback_on_bad_branch(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) result = _ur(repo, "bad\x00branch", "a" * 64) assert "Traceback" not in result.output # --------------------------------------------------------------------------- # Stress # --------------------------------------------------------------------------- class TestStress: def test_200_sequential_updates(self, tmp_path: pathlib.Path) -> None: repo = _make_repo(tmp_path) cid = _commit(repo, "stress test commit") for i in range(200): result = _ur(repo, "--json", "stress-branch", cid) assert result.exit_code == 0, f"failed at iteration {i}" data = json.loads(result.output) assert data["commit_id"] == cid # --------------------------------------------------------------------------- # Flag registration # --------------------------------------------------------------------------- class TestRegisterFlags: def _parse(self, *args: str) -> "argparse.Namespace": import argparse from muse.cli.commands.update_ref import register p = argparse.ArgumentParser() sub = p.add_subparsers() register(sub) return p.parse_args(["update-ref", *args]) def test_default_json_out_is_false(self) -> None: ns = self._parse("main") assert ns.json_out is False def test_json_flag_sets_json_out(self) -> None: ns = self._parse("--json", "main") assert ns.json_out is True def test_j_shorthand_sets_json_out(self) -> None: ns = self._parse("-j", "main") assert ns.json_out is True