commit, am too tired to add docs here
This commit is contained in:
294
tests/test_file_config.py
Normal file
294
tests/test_file_config.py
Normal file
@@ -0,0 +1,294 @@
|
||||
"""Tests for file-based configuration system."""
|
||||
|
||||
import asyncio
|
||||
import tempfile
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from guardden.services.file_config import FileConfigurationManager, ConfigurationError
|
||||
|
||||
|
||||
class TestFileConfigurationManager:
|
||||
"""Tests for the file-based configuration manager."""
|
||||
|
||||
@pytest.fixture
|
||||
def temp_config_dir(self):
|
||||
"""Create a temporary configuration directory."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
yield temp_dir
|
||||
|
||||
@pytest.fixture
|
||||
async def config_manager(self, temp_config_dir):
|
||||
"""Create a configuration manager with temporary directory."""
|
||||
manager = FileConfigurationManager(temp_config_dir)
|
||||
await manager.initialize()
|
||||
yield manager
|
||||
await manager.shutdown()
|
||||
|
||||
def test_directory_creation(self, temp_config_dir):
|
||||
"""Test that required directories are created."""
|
||||
manager = FileConfigurationManager(temp_config_dir)
|
||||
|
||||
expected_dirs = [
|
||||
"guilds",
|
||||
"wordlists",
|
||||
"schemas",
|
||||
"templates",
|
||||
"backups"
|
||||
]
|
||||
|
||||
for dir_name in expected_dirs:
|
||||
assert (Path(temp_config_dir) / dir_name).exists()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guild_config_creation(self, config_manager, temp_config_dir):
|
||||
"""Test creating a new guild configuration."""
|
||||
guild_id = 123456789
|
||||
name = "Test Guild"
|
||||
owner_id = 987654321
|
||||
|
||||
file_path = await config_manager.create_guild_config(guild_id, name, owner_id)
|
||||
|
||||
assert file_path.exists()
|
||||
assert file_path.name == f"guild-{guild_id}.yml"
|
||||
|
||||
# Verify content
|
||||
with open(file_path, 'r') as f:
|
||||
content = yaml.safe_load(f)
|
||||
|
||||
assert content["guild_id"] == guild_id
|
||||
assert content["name"] == name
|
||||
assert content["owner_id"] == owner_id
|
||||
assert "settings" in content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_duplicate_guild_config_creation(self, config_manager):
|
||||
"""Test that creating duplicate guild configs raises error."""
|
||||
guild_id = 123456789
|
||||
name = "Test Guild"
|
||||
|
||||
# Create first config
|
||||
await config_manager.create_guild_config(guild_id, name)
|
||||
|
||||
# Attempt to create duplicate should raise error
|
||||
with pytest.raises(ConfigurationError):
|
||||
await config_manager.create_guild_config(guild_id, name)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guild_config_loading(self, config_manager, temp_config_dir):
|
||||
"""Test loading guild configuration from file."""
|
||||
guild_id = 123456789
|
||||
|
||||
# Create config file manually
|
||||
config_data = {
|
||||
"guild_id": guild_id,
|
||||
"name": "Test Guild",
|
||||
"premium": False,
|
||||
"settings": {
|
||||
"general": {
|
||||
"prefix": "!",
|
||||
"locale": "en"
|
||||
},
|
||||
"ai_moderation": {
|
||||
"enabled": True,
|
||||
"sensitivity": 80,
|
||||
"nsfw_only_filtering": True
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guild_dir = Path(temp_config_dir) / "guilds"
|
||||
guild_file = guild_dir / f"guild-{guild_id}.yml"
|
||||
|
||||
with open(guild_file, 'w') as f:
|
||||
yaml.dump(config_data, f)
|
||||
|
||||
# Load config
|
||||
await config_manager._load_guild_config(guild_file)
|
||||
|
||||
# Verify loaded config
|
||||
config = config_manager.get_guild_config(guild_id)
|
||||
assert config is not None
|
||||
assert config.guild_id == guild_id
|
||||
assert config.name == "Test Guild"
|
||||
assert config.settings["ai_moderation"]["nsfw_only_filtering"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_config_validation(self, config_manager, temp_config_dir):
|
||||
"""Test that invalid configurations are rejected."""
|
||||
guild_id = 123456789
|
||||
|
||||
# Create invalid config (missing required fields)
|
||||
invalid_config = {
|
||||
"name": "Test Guild",
|
||||
# Missing guild_id
|
||||
"settings": {}
|
||||
}
|
||||
|
||||
guild_dir = Path(temp_config_dir) / "guilds"
|
||||
guild_file = guild_dir / f"guild-{guild_id}.yml"
|
||||
|
||||
with open(guild_file, 'w') as f:
|
||||
yaml.dump(invalid_config, f)
|
||||
|
||||
# Should return None for invalid config
|
||||
result = await config_manager._load_guild_config(guild_file)
|
||||
assert result is None
|
||||
|
||||
def test_configuration_validation(self, config_manager):
|
||||
"""Test configuration validation against schema."""
|
||||
valid_config = {
|
||||
"guild_id": 123456789,
|
||||
"name": "Test Guild",
|
||||
"settings": {
|
||||
"general": {"prefix": "!", "locale": "en"},
|
||||
"channels": {"log_channel_id": None},
|
||||
"roles": {"mod_role_ids": []},
|
||||
"moderation": {"automod_enabled": True},
|
||||
"automod": {"message_rate_limit": 5},
|
||||
"ai_moderation": {"enabled": True},
|
||||
"verification": {"enabled": False}
|
||||
}
|
||||
}
|
||||
|
||||
# Should return no errors for valid config
|
||||
errors = config_manager.validate_config(valid_config)
|
||||
assert len(errors) == 0
|
||||
|
||||
# Invalid config should return errors
|
||||
invalid_config = {
|
||||
"guild_id": "not-a-number", # Should be integer
|
||||
"name": "Test Guild"
|
||||
# Missing required settings
|
||||
}
|
||||
|
||||
errors = config_manager.validate_config(invalid_config)
|
||||
assert len(errors) > 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wordlist_config_loading(self, config_manager, temp_config_dir):
|
||||
"""Test loading wordlist configurations."""
|
||||
wordlist_dir = Path(temp_config_dir) / "wordlists"
|
||||
|
||||
# Create banned words config
|
||||
banned_words_config = {
|
||||
"global_patterns": [
|
||||
{
|
||||
"pattern": "badword",
|
||||
"action": "delete",
|
||||
"is_regex": False,
|
||||
"category": "profanity"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with open(wordlist_dir / "banned-words.yml", 'w') as f:
|
||||
yaml.dump(banned_words_config, f)
|
||||
|
||||
# Create allowlist config
|
||||
allowlist_config = {
|
||||
"global_allowlist": [
|
||||
{
|
||||
"domain": "discord.com",
|
||||
"reason": "Official Discord domain"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with open(wordlist_dir / "domain-allowlists.yml", 'w') as f:
|
||||
yaml.dump(allowlist_config, f)
|
||||
|
||||
# Load configs
|
||||
await config_manager._load_wordlist_configs()
|
||||
|
||||
# Verify loaded configs
|
||||
wordlist_config = config_manager.get_wordlist_config()
|
||||
assert wordlist_config is not None
|
||||
assert "global_patterns" in wordlist_config
|
||||
assert len(wordlist_config["global_patterns"]) == 1
|
||||
|
||||
allowlist = config_manager.get_allowlist_config()
|
||||
assert allowlist is not None
|
||||
assert "global_allowlist" in allowlist
|
||||
assert len(allowlist["global_allowlist"]) == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_backup(self, config_manager, temp_config_dir):
|
||||
"""Test configuration backup functionality."""
|
||||
guild_id = 123456789
|
||||
|
||||
# Create a guild config
|
||||
await config_manager.create_guild_config(guild_id, "Test Guild")
|
||||
|
||||
# Create backup
|
||||
backup_path = await config_manager.backup_config(guild_id)
|
||||
|
||||
assert backup_path.exists()
|
||||
assert "backup" in backup_path.parent.name
|
||||
assert f"guild-{guild_id}" in backup_path.name
|
||||
|
||||
# Verify backup content matches original
|
||||
original_config = config_manager.get_guild_config(guild_id)
|
||||
|
||||
with open(backup_path, 'r') as f:
|
||||
backup_content = yaml.safe_load(f)
|
||||
|
||||
assert backup_content["guild_id"] == original_config.guild_id
|
||||
assert backup_content["name"] == original_config.name
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_change_callbacks(self, config_manager, temp_config_dir):
|
||||
"""Test that configuration change callbacks are triggered."""
|
||||
callback_called = False
|
||||
callback_guild_id = None
|
||||
|
||||
def test_callback(guild_id, config):
|
||||
nonlocal callback_called, callback_guild_id
|
||||
callback_called = True
|
||||
callback_guild_id = guild_id
|
||||
|
||||
# Register callback
|
||||
config_manager.register_change_callback(test_callback)
|
||||
|
||||
# Create config (should trigger callback)
|
||||
guild_id = 123456789
|
||||
await config_manager.create_guild_config(guild_id, "Test Guild")
|
||||
|
||||
# Wait a moment for callback to be called
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
assert callback_called
|
||||
assert callback_guild_id == guild_id
|
||||
|
||||
def test_all_guild_configs_retrieval(self, config_manager, temp_config_dir):
|
||||
"""Test retrieving all guild configurations."""
|
||||
# Initially should be empty
|
||||
all_configs = config_manager.get_all_guild_configs()
|
||||
assert len(all_configs) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_nsfw_only_filtering_in_config(self, config_manager):
|
||||
"""Test that NSFW-only filtering setting is properly handled."""
|
||||
guild_id = 123456789
|
||||
|
||||
# Create config with NSFW-only filtering enabled
|
||||
file_path = await config_manager.create_guild_config(guild_id, "NSFW Test Guild")
|
||||
|
||||
# Load and modify config to enable NSFW-only filtering
|
||||
with open(file_path, 'r') as f:
|
||||
config_data = yaml.safe_load(f)
|
||||
|
||||
config_data["settings"]["ai_moderation"]["nsfw_only_filtering"] = True
|
||||
|
||||
with open(file_path, 'w') as f:
|
||||
yaml.dump(config_data, f)
|
||||
|
||||
# Reload config
|
||||
await config_manager._load_guild_config(file_path)
|
||||
|
||||
# Verify NSFW-only filtering is enabled
|
||||
config = config_manager.get_guild_config(guild_id)
|
||||
assert config is not None
|
||||
assert config.settings["ai_moderation"]["nsfw_only_filtering"] is True
|
||||
Reference in New Issue
Block a user