"""Unit tests for configuration management."""
import json
import pytest
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch, mock_open
from file_system_mcp_server.config import Config, ConfigManager
class TestConfig:
"""Test the Config dataclass."""
def test_default_config(self):
"""Test default configuration values."""
config = Config()
assert config.backup_directory == "./.mcp_backups"
assert config.max_file_size == 10 * 1024 * 1024
assert config.max_recursion_depth == 10
assert config.allowed_extensions is None
assert "/etc" in config.protected_paths
assert config.enable_backups is True
assert config.log_level == "INFO"
def test_custom_config(self):
"""Test custom configuration values."""
config = Config(
backup_directory="/tmp/backups",
max_file_size=5 * 1024 * 1024,
max_recursion_depth=5,
allowed_extensions=[".py", ".txt"],
protected_paths=["/etc"],
enable_backups=False,
log_level="DEBUG"
)
assert config.backup_directory == "/tmp/backups"
assert config.max_file_size == 5 * 1024 * 1024
assert config.max_recursion_depth == 5
assert config.allowed_extensions == [".py", ".txt"]
assert config.protected_paths == ["/etc"]
assert config.enable_backups is False
assert config.log_level == "DEBUG"
def test_invalid_max_file_size(self):
"""Test validation of max_file_size."""
with pytest.raises(ValueError, match="max_file_size must be positive"):
Config(max_file_size=0)
with pytest.raises(ValueError, match="max_file_size must be positive"):
Config(max_file_size=-1)
def test_invalid_max_recursion_depth(self):
"""Test validation of max_recursion_depth."""
with pytest.raises(ValueError, match="max_recursion_depth must be positive"):
Config(max_recursion_depth=0)
with pytest.raises(ValueError, match="max_recursion_depth must be positive"):
Config(max_recursion_depth=-1)
def test_invalid_log_level(self):
"""Test validation of log_level."""
with patch('file_system_mcp_server.config.logger') as mock_logger:
config = Config(log_level="INVALID")
assert config.log_level == "INFO"
mock_logger.warning.assert_called_once()
class TestConfigManager:
"""Test the ConfigManager class."""
def test_load_config_file_not_exists(self):
"""Test loading config when file doesn't exist."""
with TemporaryDirectory() as temp_dir:
config_path = Path(temp_dir) / "nonexistent.json"
with patch('file_system_mcp_server.config.logger') as mock_logger:
config = ConfigManager.load_config(str(config_path))
assert isinstance(config, Config)
assert config.backup_directory == "./.mcp_backups"
mock_logger.info.assert_called()
def test_load_config_from_valid_file(self):
"""Test loading config from valid JSON file."""
config_data = {
"backup_directory": "/tmp/test_backups",
"max_file_size": 5000000,
"max_recursion_depth": 5,
"allowed_extensions": [".py"],
"protected_paths": ["/etc"],
"enable_backups": False,
"log_level": "DEBUG"
}
with TemporaryDirectory() as temp_dir:
config_path = Path(temp_dir) / "config.json"
with open(config_path, 'w') as f:
json.dump(config_data, f)
config = ConfigManager.load_config(str(config_path))
assert config.backup_directory == "/tmp/test_backups"
assert config.max_file_size == 5000000
assert config.max_recursion_depth == 5
assert config.allowed_extensions == [".py"]
assert config.protected_paths == ["/etc"]
assert config.enable_backups is False
assert config.log_level == "DEBUG"
def test_load_config_invalid_json(self):
"""Test loading config from invalid JSON file."""
with TemporaryDirectory() as temp_dir:
config_path = Path(temp_dir) / "invalid.json"
with open(config_path, 'w') as f:
f.write("invalid json content")
with patch('file_system_mcp_server.config.logger') as mock_logger:
config = ConfigManager.load_config(str(config_path))
# Should return default config
assert isinstance(config, Config)
assert config.backup_directory == "./.mcp_backups"
mock_logger.error.assert_called()
def test_load_config_with_unknown_fields(self):
"""Test loading config with unknown fields (should be filtered out)."""
config_data = {
"backup_directory": "/tmp/test",
"unknown_field": "should_be_ignored",
"max_file_size": 1000000
}
with TemporaryDirectory() as temp_dir:
config_path = Path(temp_dir) / "config.json"
with open(config_path, 'w') as f:
json.dump(config_data, f)
config = ConfigManager.load_config(str(config_path))
assert config.backup_directory == "/tmp/test"
assert config.max_file_size == 1000000
# Unknown field should not cause issues
assert not hasattr(config, 'unknown_field')
def test_save_config(self):
"""Test saving configuration to file."""
config = Config(
backup_directory="/tmp/test",
max_file_size=1000000,
log_level="DEBUG"
)
with TemporaryDirectory() as temp_dir:
config_path = Path(temp_dir) / "test_config.json"
ConfigManager.save_config(config, config_path)
assert config_path.exists()
# Verify saved content
with open(config_path, 'r') as f:
saved_data = json.load(f)
assert saved_data["backup_directory"] == "/tmp/test"
assert saved_data["max_file_size"] == 1000000
assert saved_data["log_level"] == "DEBUG"
def test_validate_paths_success(self):
"""Test path validation with valid paths."""
with TemporaryDirectory() as temp_dir:
config = Config(backup_directory=temp_dir)
issues = ConfigManager.validate_paths(config)
assert len(issues) == 0
def test_validate_paths_backup_directory_issues(self):
"""Test path validation with invalid backup directory."""
# Use an invalid path that can't be created
config = Config(backup_directory="/invalid/path/that/cannot/be/created")
issues = ConfigManager.validate_paths(config)
assert len(issues) > 0
assert "backup directory" in issues[0].lower()