gabriel / muse public

test_domain_schema.py file-level

at sha256:2 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:b adding issues docs to bust staging mpack prebuild cache. · gabriel · Jun 20, 2026
1 """Tests for domain schema declaration and plugin registry lookup.
2
3 Verifies that:
4 - ``MidiPlugin.schema()`` returns a fully-typed ``DomainSchema``.
5 - All 21 MIDI dimensions are declared with the correct schema types.
6 - Independence flags match the semantic MIDI merge model.
7 - The schema is JSON round-trippable (all values are JSON-serialisable).
8 - ``schema_for()`` in the plugin registry performs the correct lookup.
9 - The protocol assertion still holds after adding ``schema()``.
10 """
11
12 import json
13
14 import pytest
15
16 from muse._version import __version__
17 from muse.core.schema import DomainSchema
18 from muse.domain import MuseDomainPlugin
19 from muse.plugins.code.plugin import CodePlugin
20 from muse.plugins.midi.plugin import MidiPlugin
21 from muse.plugins.registry import registered_domains, schema_for
22
23 # ---------------------------------------------------------------------------
24 # Expected dimension layout for the 21-dimension MIDI schema
25 # ---------------------------------------------------------------------------
26
27 # (name, independent_merge, schema_kind)
28 _EXPECTED_DIMS: list[tuple[str, bool, str]] = [
29 # Expressive note content
30 ("notes", True, "sequence"),
31 ("pitch_bend", True, "tensor"),
32 ("channel_pressure", True, "tensor"),
33 ("poly_pressure", True, "tensor"),
34 # Named CC controllers
35 ("cc_modulation", True, "tensor"),
36 ("cc_volume", True, "tensor"),
37 ("cc_pan", True, "tensor"),
38 ("cc_expression", True, "tensor"),
39 ("cc_sustain", True, "tensor"),
40 ("cc_portamento", True, "tensor"),
41 ("cc_sostenuto", True, "tensor"),
42 ("cc_soft_pedal", True, "tensor"),
43 ("cc_reverb", True, "tensor"),
44 ("cc_chorus", True, "tensor"),
45 ("cc_other", True, "tensor"),
46 # Patch selection
47 ("program_change", True, "sequence"),
48 # Non-independent timeline metadata
49 ("tempo_map", False, "sequence"),
50 ("time_signatures", False, "sequence"),
51 # Tonal / annotation metadata
52 ("key_signatures", True, "sequence"),
53 ("markers", True, "sequence"),
54 # Track structure (non-independent)
55 ("track_structure", False, "tree"),
56 ]
57
58 _NON_INDEPENDENT = frozenset(
59 name for name, independent, _ in _EXPECTED_DIMS if not independent
60 )
61
62
63 # ---------------------------------------------------------------------------
64 # Fixtures
65 # ---------------------------------------------------------------------------
66
67
68 @pytest.fixture()
69 def midi_plugin() -> MidiPlugin:
70 return MidiPlugin()
71
72
73 @pytest.fixture()
74 def midi_schema(midi_plugin: MidiPlugin) -> DomainSchema:
75 return midi_plugin.schema()
76
77
78 # ===========================================================================
79 # MidiPlugin.schema() — top-level structure
80 # ===========================================================================
81
82
83 class TestMidiPluginSchema:
84 def test_schema_returns_dict(self, midi_schema: DomainSchema) -> None:
85 assert isinstance(midi_schema, dict)
86
87 def test_domain_is_midi(self, midi_schema: DomainSchema) -> None:
88 assert midi_schema["domain"] == "midi"
89
90 def test_schema_version_matches_package(self, midi_schema: DomainSchema) -> None:
91 assert midi_schema["schema_version"] == __version__
92
93 def test_merge_mode_is_three_way(self, midi_schema: DomainSchema) -> None:
94 assert midi_schema["merge_mode"] == "three_way"
95
96 def test_description_is_non_empty(self, midi_schema: DomainSchema) -> None:
97 assert isinstance(midi_schema["description"], str)
98 assert len(midi_schema["description"]) > 0
99
100 def test_top_level_is_set_schema(self, midi_schema: DomainSchema) -> None:
101 top = midi_schema["top_level"]
102 assert top["kind"] == "set"
103
104 def test_top_level_element_type(self, midi_schema: DomainSchema) -> None:
105 top = midi_schema["top_level"]
106 assert top["kind"] == "set"
107 assert top["element_type"] == "audio_file"
108 assert top["identity"] == "by_content"
109
110
111 # ===========================================================================
112 # 21-dimension layout
113 # ===========================================================================
114
115
116 class TestMidiDimensions:
117 def test_exactly_21_dimensions(self, midi_schema: DomainSchema) -> None:
118 assert len(midi_schema["dimensions"]) == 21
119
120 def test_all_expected_dimension_names_present(self, midi_schema: DomainSchema) -> None:
121 names = {d["name"] for d in midi_schema["dimensions"]}
122 expected = {name for name, _, _ in _EXPECTED_DIMS}
123 assert names == expected
124
125 def test_dimension_order_matches_spec(self, midi_schema: DomainSchema) -> None:
126 names = [d["name"] for d in midi_schema["dimensions"]]
127 expected = [name for name, _, _ in _EXPECTED_DIMS]
128 assert names == expected
129
130 @pytest.mark.parametrize("name,independent,kind", _EXPECTED_DIMS)
131 def test_dimension_independence(
132 self, midi_schema: DomainSchema, name: str, independent: bool, kind: str
133 ) -> None:
134 dim = next(d for d in midi_schema["dimensions"] if d["name"] == name)
135 assert dim["independent_merge"] is independent, (
136 f"Dimension '{name}': expected independent_merge={independent}"
137 )
138
139 @pytest.mark.parametrize("name,independent,kind", _EXPECTED_DIMS)
140 def test_dimension_schema_kind(
141 self, midi_schema: DomainSchema, name: str, independent: bool, kind: str
142 ) -> None:
143 dim = next(d for d in midi_schema["dimensions"] if d["name"] == name)
144 assert dim["schema"]["kind"] == kind, (
145 f"Dimension '{name}': expected schema kind '{kind}', got '{dim['schema']['kind']}'"
146 )
147
148 def test_all_dimensions_have_description(self, midi_schema: DomainSchema) -> None:
149 for dim in midi_schema["dimensions"]:
150 assert isinstance(dim.get("description"), str), (
151 f"Dimension '{dim['name']}' missing description"
152 )
153 assert len(dim["description"]) > 0
154
155 def test_non_independent_set(self, midi_schema: DomainSchema) -> None:
156 non_indep = {
157 d["name"] for d in midi_schema["dimensions"] if not d["independent_merge"]
158 }
159 assert non_indep == _NON_INDEPENDENT
160
161 def test_notes_dimension_sequence_fields(self, midi_schema: DomainSchema) -> None:
162 notes = next(d for d in midi_schema["dimensions"] if d["name"] == "notes")
163 schema = notes["schema"]
164 assert schema["kind"] == "sequence"
165 assert schema["element_type"] == "note_event"
166 assert schema["diff_algorithm"] == "lcs"
167
168 def test_cc_dimensions_are_tensor_float32(self, midi_schema: DomainSchema) -> None:
169 cc_names = {name for name, _, kind in _EXPECTED_DIMS if kind == "tensor"}
170 for dim in midi_schema["dimensions"]:
171 if dim["name"] in cc_names:
172 s = dim["schema"]
173 assert s["kind"] == "tensor"
174 assert s["dtype"] == "float32"
175 assert s["diff_mode"] == "sparse"
176
177 def test_track_structure_is_tree(self, midi_schema: DomainSchema) -> None:
178 ts = next(d for d in midi_schema["dimensions"] if d["name"] == "track_structure")
179 schema = ts["schema"]
180 assert schema["kind"] == "tree"
181 assert schema["node_type"] == "track_node"
182 assert schema["diff_algorithm"] == "zhang_shasha"
183
184
185 # ===========================================================================
186 # JSON round-trip
187 # ===========================================================================
188
189
190 class TestSchemaJsonRoundtrip:
191 def test_schema_is_json_serialisable(self, midi_schema: DomainSchema) -> None:
192 serialised = json.dumps(midi_schema)
193 restored = json.loads(serialised)
194 assert restored["domain"] == midi_schema["domain"]
195 assert restored["schema_version"] == midi_schema["schema_version"]
196 assert len(restored["dimensions"]) == len(midi_schema["dimensions"])
197 assert restored["top_level"]["kind"] == midi_schema["top_level"]["kind"]
198
199 def test_all_dimension_schemas_survive_roundtrip(self, midi_schema: DomainSchema) -> None:
200 serialised = json.dumps(midi_schema)
201 restored = json.loads(serialised)
202 original_kinds = {d["name"]: d["schema"]["kind"] for d in midi_schema["dimensions"]}
203 restored_kinds = {d["name"]: d["schema"]["kind"] for d in restored["dimensions"]}
204 assert original_kinds == restored_kinds
205
206
207 # ===========================================================================
208 # Plugin registry schema lookup
209 # ===========================================================================
210
211
212 class TestPluginRegistrySchemaLookup:
213 def test_schema_for_code_returns_domain_schema(self) -> None:
214 result = schema_for("code")
215 assert result is not None
216 assert result["domain"] == "code"
217
218 def test_schema_for_unknown_domain_returns_none(self) -> None:
219 result = schema_for("nonexistent_domain_xyz")
220 assert result is None
221
222 def test_schema_for_midi_returns_none_while_suspended(self) -> None:
223 """MIDI is suspended from the registry — schema_for must return None."""
224 assert schema_for("midi") is None
225
226 def test_schema_for_matches_direct_plugin_call(self) -> None:
227 plugin = CodePlugin()
228 direct = plugin.schema()
229 via_registry = schema_for("code")
230 assert via_registry is not None
231 assert via_registry["domain"] == direct["domain"]
232 assert via_registry["schema_version"] == direct["schema_version"]
233 assert len(via_registry["dimensions"]) == len(direct["dimensions"])
234
235 def test_registered_domains_contains_code(self) -> None:
236 assert "code" in registered_domains()
237
238 def test_midi_not_in_registry_while_suspended(self) -> None:
239 """MIDI domain is temporarily suspended pending its own security audit."""
240 assert "midi" not in registered_domains()
241
242 def test_music_key_not_in_registry(self) -> None:
243 """Ensure the old 'music' key was fully removed."""
244 assert "music" not in registered_domains()
245
246 def test_schema_for_all_registered_domains_returns_non_none(self) -> None:
247 for domain in registered_domains():
248 result = schema_for(domain)
249 assert result is not None, f"schema_for({domain!r}) returned None"
250
251
252 # ===========================================================================
253 # Protocol conformance
254 # ===========================================================================
255
256
257 class TestProtocolConformance:
258 def test_midi_plugin_satisfies_protocol(self) -> None:
259 plugin = MidiPlugin()
260 assert isinstance(plugin, MuseDomainPlugin)
261
262 def test_schema_method_is_callable(self) -> None:
263 plugin = MidiPlugin()
264 assert callable(plugin.schema)
265
266 def test_schema_returns_domain_schema(self) -> None:
267 plugin = MidiPlugin()
268 result = plugin.schema()
269 assert isinstance(result, dict)
270 assert "domain" in result
271 assert "dimensions" in result