Merge "Add unit tests for validators"

This commit is contained in:
Jenkins 2017-06-19 16:01:50 +00:00 committed by Gerrit Code Review
commit 870682389a
5 changed files with 580 additions and 138 deletions

View File

@ -475,11 +475,12 @@ class error_handler(object):
def get_schema_type(attr):
if isinstance(attr, fields.IntegerField):
if isinstance(attr, fields.IntegerField) or attr is fields.Integer:
return 'integer'
elif isinstance(attr, fields.FloatField):
elif isinstance(attr, fields.FloatField) or attr is fields.Float:
return 'number'
elif isinstance(attr, fields.BooleanField):
elif isinstance(attr, fields.FlexibleBooleanField) \
or attr is fields.FlexibleBoolean:
return 'boolean'
elif isinstance(attr, glare_fields.List):
return 'array'

View File

@ -923,10 +923,8 @@ class BaseArtifact(base.VersionedObject):
schema['readOnly'] = True
if isinstance(field, glare_fields.Dict):
element_type = (utils.get_schema_type(field.element_type)
if hasattr(field, 'element_type')
else 'string')
element_type = utils.get_schema_type(field.element_type)
property_validators = schema.pop('propertyValidators', [])
if field.element_type is glare_fields.BlobFieldType:
schema['additionalProperties'] = output_blob_schema
else:
@ -938,16 +936,21 @@ class BaseArtifact(base.VersionedObject):
'type': (element_type
if key in required
else [element_type, 'null'])}
for val in property_validators:
properties[key].update(val)
schema['properties'] = properties
schema['additionalProperties'] = False
else:
schema['additionalProperties'] = {'type': element_type}
for val in property_validators:
schema['additionalProperties'].update(val)
if field_type == 'array':
if isinstance(field, glare_fields.List):
items_validators = schema.pop('itemValidators', [])
schema['items'] = {
'type': (utils.get_schema_type(field.element_type)
if hasattr(field, 'element_type')
else 'string')}
'type': utils.get_schema_type(field.element_type)}
for val in items_validators:
schema['items'].update(val)
if isinstance(field, glare_fields.BlobField):
schema.update(output_blob_schema)

View File

