Enhance the validation of tosca
Enhance the validation of tosca with a parent constraint class and different constraint sub-classes. Implements: blueprint tosca-validation Change-Id: I5c31c13c3d9cc36c8af6355472d243652bdc1eff
This commit is contained in:
@@ -13,68 +13,80 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import datetime
|
||||
import numbers
|
||||
import re
|
||||
|
||||
from translator.toscalib.utils.gettextutils import _
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return str(self.message)
|
||||
|
||||
|
||||
class Constraint(object):
|
||||
'''Parent class for constraints for a Property or Input.'''
|
||||
|
||||
CONSTRAINTS = (EQUAL, GREATER_THAN,
|
||||
GREATER_OR_EQUAL, LESS_THAN, LESS_OR_EQUAL, IN_RANGE,
|
||||
VALID_VALUES, LENGTH, MIN_LENGHT, MAX_LENGTH, PATTERN) = \
|
||||
VALID_VALUES, LENGTH, MIN_LENGTH, MAX_LENGTH, PATTERN) = \
|
||||
('equal', 'greater_than', 'greater_or_equal', 'less_than',
|
||||
'less_or_equal', 'in_range', 'valid_values', 'length',
|
||||
'min_length', 'max_length', 'pattern')
|
||||
|
||||
def __init__(self, propertyname, value, constraint):
|
||||
self.propertyname = propertyname
|
||||
self.value = value
|
||||
self.constraint = constraint
|
||||
PROPERTY_TYPES = (
|
||||
INTEGER, STRING, BOOLEAN,
|
||||
FLOAT, TIMESTAMP
|
||||
) = (
|
||||
'integer', 'string', 'boolean',
|
||||
'float', 'timestamp'
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
for key, value in self.constraint.items():
|
||||
if key == self.GREATER_OR_EQUAL:
|
||||
self.validate_greater_than(value)
|
||||
def __new__(cls, property_name, property_type, constraint):
|
||||
if cls is not Constraint:
|
||||
return super(Constraint, cls).__new__(cls)
|
||||
|
||||
def validate_equal(self):
|
||||
pass
|
||||
if(not isinstance(constraint, collections.Mapping) or
|
||||
len(constraint) != 1):
|
||||
raise ValidationError(_('Invalid constraint schema.'))
|
||||
|
||||
def validate_greater_than(self, value):
|
||||
if self.value < value:
|
||||
raise ValueError(_("%(prop)s value requires to be "
|
||||
"greater than %(val)s")
|
||||
% {'prop': self.propertyname, 'val': value})
|
||||
for type in constraint.keys():
|
||||
ConstraintClass = get_constraint_class(type)
|
||||
if not ConstraintClass:
|
||||
raise ValidationError(_('Invalid constraint type "%s".') %
|
||||
type)
|
||||
|
||||
def validate_greater_or_equal(self):
|
||||
pass
|
||||
return ConstraintClass(property_name, property_type, constraint)
|
||||
|
||||
def validate_less_than(self):
|
||||
pass
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
self.property_name = property_name
|
||||
self.property_type = property_type
|
||||
self.constraint_value = constraint[self.constraint_key]
|
||||
# check if constraint is valid for property type
|
||||
if property_type not in self.valid_prop_types:
|
||||
raise ValidationError(_('Constraint type "%(ctype)s" is not valid '
|
||||
'for data type "%(dtype)s".') %
|
||||
{'ctype': self.constraint_key,
|
||||
'dtype': property_type})
|
||||
|
||||
def validate_less_or_equal(self):
|
||||
pass
|
||||
def _err_msg(self, value):
|
||||
return _('Property %s could not be validated.') % self.property_name
|
||||
|
||||
def validate_in_range(self):
|
||||
pass
|
||||
|
||||
def validate_valid_values(self):
|
||||
pass
|
||||
|
||||
def validate_length(self):
|
||||
pass
|
||||
|
||||
def validate_min_length(self):
|
||||
pass
|
||||
|
||||
def validate_max_length(self):
|
||||
pass
|
||||
|
||||
def validate_pattern(self):
|
||||
pass
|
||||
def validate(self, value):
|
||||
if not self._is_valid(value):
|
||||
err_msg = self._err_msg(value)
|
||||
raise ValidationError(err_msg)
|
||||
|
||||
@staticmethod
|
||||
def validate_integer(value):
|
||||
if not isinstance(value, (int, long)):
|
||||
raise TypeError(_('Value is not an integer for %s') % value)
|
||||
if not isinstance(value, int):
|
||||
raise ValueError(_('Value is not an integer for %s.') % value)
|
||||
return Constraint.validate_number(value)
|
||||
|
||||
@staticmethod
|
||||
@@ -83,13 +95,15 @@ class Constraint(object):
|
||||
|
||||
@staticmethod
|
||||
def validate_string(value):
|
||||
if not isinstance(value, basestring):
|
||||
raise ValueError(_('Value must be a string %s') % value)
|
||||
if not isinstance(value, str):
|
||||
raise ValueError(_('Value must be a string %s.') % value)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def validate_list(self, value):
|
||||
pass
|
||||
if not isinstance(value, collections.Sequence):
|
||||
raise ValueError(_('Value must be a list %s.') % value)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def str_to_num(value):
|
||||
@@ -100,3 +114,385 @@ class Constraint(object):
|
||||
return int(value)
|
||||
except ValueError:
|
||||
return float(value)
|
||||
|
||||
|
||||
class Equal(Constraint):
|
||||
"""Constraint class for "equal"
|
||||
|
||||
Constrains a property or parameter to a value equal to ('=')
|
||||
the value declared.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.EQUAL
|
||||
|
||||
valid_prop_types = Constraint.PROPERTY_TYPES
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value == self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: %(pvalue)s is not equal to "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class GreaterThan(Constraint):
|
||||
"""Constraint class for "greater_than"
|
||||
|
||||
Constrains a property or parameter to a value greater than ('>')
|
||||
the value declared.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.GREATER_THAN
|
||||
|
||||
valid_types = (int, float, datetime.date,
|
||||
datetime.time, datetime.datetime)
|
||||
|
||||
valid_prop_types = (Constraint.INTEGER, Constraint.FLOAT,
|
||||
Constraint.TIMESTAMP)
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(GreaterThan, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(constraint[self.GREATER_THAN], self.valid_types):
|
||||
raise ValidationError(_('greater_than must be comparable.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value > self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: %(pvalue)s must be greater than "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class GreaterOrEqual(Constraint):
|
||||
"""Constraint class for "greater_or_equal"
|
||||
|
||||
Constrains a property or parameter to a value greater than or equal
|
||||
to ('>=') the value declared.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.GREATER_OR_EQUAL
|
||||
|
||||
valid_types = (int, float, datetime.date,
|
||||
datetime.time, datetime.datetime)
|
||||
|
||||
valid_prop_types = (Constraint.INTEGER, Constraint.FLOAT,
|
||||
Constraint.TIMESTAMP)
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(GreaterOrEqual, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('greater_or_equal must be comparable.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value >= self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: %(pvalue)s must be greater or equal '
|
||||
'to "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class LessThan(Constraint):
|
||||
"""Constraint class for "less_than"
|
||||
|
||||
Constrains a property or parameter to a value less than ('<')
|
||||
the value declared.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.LESS_THAN
|
||||
|
||||
valid_types = (int, float, datetime.date,
|
||||
datetime.time, datetime.datetime)
|
||||
|
||||
valid_prop_types = (Constraint.INTEGER, Constraint.FLOAT,
|
||||
Constraint.TIMESTAMP)
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(LessThan, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('less_than must be comparable.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value < self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: %(pvalue)s must be less than "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class LessOrEqual(Constraint):
|
||||
"""Constraint class for "less_or_equal"
|
||||
|
||||
Constrains a property or parameter to a value less than or equal
|
||||
to ('<=') the value declared.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.LESS_OR_EQUAL
|
||||
|
||||
valid_types = (int, float, datetime.date,
|
||||
datetime.time, datetime.datetime)
|
||||
|
||||
valid_prop_types = (Constraint.INTEGER, Constraint.FLOAT,
|
||||
Constraint.TIMESTAMP)
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(LessOrEqual, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('less_or_equal must be comparable.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value <= self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: %(pvalue)s must be less or '
|
||||
'equal to "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class InRange(Constraint):
|
||||
"""Constraint class for "in_range"
|
||||
|
||||
Constrains a property or parameter to a value in range of (inclusive)
|
||||
the two values declared.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.IN_RANGE
|
||||
|
||||
valid_types = (int, float, datetime.date,
|
||||
datetime.time, datetime.datetime)
|
||||
|
||||
valid_prop_types = (Constraint.INTEGER, Constraint.FLOAT,
|
||||
Constraint.TIMESTAMP)
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(InRange, self).__init__(property_name, property_type, constraint)
|
||||
if(not isinstance(self.constraint_value, collections.Sequence) or
|
||||
(len(constraint[self.IN_RANGE]) != 2)):
|
||||
raise ValidationError(_('in_range must be a list.'))
|
||||
|
||||
for value in self.constraint_value:
|
||||
if not isinstance(value, self.valid_types):
|
||||
raise ValidationError(_('in_range value must be comparable.'))
|
||||
|
||||
self.min = self.constraint_value[0]
|
||||
self.max = self.constraint_value[1]
|
||||
|
||||
def _is_valid(self, value):
|
||||
if value < self.min:
|
||||
return False
|
||||
if value > self.max:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: %(pvalue)s is out of range '
|
||||
'(min:%(vmin)s, max:%(vmax)s).') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
vmin=self.min,
|
||||
vmax=self.max))
|
||||
|
||||
|
||||
class ValidValues(Constraint):
|
||||
"""Constraint class for "valid_values"
|
||||
|
||||
Constrains a property or parameter to a value that is in the list of
|
||||
declared values.
|
||||
"""
|
||||
constraint_key = Constraint.VALID_VALUES
|
||||
|
||||
valid_prop_types = Constraint.PROPERTY_TYPES
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(ValidValues, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(self.constraint_value, collections.Sequence):
|
||||
raise ValidationError(_('valid_values must be a list.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if isinstance(value, collections.Sequence):
|
||||
return all(v in self.constraint_value for v in value)
|
||||
return value in self.constraint_value
|
||||
|
||||
def _err_msg(self, value):
|
||||
allowed = '[%s]' % ', '.join(str(a) for a in self.constraint_value)
|
||||
return (_('%(pname)s: %(pvalue)s is not an valid '
|
||||
'value "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=allowed))
|
||||
|
||||
|
||||
class Length(Constraint):
|
||||
"""Constraint class for "length"
|
||||
|
||||
Constrains the property or parameter to a value of a given length.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.LENGTH
|
||||
|
||||
valid_types = (int, )
|
||||
|
||||
valid_prop_types = (Constraint.STRING, )
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(Length, self).__init__(property_name, property_type, constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('length must be integer.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if isinstance(value, str) and len(value) == self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('length of %(pname)s: %(pvalue)s must be equal '
|
||||
'to "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class MinLength(Constraint):
|
||||
"""Constraint class for "min_length"
|
||||
|
||||
Constrains the property or parameter to a value to a minimum length.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.MIN_LENGTH
|
||||
|
||||
valid_types = (int, )
|
||||
|
||||
valid_prop_types = (Constraint.STRING, )
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(MinLength, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('min_length must be integer.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if isinstance(value, str) and len(value) >= self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('length of %(pname)s: %(pvalue)s must be '
|
||||
'at least "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class MaxLength(Constraint):
|
||||
"""Constraint class for "max_length"
|
||||
|
||||
Constrains the property or parameter to a value to a maximum length.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.MAX_LENGTH
|
||||
|
||||
valid_types = (int, )
|
||||
|
||||
valid_prop_types = (Constraint.STRING, )
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(MaxLength, self).__init__(property_name, property_type,
|
||||
constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('max_length must be integer.'))
|
||||
|
||||
def _is_valid(self, value):
|
||||
if isinstance(value, str) and len(value) <= self.constraint_value:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('length of %(pname)s: %(pvalue)s must be no greater '
|
||||
'than "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
class Pattern(Constraint):
|
||||
"""Constraint class for "pattern"
|
||||
|
||||
Constrains the property or parameter to a value that is allowed by
|
||||
the provided regular expression.
|
||||
"""
|
||||
|
||||
constraint_key = Constraint.PATTERN
|
||||
|
||||
valid_types = (str, )
|
||||
|
||||
valid_prop_types = (Constraint.STRING, )
|
||||
|
||||
def __init__(self, property_name, property_type, constraint):
|
||||
super(Pattern, self).__init__(property_name, property_type, constraint)
|
||||
if not isinstance(self.constraint_value, self.valid_types):
|
||||
raise ValidationError(_('pattern must be string.'))
|
||||
self.match = re.compile(self.constraint_value).match
|
||||
|
||||
def _is_valid(self, value):
|
||||
match = self.match(value)
|
||||
return match is not None and match.end() == len(value)
|
||||
|
||||
def _err_msg(self, value):
|
||||
return (_('%(pname)s: "%(pvalue)s" does not match '
|
||||
'pattern "%(cvalue)s".') %
|
||||
dict(pname=self.property_name,
|
||||
pvalue=value,
|
||||
cvalue=self.constraint_value))
|
||||
|
||||
|
||||
constraint_mapping = {
|
||||
Constraint.EQUAL: Equal,
|
||||
Constraint.GREATER_THAN: GreaterThan,
|
||||
Constraint.GREATER_OR_EQUAL: GreaterOrEqual,
|
||||
Constraint.LESS_THAN: LessThan,
|
||||
Constraint.LESS_OR_EQUAL: LessOrEqual,
|
||||
Constraint.IN_RANGE: InRange,
|
||||
Constraint.VALID_VALUES: ValidValues,
|
||||
Constraint.LENGTH: Length,
|
||||
Constraint.MIN_LENGTH: MinLength,
|
||||
Constraint.MAX_LENGTH: MaxLength,
|
||||
Constraint.PATTERN: Pattern
|
||||
}
|
||||
|
||||
|
||||
def get_constraint_class(type):
|
||||
return constraint_mapping.get(type)
|
||||
|
||||
@@ -74,14 +74,12 @@ class Input(object):
|
||||
field=name)
|
||||
|
||||
def validate_type(self, input_type):
|
||||
if input_type not in Property.PROPERTIY_TYPES:
|
||||
if input_type not in Constraint.PROPERTY_TYPES:
|
||||
raise ValueError(_('Invalid type %s') % type)
|
||||
|
||||
def validate_constraints(self, constraints):
|
||||
for constraint in constraints:
|
||||
for key in constraint.keys():
|
||||
if key not in Constraint.CONSTRAINTS:
|
||||
raise ValueError(_('Invalid constraint %s') % constraint)
|
||||
Constraint(self.name, self.type, constraint)
|
||||
|
||||
|
||||
class Output(object):
|
||||
|
||||
@@ -24,13 +24,6 @@ class Property(object):
|
||||
) = (
|
||||
'type', 'required', 'description', 'default', 'constraints'
|
||||
)
|
||||
PROPERTIY_TYPES = (
|
||||
INTEGER, STRING, BOOLEAN,
|
||||
FLOAT, TIMESTAMP
|
||||
) = (
|
||||
'integer', 'string', 'boolean',
|
||||
'float', 'timestamp'
|
||||
)
|
||||
|
||||
def __init__(self, property_name, value, schema=None):
|
||||
self.name = property_name
|
||||
@@ -59,26 +52,28 @@ class Property(object):
|
||||
def validate(self):
|
||||
'''Validate if not a reference property.'''
|
||||
if not isinstance(self.value, dict):
|
||||
self._validate_constraints()
|
||||
self._validate_datatype()
|
||||
self._validate_constraints()
|
||||
|
||||
def _validate_datatype(self):
|
||||
try:
|
||||
if self.TYPE in self.schema:
|
||||
dtype = self.schema[self.TYPE]
|
||||
if dtype == self.STRING:
|
||||
if dtype == Constraint.STRING:
|
||||
return Constraint.validate_string(self.value)
|
||||
elif dtype == self.INTEGER:
|
||||
elif dtype == Constraint.INTEGER:
|
||||
return Constraint.validate_integer(self.value)
|
||||
elif dtype == self.NUMBER:
|
||||
elif dtype == Constraint.NUMBER:
|
||||
return Constraint.validate_number(self.value)
|
||||
elif dtype == self.LIST:
|
||||
elif dtype == Constraint.LIST:
|
||||
return Constraint.validate_list(self.value)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _validate_constraints(self):
|
||||
constraints = self.constraints
|
||||
dtype = self.schema[self.TYPE]
|
||||
if constraints:
|
||||
for constraint in constraints:
|
||||
Constraint(self.name, self.value, constraint).validate()
|
||||
Constraint(self.name, dtype,
|
||||
constraint).validate(self.value)
|
||||
|
||||
248
translator/toscalib/tests/test_constraints.py
Normal file
248
translator/toscalib/tests/test_constraints.py
Normal file
@@ -0,0 +1,248 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import yaml
|
||||
|
||||
from translator.toscalib.elements.constraints import Constraint
|
||||
from translator.toscalib.elements.constraints import ValidationError
|
||||
from translator.toscalib.tests.base import TestCase
|
||||
|
||||
|
||||
class ConstraintTest(TestCase):
|
||||
def test_invalid_constraint_type(self):
|
||||
schema = {'invalid_type': 2}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.INTEGER,
|
||||
schema)
|
||||
self.assertEqual('Invalid constraint type "invalid_type".',
|
||||
str(error))
|
||||
|
||||
def test_invalid_prop_type(self):
|
||||
schema = {'length': 5}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.INTEGER,
|
||||
schema)
|
||||
self.assertEqual('Constraint type "length" is not valid for '
|
||||
'data type "integer".', str(error))
|
||||
|
||||
def test_invalid_validvalues(self):
|
||||
schema = {'valid_values': 2}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.INTEGER,
|
||||
schema)
|
||||
self.assertEqual('valid_values must be a list.', str(error))
|
||||
|
||||
def test_validvalues_validate(self):
|
||||
schema = {'valid_values': [2, 4, 6, 8]}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
self.assertIsNone(constraint.validate(4))
|
||||
|
||||
def test_validvalues_validate_fail(self):
|
||||
schema = {'valid_values': [2, 4, 6, 8]}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 5)
|
||||
self.assertEqual('prop: 5 is not an valid value "[2, 4, 6, 8]".',
|
||||
str(error))
|
||||
|
||||
def test_invalid_in_range(self):
|
||||
snippet = 'in_range: {2, 6}'
|
||||
schema = yaml.load(snippet)
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.INTEGER,
|
||||
schema)
|
||||
self.assertEqual('in_range must be a list.', str(error))
|
||||
|
||||
def test_in_range_min_max(self):
|
||||
schema = {'in_range': [2, 6]}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
self.assertEqual(2, constraint.min)
|
||||
self.assertEqual(6, constraint.max)
|
||||
|
||||
def test_in_range_validate(self):
|
||||
schema = {'in_range': [2, 6]}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
self.assertIsNone(constraint.validate(2))
|
||||
self.assertIsNone(constraint.validate(4))
|
||||
self.assertIsNone(constraint.validate(6))
|
||||
|
||||
def test_in_range_validate_fail(self):
|
||||
schema = {'in_range': [2, 6]}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 8)
|
||||
self.assertEqual('prop: 8 is out of range (min:2, max:6).',
|
||||
str(error))
|
||||
|
||||
def test_equal_validate(self):
|
||||
schema = {'equal': 4}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
self.assertIsNone(constraint.validate(4))
|
||||
|
||||
def test_equal_validate_fail(self):
|
||||
schema = {'equal': 4}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 8)
|
||||
self.assertEqual('prop: 8 is not equal to "4".', str(error))
|
||||
|
||||
def test_greater_than_validate(self):
|
||||
schema = {'greater_than': 4}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
self.assertIsNone(constraint.validate(6))
|
||||
|
||||
def test_greater_than_validate_fail(self):
|
||||
schema = {'greater_than': 4}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 3)
|
||||
self.assertEqual('prop: 3 must be greater than "4".', str(error))
|
||||
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 4)
|
||||
self.assertEqual('prop: 4 must be greater than "4".', str(error))
|
||||
|
||||
def test_greater_or_equal_validate(self):
|
||||
schema = {'greater_or_equal': 3.9}
|
||||
constraint = Constraint('prop', Constraint.FLOAT, schema)
|
||||
self.assertIsNone(constraint.validate(3.9))
|
||||
self.assertIsNone(constraint.validate(4.0))
|
||||
|
||||
def test_greater_or_equal_validate_fail(self):
|
||||
schema = {'greater_or_equal': 3.9}
|
||||
constraint = Constraint('prop', Constraint.FLOAT, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 3.0)
|
||||
self.assertEqual('prop: 3.0 must be greater or equal to "3.9".',
|
||||
str(error))
|
||||
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 3.8)
|
||||
self.assertEqual('prop: 3.8 must be greater or equal to "3.9".',
|
||||
str(error))
|
||||
|
||||
def test_less_than_validate(self):
|
||||
schema = {'less_than': datetime.date(2014, 0o7, 25)}
|
||||
constraint = Constraint('prop', Constraint.TIMESTAMP, schema)
|
||||
self.assertIsNone(constraint.validate(datetime.date(2014, 0o7, 20)))
|
||||
self.assertIsNone(constraint.validate(datetime.date(2014, 0o7, 24)))
|
||||
|
||||
def test_less_than_validate_fail(self):
|
||||
schema = {'less_than': datetime.date(2014, 0o7, 25)}
|
||||
constraint = Constraint('prop', Constraint.TIMESTAMP, schema)
|
||||
error = self.assertRaises(ValidationError,
|
||||
constraint.validate,
|
||||
datetime.date(2014, 0o7, 25))
|
||||
self.assertEqual('prop: 2014-07-25 must be '
|
||||
'less than "2014-07-25".',
|
||||
str(error))
|
||||
|
||||
error = self.assertRaises(ValidationError,
|
||||
constraint.validate,
|
||||
datetime.date(2014, 0o7, 27))
|
||||
self.assertEqual('prop: 2014-07-27 must be '
|
||||
'less than "2014-07-25".',
|
||||
str(error))
|
||||
|
||||
def test_less_or_equal_validate(self):
|
||||
schema = {'less_or_equal': 4}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
self.assertIsNone(constraint.validate(4))
|
||||
self.assertIsNone(constraint.validate(3))
|
||||
|
||||
def test_less_or_equal_validate_fail(self):
|
||||
schema = {'less_or_equal': 4}
|
||||
constraint = Constraint('prop', Constraint.INTEGER, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 5)
|
||||
self.assertEqual('prop: 5 must be less or equal to "4".', str(error))
|
||||
|
||||
def test_invalid_length(self):
|
||||
schema = {'length': 'four'}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.STRING,
|
||||
schema)
|
||||
self.assertEqual('length must be integer.', str(error))
|
||||
|
||||
schema = {'length': 4.5}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.STRING,
|
||||
schema)
|
||||
self.assertEqual('length must be integer.', str(error))
|
||||
|
||||
def test_length_validate(self):
|
||||
schema = {'length': 4}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
self.assertIsNone(constraint.validate('abcd'))
|
||||
|
||||
def test_length_validate_fail(self):
|
||||
schema = {'length': 4}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 'abc')
|
||||
self.assertEqual('length of prop: abc must be equal to "4".',
|
||||
str(error))
|
||||
|
||||
error = self.assertRaises(ValidationError,
|
||||
constraint.validate,
|
||||
'abcde')
|
||||
self.assertEqual('length of prop: abcde must be equal to "4".',
|
||||
str(error))
|
||||
|
||||
def test_invalid_min_length(self):
|
||||
schema = {'min_length': 'four'}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.STRING,
|
||||
schema)
|
||||
self.assertEqual('min_length must be integer.', str(error))
|
||||
|
||||
def test_min_length_validate(self):
|
||||
schema = {'min_length': 4}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
self.assertIsNone(constraint.validate('abcd'))
|
||||
self.assertIsNone(constraint.validate('abcde'))
|
||||
|
||||
def test_min_length_validate_fail(self):
|
||||
schema = {'min_length': 4}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 'abc')
|
||||
self.assertEqual('length of prop: abc must be at least "4".',
|
||||
str(error))
|
||||
|
||||
def test_invalid_max_length(self):
|
||||
schema = {'max_length': 'four'}
|
||||
error = self.assertRaises(ValidationError, Constraint,
|
||||
'prop', Constraint.STRING,
|
||||
schema)
|
||||
self.assertEqual('max_length must be integer.', str(error))
|
||||
|
||||
def test_max_length_validate(self):
|
||||
schema = {'max_length': 4}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
self.assertIsNone(constraint.validate('abcd'))
|
||||
self.assertIsNone(constraint.validate('abc'))
|
||||
|
||||
def test_max_length_validate_fail(self):
|
||||
schema = {'max_length': 4}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
error = self.assertRaises(ValidationError,
|
||||
constraint.validate,
|
||||
'abcde')
|
||||
self.assertEqual('length of prop: abcde must be no greater than "4".',
|
||||
str(error))
|
||||
|
||||
def test_pattern_validate(self):
|
||||
schema = {'pattern': '[0-9]*'}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
self.assertIsNone(constraint.validate('123'))
|
||||
|
||||
def test_pattern_validate_fail(self):
|
||||
schema = {'pattern': '[0-9]*'}
|
||||
constraint = Constraint('prop', Constraint.STRING, schema)
|
||||
error = self.assertRaises(ValidationError, constraint.validate, 'abc')
|
||||
self.assertEqual('prop: "abc" does not match pattern "[0-9]*".',
|
||||
str(error))
|
||||
Reference in New Issue
Block a user