"""Unit tests for config_manager.py - Configuration loading, validation, defaults.""" import json import tempfile from pathlib import Path import pytest from src.core.providers.config_manager import ( ProviderConfigManager, ProviderSettings, get_config_manager, ) class TestProviderSettings: """Test ProviderSettings dataclass.""" def test_default_values(self): """ProviderSettings should have sensible defaults.""" settings = ProviderSettings(name="test_provider") assert settings.name == "test_provider" assert settings.enabled is True assert settings.priority == 0 assert settings.timeout_seconds == 30 assert settings.max_retries == 3 assert settings.retry_delay_seconds == 1.0 assert settings.max_concurrent_downloads == 3 assert settings.bandwidth_limit_mbps is None assert settings.custom_headers is None assert settings.custom_params is None def test_to_dict(self): """to_dict should convert settings to dict, excluding None values.""" settings = ProviderSettings( name="test", enabled=True, priority=1, ) result = settings.to_dict() assert result["name"] == "test" assert result["enabled"] is True assert result["priority"] == 1 # None values should be excluded assert "bandwidth_limit_mbps" not in result assert "custom_headers" not in result def test_to_dict_with_optional_fields(self): """to_dict with optional fields set should include them.""" settings = ProviderSettings( name="test", bandwidth_limit_mbps=10.0, custom_headers={"X-Custom": "value"}, ) result = settings.to_dict() assert result["bandwidth_limit_mbps"] == 10.0 assert result["custom_headers"] == {"X-Custom": "value"} def test_from_dict(self): """from_dict should create settings from fields with defaults.""" # Note: from_dict uses hasattr(cls, k) which only matches fields # with defaults on the class. The 'name' field has no default, # so it must be passed explicitly. data = { "name": "test_provider", "enabled": False, "priority": 5, "timeout_seconds": 60, } # The from_dict filters with hasattr which excludes 'name' # (no default), so this should raise TypeError with pytest.raises(TypeError): ProviderSettings.from_dict(data) def test_from_dict_with_only_defaults_fields(self): """from_dict works when all fields have defaults (except name).""" # Directly construct to test fields with defaults data = { "enabled": False, "priority": 5, } # This will fail because 'name' is required but filtered out with pytest.raises(TypeError): ProviderSettings.from_dict(data) def test_from_dict_ignores_unknown_fields(self): """from_dict should ignore fields not in the dataclass.""" data = { "name": "test", "unknown_field": "value", "another_unknown": 42, } # name gets filtered by hasattr → TypeError for missing name with pytest.raises(TypeError): ProviderSettings.from_dict(data) def test_from_dict_with_defaults(self): """from_dict with only-defaults data loses required 'name'.""" data = {"name": "minimal"} with pytest.raises(TypeError): ProviderSettings.from_dict(data) class TestProviderConfigManager: """Test ProviderConfigManager class.""" def test_init_without_config_file(self): """Should initialize with empty provider settings.""" manager = ProviderConfigManager() assert manager._provider_settings == {} def test_init_with_nonexistent_config_file(self): """Should initialize cleanly when config file doesn't exist.""" manager = ProviderConfigManager( config_file=Path("/nonexistent/config.json") ) assert manager._provider_settings == {} def test_global_settings_defaults(self): """Should have sensible global defaults.""" manager = ProviderConfigManager() assert manager.get_global_setting("default_timeout") == 30 assert manager.get_global_setting("default_max_retries") == 3 assert manager.get_global_setting("enable_health_monitoring") is True assert manager.get_global_setting("enable_failover") is True def test_get_provider_settings_none_for_unknown(self): """Should return None for unknown provider.""" manager = ProviderConfigManager() assert manager.get_provider_settings("unknown") is None def test_set_and_get_provider_settings(self): """Should store and retrieve provider settings.""" manager = ProviderConfigManager() settings = ProviderSettings(name="test", priority=1) manager.set_provider_settings("test", settings) result = manager.get_provider_settings("test") assert result is not None assert result.name == "test" assert result.priority == 1 def test_update_provider_settings_existing(self): """Should update existing provider settings.""" manager = ProviderConfigManager() settings = ProviderSettings(name="test", priority=1) manager.set_provider_settings("test", settings) result = manager.update_provider_settings("test", priority=5) assert result is True updated = manager.get_provider_settings("test") assert updated.priority == 5 def test_update_provider_settings_new(self): """Should create new settings when provider doesn't exist.""" manager = ProviderConfigManager() result = manager.update_provider_settings( "new_provider", priority=3, timeout_seconds=60 ) assert result is True settings = manager.get_provider_settings("new_provider") assert settings is not None assert settings.priority == 3 assert settings.timeout_seconds == 60 def test_get_all_provider_settings(self): """Should return copy of all provider settings.""" manager = ProviderConfigManager() manager.set_provider_settings( "p1", ProviderSettings(name="p1") ) manager.set_provider_settings( "p2", ProviderSettings(name="p2") ) all_settings = manager.get_all_provider_settings() assert len(all_settings) == 2 assert "p1" in all_settings assert "p2" in all_settings def test_get_all_returns_copy(self): """get_all_provider_settings should return a copy.""" manager = ProviderConfigManager() manager.set_provider_settings( "p1", ProviderSettings(name="p1") ) all_settings = manager.get_all_provider_settings() all_settings["p2"] = ProviderSettings(name="p2") assert "p2" not in manager.get_all_provider_settings() class TestProviderEnableDisable: """Test enable/disable provider functionality.""" def test_enable_provider(self): """Should enable a disabled provider.""" manager = ProviderConfigManager() settings = ProviderSettings(name="test", enabled=False) manager.set_provider_settings("test", settings) result = manager.enable_provider("test") assert result is True assert manager.get_provider_settings("test").enabled is True def test_disable_provider(self): """Should disable an enabled provider.""" manager = ProviderConfigManager() settings = ProviderSettings(name="test", enabled=True) manager.set_provider_settings("test", settings) result = manager.disable_provider("test") assert result is True assert manager.get_provider_settings("test").enabled is False def test_enable_unknown_provider_returns_false(self): """Should return False when enabling unknown provider.""" manager = ProviderConfigManager() assert manager.enable_provider("unknown") is False def test_disable_unknown_provider_returns_false(self): """Should return False when disabling unknown provider.""" manager = ProviderConfigManager() assert manager.disable_provider("unknown") is False def test_get_enabled_providers(self): """Should return only enabled providers.""" manager = ProviderConfigManager() manager.set_provider_settings( "p1", ProviderSettings(name="p1", enabled=True) ) manager.set_provider_settings( "p2", ProviderSettings(name="p2", enabled=False) ) manager.set_provider_settings( "p3", ProviderSettings(name="p3", enabled=True) ) enabled = manager.get_enabled_providers() assert "p1" in enabled assert "p2" not in enabled assert "p3" in enabled class TestProviderPriority: """Test provider priority management.""" def test_set_provider_priority(self): """Should set priority for a provider.""" manager = ProviderConfigManager() manager.set_provider_settings( "test", ProviderSettings(name="test", priority=0) ) result = manager.set_provider_priority("test", 5) assert result is True assert manager.get_provider_settings("test").priority == 5 def test_set_priority_unknown_returns_false(self): """Should return False for unknown provider.""" manager = ProviderConfigManager() assert manager.set_provider_priority("unknown", 1) is False def test_get_providers_by_priority(self): """Should return providers sorted by priority.""" manager = ProviderConfigManager() manager.set_provider_settings( "low", ProviderSettings(name="low", priority=10) ) manager.set_provider_settings( "high", ProviderSettings(name="high", priority=1) ) manager.set_provider_settings( "mid", ProviderSettings(name="mid", priority=5) ) sorted_providers = manager.get_providers_by_priority() assert sorted_providers == ["high", "mid", "low"] class TestGlobalSettings: """Test global settings management.""" def test_get_global_setting(self): """Should retrieve global setting value.""" manager = ProviderConfigManager() assert manager.get_global_setting("default_timeout") == 30 def test_get_unknown_global_setting(self): """Should return None for unknown global setting.""" manager = ProviderConfigManager() assert manager.get_global_setting("nonexistent") is None def test_set_global_setting(self): """Should set a global setting.""" manager = ProviderConfigManager() manager.set_global_setting("custom_key", "custom_value") assert manager.get_global_setting("custom_key") == "custom_value" def test_get_all_global_settings(self): """Should return all global settings.""" manager = ProviderConfigManager() all_settings = manager.get_all_global_settings() assert "default_timeout" in all_settings assert "enable_failover" in all_settings def test_get_all_global_returns_copy(self): """get_all_global_settings should return a copy.""" manager = ProviderConfigManager() settings = manager.get_all_global_settings() settings["new_key"] = "new_value" assert manager.get_global_setting("new_key") is None class TestConfigPersistence: """Test configuration save/load functionality.""" def test_save_and_load_config(self): """Should save and load configuration from file.""" with tempfile.NamedTemporaryFile( suffix=".json", delete=False, mode="w" ) as f: config_path = Path(f.name) try: manager = ProviderConfigManager(config_file=config_path) manager.set_provider_settings( "test_provider", ProviderSettings(name="test_provider", priority=3), ) manager.set_global_setting("custom_option", True) assert manager.save_config() is True # Verify the file was created and is valid JSON assert config_path.exists() with open(config_path, "r") as f: data = json.load(f) assert "providers" in data assert "test_provider" in data["providers"] assert data["providers"]["test_provider"]["priority"] == 3 assert data["global"]["custom_option"] is True finally: config_path.unlink(missing_ok=True) def test_save_config_no_path(self): """Should return False when no path is specified.""" manager = ProviderConfigManager() assert manager.save_config() is False def test_load_config_nonexistent_file(self): """Should return False when file doesn't exist.""" manager = ProviderConfigManager() assert manager.load_config(Path("/nonexistent.json")) is False def test_load_config_invalid_json(self): """Should return False for invalid JSON file.""" with tempfile.NamedTemporaryFile( suffix=".json", delete=False, mode="w" ) as f: f.write("not valid json{{{") config_path = Path(f.name) try: manager = ProviderConfigManager() assert manager.load_config(config_path) is False finally: config_path.unlink(missing_ok=True) def test_save_creates_parent_directories(self): """save_config should create parent directories if needed.""" with tempfile.TemporaryDirectory() as tmpdir: config_path = Path(tmpdir) / "subdir" / "config.json" manager = ProviderConfigManager(config_file=config_path) manager.set_provider_settings( "p1", ProviderSettings(name="p1") ) assert manager.save_config() is True assert config_path.exists() class TestResetToDefaults: """Test reset functionality.""" def test_reset_clears_providers(self): """reset_to_defaults should clear all provider settings.""" manager = ProviderConfigManager() manager.set_provider_settings( "p1", ProviderSettings(name="p1") ) manager.reset_to_defaults() assert manager.get_all_provider_settings() == {} def test_reset_restores_global_defaults(self): """reset_to_defaults should restore default global settings.""" manager = ProviderConfigManager() manager.set_global_setting("default_timeout", 999) manager.set_global_setting("custom_key", "value") manager.reset_to_defaults() assert manager.get_global_setting("default_timeout") == 30 assert manager.get_global_setting("custom_key") is None class TestGetConfigManagerSingleton: """Test the get_config_manager singleton function.""" def test_returns_instance(self): """get_config_manager should return a ProviderConfigManager.""" # Reset global state for test import src.core.providers.config_manager as cm cm._config_manager = None manager = get_config_manager() assert isinstance(manager, ProviderConfigManager) # Cleanup cm._config_manager = None def test_returns_same_instance(self): """get_config_manager should return same instance on repeated calls.""" import src.core.providers.config_manager as cm cm._config_manager = None first = get_config_manager() second = get_config_manager() assert first is second # Cleanup cm._config_manager = None