"""Tests for role-aware preset system (presets-pack). Covers PresetTransformer, role-aware PLUGIN_PRESETS lookup, and thread-through via make_plugin → PluginDef.role → _build_plugin. """ from __future__ import annotations import base64 import json import pytest from src.reaper_builder.preset_transformer import PresetTransformer from src.reaper_builder import PLUGIN_PRESETS, PLUGIN_REGISTRY # --------------------------------------------------------------------------- # PresetTransformer unit tests # --------------------------------------------------------------------------- class TestPresetTransformer: """PresetTransformer derives role-specific preset data.""" def test_derive_serum_returns_list(self): """derive() for Serum_2 returns a list of chunks.""" default = PLUGIN_PRESETS.get(("Serum_2", "")) assert default is not None, "Serum_2 default preset must exist" result = PresetTransformer.derive("Serum_2", default, "bass") assert isinstance(result, list) assert len(result) == len(default) assert all(isinstance(c, str) for c in result) def test_derive_decapitator_returns_list(self): """derive() for Decapitator returns a list of chunks.""" default = PLUGIN_PRESETS.get(("Decapitator", "")) assert default is not None, "Decapitator default preset must exist" result = PresetTransformer.derive("Decapitator", default, "drums") assert isinstance(result, list) assert len(result) == len(default) def test_derive_omnisphere_returns_list(self): """derive() for Omnisphere returns a list of chunks.""" default = PLUGIN_PRESETS.get(("Omnisphere", "")) assert default is not None, "Omnisphere default preset must exist" result = PresetTransformer.derive("Omnisphere", default, "pad") assert isinstance(result, list) assert len(result) == len(default) def test_derive_unknown_plugin_returns_default(self): """derive() for unsupported plugin returns original chunks.""" chunks = ["mock_chunk_1", "mock_chunk_2"] result = PresetTransformer.derive("NonexistentPlugin", chunks, "lead") assert result == chunks def test_derive_preserves_chunk_count(self): """derive() output has same number of chunks as input.""" for plugin in ["Serum_2", "Decapitator", "Omnisphere"]: default = PLUGIN_PRESETS.get((plugin, "")) if not default: continue for role in ["bass", "lead", "drums", "pad"]: result = PresetTransformer.derive(plugin, default, role) assert len(result) == len(default), ( f"{plugin}/{role} chunk count mismatch: " f"got {len(result)}, expected {len(default)}" ) # --------------------------------------------------------------------------- # Role-aware preset structure tests # --------------------------------------------------------------------------- class TestRoleAwarePresets: """PLUGIN_PRESETS is structured as {(plugin, role): chunks}.""" def test_default_role_entries_exist(self): """All known plugins have a "" (default) role entry.""" flat_keys = set() for (name, role), _ in PLUGIN_PRESETS.items(): if role == "": flat_keys.add(name) # At minimum, the multi-role targets must have defaults for name in ["Serum_2", "Decapitator", "Omnisphere"]: assert name in flat_keys, f"{name} must have default role entry" def test_role_specific_entries_exist(self): """Multi-role plugins have their role-specific entries.""" roles = { "Serum_2": ["bass", "lead"], "Decapitator": ["drumloop", "bass", "clap", "perc"], "Omnisphere": ["chords", "pad"], } for plugin, expected_roles in roles.items(): for role in expected_roles: assert (plugin, role) in PLUGIN_PRESETS, ( f"Missing ({plugin}, {role}) in PLUGIN_PRESETS" ) def test_role_entries_non_empty(self): """Role-specific entries contain non-empty preset data.""" for (name, role), chunks in PLUGIN_PRESETS.items(): if role != "" and name in ("Serum_2", "Decapitator", "Omnisphere"): assert len(chunks) > 0, ( f"({name}, {role}) has empty preset data" ) def test_unknown_role_falls_back_to_default(self): """Role not present in PLUGIN_PRESETS → fall back to default.""" # Simulate: a plugin has only default entry, no "pad" role # The lookup should return the "" entry from src.reaper_builder import _resolve_preset # Gullfoss_Master doesn't have multi-role entries, only "" result = _resolve_preset("Gullfoss_Master", "pad") default = PLUGIN_PRESETS.get(("Gullfoss_Master", "")) assert result == default, ( "Unknown role should fall back to default preset" ) # --------------------------------------------------------------------------- # Integration: make_plugin + role threading # --------------------------------------------------------------------------- class TestMakePluginRoleThreading: """make_plugin with role correctly sets PluginDef.role and preset_data.""" def test_make_plugin_with_role_sets_role_field(self): """make_plugin(key, idx, role=...) sets PluginDef.role.""" from scripts.compose import make_plugin p = make_plugin("Serum_2", 0, role="bass") assert p.role == "bass" assert p.preset_data is not None def test_make_plugin_without_role_defaults_to_empty(self): """make_plugin(key, idx) without role sets role="" (backward compat).""" from scripts.compose import make_plugin p = make_plugin("Decapitator", 0) assert p.role == "" def test_make_plugin_different_roles_lookup_correct_entries(self): """Different roles resolve to their respective PLUGIN_PRESETS entries.""" from scripts.compose import make_plugin # Both should return data — in MVP they're identical but structure is correct p_bass = make_plugin("Serum_2", 0, role="bass") p_lead = make_plugin("Serum_2", 0, role="lead") # Both should have non-None preset data assert p_bass.preset_data is not None, "bass role should have preset_data" assert p_lead.preset_data is not None, "lead role should have preset_data" # Both should be lists with content assert isinstance(p_bass.preset_data, list) assert isinstance(p_lead.preset_data, list) assert len(p_bass.preset_data) > 0 assert len(p_lead.preset_data) > 0 def test_make_plugin_unknown_role_falls_back(self): """Unknown role returns preset from "" entry (if available).""" from scripts.compose import make_plugin # "pad" role is not valid for Serum_2 (Omnisphere handles pad) p = make_plugin("Serum_2", 0, role="pad") # Should still get the default Serum_2 preset assert p.preset_data is not None assert p.role == "pad" # --------------------------------------------------------------------------- # Backward compatibility # --------------------------------------------------------------------------- class TestBackwardCompatibility: """Existing behavior preserved with no role.""" def test_make_plugin_known_key_still_works(self): """make_plugin with known registry key (no role) works as before.""" from scripts.compose import make_plugin p = make_plugin("Decapitator", 0) assert p.name == "Decapitator" assert p.index == 0 assert p.role == "" def test_make_plugin_unknown_key_still_works(self): """make_plugin with unknown key (no role) returns PluginDef.""" from scripts.compose import make_plugin p = make_plugin("NonExistent", 2) assert p.name == "NonExistent" assert p.index == 2 assert p.role == ""