import os import json import pytest from pathlib import Path from config.secure_config import SecureConfig from config.config_loader import load_config, save_api_key @pytest.fixture def temp_dir(tmp_path): """Create a temporary directory for test files.""" return tmp_path @pytest.fixture def secure_config(temp_dir): """Create a SecureConfig instance with a temporary key file.""" return SecureConfig(str(temp_dir / "test_secure.key")) @pytest.fixture def test_config_dir(temp_dir, monkeypatch): """Set up a test configuration directory.""" # Create config directory config_dir = temp_dir / "config" config_dir.mkdir(parents=True, exist_ok=True) # Create test YAML config yaml_content = """ llm: provider: openai model: gpt-4 max_tokens: 2000 temperature: 0.7 timeout: 30 api: api_base: null openai: api_base: https://api.openai.com/v1 """ config_path = config_dir / "llm_config.yaml" config_path.write_text(yaml_content) # Set up environment for config loader monkeypatch.setenv("PYTHONPATH", str(temp_dir)) monkeypatch.chdir(temp_dir) return temp_dir class TestSecureConfig: def test_key_generation(self, secure_config, temp_dir): """Test that encryption key is generated correctly.""" key_path = temp_dir / "test_secure.key" assert key_path.exists() if os.name == 'nt': # Windows import win32security security = win32security.GetFileSecurity( str(key_path), win32security.DACL_SECURITY_INFORMATION ) dacl = security.GetSecurityDescriptorDacl() assert dacl is not None # Verify only one ACE exists assert dacl.GetAceCount() == 1 else: # Unix assert key_path.stat().st_mode & 0o777 == 0o600 # Check permissions def test_encryption_decryption(self, secure_config): """Test encryption and decryption of values.""" test_value = "test-api-key-123" encrypted = secure_config.encrypt_value(test_value) decrypted = secure_config.decrypt_value(encrypted) assert decrypted == test_value def test_null_handling(self, secure_config): """Test handling of null/None values.""" assert secure_config.encrypt_value(None) is None assert secure_config.decrypt_value(None) is None def test_invalid_decrypt(self, secure_config): """Test decryption of invalid data.""" assert secure_config.decrypt_value("invalid-data") is None class TestConfigLoader: def test_save_load_api_key(self, test_config_dir, monkeypatch): """Test saving and loading API key.""" test_api_key = "test-api-key-456" save_api_key(test_api_key) # Verify secure.json was created with proper permissions secure_json = test_config_dir / "config" / "secure.json" assert secure_json.exists() if os.name == 'nt': # Windows import win32security security = win32security.GetFileSecurity( str(secure_json), win32security.DACL_SECURITY_INFORMATION ) dacl = security.GetSecurityDescriptorDacl() assert dacl is not None # Verify only one ACE exists assert dacl.GetAceCount() == 1 else: # Unix assert secure_json.stat().st_mode & 0o777 == 0o600 # Load config and verify API key config = load_config() assert config["llm"]["api_key"] == test_api_key def test_config_priority(self, test_config_dir, monkeypatch): """Test configuration priority (env vars > secure storage > yaml).""" # Set up different values for each level env_api_key = "env-api-key" secure_api_key = "secure-api-key" # Save to secure storage save_api_key(secure_api_key) # Test without env var config = load_config() assert config["llm"]["api_key"] == secure_api_key # Test with env var monkeypatch.setenv("LLM_API_KEY", env_api_key) config = load_config() assert config["llm"]["api_key"] == env_api_key def test_api_base_config(self, test_config_dir, monkeypatch): """Test API base URL configuration.""" # Test default OpenAI base URL config = load_config() assert config["llm"]["api_base"] == "https://api.openai.com/v1" # Test override with env var custom_base = "https://custom-api.example.com" monkeypatch.setenv("LLM_API_BASE", custom_base) config = load_config() assert config["llm"]["api_base"] == custom_base