mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
202: Started work on type classes in ipa_types module; added corresponding unit tests
This commit is contained in:
103
ipalib/ipa_types.py
Normal file
103
ipalib/ipa_types.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# Authors:
|
||||
# Jason Gerard DeRose <jderose@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2008 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 only
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
"""
|
||||
Type system for coercing and normalizing input values.
|
||||
"""
|
||||
|
||||
from plugable import ReadOnly, lock
|
||||
import errors
|
||||
|
||||
|
||||
class Type(ReadOnly):
|
||||
"""
|
||||
Base class for all IPA types.
|
||||
"""
|
||||
|
||||
type = None # Override in base class
|
||||
|
||||
def convert(self, value):
|
||||
return self.type(value)
|
||||
|
||||
def __get_name(self):
|
||||
"""
|
||||
Convenience property to return the class name.
|
||||
"""
|
||||
return self.__class__.__name__
|
||||
name = property(__get_name)
|
||||
|
||||
|
||||
class Int(Type):
|
||||
type = int
|
||||
|
||||
def __init__(self, min_value=None, max_value=None):
|
||||
args = (min_value, max_value)
|
||||
for arg in args:
|
||||
if not (arg is None or type(arg) is int):
|
||||
raise TypeError('Must be an int or None: %r' % arg)
|
||||
if None not in args and min_value >= max_value:
|
||||
raise ValueError(
|
||||
'min_value not less than max_value: %r, %r' % (
|
||||
min_value, max_value
|
||||
)
|
||||
)
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value
|
||||
lock(self)
|
||||
|
||||
def validate(self, value):
|
||||
if type(value) is not self.type:
|
||||
return 'Must be an integer'
|
||||
if self.min_value is not None and value < self.min_value:
|
||||
return 'Cannot be smaller than %d' % self.min_value
|
||||
if self.max_value is not None and value > self.max_value:
|
||||
return 'Cannot be larger than %d' % self.max_value
|
||||
|
||||
|
||||
def check_min_max(min_name, min_value, max_name, max_value):
|
||||
assert type(min_name) is str, 'min_name must be an str'
|
||||
assert type(max_name) is str, 'max_name must be an str'
|
||||
for (name, value) in [(min_name, min_value), (max_name, max_value)]:
|
||||
if not (value is None or type(value) is int):
|
||||
raise TypeError(
|
||||
'%s must be an int or None, got: %r' % (name, value)
|
||||
)
|
||||
# if None not in (min_value, max_value) and min_value >= max_value:
|
||||
# raise ValueError(
|
||||
# 'min_value not less than max_value: %r, %r' % (
|
||||
# min_value, max_value
|
||||
# )
|
||||
# )
|
||||
|
||||
|
||||
class Unicode(Type):
|
||||
def __init__(self, min_length=None, max_length=None, pattern=None):
|
||||
integers = (min_length, max_length)
|
||||
for i in integers:
|
||||
if not (i is None or type(i) is int):
|
||||
raise TypeError('Must be an int or None: %r' % i)
|
||||
if None not in integers and min_value >= max_value:
|
||||
raise ValueError(
|
||||
'min_value not less than max_value: %r, %r' % (
|
||||
min_value, max_value
|
||||
)
|
||||
)
|
||||
self.min_length = min_length
|
||||
self.max_length = max_length
|
||||
self.pattern = pattern
|
||||
110
ipalib/tests/test_ipa_types.py
Normal file
110
ipalib/tests/test_ipa_types.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# Authors:
|
||||
# Jason Gerard DeRose <jderose@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2008 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 only
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
"""
|
||||
Unit tests for `ipalib.ipa_types` module.
|
||||
"""
|
||||
|
||||
from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker
|
||||
from ipalib import ipa_types, errors, plugable
|
||||
|
||||
|
||||
def test_check_min_max():
|
||||
"""
|
||||
Tests the `ipa_types.check_min_max` function.
|
||||
"""
|
||||
f = ipa_types.check_min_max
|
||||
fail_type = [
|
||||
'10',
|
||||
10.0,
|
||||
10L,
|
||||
True,
|
||||
False,
|
||||
object,
|
||||
]
|
||||
for value in fail_type:
|
||||
e = raises(TypeError, f, 'low', value, 'high', None)
|
||||
assert str(e) == 'low must be an int or None, got: %r' % value
|
||||
e = raises(TypeError, f, 'low', None, 'high', value)
|
||||
assert str(e) == 'high must be an int or None, got: %r' % value
|
||||
|
||||
|
||||
|
||||
class test_Type(ClassChecker):
|
||||
"""
|
||||
Tests the `ipa_types.Type` class.
|
||||
"""
|
||||
_cls = ipa_types.Type
|
||||
|
||||
def test_class(self):
|
||||
assert self.cls.__bases__ == (plugable.ReadOnly,)
|
||||
|
||||
|
||||
|
||||
class test_Int(ClassChecker):
|
||||
_cls = ipa_types.Int
|
||||
|
||||
def test_init(self):
|
||||
o = self.cls()
|
||||
assert o.min_value is None
|
||||
assert o.max_value is None
|
||||
okay = [
|
||||
(None, -5),
|
||||
(-20, None),
|
||||
(-20, -5),
|
||||
]
|
||||
fail_type = [
|
||||
(None, 10L),
|
||||
(5L, None),
|
||||
(None, '10'),
|
||||
('5', None),
|
||||
]
|
||||
fail_value = [
|
||||
(10, 5),
|
||||
(5, -5),
|
||||
(-5, -10),
|
||||
]
|
||||
for (l, h) in okay:
|
||||
o = self.cls(min_value=l, max_value=h)
|
||||
assert o.min_value is l
|
||||
assert o.max_value is h
|
||||
for (l, h) in fail_type:
|
||||
raises(TypeError, self.cls, min_value=l, max_value=h)
|
||||
for (l, h) in fail_value:
|
||||
raises(ValueError, self.cls, min_value=l, max_value=h)
|
||||
|
||||
def test_validate(self):
|
||||
o = self.cls(min_value=2, max_value=7)
|
||||
assert o.validate(2) is None
|
||||
assert o.validate(5) is None
|
||||
assert o.validate(7) is None
|
||||
assert o.validate(1) == 'Cannot be smaller than 2'
|
||||
assert o.validate(8) == 'Cannot be larger than 7'
|
||||
for val in ['5', 5.0, 5L, None, True, False, object]:
|
||||
assert o.validate(val) == 'Must be an integer'
|
||||
|
||||
|
||||
class test_Unicode(ClassChecker):
|
||||
_cls = ipa_types.Unicode
|
||||
|
||||
def test_init(self):
|
||||
o = self.cls()
|
||||
assert o.min_length is None
|
||||
assert o.max_length is None
|
||||
assert o.pattern is None
|
||||
Reference in New Issue
Block a user