Files
trends-scraper/tests/test_config_manager.py
2025-09-11 17:46:14 +03:00

165 lines
6.0 KiB
Python

"""
Unit tests for the ConfigManager module.
"""
import pytest
import tempfile
import os
import yaml
from pathlib import Path
from unittest.mock import patch, mock_open
from src.config_manager import ConfigManager
class TestConfigManager:
"""Test cases for ConfigManager class."""
def test_init_with_default_path(self):
"""Test ConfigManager initialization with default path."""
config_manager = ConfigManager()
assert config_manager.config_path == Path("config/config.yaml")
assert isinstance(config_manager.config, dict)
assert isinstance(config_manager.default_config, dict)
def test_init_with_custom_path(self):
"""Test ConfigManager initialization with custom path."""
custom_path = "custom/config.yaml"
config_manager = ConfigManager(custom_path)
assert config_manager.config_path == Path(custom_path)
def test_get_default_config(self):
"""Test default configuration structure."""
config_manager = ConfigManager()
default_config = config_manager._get_default_config()
# Check required sections
assert "scraper" in default_config
assert "sources" in default_config
assert "output" in default_config
assert "database" in default_config
assert "analysis" in default_config
# Check some default values
assert default_config["scraper"]["delay_between_requests"] == 1.0
assert default_config["scraper"]["timeout"] == 30
assert default_config["scraper"]["headless"] is True
assert isinstance(default_config["sources"], list)
assert len(default_config["sources"]) > 0
@patch('builtins.open', new_callable=mock_open, read_data="scraper:\n timeout: 60")
@patch('pathlib.Path.exists')
def test_load_config_existing_file(self, mock_exists, mock_file):
"""Test loading configuration from existing file."""
mock_exists.return_value = True
config_manager = ConfigManager()
config = config_manager.load_config()
mock_file.assert_called_once()
assert config["scraper"]["timeout"] == 60
@patch('builtins.open', new_callable=mock_open)
@patch('pathlib.Path.exists')
def test_load_config_create_default(self, mock_exists, mock_file):
"""Test creating default configuration when file doesn't exist."""
mock_exists.return_value = False
config_manager = ConfigManager()
config = config_manager.load_config()
# Verify file was created
mock_file.assert_called_once()
# Verify config is default
assert config == config_manager.default_config
@patch('builtins.open', new_callable=mock_open)
def test_save_config(self, mock_file):
"""Test saving configuration to file."""
config_manager = ConfigManager()
config_manager.config = {"test": "value"}
config_manager.save_config()
mock_file.assert_called_once()
# Verify yaml.dump was called with correct arguments
with patch('yaml.dump') as mock_dump:
config_manager.save_config()
mock_dump.assert_called_once()
def test_validate_and_merge_config(self):
"""Test configuration validation and merging."""
config_manager = ConfigManager()
# Test with partial config
partial_config = {
"scraper": {
"timeout": 60
}
}
config_manager.config = partial_config
merged = config_manager._validate_and_merge_config()
# Should have all sections
assert "sources" in merged
assert "output" in merged
# Should have updated value
assert merged["scraper"]["timeout"] == 60
# Should have default values for missing keys
assert merged["scraper"]["delay_between_requests"] == 1.0
def test_validate_and_merge_config_missing_required(self):
"""Test validation fails when required sections are missing."""
config_manager = ConfigManager()
config_manager.config = {"invalid": "config"}
with pytest.raises(ValueError, match="Missing required configuration section"):
config_manager._validate_and_merge_config()
def test_validate_and_merge_config_no_sources(self):
"""Test validation fails when no sources are configured."""
config_manager = ConfigManager()
config_manager.config = {
"scraper": {},
"sources": [],
"output": {}
}
with pytest.raises(ValueError, match="At least one data source must be configured"):
config_manager._validate_and_merge_config()
def test_get_with_dot_notation(self):
"""Test getting configuration values with dot notation."""
config_manager = ConfigManager()
config_manager.config = {
"scraper": {
"timeout": 60,
"nested": {
"value": "test"
}
}
}
assert config_manager.get("scraper.timeout") == 60
assert config_manager.get("scraper.nested.value") == "test"
assert config_manager.get("nonexistent", "default") == "default"
def test_set_with_dot_notation(self):
"""Test setting configuration values with dot notation."""
config_manager = ConfigManager()
config_manager.config = {"scraper": {}}
config_manager.set("scraper.timeout", 60)
config_manager.set("new.nested.value", "test")
assert config_manager.config["scraper"]["timeout"] == 60
assert config_manager.config["new"]["nested"]["value"] == "test"
@patch.object(ConfigManager, 'load_config')
def test_reload(self, mock_load):
"""Test reloading configuration."""
config_manager = ConfigManager()
config_manager.reload()
mock_load.assert_called_once()