"""Unit tests for muse.plugins.registry — resolve_plugin, read_domain, registered_domains.""" import json import pathlib import subprocess import sys import pytest from muse._version import __version__ from muse.core.paths import muse_dir, repo_json_path from muse.core.errors import MuseCLIError from muse.domain import MuseDomainPlugin from muse.plugins.code.plugin import CodePlugin from muse.plugins.registry import read_domain, registered_domains, resolve_plugin def _make_repo(tmp_path: pathlib.Path, domain: str = "code") -> pathlib.Path: """Scaffold a minimal .muse/repo.json so registry helpers can run.""" dot_muse = muse_dir(tmp_path) dot_muse.mkdir() repo_json_path(tmp_path).write_text( json.dumps({"repo_id": "test-id", "schema_version": __version__, "domain": domain}) ) return tmp_path class TestReadDomain: def test_returns_stored_domain(self, tmp_path: pathlib.Path) -> None: root = _make_repo(tmp_path, domain="code") assert read_domain(root) == "code" def test_defaults_to_code_when_key_missing(self, tmp_path: pathlib.Path) -> None: dot_muse = muse_dir(tmp_path) dot_muse.mkdir() repo_json_path(tmp_path).write_text(json.dumps({"repo_id": "x"})) assert read_domain(tmp_path) == "code" def test_defaults_to_code_when_repo_json_absent(self, tmp_path: pathlib.Path) -> None: muse_dir(tmp_path).mkdir() assert read_domain(tmp_path) == "code" def test_defaults_to_code_when_muse_dir_absent(self, tmp_path: pathlib.Path) -> None: assert read_domain(tmp_path) == "code" class TestResolvePlugin: def test_returns_code_plugin_for_code_domain(self, tmp_path: pathlib.Path) -> None: root = _make_repo(tmp_path, domain="code") plugin = resolve_plugin(root) assert isinstance(plugin, CodePlugin) def test_returned_plugin_satisfies_protocol(self, tmp_path: pathlib.Path) -> None: root = _make_repo(tmp_path, domain="code") plugin = resolve_plugin(root) assert isinstance(plugin, MuseDomainPlugin) def test_raises_for_unknown_domain(self, tmp_path: pathlib.Path) -> None: root = _make_repo(tmp_path, domain="unknown-domain") with pytest.raises(MuseCLIError, match="unknown-domain"): resolve_plugin(root) def test_raises_error_mentions_registered_domains(self, tmp_path: pathlib.Path) -> None: root = _make_repo(tmp_path, domain="bogus") with pytest.raises(MuseCLIError, match="code"): resolve_plugin(root) def test_defaults_to_code_plugin_when_no_domain_key(self, tmp_path: pathlib.Path) -> None: dot_muse = muse_dir(tmp_path) dot_muse.mkdir() repo_json_path(tmp_path).write_text(json.dumps({"repo_id": "x"})) plugin = resolve_plugin(tmp_path) assert isinstance(plugin, CodePlugin) class TestRegisteredDomains: def test_includes_code(self) -> None: assert "code" in registered_domains() def test_midi_suspended_by_default(self) -> None: """MIDI domain is suspended by default, pending its own security and performance audit — see MUSE_ENABLE_MIDI for the local opt-in gate.""" assert "midi" not in registered_domains() def test_returns_sorted_list(self) -> None: domains = registered_domains() assert domains == sorted(domains) def test_returns_list_of_strings(self) -> None: domains = registered_domains() assert all(isinstance(d, str) for d in domains) class TestMidiOptInGate: """The midi domain stays off by default (see test_midi_suspended_by_default above), but is fully maintained and can be opted into locally — e.g. for a demo — via MUSE_ENABLE_MIDI, without changing the default registry that ships to every other user. _REGISTRY is built once at import time, so each case needs its own fresh interpreter.""" @staticmethod def _registered_domains_with_env(env_extra: dict[str, str]) -> list[str]: result = subprocess.run( [sys.executable, "-c", "from muse.plugins.registry import registered_domains; print(','.join(registered_domains()))"], capture_output=True, text=True, timeout=15, env={**{"PATH": "/usr/bin:/bin"}, **env_extra}, ) assert result.returncode == 0, result.stderr return result.stdout.strip().split(",") def test_midi_absent_without_env_var(self) -> None: domains = self._registered_domains_with_env({}) assert "midi" not in domains def test_midi_present_when_env_var_set(self) -> None: domains = self._registered_domains_with_env({"MUSE_ENABLE_MIDI": "1"}) assert "midi" in domains def test_resolve_plugin_works_for_midi_when_opted_in(self, tmp_path: pathlib.Path) -> None: """The exact failure this gate fixes: `muse commit` in a midi-domain repo must succeed when opted in, not raise 'Unknown domain'.""" root = _make_repo(tmp_path, domain="midi") script = ( "import pathlib; " "from muse.plugins.registry import resolve_plugin; " f"plugin = resolve_plugin(pathlib.Path({str(root)!r})); " "print(type(plugin).__name__)" ) result = subprocess.run( [sys.executable, "-c", script], capture_output=True, text=True, timeout=15, env={"PATH": "/usr/bin:/bin", "MUSE_ENABLE_MIDI": "1"}, ) assert result.returncode == 0, result.stderr assert result.stdout.strip() == "MidiPlugin"