# pylint: disable=redefined-outer-name from copy import deepcopy import pytest import preserve from configspec import * @pytest.fixture def example_spec(): confspec = ConfigSpecification() confspec.add_spec("bool_a", BoolSpec(helptext="Bool A")) confspec.add_spec("boollist_a", ListSpec(BoolSpec(), helptext="Bool List A")) confspec.add_spec("int_a", IntSpec(-4, 4, helptext="Int A")) confspec.add_spec("intlist_a", ListSpec(IntSpec(-4, 4, "Int A"))) confspec.add_spec("float_a", FloatSpec(-4, 4, helptext="Float A")) confspec.add_spec("floatlist_a", ListSpec(FloatSpec(-4, 4, "Float A"))) confspec.add_spec("string_a", StringSpec(helptext="String A")) confspec.add_spec("string_a_blank", StringSpec(allowempty=True, helptext="String A Blank")) confspec.add_spec("stringlist_a", ListSpec(StringSpec(), helptext="String List A")) confdictspec = confspec.add_spec("dict_a", DictSpec(helptext="Dict A")) confdictspec.add_spec("bool_b", BoolSpec(helptext="Bool B")) confdictspec.add_spec("int_b", IntSpec(helptext="Int B")) confdictspec.add_spec("float_b", FloatSpec(helptext="Float B")) confdictspec.add_spec("string_b", StringSpec(helptext="String B")) confdictlist = confspec.add_spec("dictlist_a", ListSpec( DictSpec(), helptext="List of Dicts B")) confdictlist.add_specs({"bool_c": BoolSpec(helptext="Bool C"), "int_c": IntSpec(helptext="Int C"), "float_c": FloatSpec(helptext="Float C"), "string_c": StringSpec(helptext="String C")}) return confspec @pytest.fixture def example_values(): vals = {'bool_a': True, 'boollist_a': [False, True], 'int_a': 1, 'intlist_a': [1, 2, 3, 4], 'float_a': 1.5, 'floatlist_a': [1.2, 2.1, 3.7, 2.8], 'string_a': 'sometext', 'string_a_blank': '', 'stringlist_a': ['somemoretext', 'yetmoretext'], 'dict_a': { 'bool_b': False, 'int_b': 0, 'float_b': 0.5, 'string_b': 'texttesttext'}, 'dictlist_a': [{'bool_c': False, 'int_c': 3, 'float_c': 3.4, 'string_c': 'string1'}, {'bool_c': True, 'int_c': 0, 'float_c': 0.4, 'string_c': 'string2'}]} return vals def test_addspec(example_spec): # Most of the test is really just forming the fixture without exception assert isinstance(example_spec, ConfigSpecification) def test_preserveable(example_spec): assert isinstance(preserve.preserve(example_spec), dict) def test_config_validate(example_spec, example_values): example_spec.validate(example_values) def test_invalid_bool(): spec = BoolSpec() with pytest.raises(InvalidConfigError, match="value must be a boolean"): spec.validate("false") with pytest.raises(InvalidConfigError, match="value must be a boolean"): spec.validate(None) def test_invalid_int(): spec = IntSpec(minval=-4, maxval=4) with pytest.raises(InvalidConfigError, match="value must be >="): spec.validate(-5) with pytest.raises(InvalidConfigError, match="value must be <="): spec.validate(5) with pytest.raises(InvalidConfigError, match="value must be an integer"): spec.validate("5") with pytest.raises(InvalidConfigError, match="value must be an integer"): spec.validate(None) def test_invalid_string(): spec = StringSpec() # TODO add regex check with pytest.raises(InvalidConfigError, match="value must be a string"): spec.validate(1) with pytest.raises(InvalidConfigError, match="value must be a string"): spec.validate(None) with pytest.raises(InvalidConfigError, match="value cannot be a blank string"): spec.validate("") def test_dict_optional(): spec = DictSpec() spec.add_spec("int1", IntSpec()) spec.add_spec("int2", IntSpec(), optional=True) spec.add_spec("int3", IntSpec(), optional=True, default=1) with pytest.raises(InvalidConfigError, match="must contain key 'int1'"): spec.validate({}) with pytest.raises(InvalidConfigError, match="contains unknown key"): spec.validate({"int1": 1, "int5": 0}) d = {"int1": 1, "int2": 2, "int3": 3} spec.validate(d) assert d == {"int1": 1, "int2": 2, "int3": 3} d = {"int1": 1} spec.validate(d) assert d == {"int1": 1, "int2": None, "int3": 1} def test_man(example_spec, example_values): confman = ConfigManager() confman.add_confspec("test_bundle", example_spec) confman.load({"test_bundle": example_values}) assert confman.get_config_bundle("test_bundle") == example_values confman.freeze_value("test_bundle", "int_a") newvals = deepcopy(example_values) newvals["int_a"] = 4 confman.load({"test_bundle": newvals}) assert confman.get_config_bundle("test_bundle") == example_values confman.load_overlay({"test_bundle": {"string_a": "new text"}}) assert confman.get_config_bundle("test_bundle")["string_a"] == "new text"