"""Tests for section builder — SectionDef, build_fx_chain, effect alias mapping.""" import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parents[1])) import pytest from src.core.schema import SectionDef, PluginDef class TestSectionDef: """Test SectionDef dataclass.""" def test_section_def_instantiation(self): """SectionDef creates with name, bars, energy.""" section = SectionDef(name="chorus", bars=8, energy=0.9) assert section.name == "chorus" assert section.bars == 8 assert section.energy == 0.9 # velocity_mult and vol_mult default to 1.0 (not derived from energy) assert section.velocity_mult == 1.0 assert section.vol_mult == 1.0 def test_section_def_default_energy(self): """SectionDef defaults energy to 0.5, velocity_mult/vol_mult to 1.0.""" section = SectionDef(name="verse", bars=8) assert section.energy == 0.5 assert section.velocity_mult == 1.0 assert section.vol_mult == 1.0 def test_section_def_custom_mults(self): """SectionDef accepts custom velocity_mult and vol_mult via __init__ args.""" section = SectionDef( name="intro", bars=4, energy=0.3, velocity_mult=0.4, vol_mult=0.6 ) assert section.velocity_mult == 0.4 assert section.vol_mult == 0.6 class TestVST3Effects: """Test VST3 premium plugin mappings.""" def test_vst3_effects_defined(self): """_VST3_EFFECTS maps effect names to VST3 plugins.""" from scripts.compose import _VST3_EFFECTS assert "Pro-Q 3" in _VST3_EFFECTS assert "Pro-C 2" in _VST3_EFFECTS assert "Pro-R 2" in _VST3_EFFECTS assert "Timeless 3" in _VST3_EFFECTS def test_fruity_eq_maps_to_proq3(self): """Fruity Parametric EQ 2 → FabFilter Pro-Q 3 via normalization.""" from scripts.compose import _VST3_EFFECTS # Fruity Parametric EQ 2 normalizes to Pro-Q 3 registry_key, filename = _VST3_EFFECTS["Pro-Q 3"] assert registry_key == "FabFilter Pro-Q 3" assert filename == "FabFilter Pro-Q 3.vst3" def test_fruity_compressor_maps_to_proc2(self): """Fruity Compressor → FabFilter Pro-C 2 via normalization.""" from scripts.compose import _VST3_EFFECTS registry_key, filename = _VST3_EFFECTS["Pro-C 2"] assert registry_key == "FabFilter Pro-C 2" assert filename == "FabFilter Pro-C 2.vst3" def test_pro_r_maps_to_pror2(self): """Pro-R 2 → FabFilter Pro-R 2.""" from scripts.compose import _VST3_EFFECTS registry_key, filename = _VST3_EFFECTS["Pro-R 2"] assert registry_key == "FabFilter Pro-R 2" assert filename == "FabFilter Pro-R 2.vst3" def test_unknown_effect_returns_none(self): """Unknown effect names return no VST3 info.""" from scripts.compose import _VST3_EFFECTS assert _VST3_EFFECTS.get("Some Unknown Plugin") is None class TestBuildFxChain: """Test build_fx_chain function.""" def test_build_fx_chain_drums(self): """build_fx_chain returns PluginDef list for drums role.""" from scripts.compose import build_fx_chain genre_config = { "mix": { "per_role": { "drums": { "effects": ["Fruity Parametric EQ 2", "Fruity Compressor"], } } } } plugins = build_fx_chain("drums", genre_config, []) assert len(plugins) == 2 # Fruity Parametric EQ 2 → Pro-Q 3 assert "FabFilter" in plugins[0].name assert ".vst3" in plugins[0].path # Fruity Compressor → Pro-C 2 assert "FabFilter" in plugins[1].name def test_build_fx_chain_bass(self): """build_fx_chain returns PluginDef list for bass role.""" from scripts.compose import build_fx_chain genre_config = { "mix": { "per_role": { "bass": { "effects": ["Fruity Parametric EQ 2", "Saturn 2"], } } } } plugins = build_fx_chain("bass", genre_config, []) assert len(plugins) == 2 # Saturn 2 → FabFilter Saturn 2 assert "Saturn" in plugins[1].name def test_build_fx_chain_empty_effects(self): """build_fx_chain returns empty list when no effects configured.""" from scripts.compose import build_fx_chain genre_config = {"mix": {"per_role": {}}} plugins = build_fx_chain("drums", genre_config, []) assert plugins == [] def test_build_fx_chain_unknown_effect_uses_name(self): """Unknown effect names are used as-is.""" from scripts.compose import build_fx_chain genre_config = { "mix": { "per_role": { "lead": { "effects": ["Some Unknown FX"], } } } } plugins = build_fx_chain("lead", genre_config, []) # Unknown effects are skipped (not added to plugins) assert len(plugins) == 0 class TestInstrumentPlugins: """Test instrument plugin helpers (Serum 2, Omnisphere).""" def test_serum2_plugin_def(self): """serum2() returns PluginDef with registry key name.""" from scripts.compose import serum2 plugin = serum2() assert plugin.name == "Serum2" assert plugin.path == "Serum2.vst3" assert plugin.index == 0 def test_omnisphere_plugin_def(self): """omnisphere() returns PluginDef with registry key name.""" from scripts.compose import omnisphere plugin = omnisphere() assert plugin.name == "Omnisphere" assert plugin.path == "Omnisphere.vst3" assert plugin.index == 0 class TestCreateReturnTracks: """Test create_return_tracks function.""" def test_create_return_tracks_returns_two(self): """create_return_tracks returns [Reverb, Delay] tracks.""" from scripts.compose import create_return_tracks tracks = create_return_tracks() assert len(tracks) == 2 assert tracks[0].name == "Reverb" assert tracks[1].name == "Delay" def test_reverb_track_has_pro_r2(self): """Reverb return track has FabFilter Pro-R 2 plugin.""" from scripts.compose import create_return_tracks tracks = create_return_tracks() reverb = tracks[0] assert len(reverb.plugins) == 1 assert "FabFilter" in reverb.plugins[0].name assert ".vst3" in reverb.plugins[0].path def test_delay_track_has_timeless3(self): """Delay return track has FabFilter Timeless 3 plugin.""" from scripts.compose import create_return_tracks tracks = create_return_tracks() delay = tracks[1] assert len(delay.plugins) == 1 assert "Timeless" in delay.plugins[0].name assert ".vst3" in delay.plugins[0].path def test_return_tracks_have_volume_0_7(self): """Return tracks have volume 0.7.""" from scripts.compose import create_return_tracks tracks = create_return_tracks() for t in tracks: assert t.volume == 0.7