"""Tests for the ``muse mist`` CLI command — Phase 2. Test tiers covered ------------------ Tier 1 — Shape / API surface ``muse mist`` is registered; all 7 subcommands present in --help; all run_* functions importable; docstrings present. Tier 2 — Round-trip (local, no MuseHub) ``muse mist create`` reads a file, computes mist_id, returns JSON; round-trip with multiple artifact types (Python, MIDI magic bytes, JSON). Tier 3 — Edge cases Empty file; file at exactly 10 MiB limit; unknown extension. create with all optional flags set. Tier 5 — Data integrity mist_id in create output matches compute_mist_id of file content; artifact_type matches detect_artifact_type; symbol_anchors non-empty for Python, empty for binary; validate_tag rejects invalid tags. Tier 6 — Performance create on a 1 MiB file under 200 ms. Tier 7 — Security create rejects filenames with path traversal, null bytes, ANSI escapes; create rejects content > 10 MiB; create rejects invalid visibility; create rejects > 10 tags; _validate_tag rejects XSS, null bytes, HTML specials. Tier 8 — Docstrings All public symbols in mist.py carry non-empty docstrings. """ from __future__ import annotations import json import os import pathlib import sys import time import types import pytest # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def invoke_mist(args: list[str]) -> tuple[int, str, str]: """Run ``muse mist `` in-process and capture stdout/stderr. Returns: A tuple of (exit_code, stdout, stderr). """ from io import StringIO old_stdout, old_stderr = sys.stdout, sys.stderr sys.stdout = out = StringIO() sys.stderr = err = StringIO() exit_code = 0 try: from muse.cli.app import main main(["mist"] + args) except SystemExit as exc: exit_code = int(exc.code) if exc.code is not None else 0 finally: sys.stdout = old_stdout sys.stderr = old_stderr return exit_code, out.getvalue(), err.getvalue() @pytest.fixture() def python_file(tmp_path: pathlib.Path) -> pathlib.Path: """A valid Python source file for mist create tests.""" f = tmp_path / "compute.py" f.write_bytes( b"def compute(x: int) -> int:\n" b' """Double the input."""\n' b" return x * 2\n" ) return f @pytest.fixture() def midi_file(tmp_path: pathlib.Path) -> pathlib.Path: """A minimal MIDI file (MThd magic bytes) for mist create tests.""" f = tmp_path / "motif.mid" # Minimal MIDI header: MThd + header length (6) + format (1) + tracks (1) + division f.write_bytes(b"MThd\x00\x00\x00\x06\x00\x01\x00\x01\x01\xe0") return f @pytest.fixture() def empty_file(tmp_path: pathlib.Path) -> pathlib.Path: """An empty file.""" f = tmp_path / "empty.txt" f.write_bytes(b"") return f @pytest.fixture() def large_file(tmp_path: pathlib.Path) -> pathlib.Path: """A 1 MiB file for performance tests.""" f = tmp_path / "large.py" f.write_bytes(b"# comment\n" * 104858) # ~1 MiB return f # --------------------------------------------------------------------------- # Tier 1 — Shape / API surface # --------------------------------------------------------------------------- class TestMistCliShape: """Verify muse mist is registered and all subcommands are present.""" SUBCOMMANDS = ("create", "list", "read", "fork", "push", "embed", "delete") RUN_FUNCS = ("run_create", "run_list", "run_read", "run_fork", "run_push", "run_embed", "run_delete") def test_mist_help_exits_0(self) -> None: code, out, _ = invoke_mist(["--help"]) assert code == 0 assert "mist" in out.lower() def test_all_subcommands_in_help(self) -> None: code, out, _ = invoke_mist(["--help"]) assert code == 0 for sub in self.SUBCOMMANDS: assert sub in out, f"Subcommand {sub!r} missing from muse mist --help" @pytest.mark.parametrize("sub", SUBCOMMANDS) def test_subcommand_help_exits_0(self, sub: str) -> None: code, out, _ = invoke_mist([sub, "--help"]) assert code == 0 assert sub in out.lower() @pytest.mark.parametrize("func_name", RUN_FUNCS) def test_run_functions_importable(self, func_name: str) -> None: import muse.cli.commands.mist as mod assert hasattr(mod, func_name), f"{func_name} missing from muse.cli.commands.mist" assert callable(getattr(mod, func_name)) def test_register_function_importable(self) -> None: from muse.cli.commands.mist import register assert callable(register) def test_validate_tag_importable(self) -> None: from muse.cli.commands.mist import _validate_tag assert callable(_validate_tag) def test_mist_registered_in_app(self) -> None: """muse mist appears in the top-level command list.""" code, out, _ = invoke_mist(["--help"]) # Just verify we can invoke the mist namespace (exit 0 from --help) assert code == 0 def test_create_json_flag_present(self) -> None: code, out, _ = invoke_mist(["create", "--help"]) assert "--json" in out def test_create_push_flag_present(self) -> None: code, out, _ = invoke_mist(["create", "--help"]) assert "--push" in out def test_create_sign_flag_present(self) -> None: code, out, _ = invoke_mist(["create", "--help"]) assert "--sign" in out def test_create_visibility_flag_present(self) -> None: code, out, _ = invoke_mist(["create", "--help"]) assert "--visibility" in out # --------------------------------------------------------------------------- # Tier 2 — Round-trip (local, no MuseHub) # --------------------------------------------------------------------------- class TestMistCreateRoundTrip: """End-to-end round-trip for muse mist create (no MuseHub required).""" def test_create_python_file_exits_0(self, python_file: pathlib.Path) -> None: code, out, err = invoke_mist(["create", str(python_file), "--json"]) assert code == 0, f"Unexpected exit {code}: stderr={err}" def test_create_python_file_returns_json(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert "mist_id" in data assert "artifact_type" in data assert "filename" in data assert "size_bytes" in data def test_create_python_mist_id_is_12_chars(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert len(data["mist_id"]) == 12 def test_create_python_artifact_type_is_code(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["artifact_type"] == "code" assert data["language"] == "python" def test_create_python_symbol_anchors_non_empty(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert isinstance(data["symbol_anchors"], list) assert len(data["symbol_anchors"]) > 0 def test_create_midi_file_exits_0(self, midi_file: pathlib.Path) -> None: code, out, err = invoke_mist(["create", str(midi_file), "--json"]) assert code == 0, f"Unexpected exit {code}: stderr={err}" def test_create_midi_artifact_type_is_midi(self, midi_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(midi_file), "--json"]) data = json.loads(out) assert data["artifact_type"] == "midi" def test_create_midi_symbol_anchors_empty(self, midi_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(midi_file), "--json"]) data = json.loads(out) assert data["symbol_anchors"] == [] def test_create_size_bytes_correct(self, python_file: pathlib.Path) -> None: expected = python_file.read_bytes() _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["size_bytes"] == len(expected) def test_create_filename_correct(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["filename"] == python_file.name def test_create_no_url_without_push(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["url"] == "" def test_create_human_readable_output(self, python_file: pathlib.Path) -> None: code, out, _ = invoke_mist(["create", str(python_file)]) assert code == 0 assert "✅" in out assert "Mist created" in out def test_create_with_title_flag(self, python_file: pathlib.Path) -> None: """--title flag is accepted and does not crash.""" code, _, _ = invoke_mist(["create", str(python_file), "--title", "My test mist"]) assert code == 0 def test_create_with_description_flag(self, python_file: pathlib.Path) -> None: code, _, _ = invoke_mist(["create", str(python_file), "--description", "A test."]) assert code == 0 def test_create_with_tag_flag(self, python_file: pathlib.Path) -> None: code, _, _ = invoke_mist(["create", str(python_file), "--tag", "security", "--tag", "utils"]) assert code == 0 def test_create_with_agent_flags(self, python_file: pathlib.Path) -> None: code, out, _ = invoke_mist([ "create", str(python_file), "--agent-id", "cccode-v3", "--model-id", "claude-sonnet-4-6", "--json", ]) assert code == 0 data = json.loads(out) assert data["agent_id"] == "cccode-v3" assert data["model_id"] == "claude-sonnet-4-6" def test_create_with_secret_visibility(self, python_file: pathlib.Path) -> None: code, _, _ = invoke_mist(["create", str(python_file), "--visibility", "secret"]) assert code == 0 # --------------------------------------------------------------------------- # Tier 3 — Edge cases # --------------------------------------------------------------------------- class TestMistCreateEdgeCases: """Boundary and unusual-input tests for muse mist create.""" def test_create_empty_file_exits_0(self, empty_file: pathlib.Path) -> None: code, out, err = invoke_mist(["create", str(empty_file), "--json"]) assert code == 0, f"stderr: {err}" data = json.loads(out) assert data["size_bytes"] == 0 assert len(data["mist_id"]) == 12 def test_create_empty_file_symbol_anchors_empty(self, empty_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(empty_file), "--json"]) data = json.loads(out) assert data["symbol_anchors"] == [] def test_create_unknown_extension(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "data.xyzzy" f.write_bytes(b"\xde\xad\xbe\xef" * 10) code, out, _ = invoke_mist(["create", str(f), "--json"]) assert code == 0 data = json.loads(out) assert data["artifact_type"] == "unknown" def test_create_json_file_abi(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "contract.json" f.write_bytes(json.dumps([{"type": "function", "name": "transfer"}]).encode()) _, out, _ = invoke_mist(["create", str(f), "--json"]) data = json.loads(out) assert data["artifact_type"] == "abi" def test_create_json_file_schema(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "schema.json" f.write_bytes(json.dumps({"$schema": "http://json-schema.org/draft-07/schema#"}).encode()) _, out, _ = invoke_mist(["create", str(f), "--json"]) data = json.loads(out) assert data["artifact_type"] == "json_schema" def test_create_markdown_file(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "README.md" f.write_bytes(b"# Hello\n\nWorld\n") _, out, _ = invoke_mist(["create", str(f), "--json"]) data = json.loads(out) assert data["artifact_type"] == "code" assert data["language"] == "markdown" def test_create_solidity_file(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "Token.sol" f.write_bytes(b"// SPDX-License-Identifier: MIT\ncontract Token {}\n") _, out, _ = invoke_mist(["create", str(f), "--json"]) data = json.loads(out) assert data["artifact_type"] == "code" assert data["language"] == "solidity" def test_create_ten_tags_accepted(self, python_file: pathlib.Path) -> None: tags = [f"--tag tag{i}" for i in range(10)] flat: list[str] = ["create", str(python_file)] for i in range(10): flat += ["--tag", f"tag{i}"] code, _, _ = invoke_mist(flat) assert code == 0 def test_create_different_content_different_mist_id(self, tmp_path: pathlib.Path) -> None: f1 = tmp_path / "a.py" f1.write_bytes(b"x = 1") f2 = tmp_path / "b.py" f2.write_bytes(b"x = 2") _, out1, _ = invoke_mist(["create", str(f1), "--json"]) _, out2, _ = invoke_mist(["create", str(f2), "--json"]) id1 = json.loads(out1)["mist_id"] id2 = json.loads(out2)["mist_id"] assert id1 != id2 def test_create_same_content_same_mist_id(self, tmp_path: pathlib.Path) -> None: """Content-addressed: same bytes always yield the same mist_id.""" content = b"def stable(): return True\n" f1 = tmp_path / "f1.py" f1.write_bytes(content) f2 = tmp_path / "f2.py" f2.write_bytes(content) _, out1, _ = invoke_mist(["create", str(f1), "--json"]) _, out2, _ = invoke_mist(["create", str(f2), "--json"]) assert json.loads(out1)["mist_id"] == json.loads(out2)["mist_id"] # --------------------------------------------------------------------------- # Tier 5 — Data integrity # --------------------------------------------------------------------------- class TestMistCliDataIntegrity: """Verify correctness of computed fields in CLI output.""" def test_mist_id_matches_compute_mist_id(self, python_file: pathlib.Path) -> None: from muse.plugins.mist.plugin import compute_mist_id content = python_file.read_bytes() expected_id = compute_mist_id(content) _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["mist_id"] == expected_id def test_artifact_type_matches_detect(self, python_file: pathlib.Path) -> None: from muse.plugins.mist.plugin import detect_artifact_type content = python_file.read_bytes() expected = detect_artifact_type(python_file.name, content) _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["artifact_type"] == expected["artifact_type"] assert data["language"] == expected["language"] def test_size_bytes_matches_actual(self, python_file: pathlib.Path) -> None: content = python_file.read_bytes() _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["size_bytes"] == len(content) def test_signed_false_without_sign_flag(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert data["signed"] is False def test_symbol_anchors_list_type(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) assert isinstance(data["symbol_anchors"], list) def test_symbol_anchors_contain_function_name(self, python_file: pathlib.Path) -> None: _, out, _ = invoke_mist(["create", str(python_file), "--json"]) data = json.loads(out) anchors = data["symbol_anchors"] assert any("compute" in a for a in anchors), f"No 'compute' in {anchors}" def test_mist_id_base58_only(self, python_file: pathlib.Path) -> None: from muse.plugins.mist.plugin import _BASE58_ALPHABET _, out, _ = invoke_mist(["create", str(python_file), "--json"]) mist_id = json.loads(out)["mist_id"] for ch in mist_id: assert ch in _BASE58_ALPHABET, f"Non-base58 char {ch!r} in mist_id" def test_validate_tag_rejects_xss(self) -> None: from muse.cli.commands.mist import _validate_tag with pytest.raises(ValueError, match="HTML"): _validate_tag("") def test_validate_tag_rejects_null_byte(self) -> None: from muse.cli.commands.mist import _validate_tag with pytest.raises(ValueError, match="null byte"): _validate_tag("tag\x00") def test_validate_tag_rejects_too_long(self) -> None: from muse.cli.commands.mist import _validate_tag with pytest.raises(ValueError, match="limit"): _validate_tag("t" * 65) def test_validate_tag_rejects_control_char(self) -> None: from muse.cli.commands.mist import _validate_tag with pytest.raises(ValueError, match="control char"): _validate_tag("tag\x01") def test_validate_tag_accepts_normal_tag(self) -> None: from muse.cli.commands.mist import _validate_tag _validate_tag("security") # must not raise _validate_tag("erc-8004") _validate_tag("midi-motif") # --------------------------------------------------------------------------- # Tier 6 — Performance # --------------------------------------------------------------------------- class TestMistCliPerformance: """Timing constraints for the CLI's hot paths.""" def test_create_1mb_file_under_200ms(self, large_file: pathlib.Path) -> None: start = time.perf_counter() code, out, err = invoke_mist(["create", str(large_file), "--json"]) elapsed = time.perf_counter() - start assert code == 0, f"stderr: {err}" assert elapsed < 0.200, f"create took {elapsed:.3f}s on 1 MiB file" def test_create_deterministic_ids_10_calls_under_500ms(self, python_file: pathlib.Path) -> None: start = time.perf_counter() ids = set() for _ in range(10): _, out, _ = invoke_mist(["create", str(python_file), "--json"]) ids.add(json.loads(out)["mist_id"]) elapsed = time.perf_counter() - start assert len(ids) == 1, "Same file should always yield same mist_id" assert elapsed < 0.500, f"10 create calls took {elapsed:.3f}s" # --------------------------------------------------------------------------- # Tier 7 — Security # --------------------------------------------------------------------------- class TestMistCliSecurity: """Input sanitisation and rejection of malicious inputs.""" @pytest.mark.parametrize("filename,expected_exit", [ ("../../../etc/passwd", 1), ("file\x00hidden.py", 1), ("..", 1), ("a" * 256, 1), ]) def test_invalid_filename_rejected( self, tmp_path: pathlib.Path, filename: str, expected_exit: int, ) -> None: """validate_mist_filename rejects attack vectors before creating.""" # We can't easily test invalid filenames via CLI since the filesystem # won't let us create files with these names. Test the validator directly. from muse.plugins.mist.plugin import validate_mist_filename with pytest.raises(ValueError): validate_mist_filename(filename) def test_content_over_10mb_rejected(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "huge.py" f.write_bytes(b"x" * (10 * 1024 * 1024 + 1)) code, _, err = invoke_mist(["create", str(f), "--json"]) assert code != 0 assert "10 MiB" in err or "limit" in err.lower() def test_invalid_visibility_rejected(self, python_file: pathlib.Path) -> None: code, _, err = invoke_mist(["create", str(python_file), "--visibility", "everyone"]) assert code != 0 assert "visibility" in err.lower() or "invalid" in err.lower() def test_too_many_tags_rejected(self, python_file: pathlib.Path) -> None: flat: list[str] = ["create", str(python_file)] for i in range(11): flat += ["--tag", f"tag{i}"] code, _, err = invoke_mist(flat) assert code != 0 assert "tag" in err.lower() def test_file_not_found_exits_nonzero(self) -> None: code, _, err = invoke_mist(["create", "/nonexistent/path/file.py", "--json"]) assert code != 0 assert "not found" in err.lower() or "no such" in err.lower() def test_mist_id_not_sequential_for_sequential_content(self) -> None: from muse.plugins.mist.plugin import compute_mist_id ids = [compute_mist_id(bytes([i])) for i in range(10)] # IDs should not be lexicographically sequential assert ids != sorted(ids), "Mist IDs should not be predictably ordered" def test_xss_tag_rejected_via_validate_tag(self) -> None: from muse.cli.commands.mist import _validate_tag for payload in ("", "