Implement oneOf, anyOf, allOf and not with unit tests

This commit is contained in:
Pedro Algarvio 2015-07-03 16:24:46 +01:00
parent c59d43330c
commit 625e0b6c06
2 changed files with 304 additions and 15 deletions

View file

@ -652,21 +652,7 @@ class BaseItem(six.with_metaclass(BaseConfigItemMeta, object)):
'''
Return a serializable form of the config instance
'''
serialized = {'type': self.__type__}
for argname in self._attributes:
if argname == 'required':
# This is handled elsewhere
continue
argvalue = self._get_argname_value(argname)
if argvalue is not None:
if argvalue is Null:
argvalue = None
# None values are not meant to be included in the
# serialization, since this is not None...
if self.__serialize_attr_aliases__ and argname in self.__serialize_attr_aliases__:
argname = self.__serialize_attr_aliases__[argname]
serialized[argname] = argvalue
return serialized
raise NotImplementedError
class BaseConfigItem(BaseItem):
@ -724,6 +710,26 @@ class BaseConfigItem(BaseItem):
if not isinstance(self.enum, list):
self.enum = list(self.enum)
def serialize(self):
'''
Return a serializable form of the config instance
'''
serialized = {'type': self.__type__}
for argname in self._attributes:
if argname == 'required':
# This is handled elsewhere
continue
argvalue = self._get_argname_value(argname)
if argvalue is not None:
if argvalue is Null:
argvalue = None
# None values are not meant to be included in the
# serialization, since this is not None...
if self.__serialize_attr_aliases__ and argname in self.__serialize_attr_aliases__:
argname = self.__serialize_attr_aliases__[argname]
serialized[argname] = argvalue
return serialized
def render_as_rst(self, name):
'''
Render the configuration item as a restructured text string
@ -1035,3 +1041,74 @@ class ArrayConfig(BaseConfigItem):
for item in self.items:
items.append(item.serialize())
return items
class OneOfConfig(BaseItem):
__type__ = 'oneOf'
items = None
def __init__(self, items=None):
if items is not None:
self.items = items
super(OneOfConfig, self).__init__()
def __validate_attributes__(self):
if not self.items:
raise RuntimeError(
'The passed items must not be empty'
)
if not isinstance(self.items, (list, tuple)):
raise RuntimeError(
'The passed items must be passed as a list/tuple not '
'\'{0}\''.format(type(self.items))
)
for idx, item in enumerate(self.items):
if not isinstance(item, (Configuration, BaseItem)):
raise RuntimeError(
'The passed item at the {0} index must be of type '
'Configuration, BaseItem or BaseConfigItem, not '
'\'{1}\''.format(idx, type(item))
)
if not isinstance(self.items, list):
self.items = list(self.items)
def serialize(self):
return {self.__type__: [i.serialize() for i in self.items]}
class AnyOfConfig(OneOfConfig):
__type__ = 'anyOf'
class AllOfConfig(OneOfConfig):
__type__ = 'allOf'
class NotConfig(BaseItem):
__type__ = 'not'
item = None
def __init__(self, item=None):
if item is not None:
self.item = item
super(NotConfig, self).__init__()
def __validate_attributes__(self):
if not self.item:
raise RuntimeError(
'An item must be passed'
)
if not isinstance(self.item, (Configuration, BaseItem)):
raise RuntimeError(
'The passed item be of type Configuration, BaseItem or '
'BaseConfigItem, not \'{1}\''.format(type(self.item))
)
def serialize(self):
return {self.__type__: self.item.serialize()}

View file

@ -981,6 +981,218 @@ class ConfigTestCase(TestCase):
format_checker=jsonschema.FormatChecker())
self.assertIn('is not one of', excinfo.exception.message)
def test_oneof_config(self):
item = config.OneOfConfig(
items=(config.StringConfig(title='Yes', enum=['yes']),
config.StringConfig(title='No', enum=['no']))
)
self.assertEqual(
item.serialize(), {
'oneOf': [i.serialize() for i in item.items]
}
)
@skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
def test_oneof_config_validation(self):
class TestConf(config.Configuration):
item = config.ArrayConfig(
title='Hungry',
description='Are you hungry?',
items=config.OneOfConfig(
items=(config.StringConfig(title='Yes', enum=['yes']),
config.StringConfig(title='No', enum=['no']))
)
)
try:
jsonschema.validate({'item': ['no']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': 2}, TestConf.serialize())
self.assertIn('is not of type', excinfo.exception.message)
def test_anyof_config(self):
item = config.AnyOfConfig(
items=(config.StringConfig(title='Yes', enum=['yes']),
config.StringConfig(title='No', enum=['no']))
)
self.assertEqual(
item.serialize(), {
'anyOf': [i.serialize() for i in item.items]
}
)
@skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
def test_anyof_config_validation(self):
class TestConf(config.Configuration):
item = config.ArrayConfig(
title='Hungry',
description='Are you hungry?',
items=config.AnyOfConfig(
items=(config.StringConfig(title='Yes', enum=['yes']),
config.StringConfig(title='No', enum=['no']),
config.BooleanConfig())
)
)
try:
jsonschema.validate({'item': ['no']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': ['yes']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': [True]}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': [False]}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': 2}, TestConf.serialize())
self.assertIn('is not of type', excinfo.exception.message)
def test_allof_config(self):
item = config.AllOfConfig(
items=(config.StringConfig(min_length=2),
config.StringConfig(max_length=3))
)
self.assertEqual(
item.serialize(), {
'allOf': [i.serialize() for i in item.items]
}
)
@skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
def test_allof_config_validation(self):
class TestConf(config.Configuration):
item = config.ArrayConfig(
title='Hungry',
description='Are you hungry?',
items=config.AllOfConfig(
items=(config.StringConfig(min_length=2),
config.StringConfig(max_length=3))
)
)
try:
jsonschema.validate({'item': ['no']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': ['yes']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
self.assertIn('is too long', excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': ['hmmmm']}, TestConf.serialize())
self.assertIn('is too long', excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': 2}, TestConf.serialize())
self.assertIn('is not of type', excinfo.exception.message)
@skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
def test_anyof_config_validation(self):
class TestConf(config.Configuration):
item = config.ArrayConfig(
title='Hungry',
description='Are you hungry?',
items=config.AnyOfConfig(
items=(config.StringConfig(title='Yes', enum=['yes']),
config.StringConfig(title='No', enum=['no']),
config.BooleanConfig())
)
)
try:
jsonschema.validate({'item': ['no']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': ['yes']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': [True]}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': [False]}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': 2}, TestConf.serialize())
self.assertIn('is not of type', excinfo.exception.message)
def test_not_config(self):
item = config.NotConfig(item=config.BooleanConfig())
self.assertEqual(
item.serialize(), {
'not': item.item.serialize()
}
)
@skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
def test_not_config_validation(self):
class TestConf(config.Configuration):
item = config.ArrayConfig(
title='Hungry',
description='Are you hungry?',
items=config.NotConfig(item=config.BooleanConfig())
)
try:
jsonschema.validate({'item': ['no']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
try:
jsonschema.validate({'item': ['yes']}, TestConf.serialize())
except jsonschema.exceptions.ValidationError as exc:
self.fail('ValidationError raised: {0}'.format(exc))
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': [True]}, TestConf.serialize())
self.assertIn('is not allowed for', excinfo.exception.message)
with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
jsonschema.validate({'item': [False]}, TestConf.serialize())
self.assertIn('is not allowed for', excinfo.exception.message)
if __name__ == '__main__':
from integration import run_tests