mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
New Param: added Param.validate() and Param._validate_scalar() methods; added corresponding unit tests
This commit is contained in:
@@ -465,9 +465,17 @@ class OptionError(InvocationError):
|
||||
class RequirementError(InvocationError):
|
||||
"""
|
||||
**3005** Raised when a required parameter is not provided.
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise RequirementError(name='givenname')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
RequirementError: 'givenname' is required
|
||||
"""
|
||||
|
||||
errno = 3005
|
||||
format = _('%(name)r is required')
|
||||
|
||||
|
||||
class ConversionError(InvocationError):
|
||||
@@ -481,9 +489,17 @@ class ConversionError(InvocationError):
|
||||
class ValidationError(InvocationError):
|
||||
"""
|
||||
**3007** Raised when a parameter value fails a validation rule.
|
||||
|
||||
For example:
|
||||
|
||||
>>> raise ValidationError(name='sn', error='can be at most 128 characters')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: invalid 'sn': can be at most 128 characters
|
||||
"""
|
||||
|
||||
errno = 3007
|
||||
format = _('invalid %(name)r: %(error)s')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from types import NoneType
|
||||
from util import make_repr
|
||||
from request import ugettext
|
||||
from plugable import ReadOnly, lock, check_name
|
||||
from errors2 import RequirementError, ValidationError
|
||||
from constants import NULLS, TYPE_ERROR, CALLABLE_ERROR
|
||||
|
||||
|
||||
@@ -207,6 +208,7 @@ class Param(ReadOnly):
|
||||
('primary_key', bool, False),
|
||||
('normalizer', callable, None),
|
||||
('default_from', callable, None),
|
||||
('create_default', callable, None),
|
||||
('flags', frozenset, frozenset()),
|
||||
|
||||
# The 'default' kwarg gets appended in Param.__init__():
|
||||
@@ -432,6 +434,39 @@ class Param(ReadOnly):
|
||||
|
||||
:param value: A proposed value for this parameter.
|
||||
"""
|
||||
if value is None:
|
||||
if self.required:
|
||||
raise RequirementError(name=self.name)
|
||||
return
|
||||
if self.multivalue:
|
||||
if type(value) is not tuple:
|
||||
raise TypeError(
|
||||
TYPE_ERROR % ('value', tuple, value, type(value))
|
||||
)
|
||||
if len(value) < 1:
|
||||
raise ValueError('value: empty tuple must be converted to None')
|
||||
for (i, v) in enumerate(value):
|
||||
self._validate_scalar(v, i)
|
||||
else:
|
||||
self._validate_scalar(value)
|
||||
|
||||
def _validate_scalar(self, value, index=None):
|
||||
if type(value) is not self.type:
|
||||
if index is None:
|
||||
name = 'value'
|
||||
else:
|
||||
name = 'value[%d]' % index
|
||||
raise TypeError(
|
||||
TYPE_ERROR % (name, self.type, value, type(value))
|
||||
)
|
||||
if index is not None and type(index) is not int:
|
||||
raise TypeError(
|
||||
TYPE_ERROR % ('index', int, index, type(index))
|
||||
)
|
||||
for rule in self.all_rules:
|
||||
error = rule(ugettext, value)
|
||||
if error is not None:
|
||||
raise ValidationError(name=self.name, error=error, index=index)
|
||||
|
||||
|
||||
class Bool(Param):
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
Test the `ipalib.parameter` module.
|
||||
"""
|
||||
|
||||
from types import NoneType
|
||||
from tests.util import raises, ClassChecker, read_only
|
||||
from tests.util import dummy_ugettext, assert_equal
|
||||
from tests.data import binary_bytes, utf8_bytes, unicode_str
|
||||
from ipalib import parameter, request
|
||||
from ipalib import parameter, request, errors2
|
||||
from ipalib.constants import TYPE_ERROR, CALLABLE_ERROR, NULLS
|
||||
|
||||
|
||||
@@ -113,6 +114,20 @@ def test_parse_param_spec():
|
||||
assert str(e) == "spec must be at least 2 characters; got 'n'"
|
||||
|
||||
|
||||
class DummyRule(object):
|
||||
def __init__(self, error=None):
|
||||
assert error is None or type(error) is unicode
|
||||
self.error = error
|
||||
self.reset()
|
||||
|
||||
def __call__(self, *args):
|
||||
self.calls.append(args)
|
||||
return self.error
|
||||
|
||||
def reset(self):
|
||||
self.calls = []
|
||||
|
||||
|
||||
class test_Param(ClassChecker):
|
||||
"""
|
||||
Test the `ipalib.parameter.Param` class.
|
||||
@@ -145,6 +160,7 @@ class test_Param(ClassChecker):
|
||||
assert o.normalizer is None
|
||||
assert o.default is None
|
||||
assert o.default_from is None
|
||||
assert o.create_default is None
|
||||
assert o.flags == frozenset()
|
||||
|
||||
# Test that ValueError is raised when a kwarg from a subclass
|
||||
@@ -287,6 +303,130 @@ class test_Param(ClassChecker):
|
||||
e = raises(NotImplementedError, o._convert_scalar, 'some value')
|
||||
assert str(e) == 'Subclass._convert_scalar()'
|
||||
|
||||
def test_validate(self):
|
||||
"""
|
||||
Test the `ipalib.parameter.Param.validate` method.
|
||||
"""
|
||||
|
||||
# Test with required=True/False:
|
||||
o = self.cls('my_param')
|
||||
assert o.required is True
|
||||
e = raises(errors2.RequirementError, o.validate, None)
|
||||
assert e.name == 'my_param'
|
||||
o = self.cls('my_param', required=False)
|
||||
assert o.required is False
|
||||
assert o.validate(None) is None
|
||||
|
||||
# Test with multivalue=True:
|
||||
o = self.cls('my_param', multivalue=True)
|
||||
e = raises(TypeError, o.validate, [])
|
||||
assert str(e) == TYPE_ERROR % ('value', tuple, [], list)
|
||||
e = raises(ValueError, o.validate, tuple())
|
||||
assert str(e) == 'value: empty tuple must be converted to None'
|
||||
|
||||
# Test with wrong (scalar) type:
|
||||
e = raises(TypeError, o.validate, (None, None, 42, None))
|
||||
assert str(e) == TYPE_ERROR % ('value[2]', NoneType, 42, int)
|
||||
o = self.cls('my_param')
|
||||
e = raises(TypeError, o.validate, 'Hello')
|
||||
assert str(e) == TYPE_ERROR % ('value', NoneType, 'Hello', str)
|
||||
|
||||
class Example(self.cls):
|
||||
type = int
|
||||
|
||||
# Test with some rules and multivalue=False
|
||||
pass1 = DummyRule()
|
||||
pass2 = DummyRule()
|
||||
fail = DummyRule(u'no good')
|
||||
o = Example('example', pass1, pass2)
|
||||
assert o.multivalue is False
|
||||
assert o.validate(11) is None
|
||||
assert pass1.calls == [(request.ugettext, 11)]
|
||||
assert pass2.calls == [(request.ugettext, 11)]
|
||||
pass1.reset()
|
||||
pass2.reset()
|
||||
o = Example('example', pass1, pass2, fail)
|
||||
e = raises(errors2.ValidationError, o.validate, 42)
|
||||
assert e.name == 'example'
|
||||
assert e.error == u'no good'
|
||||
assert e.index is None
|
||||
assert pass1.calls == [(request.ugettext, 42)]
|
||||
assert pass2.calls == [(request.ugettext, 42)]
|
||||
assert fail.calls == [(request.ugettext, 42)]
|
||||
|
||||
# Test with some rules and multivalue=True
|
||||
pass1 = DummyRule()
|
||||
pass2 = DummyRule()
|
||||
fail = DummyRule(u'this one is not good')
|
||||
o = Example('example', pass1, pass2, multivalue=True)
|
||||
assert o.multivalue is True
|
||||
assert o.validate((3, 9)) is None
|
||||
assert pass1.calls == [
|
||||
(request.ugettext, 3),
|
||||
(request.ugettext, 9),
|
||||
]
|
||||
assert pass2.calls == [
|
||||
(request.ugettext, 3),
|
||||
(request.ugettext, 9),
|
||||
]
|
||||
pass1.reset()
|
||||
pass2.reset()
|
||||
o = Example('multi_example', pass1, pass2, fail, multivalue=True)
|
||||
assert o.multivalue is True
|
||||
e = raises(errors2.ValidationError, o.validate, (3, 9))
|
||||
assert e.name == 'multi_example'
|
||||
assert e.error == u'this one is not good'
|
||||
assert e.index == 0
|
||||
assert pass1.calls == [(request.ugettext, 3)]
|
||||
assert pass2.calls == [(request.ugettext, 3)]
|
||||
assert fail.calls == [(request.ugettext, 3)]
|
||||
|
||||
def test_validate_scalar(self):
|
||||
"""
|
||||
Test the `ipalib.parameter.Param._validate_scalar` method.
|
||||
"""
|
||||
class MyParam(self.cls):
|
||||
type = bool
|
||||
okay = DummyRule()
|
||||
o = MyParam('my_param', okay)
|
||||
|
||||
# Test that TypeError is appropriately raised:
|
||||
e = raises(TypeError, o._validate_scalar, 0)
|
||||
assert str(e) == TYPE_ERROR % ('value', bool, 0, int)
|
||||
e = raises(TypeError, o._validate_scalar, 'Hi', index=4)
|
||||
assert str(e) == TYPE_ERROR % ('value[4]', bool, 'Hi', str)
|
||||
e = raises(TypeError, o._validate_scalar, True, index=3.0)
|
||||
assert str(e) == TYPE_ERROR % ('index', int, 3.0, float)
|
||||
|
||||
# Test with passing rule:
|
||||
assert o._validate_scalar(True, index=None) is None
|
||||
assert o._validate_scalar(False, index=None) is None
|
||||
assert okay.calls == [
|
||||
(request.ugettext, True),
|
||||
(request.ugettext, False),
|
||||
]
|
||||
|
||||
# Test with a failing rule:
|
||||
okay = DummyRule()
|
||||
fail = DummyRule(u'this describes the error')
|
||||
o = MyParam('my_param', okay, fail)
|
||||
e = raises(errors2.ValidationError, o._validate_scalar, True)
|
||||
assert e.name == 'my_param'
|
||||
assert e.error == u'this describes the error'
|
||||
assert e.index is None
|
||||
e = raises(errors2.ValidationError, o._validate_scalar, False, index=2)
|
||||
assert e.name == 'my_param'
|
||||
assert e.error == u'this describes the error'
|
||||
assert e.index == 2
|
||||
assert okay.calls == [
|
||||
(request.ugettext, True),
|
||||
(request.ugettext, False),
|
||||
]
|
||||
assert fail.calls == [
|
||||
(request.ugettext, True),
|
||||
(request.ugettext, False),
|
||||
]
|
||||
|
||||
|
||||
class test_Bytes(ClassChecker):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user