"""Tests for configuration management module.""" import json from pathlib import Path import pytest from webdrop_bridge.config import Config, ConfigurationError from webdrop_bridge.core.config_manager import ConfigExporter, ConfigProfile, ConfigValidator class TestConfigValidator: """Test configuration validation.""" def test_validate_valid_config(self): """Test validation passes for valid configuration.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", "log_level": "INFO", "log_file": None, "allowed_roots": ["/home", "/data"], "allowed_urls": ["http://example.com"], "webapp_url": "http://localhost:8080", "window_width": 800, "window_height": 600, "enable_logging": True, } errors = ConfigValidator.validate(config_dict) assert errors == [] def test_validate_missing_required_field(self): """Test validation fails for missing required fields.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", } errors = ConfigValidator.validate(config_dict) assert len(errors) > 0 assert any("log_level" in e for e in errors) def test_validate_invalid_type(self): """Test validation fails for invalid type.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", "log_level": "INFO", "log_file": None, "allowed_roots": ["/home"], "allowed_urls": ["http://example.com"], "webapp_url": "http://localhost:8080", "window_width": "800", # Should be int "window_height": 600, "enable_logging": True, } errors = ConfigValidator.validate(config_dict) assert len(errors) > 0 assert any("window_width" in e for e in errors) def test_validate_invalid_log_level(self): """Test validation fails for invalid log level.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", "log_level": "TRACE", # Invalid "log_file": None, "allowed_roots": [], "allowed_urls": [], "webapp_url": "http://localhost:8080", "window_width": 800, "window_height": 600, "enable_logging": True, } errors = ConfigValidator.validate(config_dict) assert len(errors) > 0 assert any("log_level" in e for e in errors) def test_validate_invalid_version_format(self): """Test validation fails for invalid version format.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0", # Should be X.Y.Z "log_level": "INFO", "log_file": None, "allowed_roots": [], "allowed_urls": [], "webapp_url": "http://localhost:8080", "window_width": 800, "window_height": 600, "enable_logging": True, } errors = ConfigValidator.validate(config_dict) # Note: Current implementation doesn't check regex pattern # This test documents the expected behavior for future enhancement def test_validate_out_of_range_value(self): """Test validation fails for values outside allowed range.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", "log_level": "INFO", "log_file": None, "allowed_roots": [], "allowed_urls": [], "webapp_url": "http://localhost:8080", "window_width": 100, # Below minimum of 400 "window_height": 600, "enable_logging": True, } errors = ConfigValidator.validate(config_dict) assert len(errors) > 0 assert any("window_width" in e for e in errors) def test_validate_or_raise_valid(self): """Test validate_or_raise succeeds for valid config.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", "log_level": "INFO", "log_file": None, "allowed_roots": [], "allowed_urls": [], "webapp_url": "http://localhost:8080", "window_width": 800, "window_height": 600, "enable_logging": True, } # Should not raise ConfigValidator.validate_or_raise(config_dict) def test_validate_or_raise_invalid(self): """Test validate_or_raise raises for invalid config.""" config_dict = { "app_name": "WebDrop Bridge", "app_version": "1.0.0", } with pytest.raises(ConfigurationError) as exc_info: ConfigValidator.validate_or_raise(config_dict) assert "Configuration validation failed" in str(exc_info.value) class TestConfigProfile: """Test configuration profile management.""" @pytest.fixture def profile_manager(self, tmp_path, monkeypatch): """Create profile manager with temporary directory.""" monkeypatch.setattr(ConfigProfile, "PROFILES_DIR", tmp_path / "profiles") return ConfigProfile() @pytest.fixture def sample_config(self): """Create sample configuration.""" return Config( app_name="WebDrop Bridge", app_version="1.0.0", log_level="INFO", log_file=None, allowed_roots=[Path("/home"), Path("/data")], allowed_urls=["http://example.com"], webapp_url="http://localhost:8080", window_width=800, window_height=600, enable_logging=True, ) def test_save_profile(self, profile_manager, sample_config): """Test saving a profile.""" profile_path = profile_manager.save_profile("work", sample_config) assert profile_path.exists() assert profile_path.name == "work.json" def test_load_profile(self, profile_manager, sample_config): """Test loading a profile.""" profile_manager.save_profile("work", sample_config) loaded = profile_manager.load_profile("work") assert loaded["app_name"] == "WebDrop Bridge" assert loaded["log_level"] == "INFO" assert loaded["window_width"] == 800 def test_load_nonexistent_profile(self, profile_manager): """Test loading nonexistent profile raises error.""" with pytest.raises(ConfigurationError) as exc_info: profile_manager.load_profile("nonexistent") assert "Profile not found" in str(exc_info.value) def test_list_profiles(self, profile_manager, sample_config): """Test listing profiles.""" profile_manager.save_profile("work", sample_config) profile_manager.save_profile("personal", sample_config) profiles = profile_manager.list_profiles() assert "work" in profiles assert "personal" in profiles assert len(profiles) == 2 def test_delete_profile(self, profile_manager, sample_config): """Test deleting a profile.""" profile_manager.save_profile("work", sample_config) assert profile_manager.list_profiles() == ["work"] profile_manager.delete_profile("work") assert profile_manager.list_profiles() == [] def test_delete_nonexistent_profile(self, profile_manager): """Test deleting nonexistent profile raises error.""" with pytest.raises(ConfigurationError) as exc_info: profile_manager.delete_profile("nonexistent") assert "Profile not found" in str(exc_info.value) def test_invalid_profile_name(self, profile_manager, sample_config): """Test invalid profile names are rejected.""" with pytest.raises(ConfigurationError) as exc_info: profile_manager.save_profile("work/personal", sample_config) assert "Invalid profile name" in str(exc_info.value) class TestConfigExporter: """Test configuration export/import.""" @pytest.fixture def sample_config(self): """Create sample configuration.""" return Config( app_name="WebDrop Bridge", app_version="1.0.0", log_level="INFO", log_file=None, allowed_roots=[Path("/home"), Path("/data")], allowed_urls=["http://example.com"], webapp_url="http://localhost:8080", window_width=800, window_height=600, window_title="WebDrop Bridge v1.0.0", enable_logging=True, ) def test_export_to_json(self, tmp_path, sample_config): """Test exporting configuration to JSON.""" output_file = tmp_path / "config.json" ConfigExporter.export_to_json(sample_config, output_file) assert output_file.exists() data = json.loads(output_file.read_text()) assert data["app_name"] == "WebDrop Bridge" assert data["log_level"] == "INFO" def test_import_from_json(self, tmp_path, sample_config): """Test importing configuration from JSON.""" # Export first output_file = tmp_path / "config.json" ConfigExporter.export_to_json(sample_config, output_file) # Import imported = ConfigExporter.import_from_json(output_file) assert imported["app_name"] == "WebDrop Bridge" assert imported["log_level"] == "INFO" assert imported["window_width"] == 800 def test_import_nonexistent_file(self): """Test importing nonexistent file raises error.""" with pytest.raises(ConfigurationError) as exc_info: ConfigExporter.import_from_json(Path("/nonexistent/file.json")) assert "File not found" in str(exc_info.value) def test_import_invalid_json(self, tmp_path): """Test importing invalid JSON raises error.""" invalid_file = tmp_path / "invalid.json" invalid_file.write_text("{ invalid json }") with pytest.raises(ConfigurationError) as exc_info: ConfigExporter.import_from_json(invalid_file) assert "Invalid JSON" in str(exc_info.value) def test_import_invalid_config(self, tmp_path): """Test importing JSON with invalid config raises error.""" invalid_file = tmp_path / "invalid_config.json" invalid_file.write_text('{"app_name": "test"}') # Missing required fields with pytest.raises(ConfigurationError) as exc_info: ConfigExporter.import_from_json(invalid_file) assert "Configuration validation failed" in str(exc_info.value)