""" Unit tests for Settings service. Tests cover: - Settings initialization and loading - Getting and setting values - Default values - Plugin enable/disable - Settings reset - Signal emission """ import pytest import json from pathlib import Path from unittest.mock import patch, mock_open, MagicMock from core.settings import Settings, get_settings @pytest.mark.unit class TestSettingsInitialization: """Test Settings initialization.""" def test_default_config_file(self): """Test default configuration file path.""" with patch('core.settings.Path.exists', return_value=False): settings = Settings() assert settings.config_file == Path("data/settings.json") def test_custom_config_file(self, tmp_path): """Test custom configuration file path.""" config_file = tmp_path / "custom_settings.json" settings = Settings(config_file=str(config_file)) assert settings.config_file == config_file def test_load_existing_settings(self, tmp_path): """Test loading existing settings file.""" config_file = tmp_path / "settings.json" saved_settings = { "overlay_enabled": False, "custom_key": "custom_value" } with open(config_file, 'w') as f: json.dump(saved_settings, f) settings = Settings(config_file=str(config_file)) assert settings.get("overlay_enabled") is False assert settings.get("custom_key") == "custom_value" def test_load_corrupted_settings(self, tmp_path): """Test loading corrupted settings file.""" config_file = tmp_path / "settings.json" with open(config_file, 'w') as f: f.write("not valid json") # Should use defaults, not raise settings = Settings(config_file=str(config_file)) assert settings.get("overlay_enabled") is True # Default value @pytest.mark.unit class TestGetAndSet: """Test getting and setting settings.""" def test_get_existing_value(self, tmp_path): """Test getting an existing setting value.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) # overlay_enabled is in defaults assert settings.get("overlay_enabled") is True def test_get_with_default(self, tmp_path): """Test getting a value with custom default.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) result = settings.get("nonexistent_key", "default_value") assert result == "default_value" def test_set_value(self, tmp_path): """Test setting a value.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) settings.set("test_key", "test_value") assert settings.get("test_key") == "test_value" def test_set_saves_to_file(self, tmp_path): """Test that set() saves to file.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) settings.set("test_key", "test_value") # Read file directly with open(config_file, 'r') as f: saved = json.load(f) assert saved["test_key"] == "test_value" @pytest.mark.unit class TestDefaultValues: """Test default values.""" def test_overlay_defaults(self, tmp_path): """Test overlay-related defaults.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) assert settings.get("overlay_enabled") is True assert settings.get("overlay_opacity") == 0.9 assert settings.get("overlay_theme") == "dark" def test_hotkey_defaults(self, tmp_path): """Test hotkey defaults.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) assert settings.get("hotkey_toggle") == "ctrl+shift+u" assert settings.get("hotkey_search") == "ctrl+shift+f" assert settings.get("hotkey_calculator") == "ctrl+shift+c" def test_plugin_defaults(self, tmp_path): """Test plugin-related defaults.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) enabled = settings.get("enabled_plugins") assert "universal_search" in enabled assert "calculator" in enabled assert "spotify_controller" in enabled def test_all_defaults(self, tmp_path): """Test that all defaults are present.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) all_settings = settings.all_settings() # Check all default keys are present for key in Settings.DEFAULTS: assert key in all_settings @pytest.mark.unit class TestPluginManagement: """Test plugin enable/disable functionality.""" def test_is_plugin_enabled(self, tmp_path): """Test checking if plugin is enabled.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) assert settings.is_plugin_enabled("universal_search") is True assert settings.is_plugin_enabled("nonexistent_plugin") is False def test_enable_plugin(self, tmp_path): """Test enabling a plugin.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) # First disable settings.disable_plugin("test_plugin") assert settings.is_plugin_enabled("test_plugin") is False # Then enable settings.enable_plugin("test_plugin") assert settings.is_plugin_enabled("test_plugin") is True def test_disable_plugin(self, tmp_path): """Test disabling a plugin.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) settings.enable_plugin("test_plugin") assert settings.is_plugin_enabled("test_plugin") is True settings.disable_plugin("test_plugin") assert settings.is_plugin_enabled("test_plugin") is False def test_disable_moves_to_disabled_list(self, tmp_path): """Test that disabling moves plugin to disabled list.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) settings.enable_plugin("test_plugin") settings.disable_plugin("test_plugin") disabled = settings.get("disabled_plugins") assert "test_plugin" in disabled def test_enable_removes_from_disabled_list(self, tmp_path): """Test that enabling removes plugin from disabled list.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) settings.enable_plugin("test_plugin") settings.disable_plugin("test_plugin") assert "test_plugin" in settings.get("disabled_plugins") settings.enable_plugin("test_plugin") assert "test_plugin" not in settings.get("disabled_plugins") @pytest.mark.unit class TestReset: """Test settings reset functionality.""" def test_reset_single_key(self, tmp_path): """Test resetting a single key to default.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) # Change value settings.set("overlay_enabled", False) assert settings.get("overlay_enabled") is False # Reset settings.reset("overlay_enabled") assert settings.get("overlay_enabled") is True # Back to default def test_reset_all(self, tmp_path): """Test resetting all settings to defaults.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) # Change multiple values settings.set("overlay_enabled", False) settings.set("overlay_opacity", 0.5) settings.set("custom_key", "custom_value") # Reset all settings.reset() assert settings.get("overlay_enabled") is True assert settings.get("overlay_opacity") == 0.9 assert settings.get("custom_key") is None # Non-default key removed @pytest.mark.unit class TestSignals: """Test Qt signal emission.""" def test_signal_emitted_on_set(self, tmp_path): """Test that signal is emitted when setting a value.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) received = [] def on_setting_changed(key, value): received.append((key, value)) settings.setting_changed.connect(on_setting_changed) settings.set("test_key", "test_value") assert len(received) == 1 assert received[0] == ("test_key", "test_value") def test_signal_emitted_on_reset(self, tmp_path): """Test that signal is emitted when resetting.""" config_file = tmp_path / "settings.json" settings = Settings(config_file=str(config_file)) received = [] def on_setting_changed(key, value): received.append((key, value)) settings.set("overlay_enabled", False) settings.setting_changed.connect(on_setting_changed) settings.reset("overlay_enabled") assert len(received) == 1 assert received[0][0] == "overlay_enabled" assert received[0][1] is True # Default value @pytest.mark.unit class TestSingleton: """Test Settings singleton.""" def test_singleton_instance(self): """Test that get_settings returns same instance.""" # Note: Settings is not strictly a singleton, but get_settings caches with patch('core.settings.Path.exists', return_value=False): settings1 = get_settings() settings2 = get_settings() assert settings1 is settings2