"""Tests for ``muse format-patch`` — content-addressed, domain-aware Muse patch export. Test tiers ---------- - Unit: output schema, required fields, JSON format, action_label on ops - Integration: initial commit, two-commit delta, multi-file changes - Data integrity: patch_id sha256-prefixed, duration_ms/exit_code present - Security: error to stderr, malicious ref rejected - Performance: duration_ms plausible - Edge: empty diff (no file changes), --output-dir creates .mpatch file """ from __future__ import annotations from collections.abc import Mapping import datetime import json import pathlib import pytest from tests.cli_test_helper import CliRunner, InvokeResult 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.object_store import write_object from muse.core.types import long_id, split_id, blob_id from muse.core.paths import muse_dir, ref_path runner = CliRunner() # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _init_repo(path: pathlib.Path) -> pathlib.Path: dot_muse = muse_dir(path) for sub in ("commits", "snapshots", "objects", "refs/heads"): (dot_muse / sub).mkdir(parents=True, exist_ok=True) (dot_muse / "HEAD").write_text("ref: refs/heads/main\n") (dot_muse / "repo.json").write_text(json.dumps({"repo_id": "test-repo", "domain": "code"})) return path def _write_object(repo: pathlib.Path, content: bytes) -> str: """Write bytes to the object store and return the sha256: prefixed ID.""" oid = blob_id(content) write_object(repo, oid, content) return oid def _commit( repo: pathlib.Path, msg: str, manifest: dict[str, str], branch: str = "main", parent: str | None = None, ts: datetime.datetime | None = None, ) -> str: ts = ts or datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc) sid = hash_snapshot(manifest) write_snapshot(repo, SnapshotRecord(snapshot_id=sid, manifest=manifest, created_at=ts)) parent_ids = [parent] if parent else [] cid = hash_commit( parent_ids=parent_ids, snapshot_id=sid, message=msg, committed_at_iso=ts.isoformat(), author="gabriel", ) write_commit(repo, CommitRecord( commit_id=cid, branch=branch, snapshot_id=sid, message=msg, committed_at=ts, author="gabriel", parent_commit_id=parent, parent2_commit_id=None, )) branch_ref = ref_path(repo, branch) branch_ref.parent.mkdir(parents=True, exist_ok=True) branch_ref.write_text(cid) return cid def _fp(repo: pathlib.Path, *args: str) -> InvokeResult: return runner.invoke(None, ["format-patch", *args], env={"MUSE_REPO_ROOT": str(repo)}) def _json(r: InvokeResult) -> Mapping[str, object]: return json.loads(r.output) # --------------------------------------------------------------------------- # JSON output schema # --------------------------------------------------------------------------- class TestJsonSchema: def test_exits_zero_on_success(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"print('hello')\n") _commit(repo, "init: add hello.py", {"hello.py": oid}) r = _fp(repo, "--json") assert r.exit_code == 0 def test_has_patch_id(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert "patch_id" in _json(_fp(repo, "--json")) def test_patch_id_sha256_prefix(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert _json(_fp(repo, "--json"))["patch_id"].startswith("sha256:") def test_patch_id_full_length(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) pid = _json(_fp(repo, "--json"))["patch_id"] assert len(pid) == 71 # "sha256:" (7) + 64 hex def test_has_from_snapshot_id(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert "from_snapshot_id" in data def test_has_to_snapshot_id(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert "to_snapshot_id" in data assert data["to_snapshot_id"].startswith("sha256:") def test_has_from_commit_id(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"a.py": oid1}) oid2 = _write_object(repo, b"x = 2\n") _commit(repo, "c2", {"a.py": oid2}, parent=c1) data = _json(_fp(repo, "--json")) assert "from_commit_id" in data def test_has_to_commit_id(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert "to_commit_id" in data assert data["to_commit_id"].startswith("sha256:") def test_domain_matches_repo(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert data["domain"] == "code" def test_format_version_is_1_0(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert _json(_fp(repo, "--json"))["format_version"] == "1.0" def test_duration_ms_present(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert "duration_ms" in _json(_fp(repo, "--json")) def test_duration_ms_is_float(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) v = _json(_fp(repo, "--json"))["duration_ms"] assert isinstance(v, float) assert v >= 0.0 def test_duration_ms_six_decimal_places(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) v = _json(_fp(repo, "--json"))["duration_ms"] assert v == round(v, 6) def test_exit_code_zero_in_json(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert _json(_fp(repo, "--json"))["exit_code"] == 0 def test_exit_code_is_int_not_bool(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert type(_json(_fp(repo, "--json"))["exit_code"]) is int def test_sem_ver_bump_present(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert "sem_ver_bump" in data assert data["sem_ver_bump"] in ("major", "minor", "patch") def test_summary_is_string(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert isinstance(_json(_fp(repo, "--json"))["summary"], str) def test_ops_is_list(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert isinstance(_json(_fp(repo, "--json"))["ops"], list) def test_applicability_has_requires_snapshot(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert "requires_snapshot" in data["applicability"] def test_applicability_has_conflict_free(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert isinstance(data["applicability"]["conflict_free"], bool) def test_applicability_has_independent_dimensions(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) data = _json(_fp(repo, "--json")) assert isinstance(data["applicability"]["independent_dimensions"], list) def test_output_is_compact_single_line(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) r = _fp(repo, "--json") assert len(r.output.strip().splitlines()) == 1 # --------------------------------------------------------------------------- # File change tracking # --------------------------------------------------------------------------- class TestFileChanges: def test_initial_commit_files_in_files_added(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert "hello.py" in _json(_fp(repo, "--json"))["files_added"] def test_modified_file_in_files_modified(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"a.py": oid1}) oid2 = _write_object(repo, b"x = 2\n") _commit(repo, "c2", {"a.py": oid2}, parent=c1) assert "a.py" in _json(_fp(repo, "--json"))["files_modified"] def test_removed_file_in_files_deleted(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") oid2 = _write_object(repo, b"y = 2\n") c1 = _commit(repo, "c1", {"old.py": oid1, "keep.py": oid2}) _commit(repo, "c2", {"keep.py": oid2}, parent=c1) assert "old.py" in _json(_fp(repo, "--json"))["files_deleted"] def test_empty_diff_all_lists_empty(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"a.py": oid}) _commit(repo, "c2", {"a.py": oid}, parent=c1) data = _json(_fp(repo, "--json")) assert data["files_added"] == [] assert data["files_modified"] == [] assert data["files_deleted"] == [] def test_required_objects_is_list(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert isinstance(_json(_fp(repo, "--json"))["required_objects"], list) def test_from_manifest_is_dict(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert isinstance(_json(_fp(repo, "--json"))["from_manifest"], dict) def test_to_manifest_is_dict(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) assert isinstance(_json(_fp(repo, "--json"))["to_manifest"], dict) def test_applicability_requires_snapshot_matches_from_snapshot(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"a.py": oid1}) oid2 = _write_object(repo, b"x = 2\n") _commit(repo, "c2", {"a.py": oid2}, parent=c1) data = _json(_fp(repo, "--json")) assert data["applicability"]["requires_snapshot"] == data["from_snapshot_id"] def test_count_equals_len_refs(self, tmp_path: pathlib.Path) -> None: """files_added + files_modified + files_deleted must account for all changed paths.""" repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") oid2 = _write_object(repo, b"y = 2\n") c1 = _commit(repo, "c1", {"a.py": oid1, "b.py": oid2}) oid3 = _write_object(repo, b"x = 99\n") _commit(repo, "c2", {"a.py": oid3}, parent=c1) # modify a, delete b data = _json(_fp(repo, "--json")) total = len(data["files_added"]) + len(data["files_modified"]) + len(data["files_deleted"]) assert total == 2 # a.py modified, b.py deleted # --------------------------------------------------------------------------- # Cohen action labels on ops # --------------------------------------------------------------------------- class TestCohenActionLabels: def test_inserted_label_on_added_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) labels = [op.get("action_label") for op in _json(_fp(repo, "--json"))["ops"]] assert "inserted" in labels def test_deleted_label_on_removed_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"old.py": oid}) _commit(repo, "c2", {}, parent=c1) labels = [op.get("action_label") for op in _json(_fp(repo, "--json"))["ops"]] assert "deleted" in labels def test_modified_label_on_changed_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"a.py": oid1}) oid2 = _write_object(repo, b"x = 2\n") _commit(repo, "c2", {"a.py": oid2}, parent=c1) labels = [op.get("action_label") for op in _json(_fp(repo, "--json"))["ops"]] assert "modified" in labels def test_all_ops_have_action_label(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") oid2 = _write_object(repo, b"y = 2\n") _commit(repo, "init", {"a.py": oid1, "b.py": oid2}) data = _json(_fp(repo, "--json")) for op in data["ops"]: assert "action_label" in op, f"missing action_label in op: {op}" def test_action_label_values_are_valid(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"a.py": oid1}) valid = {"inserted", "deleted", "modified", "moved", "renamed"} for op in _json(_fp(repo, "--json"))["ops"]: assert op["action_label"] in valid # --------------------------------------------------------------------------- # --output-dir writes .mpatch file # --------------------------------------------------------------------------- class TestOutputDir: def test_writes_mpatch_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) out_dir = tmp_path / "patches" out_dir.mkdir() r = _fp(repo, "--output-dir", str(out_dir)) assert r.exit_code == 0 assert len(list(out_dir.glob("*.mpatch"))) == 1 def test_mpatch_file_is_valid_json(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) out_dir = tmp_path / "patches" out_dir.mkdir() _fp(repo, "--output-dir", str(out_dir)) patch_file = list(out_dir.glob("*.mpatch"))[0] data = json.loads(patch_file.read_bytes()) assert "patch_id" in data assert "domain" in data def test_mpatch_filename_includes_subject(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "feat: add hello", {"hello.py": oid}) out_dir = tmp_path / "patches" out_dir.mkdir() _fp(repo, "--output-dir", str(out_dir)) names = [f.name for f in out_dir.glob("*.mpatch")] assert any("feat" in n for n in names) def test_nonexistent_output_dir_fails(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) r = _fp(repo, "--output-dir", str(tmp_path / "does_not_exist")) assert r.exit_code != 0 # --------------------------------------------------------------------------- # Patch ID stability (data integrity) # --------------------------------------------------------------------------- class TestPatchIdStability: def test_same_commit_same_patch_id(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) pid1 = _json(_fp(repo, "--json"))["patch_id"] pid2 = _json(_fp(repo, "--json"))["patch_id"] assert pid1 == pid2 def test_different_commits_different_patch_ids(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid1 = _write_object(repo, b"x = 1\n") c1 = _commit(repo, "c1", {"a.py": oid1}) oid2 = _write_object(repo, b"x = 2\n") _commit(repo, "c2", {"a.py": oid2}, parent=c1) # HEAD is c2; compare its patch_id to c1's patch_id via explicit ref pid_c2 = _json(_fp(repo, "--json"))["patch_id"] pid_c1 = _json(_fp(repo, split_id(c1)[1], "--json"))["patch_id"] assert pid_c2 != pid_c1 # --------------------------------------------------------------------------- # Error paths # --------------------------------------------------------------------------- class TestErrorPaths: def test_empty_repo_exits_nonzero(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) r = _fp(repo, "--json") assert r.exit_code != 0 def test_bad_ref_exits_nonzero(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) oid = _write_object(repo, b"x = 1\n") _commit(repo, "init", {"hello.py": oid}) r = _fp(repo, "nonexistent-branch", "--json") assert r.exit_code != 0 class TestRegisterFlags: def test_default_json_out_is_false(self) -> None: import argparse from muse.cli.commands.format_patch import register p = argparse.ArgumentParser() subs = p.add_subparsers() register(subs) args = p.parse_args(["format-patch"]) assert args.json_out is False def test_json_flag_sets_json_out(self) -> None: import argparse from muse.cli.commands.format_patch import register p = argparse.ArgumentParser() subs = p.add_subparsers() register(subs) args = p.parse_args(["format-patch", "--json"]) assert args.json_out is True def test_j_shorthand_sets_json_out(self) -> None: import argparse from muse.cli.commands.format_patch import register p = argparse.ArgumentParser() subs = p.add_subparsers() register(subs) args = p.parse_args(["format-patch", "-j"]) assert args.json_out is True