summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2009-01-12 16:14:46 -0700
committerJason Gerard DeRose <jderose@redhat.com>2009-01-12 16:14:46 -0700
commit5c7c0b35bb2484efad2a8776b42fbf4066618706 (patch)
treec47c304de26396dbe611556a48aaba7119490f53
parent5e6ea11178f3a784c9cd589e958ef752890f8a21 (diff)
downloadfreeipa-5c7c0b35bb2484efad2a8776b42fbf4066618706.tar.gz
freeipa-5c7c0b35bb2484efad2a8776b42fbf4066618706.tar.xz
freeipa-5c7c0b35bb2484efad2a8776b42fbf4066618706.zip
New Param: added Param.validate() and Param._validate_scalar() methods; added corresponding unit tests
-rw-r--r--ipalib/errors2.py16
-rw-r--r--ipalib/parameter.py35
-rw-r--r--tests/test_ipalib/test_parameter.py142
3 files changed, 192 insertions, 1 deletions
diff --git a/ipalib/errors2.py b/ipalib/errors2.py
index b052882da..81b1fb2ed 100644
--- a/ipalib/errors2.py
+++ b/ipalib/errors2.py
@@ -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')
diff --git a/ipalib/parameter.py b/ipalib/parameter.py
index 204fda66f..0890160a0 100644
--- a/ipalib/parameter.py
+++ b/ipalib/parameter.py
@@ -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):
diff --git a/tests/test_ipalib/test_parameter.py b/tests/test_ipalib/test_parameter.py
index e2016f06d..b379379cd 100644
--- a/tests/test_ipalib/test_parameter.py
+++ b/tests/test_ipalib/test_parameter.py
@@ -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):
"""