gabriel / muse public

test_plugin_registry.py file-level

at sha256:b · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:b docs: fix systemic --format→--json drift and broken plumbing-namespace … · gabriel · Sep 12, 2026
1 """Unit tests for muse.plugins.registry — resolve_plugin, read_domain, registered_domains."""
2
3 import json
4 import pathlib
5 import subprocess
6 import sys
7
8 import pytest
9
10 from muse._version import __version__
11 from muse.core.paths import muse_dir, repo_json_path
12 from muse.core.errors import MuseCLIError
13 from muse.domain import MuseDomainPlugin
14 from muse.plugins.code.plugin import CodePlugin
15 from muse.plugins.registry import read_domain, registered_domains, resolve_plugin
16
17
18 def _make_repo(tmp_path: pathlib.Path, domain: str = "code") -> pathlib.Path:
19 """Scaffold a minimal .muse/repo.json so registry helpers can run."""
20 dot_muse = muse_dir(tmp_path)
21 dot_muse.mkdir()
22 repo_json_path(tmp_path).write_text(
23 json.dumps({"repo_id": "test-id", "schema_version": __version__, "domain": domain})
24 )
25 return tmp_path
26
27
28 class TestReadDomain:
29 def test_returns_stored_domain(self, tmp_path: pathlib.Path) -> None:
30 root = _make_repo(tmp_path, domain="code")
31 assert read_domain(root) == "code"
32
33 def test_defaults_to_code_when_key_missing(self, tmp_path: pathlib.Path) -> None:
34 dot_muse = muse_dir(tmp_path)
35 dot_muse.mkdir()
36 repo_json_path(tmp_path).write_text(json.dumps({"repo_id": "x"}))
37 assert read_domain(tmp_path) == "code"
38
39 def test_defaults_to_code_when_repo_json_absent(self, tmp_path: pathlib.Path) -> None:
40 muse_dir(tmp_path).mkdir()
41 assert read_domain(tmp_path) == "code"
42
43 def test_defaults_to_code_when_muse_dir_absent(self, tmp_path: pathlib.Path) -> None:
44 assert read_domain(tmp_path) == "code"
45
46
47 class TestResolvePlugin:
48 def test_returns_code_plugin_for_code_domain(self, tmp_path: pathlib.Path) -> None:
49 root = _make_repo(tmp_path, domain="code")
50 plugin = resolve_plugin(root)
51 assert isinstance(plugin, CodePlugin)
52
53 def test_returned_plugin_satisfies_protocol(self, tmp_path: pathlib.Path) -> None:
54 root = _make_repo(tmp_path, domain="code")
55 plugin = resolve_plugin(root)
56 assert isinstance(plugin, MuseDomainPlugin)
57
58 def test_raises_for_unknown_domain(self, tmp_path: pathlib.Path) -> None:
59 root = _make_repo(tmp_path, domain="unknown-domain")
60 with pytest.raises(MuseCLIError, match="unknown-domain"):
61 resolve_plugin(root)
62
63 def test_raises_error_mentions_registered_domains(self, tmp_path: pathlib.Path) -> None:
64 root = _make_repo(tmp_path, domain="bogus")
65 with pytest.raises(MuseCLIError, match="code"):
66 resolve_plugin(root)
67
68 def test_defaults_to_code_plugin_when_no_domain_key(self, tmp_path: pathlib.Path) -> None:
69 dot_muse = muse_dir(tmp_path)
70 dot_muse.mkdir()
71 repo_json_path(tmp_path).write_text(json.dumps({"repo_id": "x"}))
72 plugin = resolve_plugin(tmp_path)
73 assert isinstance(plugin, CodePlugin)
74
75
76 class TestRegisteredDomains:
77 def test_includes_code(self) -> None:
78 assert "code" in registered_domains()
79
80 def test_midi_suspended_by_default(self) -> None:
81 """MIDI domain is suspended by default, pending its own security and
82 performance audit — see MUSE_ENABLE_MIDI for the local opt-in gate."""
83 assert "midi" not in registered_domains()
84
85 def test_returns_sorted_list(self) -> None:
86 domains = registered_domains()
87 assert domains == sorted(domains)
88
89 def test_returns_list_of_strings(self) -> None:
90 domains = registered_domains()
91 assert all(isinstance(d, str) for d in domains)
92
93
94 class TestMidiOptInGate:
95 """The midi domain stays off by default (see test_midi_suspended_by_default
96 above), but is fully maintained and can be opted into locally — e.g. for a
97 demo — via MUSE_ENABLE_MIDI, without changing the default registry that
98 ships to every other user. _REGISTRY is built once at import time, so
99 each case needs its own fresh interpreter."""
100
101 @staticmethod
102 def _registered_domains_with_env(env_extra: dict[str, str]) -> list[str]:
103 result = subprocess.run(
104 [sys.executable, "-c", "from muse.plugins.registry import registered_domains; print(','.join(registered_domains()))"],
105 capture_output=True,
106 text=True,
107 timeout=15,
108 env={**{"PATH": "/usr/bin:/bin"}, **env_extra},
109 )
110 assert result.returncode == 0, result.stderr
111 return result.stdout.strip().split(",")
112
113 def test_midi_absent_without_env_var(self) -> None:
114 domains = self._registered_domains_with_env({})
115 assert "midi" not in domains
116
117 def test_midi_present_when_env_var_set(self) -> None:
118 domains = self._registered_domains_with_env({"MUSE_ENABLE_MIDI": "1"})
119 assert "midi" in domains
120
121 def test_resolve_plugin_works_for_midi_when_opted_in(self, tmp_path: pathlib.Path) -> None:
122 """The exact failure this gate fixes: `muse commit` in a midi-domain
123 repo must succeed when opted in, not raise 'Unknown domain'."""
124 root = _make_repo(tmp_path, domain="midi")
125 script = (
126 "import pathlib; "
127 "from muse.plugins.registry import resolve_plugin; "
128 f"plugin = resolve_plugin(pathlib.Path({str(root)!r})); "
129 "print(type(plugin).__name__)"
130 )
131 result = subprocess.run(
132 [sys.executable, "-c", script],
133 capture_output=True,
134 text=True,
135 timeout=15,
136 env={"PATH": "/usr/bin:/bin", "MUSE_ENABLE_MIDI": "1"},
137 )
138 assert result.returncode == 0, result.stderr
139 assert result.stdout.strip() == "MidiPlugin"