# -*- coding: utf-8 -*- # Copyright: (c) 2017, Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import annotations import os import os.path import pytest from ansible.config.manager import ConfigManager, ensure_type, resolve_path, get_config_type from ansible.errors import AnsibleOptionsError, AnsibleError from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode curdir = os.path.dirname(__file__) cfg_file = os.path.join(curdir, 'test.cfg') cfg_file2 = os.path.join(curdir, 'test2.cfg') cfg_file3 = os.path.join(curdir, 'test3.cfg') ensure_test_data = [ ('a,b', 'list', list), (['a', 'b'], 'list', list), ('y', 'bool', bool), ('yes', 'bool', bool), ('on', 'bool', bool), ('1', 'bool', bool), ('true', 'bool', bool), ('t', 'bool', bool), (1, 'bool', bool), (1.0, 'bool', bool), (True, 'bool', bool), ('n', 'bool', bool), ('no', 'bool', bool), ('off', 'bool', bool), ('0', 'bool', bool), ('false', 'bool', bool), ('f', 'bool', bool), (0, 'bool', bool), (0.0, 'bool', bool), (False, 'bool', bool), ('10', 'int', int), (20, 'int', int), ('0.10', 'float', float), (0.2, 'float', float), ('/tmp/test.yml', 'pathspec', list), ('/tmp/test.yml,/home/test2.yml', 'pathlist', list), ('a', 'str', str), ('a', 'string', str), ('Café', 'string', str), ('', 'string', str), ('29', 'str', str), ('13.37', 'str', str), ('123j', 'string', str), ('0x123', 'string', str), ('true', 'string', str), ('True', 'string', str), (0, 'str', str), (29, 'str', str), (13.37, 'str', str), (123j, 'string', str), (0x123, 'string', str), (True, 'string', str), ('None', 'none', type(None)) ] ensure_unquoting_test_data = [ ('"value"', '"value"', 'str', 'env'), ('"value"', '"value"', 'str', 'yaml'), ('"value"', 'value', 'str', 'ini'), ('\'value\'', 'value', 'str', 'ini'), ('\'\'value\'\'', '\'value\'', 'str', 'ini'), ('""value""', '"value"', 'str', 'ini') ] class TestConfigManager: @classmethod def setup_class(cls): cls.manager = ConfigManager(cfg_file, os.path.join(curdir, 'test.yml')) @classmethod def teardown_class(cls): cls.manager = None @pytest.mark.parametrize("value, expected_type, python_type", ensure_test_data) def test_ensure_type(self, value, expected_type, python_type): assert isinstance(ensure_type(value, expected_type), python_type) @pytest.mark.parametrize("value, expected_value, value_type, origin", ensure_unquoting_test_data) def test_ensure_type_unquoting(self, value, expected_value, value_type, origin): actual_value = ensure_type(value, value_type, origin) assert actual_value == expected_value def test_resolve_path(self): assert os.path.join(curdir, 'test.yml') == resolve_path('./test.yml', cfg_file) def test_resolve_path_cwd(self): assert os.path.join(os.getcwd(), 'test.yml') == resolve_path('{{CWD}}/test.yml') assert os.path.join(os.getcwd(), 'test.yml') == resolve_path('./test.yml') def test_value_and_origin_from_ini(self): assert self.manager.get_config_value_and_origin('config_entry') == ('fromini', cfg_file) def test_value_from_ini(self): assert self.manager.get_config_value('config_entry') == 'fromini' def test_value_and_origin_from_alt_ini(self): assert self.manager.get_config_value_and_origin('config_entry', cfile=cfg_file2) == ('fromini2', cfg_file2) def test_value_from_alt_ini(self): assert self.manager.get_config_value('config_entry', cfile=cfg_file2) == 'fromini2' def test_config_types(self): assert get_config_type('/tmp/ansible.ini') == 'ini' assert get_config_type('/tmp/ansible.cfg') == 'ini' assert get_config_type('/tmp/ansible.yaml') == 'yaml' assert get_config_type('/tmp/ansible.yml') == 'yaml' def test_config_types_negative(self): with pytest.raises(AnsibleOptionsError) as exec_info: get_config_type('/tmp/ansible.txt') assert "Unsupported configuration file extension for" in str(exec_info.value) def test_read_config_yaml_file(self): assert isinstance(self.manager._read_config_yaml_file(os.path.join(curdir, 'test.yml')), dict) def test_read_config_yaml_file_negative(self): with pytest.raises(AnsibleError) as exec_info: self.manager._read_config_yaml_file(os.path.join(curdir, 'test_non_existent.yml')) assert "Missing base YAML definition file (bad install?)" in str(exec_info.value) def test_entry_as_vault_var(self): class MockVault: def decrypt(self, value, filename=None, obj=None): return value vault_var = AnsibleVaultEncryptedUnicode(b"vault text") vault_var.vault = MockVault() actual_value, actual_origin = self.manager._loop_entries({'name': vault_var}, [{'name': 'name'}]) assert actual_value == "vault text" assert actual_origin == "name" @pytest.mark.parametrize("value_type", ("str", "string", None)) def test_ensure_type_with_vaulted_str(self, value_type): class MockVault: def decrypt(self, value, filename=None, obj=None): return value vault_var = AnsibleVaultEncryptedUnicode(b"vault text") vault_var.vault = MockVault() actual_value = ensure_type(vault_var, value_type) assert actual_value == "vault text" @pytest.mark.parametrize(("key", "expected_value"), ( ("COLOR_UNREACHABLE", "bright red"), ("COLOR_VERBOSE", "rgb013"), ("COLOR_DEBUG", "gray10"))) def test_256color_support(key, expected_value): # GIVEN: a config file containing 256-color values with default definitions manager = ConfigManager(cfg_file3) # WHEN: get config values actual_value = manager.get_config_value(key) # THEN: no error assert actual_value == expected_value