"""SUPERCHARGE tests for ``muse unpack-objects``. Coverage tiers -------------- - U (Unit): duration_ms / exit_code fields, tags_written field - E (Error routing): JSON errors → stdout when --json; stderr otherwise - S (Schema): error payload has exactly {error, message, duration_ms, exit_code} - D (Data integrity): duration_ms is float >= 0; exit_code 0/1/3 semantics - P (Performance): duration_ms is a sane duration for typical payloads - Sec (Security): no traceback on any error path (mocked write failure) """ from __future__ import annotations from collections.abc import Mapping import datetime import argparse import json import pathlib from unittest import mock import msgpack import pytest from muse.core.errors import ExitCode from muse.core.paths import muse_dir, ref_path from muse.core.ids import hash_commit as compute_commit_id, hash_snapshot as compute_snapshot_id from muse.core.commits import ( CommitRecord, write_commit, ) from muse.core.snapshots import ( SnapshotRecord, write_snapshot, ) from muse.core.types import Manifest from tests.cli_test_helper import CliRunner, InvokeResult runner = CliRunner() # --------------------------------------------------------------------------- # Helpers (copied from test_mpack_cmd_pack_unpack to stay self-contained) # --------------------------------------------------------------------------- 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, manifest: Manifest | None = None) -> str: m = manifest or {} snap_id = compute_snapshot_id(m) write_snapshot(repo, SnapshotRecord( snapshot_id=snap_id, manifest=m, created_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc), )) return snap_id def _commit(repo: pathlib.Path, snap_id: str, *, parent: str | None = None, message: str = "test") -> str: committed_at = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc) parent_ids: list[str] = [parent] if parent else [] commit_id = compute_commit_id( parent_ids=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, parent_commit_id=parent, )) return commit_id def _set_head(repo: pathlib.Path, commit_id: str) -> None: ref_path(repo, "main").write_text(commit_id) def _po(repo: pathlib.Path, *args: str) -> InvokeResult: from muse.cli.app import main as cli return runner.invoke(cli, ["pack-objects", *args], env={"MUSE_REPO_ROOT": str(repo)}) def _uo(repo: pathlib.Path, input_bytes: bytes, *args: str) -> InvokeResult: from muse.cli.app import main as cli return runner.invoke(cli, ["unpack-objects", *args], input=input_bytes, env={"MUSE_REPO_ROOT": str(repo)}) def _make_pack(tmp_path: pathlib.Path) -> tuple[pathlib.Path, bytes]: """Create a minimal repo and return (repo_path, pack_bytes).""" repo = _make_repo(tmp_path) sid = _snap(repo) cid = _commit(repo, sid) _set_head(repo, cid) pack_bytes = _po(repo, cid).stdout_bytes assert pack_bytes, "pack-objects returned empty bytes" return repo, pack_bytes # --------------------------------------------------------------------------- # U — Unit: duration_ms and exit_code in success JSON # --------------------------------------------------------------------------- class TestElapsedMsExitCode: """U1–U5: success JSON always carries duration_ms and exit_code.""" def test_u1_success_json_has_duration_ms(self, tmp_path: pathlib.Path) -> None: """U1: duration_ms key present in JSON success response.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert "duration_ms" in data, "duration_ms must be present in success JSON" def test_u2_success_json_has_exit_code_zero(self, tmp_path: pathlib.Path) -> None: """U2: exit_code is 0 in JSON success response.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert data["exit_code"] == 0 def test_u3_duration_ms_is_float(self, tmp_path: pathlib.Path) -> None: """U3: duration_ms is a float (not int, not string).""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert isinstance(data["duration_ms"], float), f"expected float, got {type(data['duration_ms'])}" def test_u4_duration_ms_non_negative(self, tmp_path: pathlib.Path) -> None: """U4: duration_ms >= 0.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert data["duration_ms"] >= 0.0 def test_u5_tags_written_in_success_json(self, tmp_path: pathlib.Path) -> None: """U5: tags_written key present in JSON success response.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert "tags_written" in data, "tags_written must be present in success JSON" assert isinstance(data["tags_written"], int) # --------------------------------------------------------------------------- # E — Error routing: JSON errors → stdout when --json, stderr otherwise # --------------------------------------------------------------------------- class TestJsonErrorsToStdout: """E1–E5: when --json, all errors land on stdout (not stderr).""" def test_e1_corrupted_msgpack_json_error_on_stdout(self, tmp_path: pathlib.Path) -> None: """E1: corrupted msgpack with --json → parseable JSON error; stderr is empty. r.output is stdout+stderr combined. Proving stderr is empty means everything in r.output came from stdout — so the JSON is on stdout. """ repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe corrupted!", "--json") assert r.exit_code != 0 assert r.stderr.strip() == "", f"stderr must be empty in JSON mode, got: {r.stderr!r}" data = json.loads(r.output) assert "error" in data def test_e2_corrupted_msgpack_error_is_valid_json(self, tmp_path: pathlib.Path) -> None: """E2: corrupted msgpack with --json → output is parseable JSON.""" repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe corrupted!", "--json") assert r.exit_code != 0 # If this parse fails, the error routing produced non-JSON output data = json.loads(r.output) assert "error" in data assert "message" in data def test_e3_not_a_dict_json_error_on_stdout(self, tmp_path: pathlib.Path) -> None: """E3: valid msgpack but not a map → JSON error; stderr is empty.""" repo = _make_repo(tmp_path) not_a_map = msgpack.packb(["a", "list", "not", "a", "map"], use_bin_type=True) r = _uo(repo, not_a_map, "--json") assert r.exit_code != 0 assert r.stderr.strip() == "", f"stderr must be empty in JSON mode, got: {r.stderr!r}" data = json.loads(r.output) assert "error" in data def test_e4_apply_mpack_oserror_exit_3_json_on_stdout(self, tmp_path: pathlib.Path) -> None: """E4: apply_mpack OSError → exit 3, JSON error; stderr is empty.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes with mock.patch("muse.cli.commands.unpack_objects.apply_mpack", side_effect=OSError("disk full")): r = _uo(dst, pack_bytes, "--json") assert r.exit_code == ExitCode.INTERNAL_ERROR assert r.stderr.strip() == "", f"stderr must be empty in JSON mode, got: {r.stderr!r}" data = json.loads(r.output) assert "error" in data assert "disk full" in data.get("message", "") def test_e5_apply_mpack_oserror_stderr_empty_in_json_mode(self, tmp_path: pathlib.Path) -> None: """E5: stderr must be empty when apply_mpack raises OSError in JSON mode.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes with mock.patch("muse.cli.commands.unpack_objects.apply_mpack", side_effect=OSError("disk full")): r = _uo(dst, pack_bytes, "--json") assert r.stderr.strip() == "", f"stderr should be empty in JSON mode, got: {r.stderr!r}" def test_e6_text_mode_errors_on_stderr(self, tmp_path: pathlib.Path) -> None: """E6: in text mode (no --json), msgpack errors go to stderr (stdout_bytes is empty).""" repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe corrupted!") assert r.exit_code != 0 assert r.stdout_bytes == b"", f"stdout_bytes should be empty in text mode, got: {r.stdout_bytes!r}" assert "error" in r.stderr.lower() or "invalid" in r.stderr.lower() # --------------------------------------------------------------------------- # S — Schema: error payload structure # --------------------------------------------------------------------------- class TestErrorJsonSchema: """S1–S3: every JSON error has exactly the right keys.""" def _parse_error(self, r: InvokeResult) -> Mapping[str, object]: return json.loads(r.output) def test_s1_corrupted_msgpack_error_schema(self, tmp_path: pathlib.Path) -> None: """S1: corrupted msgpack error has {error, message, duration_ms, exit_code}.""" repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe corrupted!", "--json") data = self._parse_error(r) for key in ("error", "message", "duration_ms", "exit_code"): assert key in data, f"missing key {key!r} in error JSON" def test_s2_not_a_dict_error_schema(self, tmp_path: pathlib.Path) -> None: """S2: not-a-map error has {error, message, duration_ms, exit_code}.""" repo = _make_repo(tmp_path) not_a_map = msgpack.packb(42, use_bin_type=True) r = _uo(repo, not_a_map, "--json") data = self._parse_error(r) for key in ("error", "message", "duration_ms", "exit_code"): assert key in data, f"missing key {key!r} in error JSON" def test_s3_apply_mpack_oserror_schema(self, tmp_path: pathlib.Path) -> None: """S3: apply_mpack OSError has {error, message, duration_ms, exit_code}.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes with mock.patch("muse.cli.commands.unpack_objects.apply_mpack", side_effect=OSError("no space left")): r = _uo(dst, pack_bytes, "--json") data = self._parse_error(r) for key in ("error", "message", "duration_ms", "exit_code"): assert key in data, f"missing key {key!r} in error JSON" def test_s4_error_duration_ms_is_float(self, tmp_path: pathlib.Path) -> None: """S4: duration_ms in error JSON is a float >= 0.""" repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe corrupted!", "--json") data = self._parse_error(r) assert isinstance(data["duration_ms"], float) assert data["duration_ms"] >= 0.0 def test_s5_error_exit_code_matches_process_exit(self, tmp_path: pathlib.Path) -> None: """S5: exit_code in JSON matches actual process exit code.""" repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe corrupted!", "--json") data = self._parse_error(r) assert data["exit_code"] == r.exit_code # --------------------------------------------------------------------------- # D — Data integrity # --------------------------------------------------------------------------- class TestDataIntegrity: """D1–D5: output values are semantically correct.""" def test_d1_exit_code_1_for_invalid_msgpack_json_mode(self, tmp_path: pathlib.Path) -> None: """D1: corrupted msgpack → exit_code 1 (user error) in JSON.""" repo = _make_repo(tmp_path) r = _uo(repo, b"\xff\xfe bad", "--json") assert r.exit_code == ExitCode.USER_ERROR def test_d2_exit_code_1_for_not_a_dict_json_mode(self, tmp_path: pathlib.Path) -> None: """D2: msgpack integer → exit_code 1 (user error) in JSON.""" repo = _make_repo(tmp_path) not_map = msgpack.packb(99, use_bin_type=True) r = _uo(repo, not_map, "--json") assert r.exit_code == ExitCode.USER_ERROR def test_d3_exit_code_3_for_write_failure_json_mode(self, tmp_path: pathlib.Path) -> None: """D3: apply_mpack OSError → exit_code 3 (internal error) in JSON.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes with mock.patch("muse.cli.commands.unpack_objects.apply_mpack", side_effect=OSError("ENOSPC")): r = _uo(dst, pack_bytes, "--json") assert r.exit_code == ExitCode.INTERNAL_ERROR def test_d4_tags_written_is_zero_for_tagless_pack(self, tmp_path: pathlib.Path) -> None: """D4: a pack with no tags yields tags_written=0.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert data["tags_written"] == 0 def test_d5_success_json_all_count_fields_present(self, tmp_path: pathlib.Path) -> None: """D5: success JSON has all expected count fields.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) expected_keys = { "commits_written", "snapshots_written", "objects_written", "objects_skipped", "tags_written", "duration_ms", "exit_code", } missing = expected_keys - data.keys() assert not missing, f"missing keys in success JSON: {missing}" # --------------------------------------------------------------------------- # P — Performance # --------------------------------------------------------------------------- class TestPerformance: """P1–P2: duration_ms is a realistic duration.""" def test_p1_duration_ms_under_5000ms_for_single_commit(self, tmp_path: pathlib.Path) -> None: """P1: unpacking a single commit pack finishes in < 5 seconds.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert data["duration_ms"] < 5000.0, f"too slow: {data['duration_ms']} ms" def test_p2_duration_ms_under_5000ms_for_five_commit_chain(self, tmp_path: pathlib.Path) -> None: """P2: unpacking a 5-commit chain finishes in < 5 seconds.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) prev = None last_cid = "" for i in range(5): prev = _commit(src, sid, parent=prev, message=f"commit-{i}") last_cid = prev pack_bytes = _po(src, last_cid).stdout_bytes r = _uo(dst, pack_bytes, "--json") assert r.exit_code == 0 data = json.loads(r.output) assert data["duration_ms"] < 5000.0, f"too slow for 5-commit chain: {data['duration_ms']} ms" # --------------------------------------------------------------------------- # Sec — Security: no traceback on any error path # --------------------------------------------------------------------------- class TestSecurity: """Sec1–Sec3: error paths must never produce raw Python tracebacks.""" def test_sec1_no_traceback_on_corrupted_msgpack_json_mode(self, tmp_path: pathlib.Path) -> None: """Sec1: corrupted msgpack with --json → no Traceback in output.""" repo = _make_repo(tmp_path) r = _uo(repo, b"\x00\x01\x02 garbage", "--json") assert r.exit_code != 0 assert "Traceback" not in r.output assert "Traceback" not in r.stderr def test_sec2_no_traceback_on_apply_mpack_oserror(self, tmp_path: pathlib.Path) -> None: """Sec2: mocked write failure with --json → no Traceback in output.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes with mock.patch("muse.cli.commands.unpack_objects.apply_mpack", side_effect=OSError("permission denied")): r = _uo(dst, pack_bytes, "--json") assert r.exit_code != 0 assert "Traceback" not in r.output assert "Traceback" not in r.stderr def test_sec3_no_traceback_on_apply_mpack_oserror_text_mode(self, tmp_path: pathlib.Path) -> None: """Sec3: mocked write failure in text mode → no Traceback in output.""" src = _make_repo(tmp_path / "src") dst = _make_repo(tmp_path / "dst") sid = _snap(src) cid = _commit(src, sid) pack_bytes = _po(src, cid).stdout_bytes with mock.patch("muse.cli.commands.unpack_objects.apply_mpack", side_effect=OSError("permission denied")): r = _uo(dst, pack_bytes) assert r.exit_code != 0 assert "Traceback" not in r.output assert "Traceback" not in r.stderr # --------------------------------------------------------------------------- # Flag registration # --------------------------------------------------------------------------- class TestRegisterFlags: def _parse(self, *args: str) -> "argparse.Namespace": import argparse from muse.cli.commands.unpack_objects import register p = argparse.ArgumentParser() sub = p.add_subparsers() register(sub) return p.parse_args(["unpack-objects", *args]) def test_default_json_out_is_false(self) -> None: ns = self._parse() assert ns.json_out is False def test_json_flag_sets_json_out(self) -> None: ns = self._parse("--json") assert ns.json_out is True def test_j_shorthand_sets_json_out(self) -> None: ns = self._parse("-j") assert ns.json_out is True