@ -13,23 +13,27 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from oslo_utils import encodeutils
from oslo_versionedobjects import fields
import abc
import uuid
from oslo_log import log as logging
from oslo_versionedobjects import fields
import six
from glare.common import exception
from glare.i18n import _
from glare.objects.meta import fields as glare_fields
LOG = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class Validator(object):
"""Common interface for all validators."""
def validate(self, value):
raise NotImplementedError()
def get_allowed_types(self):
@staticmethod
@abc.abstractmethod
def get_allowed_types():
raise NotImplementedError()
def check_type_allowed(self, field_type):
@ -40,7 +44,7 @@ class Validator(object):
for field in self.get_allowed_types()
if hasattr(field, 'AUTO_TYPE'))
if not issubclass(field_type, allowed_field_types):
raise TypeError(
raise exception.IncorrectArtifactType(
_("%(type)s is not allowed for validator "
"%(val)s. Allowed types are %(allowed)s.") % {
"type": str(field_type),
@ -50,23 +54,19 @@ class Validator(object):
def to_jsonschema(self):
return {}
@abc.abstractmethod
def __call__(self, value):
try:
self.validate(value)
except ValueError:
raise
except TypeError as e:
# we are raising all expected ex Type Errors as ValueErrors
LOG.exception(e)
raise ValueError(encodeutils.exception_to_unicode(e))
raise NotImplemented
class UUID(Validator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return fields.StringField,
def validate(self, value):
pass
def __call__(self, value):
uuid.UUID(value, version=4)
def to_jsonschema(self):
return {'pattern': ('^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F])'
@ -74,26 +74,30 @@ class UUID(Validator):
class AllowedValues(Validator):
def __init__(self, allowed_values):
self.allowed_values = allowed_values
def get_allowed_types(self):
return fields.StringField,
@staticmethod
def get_allowed_types():
return fields.StringField, fields.IntegerField, fields.FloatField
def validate(self, value):
def __call__(self, value):
if value not in self.allowed_values:
raise ValueError(_("Value must be one of the following: %s") %
', '.join(self.allowed_values))
', '.join(map(str, self.allowed_values)))
def to_jsonschema(self):
return {'enum': self.allowed_values}
class Version(Validator):
def get_allowed_types(self):
return glare_fields.VersionField
def validate(self, value):
@staticmethod
def get_allowed_types():
return glare_fields.VersionField,
def __call__(self, value):
pass
def to_jsonschema(self):
@ -101,77 +105,87 @@ class Version(Validator):
'+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/')}
@six.add_metaclass(abc.ABCMeta)
class SizeValidator(Validator):
def __init__(self, size):
self.size = size
class MaxStrLen(SizeValidator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return fields.StringField,
def validate(self, value):
def __call__(self, value):
l = len(value)
if l > self.size:
raise ValueError(
_("String length must be less than %(size)s. "
"Current size: %(cur)s") % {'size': self.size,
'cur': l})
_("String length must be less than %(size)d. "
"Current length: %(cur)d") % {'size': self.size,
'cur': l})
def to_jsonschema(self):
return {'maxLength': self.size}
class MinStrLen(SizeValidator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return fields.StringField,
def validate(self, value):
def __call__(self, value):
l = len(value)
if l < self.size:
raise ValueError(
_("String length must be more than %(size)s. "
"Current size: %(cur)s") % {'size': self.size,
'cur': l})
_("String length must be less than %(size)d. "
"Current length: %(cur)d") % {'size': self.size,
'cur': l})
def to_jsonschema(self):
return {'minLength': self.size}
class ForbiddenChars(Validator):
def __init__(self, forbidden_chars):
self.forbidden_chars = forbidden_chars
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return fields.StringField,
def validate(self, value):
def __call__(self, value):
for fc in self.forbidden_chars:
if fc in value:
raise ValueError(
_("Forbidden character %(char) found in string "
_("Forbidden character %(char)c found in string "
"%(string)s")
% {"char": fc, "string": value})
def to_jsonschema(self):
return {'pattern': '^[^%s]+$' % ''.join(self.forbidden_chars)}
@six.add_metaclass(abc.ABCMeta)
class MaxSize(SizeValidator):
def validate(self, value):
def __call__(self, value):
l = len(value)
if l > self.size:
raise ValueError(
_("Number of items must be less than "
"%(size)s. Current size: %(cur)s") %
"%(size)d. Current size: %(cur)d") %
{'size': self.size, 'cur': l})
def to_jsonschema(self):
return {'maxItems': self.size}
class MaxDictSize(MaxSize):
def get_allowed_types(self):
return glare_fields.Dict
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def to_jsonschema(self):
return {'maxProperties': self.size}
@ -179,20 +193,55 @@ class MaxDictSize(MaxSize):
class MaxListSize(MaxSize):
def get_allowed_types(self):
return glare_fields.List
@staticmethod
def get_allowed_types():
return glare_fields.List,
def to_jsonschema(self):
return {'maxItems': self.size}
class MaxNumberSize(SizeValidator):
def validate(self, value):
if value > self.size:
raise ValueError("Number is too big: %s. Max allowed number is "
"%s" % (value, self.size))
@six.add_metaclass(abc.ABCMeta)
class MinSize(SizeValidator):
def get_allowed_types(self):
def __call__(self, value):
l = len(value)
if l < self.size:
raise ValueError(
_("Number of items must be greater than "
"%(size)d. Current size: %(cur)d") %
{'size': self.size, 'cur': l})
class MinDictSize(MinSize):
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def to_jsonschema(self):
return {'minProperties': self.size}
class MinListSize(MinSize):
@staticmethod
def get_allowed_types():
return glare_fields.List,
def to_jsonschema(self):
return {'minItems': self.size}
class MaxNumberSize(SizeValidator):
def __call__(self, value):
if value > self.size:
raise ValueError("Number is too big: %d. Max allowed number is "
"%d" % (value, self.size))
@staticmethod
def get_allowed_types():
return fields.IntegerField, fields.FloatField
def to_jsonschema(self):
@ -200,12 +249,14 @@ class MaxNumberSize(SizeValidator):
class MinNumberSize(SizeValidator):
def validate(self, value):
if value < self.size:
raise ValueError("Number is too small: %s. Min allowed number is "
"%s" % (value, self.size))
def get_allowed_types(self):
def __call__(self, value):
if value < self.size:
raise ValueError("Number is too small: %d. Min allowed number is "
"%d" % (value, self.size))
@staticmethod
def get_allowed_types():
return fields.IntegerField, fields.FloatField
def to_jsonschema(self):
@ -213,50 +264,34 @@ class MinNumberSize(SizeValidator):
class Unique(Validator):
def __init__(self, convert_to_set=False):
self.convert_to_set = convert_to_set
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.List,
def validate(self, value):
def __call__(self, value):
if self.convert_to_set:
value[:] = list(set(value))
elif len(value) != len(set(value)):
raise ValueError(_("List items %s must be unique.") % value)
def to_jsonschema(self):
return {'unique': True}
class AllowedListValues(Validator):
def __init__(self, allowed_values):
self.allowed_items = allowed_values
def get_allowed_types(self):
return glare_fields.List,
def validate(self, value):
for item in value:
if item not in self.allowed_items:
raise ValueError(
_("Value %(item)s is not allowed in list. "
"Allowed list values: %(allowed)s") %
{"item": item,
"allowed": self.allowed_items})
def to_jsonschema(self):
return {'enum': self.allowed_items}
return {'uniqueItems': True}
class AllowedDictKeys(Validator):
def __init__(self, allowed_keys):
self.allowed_items = allowed_keys
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def validate(self, value):
def __call__(self, value):
for item in value:
if item not in self.allowed_items:
raise ValueError(_("Key %(item)s is not allowed in dict. "
@ -271,17 +306,19 @@ class AllowedDictKeys(Validator):
class RequiredDictKeys(Validator):
def __init__(self, required_keys):
self.required_items = required_keys
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def validate(self, value):
def __call__(self, value):
for item in self.required_items:
if item not in value:
raise ValueError(_("Key \"%(item)s\" is required in property "
"dictionary: %(value)s.") %
raise ValueError(_("Key \"%(item)s\" is required in "
"dictionary %(value)s.") %
{"item": item,
"value": ''.join(
'{}:{}, '.format(key, val)
@ -292,49 +329,69 @@ class RequiredDictKeys(Validator):
class MaxDictKeyLen(SizeValidator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def validate(self, value):
def __call__(self, value):
for key in value:
if len(str(key)) > self.size:
raise ValueError(_("Dict key length %(key)s must be less than "
"%(size)s.") % {'key': key,
"%(size)d.") % {'key': key,
'size': self.size})
class MinDictKeyLen(SizeValidator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def validate(self, value):
def __call__(self, value):
for key in value:
if len(str(key)) < self.size:
raise ValueError(_("Dict key length %(key)s must be bigger "
"than %(size)s.") % {'key': key,
"than %(size)d.") % {'key': key,
'size': self.size})
@six.add_metaclass(abc.ABCMeta)
class ElementValidator(Validator):
def __init__(self, validators):
self.validators = validators
class ListElementValidator(ElementValidator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.List,
def validate(self, value):
def __call__(self, value):
for v in value:
for validator in self.validators:
validator(v)
def to_jsonschema(self):
return {'itemValidators': [
val.to_jsonschema() for val in self.validators
]}
class DictElementValidator(ElementValidator):
def get_allowed_types(self):
@staticmethod
def get_allowed_types():
return glare_fields.Dict,
def validate(self, value):
def __call__(self, value):
for v in value.values():
for validator in self.validators:
validator(v)
def to_jsonschema(self):
return {'propertyValidators': [
val.to_jsonschema() for val in self.validators
]}

View File

@ -57,7 +57,9 @@ fixture_base_props = {
u'readOnly': True,
u'sortable': True,
u'type': u'string'},
u'metadata': {u'additionalProperties': {u'type': u'string'},
u'metadata': {u'additionalProperties': {u'maxLength': 255,
u'minLength': 1,
u'type': u'string'},
u'default': {},
u'description': u'Key-value dict with useful information '
u'about an artifact.',
@ -102,12 +104,15 @@ fixture_base_props = {
u'description': u'List of tags added to Artifact.',
u'filter_ops': [],
u'glareType': u'StringList',
u'items': {u'type': u'string'},
u'items': {u'maxLength': 255,
u'minLength': 1,
u'pattern': u'^[^,/]+$',
u'type': u'string'},
u'maxItems': 255,
u'mutable': True,
u'required_on_activate': False,
u'type': [u'array', u'null'],
u'unique': True},
u'uniqueItems': True},
u'updated_at': {
u'description': u'Datetime when artifact has been updated last time.',
u'filter_ops': [u'lt', u'gt'],
@ -188,13 +193,13 @@ fixtures = {
u'filter_ops': [u'eq'],
u'glareType': u'Boolean',
u'required_on_activate': False,
u'type': [u'string',
u'type': [u'boolean',
u'null']},
u'bool2': {u'default': False,
u'filter_ops': [u'eq'],
u'glareType': u'Boolean',
u'required_on_activate': False,
u'type': [u'string',
u'type': [u'boolean',
u'null']},
u'link1': {u'filter_ops': [u'eq',
u'neq'],
@ -245,7 +250,7 @@ fixtures = {
u'null']},
u'dict_of_int': {
u'additionalProperties': {
u'type': u'string'},
u'type': u'integer'},
u'default': {},
u'filter_ops': [u'eq', u'in'],
u'glareType': u'IntegerDict',
@ -270,8 +275,8 @@ fixtures = {
u'required_on_activate': False,
u'type': [u'array', u'null']},
u'dict_of_str': {
u'additionalProperties': {
u'type': u'string'},
u'additionalProperties': {u'maxLength': 255,
u'type': u'string'},
u'default': {},
u'filter_ops': [u'eq', u'in'],
u'glareType': u'StringDict',
@ -284,15 +289,18 @@ fixtures = {
u'filter_ops': [],
u'glareType': u'StringDict',
u'maxProperties': 3,
u'properties': {
u'abc': {u'type': [u'string',
u'null']},
u'def': {u'type': [u'string',
u'null']},
u'ghi': {u'type': [u'string',
u'null']},
u'jkl': {u'type': [u'string',
u'null']}},
u'properties': {u'abc': {u'maxLength': 255,
u'type': [u'string',
u'null']},
u'def': {u'maxLength': 255,
u'type': [u'string',
u'null']},
u'ghi': {u'maxLength': 255,
u'type': [u'string',
u'null']},
u'jkl': {u'maxLength': 255,
u'type': [u'string',
u'null']}},
u'required_on_activate': False,
u'type': [u'object',
u'null']},
@ -361,7 +369,7 @@ fixtures = {
u'filter_ops': [u'eq', u'in'],
u'glareType': u'IntegerList',
u'items': {
u'type': u'string'},
u'type': u'integer'},
u'maxItems': 255,
u'required_on_activate': False,
u'type': [u'array',
@ -369,8 +377,8 @@ fixtures = {
u'list_of_str': {u'default': [],
u'filter_ops': [u'eq', u'in'],
u'glareType': u'StringList',
u'items': {
u'type': u'string'},
u'items': {u'maxLength': 255,
u'type': u'string'},
u'maxItems': 255,
u'required_on_activate': False,
u'type': [u'array',
@ -378,13 +386,13 @@ fixtures = {
u'list_validators': {u'default': [],
u'filter_ops': [],
u'glareType': u'StringList',
u'items': {
u'type': u'string'},
u'items': {u'maxLength': 255,
u'type': u'string'},
u'maxItems': 3,
u'required_on_activate': False,
u'type': [u'array',
u'null'],
u'unique': True},
u'uniqueItems': True},
u'small_blob': {u'additionalProperties': False,
u'filter_ops': [],
u'glareType': u'Blob',
@ -520,7 +528,8 @@ fixtures = {
u'neq',
u'in'],
u'glareType': u'StringList',
u'items': {u'type': u'string'},
u'items': {u'maxLength': 255,
u'type': u'string'},
u'maxItems': 255,
u'mutable': True,
u'type': [u'array',
@ -533,11 +542,12 @@ fixtures = {
u'neq',
u'in'],
u'glareType': u'StringList',
u'items': {u'type': u'string'},
u'items': {u'maxLength': 255,
u'type': u'string'},
u'maxItems': 255,
u'type': [u'array',
u'null'],
u'unique': True},
u'uniqueItems': True},
u'dependencies': {
u'default': [],
u'description': u'List of package dependencies for '
@ -561,7 +571,8 @@ fixtures = {
u'type': [u'string',
u'null']},
u'inherits': {
u'additionalProperties': {u'type': u'string'},
u'additionalProperties': {u'maxLength': 255,
u'type': u'string'},
u'default': {},
u'filter_ops': [u'eq',
u'neq',
@ -575,7 +586,8 @@ fixtures = {
u'neq',
u'in'],
u'glareType': u'StringList',
u'items': {u'type': u'string'},
u'items': {u'maxLength': 255,
u'type': u'string'},
u'maxItems': 255,
u'mutable': True,
u'type': [u'array',
@ -792,7 +804,8 @@ fixtures = {
u'name': u'heat_templates',
u'properties': generate_type_props({
u'default_envs': {
u'additionalProperties': {u'type': u'string'},
u'additionalProperties': {u'maxLength': 255,
u'type': u'string'},
u'default': {},
u'description': u'Default environments that can '
u'be applied to the template if no '

View File

@ -0,0 +1,368 @@
# Copyright 2017 - Nokia Networks
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo_versionedobjects import fields
from glare.objects.meta import fields as glare_fields
from glare.objects.meta import validators
from glare.tests.unit import base
class TestValidators(base.BaseTestArtifactAPI):
"""Class for testing field validators."""
def test_uuid(self):
# test if applied string is uuid4
validator = validators.UUID()
# valid string - no exception
validator('167f8083-6bef-4f37-bf04-250343a2d53c')
# invalid string - ValueError
self.assertRaises(ValueError, validator, 'INVALID')
# only strings can be applied as values
self.assertEqual((fields.StringField,),
validators.UUID.get_allowed_types())
self.assertEqual(
{'pattern': ('^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F])'
'{4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$')},
validator.to_jsonschema())
def test_allowed_values(self):
# test that field may have preoccupied values
validator_s = validators.AllowedValues(['aaa', 'bbb'])
validator_i = validators.AllowedValues([1, 2, 3])
validator_f = validators.AllowedValues([1.0, 2.0, 3.0])
# allowed value - no exception
validator_s('aaa')
validator_s('bbb')
validator_i(1)
validator_i(3)
validator_f(1.0)
validator_f(3.0)
# not allowed value - value error
self.assertRaises(ValueError, validator_s, 'a')
self.assertRaises(ValueError, validator_i, 4)
self.assertRaises(ValueError, validator_f, 4.0)
# only strings, integers and floats can be applied as values
self.assertEqual(
(fields.StringField, fields.IntegerField, fields.FloatField),
validators.AllowedValues.get_allowed_types())
self.assertEqual({'enum': ['aaa', 'bbb']}, validator_s.to_jsonschema())
self.assertEqual({'enum': [1, 2, 3]}, validator_i.to_jsonschema())
self.assertEqual({'enum': [1.0, 2.0, 3.0]},
validator_f.to_jsonschema())
def test_max_str_len(self):
# test max allowed string length
validator = validators.MaxStrLen(10)
# allowed length - no exception
validator('a' * 10)
validator('')
# too long string - value error
self.assertRaises(ValueError, validator, 'a' * 11)
# only strings can be applied as values
self.assertEqual((fields.StringField,),
validators.MaxStrLen.get_allowed_types())
self.assertEqual({'maxLength': 10}, validator.to_jsonschema())
def test_min_str_len(self):
# test min allowed string length
validator = validators.MinStrLen(10)
# allowed length - no exception
validator('a' * 10)
# too short string - value error
self.assertRaises(ValueError, validator, 'a' * 9)
self.assertRaises(ValueError, validator, '')
# only strings can be applied as values
self.assertEqual((fields.StringField,),
validators.MinStrLen.get_allowed_types())
self.assertEqual({'minLength': 10}, validator.to_jsonschema())
def test_forbidden_chars(self):
# test that string has no forbidden chars
validator = validators.ForbiddenChars(['a', '?'])
# allowed length - no exception
validator('b' * 10)
# string contains forbidden chars - value error
self.assertRaises(ValueError, validator, 'abc')
self.assertRaises(ValueError, validator, '?')
# only strings can be applied as values
self.assertEqual((fields.StringField,),
validators.ForbiddenChars.get_allowed_types())
self.assertEqual({'pattern': '^[^a?]+$'}, validator.to_jsonschema())
def test_max_dict_size(self):
# test max dict size
validator = validators.MaxDictSize(3)
# allowed size - no exception
validator({'a': 1, 'b': 2, 'c': 3})
validator({})
# too big dictionary - value error
self.assertRaises(ValueError, validator,
{'a': 1, 'b': 2, 'c': 3, 'd': 4})
# only dicts can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.MaxDictSize.get_allowed_types())
self.assertEqual({'maxProperties': 3}, validator.to_jsonschema())
def test_min_dict_size(self):
# test min dict size
validator = validators.MinDictSize(3)
# allowed size - no exception
validator({'a': 1, 'b': 2, 'c': 3})
# too small dictionary - value error
self.assertRaises(ValueError, validator,
{'a': 1, 'b': 2})
self.assertRaises(ValueError, validator, {})
# only dicts can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.MinDictSize.get_allowed_types())
self.assertEqual({'minProperties': 3}, validator.to_jsonschema())
def test_max_list_size(self):
# test max list size
validator = validators.MaxListSize(3)
# allowed size - no exception
validator(['a', 'b', 'c'])
validator([])
# too big list - value error
self.assertRaises(ValueError, validator,
['a', 'b', 'c', 'd'])
# only lists can be applied as values
self.assertEqual((glare_fields.List,),
validators.MaxListSize.get_allowed_types())
self.assertEqual({'maxItems': 3}, validator.to_jsonschema())
def test_min_list_size(self):
# test max list size
validator = validators.MinListSize(3)
# allowed size - no exception
validator(['a', 'b', 'c'])
# too small list - value error
self.assertRaises(ValueError, validator, ['a', 'b'])
self.assertRaises(ValueError, validator, [])
# only lists can be applied as values
self.assertEqual((glare_fields.List,),
validators.MinListSize.get_allowed_types())
self.assertEqual({'minItems': 3}, validator.to_jsonschema())
def test_max_number_size(self):
# test max number size
validator = validators.MaxNumberSize(10)
# allowed size - no exception
validator(10)
validator(0)
validator(10.0)
validator(0.0)
# too big number - value error
self.assertRaises(ValueError, validator, 11)
self.assertRaises(ValueError, validator, 10.1)
# only integers and floats can be applied as values
self.assertEqual((fields.IntegerField, fields.FloatField),
validators.MaxNumberSize.get_allowed_types())
self.assertEqual({'maximum': 10}, validator.to_jsonschema())
def test_min_number_size(self):
# test min number size
validator = validators.MinNumberSize(10)
# allowed size - no exception
validator(10)
validator(10.0)
# too small number - value error
self.assertRaises(ValueError, validator, 9)
self.assertRaises(ValueError, validator, 9.9)
self.assertRaises(ValueError, validator, 0)
self.assertRaises(ValueError, validator, 0)
# only integers and floats can be applied as values
self.assertEqual((fields.IntegerField, fields.FloatField),
validators.MinNumberSize.get_allowed_types())
self.assertEqual({'minimum': 10}, validator.to_jsonschema())
def test_unique(self):
# test uniqueness of list elements
# validator raises exception in case of duplicates in the list
validator = validators.Unique()
# non strict validator removes duplicates without raising of ValueError
validator_nonstrict = validators.Unique(convert_to_set=True)
# all elements unique - no exception
validator(['a', 'b', 'c'])
validator([])
# duplicates in the list - value error
self.assertRaises(ValueError, validator, ['a', 'a', 'b'])
# non-strict validator converts list to set of elements
l = ['a', 'a', 'b']
validator_nonstrict(l)
self.assertEqual({'a', 'b'}, set(l))
# only lists can be applied as values
self.assertEqual((glare_fields.List,),
validators.Unique.get_allowed_types())
self.assertEqual({'uniqueItems': True}, validator.to_jsonschema())
def test_allowed_dict_keys(self):
# test that dictionary contains only allowed keys
validator = validators.AllowedDictKeys(['aaa', 'bbb', 'ccc'])
# only allowed keys - no exception
validator({'aaa': 5, 'bbb': 6})
validator({})
# if dictionary has other keys - value error
self.assertRaises(ValueError, validator, {'aaa': 5, 'a': 7, 'bbb': 6})
# only dicts can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.AllowedDictKeys.get_allowed_types())
self.assertEqual({'properties': {'aaa': {}, 'bbb': {}, 'ccc': {}}},
validator.to_jsonschema())
def test_required_dict_keys(self):
# test that dictionary has required keys
validator = validators.RequiredDictKeys(['aaa', 'bbb'])
# if dict has required keys - no exception
validator({'aaa': 5, 'bbb': 6})
validator({'aaa': 5, 'bbb': 6, 'ccc': 7})
# in other case - value error
self.assertRaises(ValueError, validator, {'aaa': 5, 'a': 7})
self.assertRaises(ValueError, validator, {})
# only dicts can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.RequiredDictKeys.get_allowed_types())
self.assertEqual({'required': ['aaa', 'bbb']},
validator.to_jsonschema())
def test_max_dict_key_len(self):
# test max limit for dict key length
validator = validators.MaxDictKeyLen(5)
# if key length less than the limit - no exception
validator({'aaaaa': 5, 'bbbbb': 4})
# in other case - value error
self.assertRaises(ValueError, validator, {'aaaaaa': 5, 'a': 7})
# only dicts can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.MaxDictKeyLen.get_allowed_types())
def test_mix_dict_key_len(self):
# test min limit for dict key length
validator = validators.MinDictKeyLen(5)
# if key length bigger than the limit - no exception
validator({'aaaaa': 5, 'bbbbb': 4})
# in other case - value error
self.assertRaises(ValueError, validator, {'aaaaa': 5, 'a': 7})
# only dicts can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.MinDictKeyLen.get_allowed_types())
def test_allowed_list_values(self):
# test that list contains only allowed values
# AllowedValues validator will be applied to each element of the list
validator = validators.ListElementValidator(
[validators.AllowedValues(['aaa', 'bbb', 'ccc'])])
# only allowed values - no exception
validator(['aaa', 'bbb'])
validator([])
# if list has other values - value error
self.assertRaises(ValueError, validator, ['aaa', 'a', 'bbb'])
self.assertRaises(ValueError, validator, ['ccc', {'aaa': 'bbb'}])
# only lists can be applied as values
self.assertEqual((glare_fields.List,),
validators.ListElementValidator.get_allowed_types())
self.assertEqual({'itemValidators': [{'enum': ['aaa', 'bbb', 'ccc']}]},
validator.to_jsonschema())
def test_allowed_dict_values(self):
# test that dict contains only allowed values
# AllowedValues validator will be applied to each element of the dict
validator = validators.DictElementValidator(
[validators.AllowedValues(['aaa', 'bbb', 'ccc'])])
# only allowed values - no exception
validator({'a': 'aaa', 'b': 'bbb'})
validator({})
# if dict has other values - value error
self.assertRaises(ValueError, validator,
{'a': 'aaa', 'b': 'bbb', 'c': 'c'})
# only dict can be applied as values
self.assertEqual((glare_fields.Dict,),
validators.DictElementValidator.get_allowed_types())
self.assertEqual(
{'propertyValidators': [{'enum': ['aaa', 'bbb', 'ccc']}]},
validator.to_jsonschema())