"""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 json import pathlib from unittest import mock import msgpack import pytest from muse.core.errors import ExitCode from muse.core.snapshot import compute_commit_id, compute_snapshot_id from muse.core.store import CommitRecord, SnapshotRecord, write_commit, 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" muse = repo / ".muse" for sub in ("objects", "commits", "snapshots", "refs/heads"): (muse / sub).mkdir(parents=True) (muse / "HEAD").write_text("ref: refs/heads/main") (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( repo_id="test-repo", parent_ids=parent_ids, snapshot_id=snap_id, message=message, committed_at_iso=committed_at.isoformat(), ) write_commit(repo, CommitRecord( commit_id=commit_id, repo_id="test-repo", created_on_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: (repo / ".muse" / "refs" / "heads" / "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): 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