def test_custom_exception(self, mocker):
        stub = mocker.stub(name='custom_on_error')
        string_field = StringField(choices=['foo'], on_error=stub)

        with pytest.raises(ConfigError):
            string_field.validate('bar', 'foo')
        stub.assert_called_once_with('bar', 'foo', ANY)
    def test_expects_string(self):
        string_field = StringField()

        with pytest.raises(ConfigError):
            string_field.validate(b"foo")
        with pytest.raises(ConfigError):
            string_field.validate({})
        with pytest.raises(ConfigError):
            string_field.validate(42)

        string_field.validate("foo")
    def test_case_sensitive(self):
        string_field = StringField(choices=['foo', 'bar'], case_sensitive=False)

        string_field.validate('foo')
        string_field.validate('FOO')

        string_field = StringField(choices=['foo', 'bar'], case_sensitive=True)

        string_field.validate('foo')
        with pytest.raises(ConfigError):
            string_field.validate('FOO')
    def test_regex(self):
        string_field = StringField(regex=r'foo\d*')

        string_field.validate('foo')
        string_field.validate('foo42')

        with pytest.raises(ConfigError):
            string_field.validate('baz')
    def test_choices(self):
        string_field = StringField(choices=['foo', 'bar'])

        with pytest.raises(ConfigError):
            string_field.validate('baz')

        string_field.validate('bar')
    def test_validates_values(self):
        list_field = ListField()
        list_field.validate(['foo', 42])

        list_field = ListField(value_type=str)
        list_field.validate(['foo', 'bar'])
        with pytest.raises(ConfigError):
            list_field.validate(['foo', 42])

        list_field = ListField(value_type=StringField(choices=['foo', 'bar']))
        list_field.validate(['foo', 'bar'])
        with pytest.raises(ConfigError):
            list_field.validate(['foo', 42])
        with pytest.raises(ConfigError):
            list_field.validate(['foo', 'bar', 'baz'])
    def test_validates_valuess(self):
        dict_field = DictField()
        dict_field.validate({'foo': 42, 1: 'bar'})

        dict_field = DictField(value_type=str)
        dict_field.validate({'foo': 'foo', 1: 'bar'})
        with pytest.raises(ConfigError):
            dict_field.validate({'foo': 42, 1: 2})

        dict_field = DictField(value_type=StringField(choices=['foo', 'bar']))
        dict_field.validate({1: 'foo', 'bar': 'bar'})
        with pytest.raises(ConfigError):
            dict_field.validate({1: 'foo', 2: 3})
        with pytest.raises(ConfigError):
            dict_field.validate({1: 'foo', 2: 'baz'})
    def test_validates_keys(self):
        dict_field = DictField()
        dict_field.validate({'foo': 42, 1: 'bar'})

        dict_field = DictField(key_type=str)
        dict_field.validate({'foo': 42, 'bar': 'bar'})
        with pytest.raises(ConfigError):
            dict_field.validate({'foo': 42, 1: 'bar'})

        dict_field = DictField(key_type=StringField(choices=['foo', 'bar']))
        dict_field.validate({'foo': 42, 'bar': 42})
        with pytest.raises(ConfigError):
            dict_field.validate({'foo': 42, 1: 'bar'})
        with pytest.raises(ConfigError):
            dict_field.validate({'foo': 42, 'baz': 42})
    def test_custom_validator(self, mocker):
        stub = mocker.stub(name='custom_validator')
        string_field = StringField(choices=['foo'], additional_validator=stub)

        string_field.validate('foo', 'baz')
        stub.assert_called_once_with('foo', 'baz')
 class DerivedValidator(ParentValidator):
     bar = StringField(choices=['bar'])
 class ParentValidator(ConfigValidator):
     foo = StringField(choices=['foo'])
 class InnerValidator(ConfigValidator):
     foo = StringField(choices=['foo'])
 class SampleValidator(ConfigValidator):
     foo = StringField(choices=['foo'])
 class SampleValidatorIgnoresExtra(ConfigValidator):
     foo = StringField(choices=['foo'])
 class SampleValidatorErrorOnExtra(ConfigValidator):
     foo = StringField(choices=['foo'])
 class SampleValidatorWithOptionals(ConfigValidator):
     foo = StringField(choices=['foo'])
     bar = NumberField(optional=True)