from pathlib import Path from unittest.mock import patch from src.utilities.config import Config, _CONFIG_ATTRS class TestConfigDefaults: """Default attribute values.""" def test_default_upload_folder(self): assert Config.upload_folder == '~/zordon-uploads/' def test_default_port(self): assert Config.port_number == 8080 def test_default_server_log_level(self): assert Config.server_log_level == 'debug' def test_default_enable_split_jobs(self): assert Config.enable_split_jobs is True def test_default_worker_timeout(self): assert Config.worker_process_timeout == 120 class TestConfigInstance: """Instance creation and attribute initialisation.""" def test_init_copies_class_attrs(self): cfg = Config() assert cfg.upload_folder == '~/zordon-uploads/' assert cfg.port_number == 8080 def test_init_has_all_attrs(self): cfg = Config() for attr in _CONFIG_ATTRS: assert hasattr(cfg, attr), f'missing attr: {attr}' class TestConfigLoad: """Loading configuration from YAML.""" def test_load_sets_attributes(self, tmp_path): config_file = tmp_path / 'config.yaml' config_file.write_text('port_number: 9090\nserver_log_level: info\n') cfg = Config() cfg.load(config_file) assert cfg.port_number == 9090 assert cfg.server_log_level == 'info' def test_load_expands_upload_folder(self, tmp_path): config_file = tmp_path / 'config.yaml' config_file.write_text('upload_folder: ~/custom-uploads/\n') cfg = Config() cfg.load(config_file) # expanduser strips trailing slash assert cfg.upload_folder.endswith('/custom-uploads') def test_load_ignores_unknown_keys(self, tmp_path): config_file = tmp_path / 'config.yaml' config_file.write_text('nonexistent_key: value\nport_number: 7070\n') cfg = Config() cfg.load(config_file) assert cfg.port_number == 7070 def test_load_raises_on_missing_file(self): cfg = Config() try: cfg.load(Path('/nonexistent/path.yaml')) assert False, 'expected FileNotFoundError' except FileNotFoundError: pass class TestConfigSyncClass: """_sync_class propagates instance attrs to class level.""" def test_sync_class_propagates_attrs(self): orig = Config._default_instance try: cfg = Config() cfg.port_number = 7777 Config._default_instance = cfg Config._sync_class() assert Config.port_number == 7777 finally: Config._default_instance = orig Config._sync_class() def test_sync_class_noop_when_no_instance(self): orig = Config._default_instance try: Config._default_instance = None Config._sync_class() finally: Config._default_instance = orig Config._sync_class() class TestConfigLoadConfig: """Classmethod load_config — full bootstrap.""" def test_load_config_sets_default_instance(self, tmp_path): orig = Config._default_instance try: config_file = tmp_path / 'config.yaml' config_file.write_text('port_number: 9999\n') Config._default_instance = None Config.load_config(config_file) assert Config._default_instance is not None assert Config.port_number == 9999 finally: Config._default_instance = orig Config._sync_class() class TestConfigDir: """config_dir() returns OS-specific path.""" @patch('src.utilities.config.current_system_os') def test_config_dir_macos(self, mock_os): mock_os.return_value = 'macos' result = Config.config_dir() assert 'Library/Application Support/Zordon' in str(result) @patch('src.utilities.config.current_system_os') def test_config_dir_windows(self, mock_os): mock_os.return_value = 'windows' with patch.dict('os.environ', {'APPDATA': 'C:\\Users\\Test\\AppData\\Roaming'}): result = Config.config_dir() assert 'Zordon' in str(result) @patch('src.utilities.config.current_system_os') def test_config_dir_linux(self, mock_os): mock_os.return_value = 'linux' result = Config.config_dir() assert '.config/Zordon' in str(result) class TestSetupConfigDir: """setup_config_dir creates dir and copies template.""" @patch('src.utilities.config.copy_directory_contents') @patch('src.utilities.config.os.makedirs') @patch('src.utilities.config.os.path.exists') def test_creates_dir_when_missing(self, mock_exists, mock_makedirs, mock_copy): mock_exists.return_value = False Config.setup_config_dir() mock_makedirs.assert_called_once() @patch('src.utilities.config.copy_directory_contents') @patch('src.utilities.config.os.makedirs') @patch('src.utilities.config.os.path.exists') def test_skips_when_dir_exists(self, mock_exists, mock_makedirs, mock_copy): mock_exists.return_value = True Config.setup_config_dir() mock_makedirs.assert_not_called() mock_copy.assert_not_called()