1import configparser 2from dataclasses import dataclass 3from datetime import datetime 4from decimal import Decimal 5from pathlib import Path 6 7import pytest 8 9from mako.testing._config import ConfigValueTypeError 10from mako.testing._config import MissingConfig 11from mako.testing._config import MissingConfigItem 12from mako.testing._config import MissingConfigSection 13from mako.testing._config import ReadsCfg 14from mako.testing.assertions import assert_raises_message_with_given_cause 15from mako.testing.assertions import assert_raises_with_given_cause 16 17PATH_TO_TEST_CONFIG = Path(__file__).parent / "dummy.cfg" 18 19 20@dataclass 21class BasicConfig(ReadsCfg): 22 int_value: int 23 bool_value: bool 24 float_value: float 25 str_value: str 26 27 section_header = "basic_values" 28 29 30@dataclass 31class BooleanConfig(ReadsCfg): 32 yes: bool 33 one: bool 34 true: bool 35 on: bool 36 no: bool 37 zero: bool 38 false: bool 39 off: bool 40 41 section_header = "boolean_values" 42 43 44@dataclass 45class UnsupportedTypesConfig(ReadsCfg): 46 decimal_value: Decimal 47 datetime_value: datetime 48 49 section_header = "additional_types" 50 51 52@dataclass 53class SupportedTypesConfig(ReadsCfg): 54 decimal_value: Decimal 55 datetime_value: datetime 56 57 section_header = "additional_types" 58 converters = { 59 Decimal: lambda v: Decimal(str(v)), 60 datetime: lambda v: datetime.fromisoformat(v), 61 } 62 63 64@dataclass 65class NonexistentSectionConfig(ReadsCfg): 66 some_value: str 67 another_value: str 68 69 section_header = "i_dont_exist" 70 71 72@dataclass 73class TypeMismatchConfig(ReadsCfg): 74 int_value: int 75 76 section_header = "type_mismatch" 77 78 79@dataclass 80class MissingItemConfig(ReadsCfg): 81 present_item: str 82 missing_item: str 83 84 section_header = "missing_item" 85 86 87class BasicConfigTest: 88 @pytest.fixture(scope="class") 89 def config(self): 90 return BasicConfig.from_cfg_file(PATH_TO_TEST_CONFIG) 91 92 def test_coercions(self, config): 93 assert isinstance(config.int_value, int) 94 assert isinstance(config.bool_value, bool) 95 assert isinstance(config.float_value, float) 96 assert isinstance(config.str_value, str) 97 98 def test_values(self, config): 99 assert config.int_value == 15421 100 assert config.bool_value == True 101 assert config.float_value == 14.01 102 assert config.str_value == "Ceci n'est pas une chaîne" 103 104 def test_error_on_loading_from_nonexistent_file(self): 105 assert_raises_with_given_cause( 106 MissingConfig, 107 FileNotFoundError, 108 BasicConfig.from_cfg_file, 109 "./n/o/f/i/l/e/h.ere", 110 ) 111 112 def test_error_on_loading_from_nonexistent_section(self): 113 assert_raises_with_given_cause( 114 MissingConfigSection, 115 configparser.NoSectionError, 116 NonexistentSectionConfig.from_cfg_file, 117 PATH_TO_TEST_CONFIG, 118 ) 119 120 121class BooleanConfigTest: 122 @pytest.fixture(scope="class") 123 def config(self): 124 return BooleanConfig.from_cfg_file(PATH_TO_TEST_CONFIG) 125 126 def test_values(self, config): 127 assert config.yes is True 128 assert config.one is True 129 assert config.true is True 130 assert config.on is True 131 assert config.no is False 132 assert config.zero is False 133 assert config.false is False 134 assert config.off is False 135 136 137class UnsupportedTypesConfigTest: 138 @pytest.fixture(scope="class") 139 def config(self): 140 return UnsupportedTypesConfig.from_cfg_file(PATH_TO_TEST_CONFIG) 141 142 def test_values(self, config): 143 assert config.decimal_value == "100001.01" 144 assert config.datetime_value == "2021-12-04 00:05:23.283" 145 146 147class SupportedTypesConfigTest: 148 @pytest.fixture(scope="class") 149 def config(self): 150 return SupportedTypesConfig.from_cfg_file(PATH_TO_TEST_CONFIG) 151 152 def test_values(self, config): 153 assert config.decimal_value == Decimal("100001.01") 154 assert config.datetime_value == datetime(2021, 12, 4, 0, 5, 23, 283000) 155 156 157class TypeMismatchConfigTest: 158 def test_error_on_load(self): 159 assert_raises_message_with_given_cause( 160 ConfigValueTypeError, 161 "Wrong value type for int_value", 162 ValueError, 163 TypeMismatchConfig.from_cfg_file, 164 PATH_TO_TEST_CONFIG, 165 ) 166 167 168class MissingItemConfigTest: 169 def test_error_on_load(self): 170 assert_raises_message_with_given_cause( 171 MissingConfigItem, 172 "No config item for missing_item", 173 configparser.NoOptionError, 174 MissingItemConfig.from_cfg_file, 175 PATH_TO_TEST_CONFIG, 176 ) 177