# -*- coding: utf-8 -*- """Test the cli_helpers.config module.""" from __future__ import unicode_literals import os from unittest.mock import MagicMock import pytest from cli_helpers.compat import MAC, text_type, WIN from cli_helpers.config import (Config, DefaultConfigValidationError, get_system_config_dirs, get_user_config_dir, _pathify) from .utils import with_temp_dir APP_NAME, APP_AUTHOR = 'Test', 'Acme' TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), 'config_data') DEFAULT_CONFIG = { 'section': { 'test_boolean_default': 'True', 'test_string_file': '~/myfile', 'test_option': 'foobar' }, 'section2': {} } DEFAULT_VALID_CONFIG = { 'section': { 'test_boolean_default': True, 'test_string_file': '~/myfile', 'test_option': 'foobar' }, 'section2': {} } def _mocked_user_config(temp_dir, *args, **kwargs): config = Config(*args, **kwargs) config.user_config_file = MagicMock(return_value=os.path.join( temp_dir, config.filename)) return config def test_user_config_dir(): """Test that the config directory is a string with the app name in it.""" if 'XDG_CONFIG_HOME' in os.environ: del os.environ['XDG_CONFIG_HOME'] config_dir = get_user_config_dir(APP_NAME, APP_AUTHOR) assert isinstance(config_dir, text_type) assert (config_dir.endswith(APP_NAME) or config_dir.endswith(_pathify(APP_NAME))) def test_sys_config_dirs(): """Test that the sys config directories are returned correctly.""" if 'XDG_CONFIG_DIRS' in os.environ: del os.environ['XDG_CONFIG_DIRS'] config_dirs = get_system_config_dirs(APP_NAME, APP_AUTHOR) assert isinstance(config_dirs, list) assert (config_dirs[0].endswith(APP_NAME) or config_dirs[0].endswith(_pathify(APP_NAME))) @pytest.mark.skipif(not WIN, reason="requires Windows") def test_windows_user_config_dir_no_roaming(): """Test that Windows returns the user config directory without roaming.""" config_dir = get_user_config_dir(APP_NAME, APP_AUTHOR, roaming=False) assert isinstance(config_dir, text_type) assert config_dir.endswith(APP_NAME) assert 'Local' in config_dir @pytest.mark.skipif(not MAC, reason="requires macOS") def test_mac_user_config_dir_no_xdg(): """Test that macOS returns the user config directory without XDG.""" config_dir = get_user_config_dir(APP_NAME, APP_AUTHOR, force_xdg=False) assert isinstance(config_dir, text_type) assert config_dir.endswith(APP_NAME) assert 'Library' in config_dir @pytest.mark.skipif(not MAC, reason="requires macOS") def test_mac_system_config_dirs_no_xdg(): """Test that macOS returns the system config directories without XDG.""" config_dirs = get_system_config_dirs(APP_NAME, APP_AUTHOR, force_xdg=False) assert isinstance(config_dirs, list) assert config_dirs[0].endswith(APP_NAME) assert 'Library' in config_dirs[0] def test_config_reading_raise_errors(): """Test that instantiating Config will raise errors when appropriate.""" with pytest.raises(ValueError): Config(APP_NAME, APP_AUTHOR, 'test_config', write_default=True) with pytest.raises(ValueError): Config(APP_NAME, APP_AUTHOR, 'test_config', validate=True) with pytest.raises(TypeError): Config(APP_NAME, APP_AUTHOR, 'test_config', default=b'test') def test_config_user_file(): """Test that the Config user_config_file is appropriate.""" config = Config(APP_NAME, APP_AUTHOR, 'test_config') assert (get_user_config_dir(APP_NAME, APP_AUTHOR) in config.user_config_file()) def test_config_reading_default_dict(): """Test that the Config constructor will read in defaults from a dict.""" default = {'main': {'foo': 'bar'}} config = Config(APP_NAME, APP_AUTHOR, 'test_config', default=default) assert config.data == default def test_config_reading_no_default(): """Test that the Config constructor will work without any defaults.""" config = Config(APP_NAME, APP_AUTHOR, 'test_config') assert config.data == {} def test_config_reading_default_file(): """Test that the Config will work with a default file.""" config = Config(APP_NAME, APP_AUTHOR, 'test_config', default=os.path.join(TEST_DATA_DIR, 'configrc')) config.read_default_config() assert config.data == DEFAULT_CONFIG def test_config_reading_configspec(): """Test that the Config default file will work with a configspec.""" config = Config(APP_NAME, APP_AUTHOR, 'test_config', validate=True, default=os.path.join(TEST_DATA_DIR, 'configspecrc')) config.read_default_config() assert config.data == DEFAULT_VALID_CONFIG def test_config_reading_configspec_with_error(): """Test that reading an invalid configspec raises and exception.""" with pytest.raises(DefaultConfigValidationError): config = Config(APP_NAME, APP_AUTHOR, 'test_config', validate=True, default=os.path.join(TEST_DATA_DIR, 'invalid_configspecrc')) config.read_default_config() @with_temp_dir def test_write_and_read_default_config(temp_dir=None): config_file = 'test_config' default_file = os.path.join(TEST_DATA_DIR, 'configrc') temp_config_file = os.path.join(temp_dir, config_file) config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file) config.read_default_config() config.write_default_config() user_config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file) user_config.read() assert temp_config_file in user_config.config_filenames assert user_config == config with open(temp_config_file) as f: contents = f.read() assert '# Test file comment' in contents assert '# Test section comment' in contents assert '# Test field comment' in contents assert '# Test field commented out' in contents @with_temp_dir def test_write_and_read_default_config_from_configspec(temp_dir=None): config_file = 'test_config' default_file = os.path.join(TEST_DATA_DIR, 'configspecrc') temp_config_file = os.path.join(temp_dir, config_file) config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file, validate=True) config.read_default_config() config.write_default_config() user_config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file, validate=True) user_config.read() assert temp_config_file in user_config.config_filenames assert user_config == config with open(temp_config_file) as f: contents = f.read() assert '# Test file comment' in contents assert '# Test section comment' in contents assert '# Test field comment' in contents assert '# Test field commented out' in contents @with_temp_dir def test_overwrite_default_config_from_configspec(temp_dir=None): config_file = 'test_config' default_file = os.path.join(TEST_DATA_DIR, 'configspecrc') temp_config_file = os.path.join(temp_dir, config_file) config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file, validate=True) config.read_default_config() config.write_default_config() with open(temp_config_file, 'a') as f: f.write('--APPEND--') config.write_default_config() with open(temp_config_file) as f: assert '--APPEND--' in f.read() config.write_default_config(overwrite=True) with open(temp_config_file) as f: assert '--APPEND--' not in f.read() def test_read_invalid_config_file(): config_file = 'invalid_configrc' config = _mocked_user_config(TEST_DATA_DIR, APP_NAME, APP_AUTHOR, config_file) config.read() assert 'section' in config assert 'test_string_file' in config['section'] assert 'test_boolean_default' not in config['section'] assert 'section2' in config @with_temp_dir def test_write_to_user_config(temp_dir=None): config_file = 'test_config' default_file = os.path.join(TEST_DATA_DIR, 'configrc') temp_config_file = os.path.join(temp_dir, config_file) config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file) config.read_default_config() config.write_default_config() with open(temp_config_file) as f: assert 'test_boolean_default = True' in f.read() config['section']['test_boolean_default'] = False config.write() with open(temp_config_file) as f: assert 'test_boolean_default = False' in f.read() @with_temp_dir def test_write_to_outfile(temp_dir=None): config_file = 'test_config' outfile = os.path.join(temp_dir, 'foo') default_file = os.path.join(TEST_DATA_DIR, 'configrc') config = _mocked_user_config(temp_dir, APP_NAME, APP_AUTHOR, config_file, default=default_file) config.read_default_config() config.write_default_config() config['section']['test_boolean_default'] = False config.write(outfile=outfile) with open(outfile) as f: assert 'test_boolean_default = False' in f.read()