summaryrefslogtreecommitdiffstats
path: root/tests/test_ipalib/test_ipa_types.py
blob: 81261a3572c7baedfb321cd73573e2d8624a09bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# 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

"""
Test the `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
    okay = [
            (None, -5),
            (-20, None),
            (-20, -5),
    ]
    for (l, h) in okay:
        assert f(l, h, 'low', 'high') is None
    fail_type = [
        '10',
        10.0,
        10L,
        True,
        False,
        object,
    ]
    for value in fail_type:
        e = raises(TypeError, f, value, None, 'low', 'high')
        assert str(e) == 'low must be an int or None, got: %r' % value
        e = raises(TypeError, f, None, value, 'low', 'high')
        assert str(e) == 'high must be an int or None, got: %r' % value
    fail_value = [
        (10, 5),
        (-5, -10),
        (5, -10),
    ]
    for (l, h) in fail_value:
        e = raises(ValueError, f, l, h, 'low', 'high')
        assert str(e) == 'low > high: low=%r, high=%r' % (l, h)


class test_Type(ClassChecker):
    """
    Tests the `ipa_types.Type` class.
    """
    _cls = ipa_types.Type

    def test_class(self):
        assert self.cls.__bases__ == (plugable.ReadOnly,)

    def test_init(self):
        okay = (bool, int, float, unicode)
        for t in okay:
            o = self.cls(t)
            assert o.__islocked__() is True
            assert read_only(o, 'type') is t
            assert read_only(o, 'name') is 'Type'

        type_errors = (None, True, 8, 8.0, u'hello')
        for t in type_errors:
            e = raises(TypeError, self.cls, t)
            assert str(e) == '%r is not %r' % (type(t), type)

        value_errors = (long, complex, str, tuple, list, dict, set, frozenset)
        for t in value_errors:
            e = raises(ValueError, self.cls, t)
            assert str(e) == 'not an allowed type: %r' % t

    def test_validate(self):
        o = self.cls(unicode)
        for value in (None, u'Hello', 'Hello', 42, False):
            assert o.validate(value) is None


class test_Bool(ClassChecker):
    _cls = ipa_types.Bool

    def test_class(self):
        assert self.cls.__bases__ == (ipa_types.Type,)

    def test_init(self):
        o = self.cls()
        assert o.__islocked__() is True
        assert read_only(o, 'type') is bool
        assert read_only(o, 'name') == 'Bool'
        assert read_only(o, 'true') == 'Yes'
        assert read_only(o, 'false') == 'No'

        keys = ('true', 'false')
        val = 'some value'
        for key in keys:
            # Check that kwarg sets appropriate attribute:
            o = self.cls(**{key: val})
            assert read_only(o, key) is val
            # Check that None raises TypeError:
            e = raises(TypeError, self.cls, **{key: None})
            assert str(e) == '`%s` cannot be None' % key

        # Check that ValueError is raise if true == false:
        e = raises(ValueError, self.cls, true=1L, false=1.0)
        assert str(e) == 'cannot be equal: true=1L, false=1.0'

    def test_call(self):
        o = self.cls()
        assert o(True) is True
        assert o('Yes') is True
        assert o(False) is False
        assert o('No') is False
        for value in (0, 1, 'True', 'False', 'yes', 'no'):
            # value is not be converted, so None is returned
            assert o(value) is None


class test_Int(ClassChecker):
    _cls = ipa_types.Int

    def test_class(self):
        assert self.cls.__bases__ == (ipa_types.Type,)

    def test_init(self):
        o = self.cls()
        assert o.__islocked__() is True
        assert read_only(o, 'type') is int
        assert read_only(o, 'name') == 'Int'
        assert read_only(o, 'min_value') is None
        assert read_only(o, 'max_value') is None

        okay = [
            (None, -5),
            (-20, None),
            (-20, -5),
        ]
        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

        fail_type = [
            '10',
            10.0,
            10L,
            True,
            False,
            object,
        ]
        for value in fail_type:
            e = raises(TypeError, self.cls, min_value=value)
            assert str(e) == (
                'min_value must be an int or None, got: %r' % value
            )
            e = raises(TypeError, self.cls, max_value=value)
            assert str(e) == (
                'max_value must be an int or None, got: %r' % value
            )

        fail_value = [
            (10, 5),
            (5, -5),
            (-5, -10),
        ]
        for (l, h) in fail_value:
            e = raises(ValueError, self.cls, min_value=l, max_value=h)
            assert str(e) == (
                'min_value > max_value: min_value=%d, max_value=%d' % (l, h)
            )

    def test_call(self):
        o = self.cls()

        # Test calling with None
        e = raises(TypeError, o, None)
        assert str(e) == 'value cannot be None'

        # Test with values that can be converted:
        okay = [
            3,
            '3',
            ' 3 ',
            3L,
            3.0,
        ]
        for value in okay:
            assert o(value) == 3

        # Test with values that cannot be converted:
        fail = [
            object,
            '3.0',
            '3L',
            'whatever',
        ]
        for value in fail:
            assert o(value) is None


    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_class(self):
        assert self.cls.__bases__ == (ipa_types.Type,)

    def test_init(self):
        o = self.cls()
        assert o.__islocked__() is True
        assert read_only(o, 'type') is unicode
        assert read_only(o, 'name') == 'Unicode'
        assert read_only(o, 'min_length') is None
        assert read_only(o, 'max_length') is None
        assert read_only(o, 'pattern') is None
        assert read_only(o, 'regex') is None

        # Test min_length, max_length:
        okay = (
            (0, 1),
            (8, 8),
        )
        for (l, h) in okay:
            o = self.cls(min_length=l, max_length=h)
            assert o.min_length == l
            assert o.max_length == h

        fail_type = [
            '10',
            10.0,
            10L,
            True,
            False,
            object,
        ]
        for value in fail_type:
            e = raises(TypeError, self.cls, min_length=value)
            assert str(e) == (
                'min_length must be an int or None, got: %r' % value
            )
            e = raises(TypeError, self.cls, max_length=value)
            assert str(e) == (
                'max_length must be an int or None, got: %r' % value
            )

        fail_value = [
            (10, 5),
            (5, -5),
            (0, -10),
        ]
        for (l, h) in fail_value:
            e = raises(ValueError, self.cls, min_length=l, max_length=h)
            assert str(e) == (
                'min_length > max_length: min_length=%d, max_length=%d' % (l, h)
            )

        for (key, lower) in [('min_length', 0), ('max_length', 1)]:
            value = lower - 1
            kw = {key: value}
            e = raises(ValueError, self.cls, **kw)
            assert str(e) == '%s must be >= %d, got: %d' % (key, lower, value)

        # Test pattern:
        okay = [
            '(hello|world)',
            u'(take the blue pill|take the red pill)',
        ]
        for value in okay:
            o = self.cls(pattern=value)
            assert o.pattern is value
            assert o.regex is not None

        fail = [
            42,
            True,
            False,
            object,
        ]
        for value in fail:
            e = raises(TypeError, self.cls, pattern=value)
            assert str(e) == (
                'pattern must be a basestring or None, got: %r' % value
            )

        # Test regex:
        pat = '^(hello|world)$'
        o = self.cls(pattern=pat)
        for value in ('hello', 'world'):
            m = o.regex.match(value)
            assert m.group(1) == value
        for value in ('hello beautiful', 'world!'):
            assert o.regex.match(value) is None

    def test_validate(self):
        pat = '^a_*b$'
        o = self.cls(min_length=3, max_length=4, pattern=pat)
        assert o.validate(u'a_b') is None
        assert o.validate(u'a__b') is None
        assert o.validate('a_b') == 'Must be a string'
        assert o.validate(u'ab') == 'Must be at least 3 characters long'
        assert o.validate(u'a___b') == 'Can be at most 4 characters long'
        assert o.validate(u'a-b') == 'Must match %r' % pat
        assert o.validate(u'a--b') == 'Must match %r' % pat


class test_Enum(ClassChecker):
    _cls = ipa_types.Enum

    def test_class(self):
        assert self.cls.__bases__ == (ipa_types.Type,)

    def test_init(self):
        for t in (unicode, int, float):
            values = (t(1), t(2), t(3))
            o = self.cls(*values)
            assert o.__islocked__() is True
            assert read_only(o, 'type') is t
            assert read_only(o, 'name') is 'Enum'
            assert read_only(o, 'values') == values
            assert read_only(o, 'frozenset') == frozenset(values)

        # Check that ValueError is raised when no values are given:
        e = raises(ValueError, self.cls)
        assert str(e) == 'Enum requires at least one value'

        # Check that TypeError is raised when type of first value is not
        # allowed:
        e = raises(TypeError, self.cls, 'hello')
        assert str(e) == '%r: %r not unicode, int, nor float' % ('hello', str)
        #self.cls('hello')

        # Check that TypeError is raised when subsequent values aren't same
        # type as first:
        e = raises(TypeError, self.cls, u'hello', 'world')
        assert str(e) == '%r: %r is not %r' % ('world', str, unicode)

    def test_validate(self):
        values = (u'hello', u'naughty', u'nurse')
        o = self.cls(*values)
        for value in values:
            assert o.validate(value) is None
            assert o.validate(str(value)) == 'Incorrect type'
        for value in (u'one fish', u'two fish'):
            assert o.validate(value) == 'Invalid value'
            assert o.validate(str(value)) == 'Incorrect type'