"""Tests for the Voice Analysis settings — persistence + MCP config tools. The config file (~/.fcp-mcp-server/config.json) is redirected to a tmp_path so these never touch the developer's real settings. """ import json import pytest from fcpxml import model_manager @pytest.fixture(autouse=True) def isolated_config(tmp_path, monkeypatch): """Point model_manager's config file at a throwaway directory.""" monkeypatch.setattr(model_manager, "_CONFIG_DIR", tmp_path) monkeypatch.setattr(model_manager, "_CONFIG_FILE", tmp_path / "config.json") return tmp_path / "config.json" class TestLoadVoiceAnalysisConfig: def test_defaults_when_nothing_stored(self): cfg = model_manager.load_voice_analysis_config() assert cfg == model_manager.DEFAULT_VOICE_ANALYSIS_CONFIG def test_defaults_are_not_shared_mutable_state(self): cfg = model_manager.load_voice_analysis_config() cfg["emphasis_weights"]["energy"] = 0.99 fresh = model_manager.load_voice_analysis_config() assert fresh["emphasis_weights"]["energy"] == 0.30 def test_malformed_stored_values_fall_back_to_defaults(self, isolated_config): isolated_config.write_text(json.dumps({"voice_analysis": {"energy_threshold": "loud"}})) cfg = model_manager.load_voice_analysis_config() assert cfg["energy_threshold"] == 0.5 def test_non_dict_stored_value_falls_back(self, isolated_config): isolated_config.write_text(json.dumps({"voice_analysis": "nonsense"})) assert model_manager.load_voice_analysis_config() == model_manager.DEFAULT_VOICE_ANALYSIS_CONFIG def test_thresholds_are_clamped_to_unit_range(self, isolated_config): isolated_config.write_text( json.dumps({"voice_analysis": {"energy_threshold": 5.0, "emphasis_floor": -2.0}}) ) cfg = model_manager.load_voice_analysis_config() assert cfg["energy_threshold"] == 1.0 assert cfg["emphasis_floor"] == 0.0 class TestSaveVoiceAnalysisConfig: def test_saves_and_reloads(self): model_manager.save_voice_analysis_config(energy_threshold=0.7, emotion_enabled=True) cfg = model_manager.load_voice_analysis_config() assert cfg["energy_threshold"] == 0.7 assert cfg["emotion_enabled"] is True def test_omitted_fields_keep_current_value(self): model_manager.save_voice_analysis_config(energy_threshold=0.7) model_manager.save_voice_analysis_config(emphasis_floor=0.9) cfg = model_manager.load_voice_analysis_config() assert cfg["energy_threshold"] == 0.7 assert cfg["emphasis_floor"] == 0.9 def test_partial_weight_update_keeps_other_weights(self): model_manager.save_voice_analysis_config(emphasis_weights={"energy": 0.55}) weights = model_manager.load_voice_analysis_config()["emphasis_weights"] assert weights["energy"] == 0.55 assert weights["pitch_variation"] == 0.25 def test_unknown_weight_key_is_ignored(self): model_manager.save_voice_analysis_config(emphasis_weights={"loudness": 9.0}) weights = model_manager.load_voice_analysis_config()["emphasis_weights"] assert "loudness" not in weights def test_does_not_clobber_unrelated_config_keys(self): model_manager.save_hf_token("tok123") model_manager.save_voice_analysis_config(energy_threshold=0.7) assert model_manager.load_hf_token() == "tok123" def test_returns_full_merged_config(self): returned = model_manager.save_voice_analysis_config(energy_threshold=0.7) assert returned == model_manager.load_voice_analysis_config() class TestVoiceAnalysisConfigTools: async def test_get_reports_current_settings(self): from server import handle_get_voice_analysis_config result = await handle_get_voice_analysis_config({}) text = result[0].text assert "Voice Analysis Settings" in text assert "Emphasis Weights" in text async def test_save_persists_and_echoes_back(self): from server import handle_save_voice_analysis_config result = await handle_save_voice_analysis_config( {"energy_threshold": 0.8, "emotion_enabled": True} ) assert "0.80" in result[0].text cfg = model_manager.load_voice_analysis_config() assert cfg["energy_threshold"] == 0.8 assert cfg["emotion_enabled"] is True async def test_save_with_no_arguments_is_a_noop(self): from server import handle_save_voice_analysis_config before = model_manager.load_voice_analysis_config() await handle_save_voice_analysis_config({}) assert model_manager.load_voice_analysis_config() == before