diff options
Diffstat (limited to 'ipatests/test_ipapython')
-rw-r--r-- | ipatests/test_ipapython/__init__.py | 22 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_cookie.py | 478 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_dn.py | 1937 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_ipautil.py | 69 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_keyring.py | 147 | ||||
-rw-r--r-- | ipatests/test_ipapython/test_ssh.py | 76 |
6 files changed, 2729 insertions, 0 deletions
diff --git a/ipatests/test_ipapython/__init__.py b/ipatests/test_ipapython/__init__.py new file mode 100644 index 000000000..fa0e44bb5 --- /dev/null +++ b/ipatests/test_ipapython/__init__.py @@ -0,0 +1,22 @@ +# Authors: +# Jan Cholasta <jcholast@redhat.com> +# +# Copyright (C) 2011 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. + +""" +Sub-package containing unit tests for `ipapython` package. +""" diff --git a/ipatests/test_ipapython/test_cookie.py b/ipatests/test_ipapython/test_cookie.py new file mode 100644 index 000000000..b8a2d36da --- /dev/null +++ b/ipatests/test_ipapython/test_cookie.py @@ -0,0 +1,478 @@ +# Authors: +# John Dennis <jdennis@redhat.com> +# +# Copyright (C) 2012 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. + +import unittest +import time +import datetime +import email.utils +import calendar +from ipapython.cookie import Cookie + +class TestParse(unittest.TestCase): + + def test_parse(self): + # Empty string + s = '' + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 0) + + # Invalid single token + s = 'color' + with self.assertRaises(ValueError): + cookies = Cookie.parse(s) + + # Invalid single token that's keyword + s = 'HttpOnly' + with self.assertRaises(ValueError): + cookies = Cookie.parse(s) + + # Invalid key/value pair whose key is a keyword + s = 'domain=example.com' + with self.assertRaises(ValueError): + cookies = Cookie.parse(s) + + # 1 cookie with name/value + s = 'color=blue' + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 1) + cookie = cookies[0] + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue") + self.assertEqual(cookie.http_cookie(), "color=blue;") + + # 1 cookie with whose value is quoted + # Use "get by name" utility to extract specific cookie + s = 'color="blue"' + cookie = Cookie.get_named_cookie_from_string(s, 'color') + self.assertIsNotNone(cookie) + self.assertIsNotNone(cookie, Cookie) + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue") + self.assertEqual(cookie.http_cookie(), "color=blue;") + + # 1 cookie with name/value and domain, path attributes. + # Change up the whitespace a bit. + s = 'color =blue; domain= example.com ; path = /toplevel ' + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 1) + cookie = cookies[0] + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, 'example.com') + self.assertEqual(cookie.path, '/toplevel') + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue; Domain=example.com; Path=/toplevel") + self.assertEqual(cookie.http_cookie(), "color=blue;") + + # 2 cookies, various attributes + s = 'color=blue; Max-Age=3600; temperature=hot; HttpOnly' + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 2) + cookie = cookies[0] + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, 3600) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue; Max-Age=3600") + self.assertEqual(cookie.http_cookie(), "color=blue;") + cookie = cookies[1] + self.assertEqual(cookie.key, 'temperature') + self.assertEqual(cookie.value, 'hot') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, True) + self.assertEqual(str(cookie), "temperature=hot; HttpOnly") + self.assertEqual(cookie.http_cookie(), "temperature=hot;") + +class TestExpires(unittest.TestCase): + + def setUp(self): + # Force microseconds to zero because cookie timestamps only have second resolution + self.now = datetime.datetime.utcnow().replace(microsecond=0) + self.now_timestamp = calendar.timegm(self.now.utctimetuple()) + self.now_string = email.utils.formatdate(self.now_timestamp, usegmt=True) + + self.max_age = 3600 # 1 hour + self.age_expiration = self.now + datetime.timedelta(seconds=self.max_age) + self.age_timestamp = calendar.timegm(self.age_expiration.utctimetuple()) + self.age_string = email.utils.formatdate(self.age_timestamp, usegmt=True) + + self.expires = self.now + datetime.timedelta(days=1) # 1 day + self.expires_timestamp = calendar.timegm(self.expires.utctimetuple()) + self.expires_string = email.utils.formatdate(self.expires_timestamp, usegmt=True) + + def test_expires(self): + # 1 cookie with name/value and no Max-Age and no Expires + s = 'color=blue;' + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 1) + cookie = cookies[0] + # Force timestamp to known value + cookie.timestamp = self.now + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue") + self.assertEqual(cookie.get_expiration(), None) + # Normalize + self.assertEqual(cookie.normalize_expiration(), None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(str(cookie), "color=blue") + + # 1 cookie with name/value and Max-Age + s = 'color=blue; max-age=%d' % (self.max_age) + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 1) + cookie = cookies[0] + # Force timestamp to known value + cookie.timestamp = self.now + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, self.max_age) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue; Max-Age=%d" % (self.max_age)) + self.assertEqual(cookie.get_expiration(), self.age_expiration) + # Normalize + self.assertEqual(cookie.normalize_expiration(), self.age_expiration) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, self.age_expiration) + self.assertEqual(str(cookie), "color=blue; Expires=%s" % (self.age_string)) + + + # 1 cookie with name/value and Expires + s = 'color=blue; Expires=%s' % (self.expires_string) + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 1) + cookie = cookies[0] + # Force timestamp to known value + cookie.timestamp = self.now + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, self.expires) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue; Expires=%s" % (self.expires_string)) + self.assertEqual(cookie.get_expiration(), self.expires) + # Normalize + self.assertEqual(cookie.normalize_expiration(), self.expires) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, self.expires) + self.assertEqual(str(cookie), "color=blue; Expires=%s" % (self.expires_string)) + + # 1 cookie with name/value witht both Max-Age and Expires, Max-Age takes precedence + s = 'color=blue; Expires=%s; max-age=%d' % (self.expires_string, self.max_age) + cookies = Cookie.parse(s) + self.assertEqual(len(cookies), 1) + cookie = cookies[0] + # Force timestamp to known value + cookie.timestamp = self.now + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, self.max_age) + self.assertEqual(cookie.expires, self.expires) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue; Max-Age=%d; Expires=%s" % (self.max_age, self.expires_string)) + self.assertEqual(cookie.get_expiration(), self.age_expiration) + # Normalize + self.assertEqual(cookie.normalize_expiration(), self.age_expiration) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, self.age_expiration) + self.assertEqual(str(cookie), "color=blue; Expires=%s" % (self.age_string)) + + # Verify different types can be assigned to the timestamp and + # expires attribute. + + cookie = Cookie('color', 'blue') + cookie.timestamp = self.now + self.assertEqual(cookie.timestamp, self.now) + cookie.timestamp = self.now_timestamp + self.assertEqual(cookie.timestamp, self.now) + cookie.timestamp = self.now_string + self.assertEqual(cookie.timestamp, self.now) + + self.assertEqual(cookie.expires, None) + + cookie.expires = self.expires + self.assertEqual(cookie.expires, self.expires) + cookie.expires = self.expires_timestamp + self.assertEqual(cookie.expires, self.expires) + cookie.expires = self.expires_string + self.assertEqual(cookie.expires, self.expires) + +class TestInvalidAttributes(unittest.TestCase): + def test_invalid(self): + # Invalid Max-Age + s = 'color=blue; Max-Age=over-the-hill' + with self.assertRaises(ValueError): + cookies = Cookie.parse(s) + + cookie = Cookie('color', 'blue') + with self.assertRaises(ValueError): + cookie.max_age = 'over-the-hill' + + # Invalid Expires + s = 'color=blue; Expires=Sun, 06 Xxx 1994 08:49:37 GMT' + with self.assertRaises(ValueError): + cookies = Cookie.parse(s) + + cookie = Cookie('color', 'blue') + with self.assertRaises(ValueError): + cookie.expires = 'Sun, 06 Xxx 1994 08:49:37 GMT' + + +class TestAttributes(unittest.TestCase): + def test_attributes(self): + cookie = Cookie('color', 'blue') + self.assertEqual(cookie.key, 'color') + self.assertEqual(cookie.value, 'blue') + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + self.assertEqual(cookie.max_age, None) + self.assertEqual(cookie.expires, None) + self.assertEqual(cookie.secure, None) + self.assertEqual(cookie.httponly, None) + + cookie.domain = 'example.com' + self.assertEqual(cookie.domain, 'example.com') + cookie.domain = None + self.assertEqual(cookie.domain, None) + + cookie.path = '/toplevel' + self.assertEqual(cookie.path, '/toplevel') + cookie.path = None + self.assertEqual(cookie.path, None) + + cookie.max_age = 400 + self.assertEqual(cookie.max_age, 400) + cookie.max_age = None + self.assertEqual(cookie.max_age, None) + + cookie.expires = 'Sun, 06 Nov 1994 08:49:37 GMT' + self.assertEqual(cookie.expires, datetime.datetime(1994, 11, 6, 8, 49, 37)) + cookie.expires = None + self.assertEqual(cookie.expires, None) + + cookie.secure = True + self.assertEqual(cookie.secure, True) + self.assertEqual(str(cookie), "color=blue; Secure") + cookie.secure = False + self.assertEqual(cookie.secure, False) + self.assertEqual(str(cookie), "color=blue") + cookie.secure = None + self.assertEqual(cookie.secure, None) + self.assertEqual(str(cookie), "color=blue") + + cookie.httponly = True + self.assertEqual(cookie.httponly, True) + self.assertEqual(str(cookie), "color=blue; HttpOnly") + cookie.httponly = False + self.assertEqual(cookie.httponly, False) + self.assertEqual(str(cookie), "color=blue") + cookie.httponly = None + self.assertEqual(cookie.httponly, None) + self.assertEqual(str(cookie), "color=blue") + + +class TestHTTPReturn(unittest.TestCase): + def setUp(self): + self.url = 'http://www.foo.bar.com/one/two' + + def test_no_attributes(self): + cookie = Cookie('color', 'blue') + self.assertTrue(cookie.http_return_ok(self.url)) + + def test_domain(self): + cookie = Cookie('color', 'blue', domain='www.foo.bar.com') + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', domain='.foo.bar.com') + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', domain='.bar.com') + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', domain='bar.com') + with self.assertRaises(Cookie.URLMismatch): + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', domain='bogus.com') + with self.assertRaises(Cookie.URLMismatch): + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', domain='www.foo.bar.com') + with self.assertRaises(Cookie.URLMismatch): + self.assertTrue(cookie.http_return_ok('http://192.168.1.1/one/two')) + + def test_path(self): + cookie = Cookie('color', 'blue') + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', path='/') + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', path='/one') + self.assertTrue(cookie.http_return_ok(self.url)) + + cookie = Cookie('color', 'blue', path='/oneX') + with self.assertRaises(Cookie.URLMismatch): + self.assertTrue(cookie.http_return_ok(self.url)) + + def test_expires(self): + now = datetime.datetime.utcnow().replace(microsecond=0) + + # expires 1 day from now + expires = now + datetime.timedelta(days=1) + + cookie = Cookie('color', 'blue', expires=expires) + self.assertTrue(cookie.http_return_ok(self.url)) + + # expired 1 day ago + expires = now + datetime.timedelta(days=-1) + cookie = Cookie('color', 'blue', expires=expires) + with self.assertRaises(Cookie.Expired): + self.assertTrue(cookie.http_return_ok(self.url)) + + + def test_httponly(self): + cookie = Cookie('color', 'blue', httponly=True) + self.assertTrue(cookie.http_return_ok('http://example.com')) + self.assertTrue(cookie.http_return_ok('https://example.com')) + + with self.assertRaises(Cookie.URLMismatch): + self.assertTrue(cookie.http_return_ok('ftp://example.com')) + + def test_secure(self): + cookie = Cookie('color', 'blue', secure=True) + self.assertTrue(cookie.http_return_ok('https://Xexample.com')) + + with self.assertRaises(Cookie.URLMismatch): + self.assertTrue(cookie.http_return_ok('http://Xexample.com')) + +class TestNormalization(unittest.TestCase): + def setUp(self): + # Force microseconds to zero because cookie timestamps only have second resolution + self.now = datetime.datetime.utcnow().replace(microsecond=0) + self.now_timestamp = calendar.timegm(self.now.utctimetuple()) + self.now_string = email.utils.formatdate(self.now_timestamp, usegmt=True) + + self.max_age = 3600 # 1 hour + self.age_expiration = self.now + datetime.timedelta(seconds=self.max_age) + self.age_timestamp = calendar.timegm(self.age_expiration.utctimetuple()) + self.age_string = email.utils.formatdate(self.age_timestamp, usegmt=True) + + self.expires = self.now + datetime.timedelta(days=1) # 1 day + self.expires_timestamp = calendar.timegm(self.expires.utctimetuple()) + self.expires_string = email.utils.formatdate(self.expires_timestamp, usegmt=True) + + def test_path_normalization(self): + self.assertEqual(Cookie.normalize_url_path(''), '/') + self.assertEqual(Cookie.normalize_url_path('foo'), '/') + self.assertEqual(Cookie.normalize_url_path('foo/'), '/') + self.assertEqual(Cookie.normalize_url_path('/foo'), '/') + self.assertEqual(Cookie.normalize_url_path('/foo/'), '/foo') + self.assertEqual(Cookie.normalize_url_path('/Foo/bar'), '/foo') + self.assertEqual(Cookie.normalize_url_path('/foo/baR/'), '/foo/bar') + + def test_normalization(self): + cookie = Cookie('color', 'blue', expires=self.expires) + cookie.timestamp = self.now_timestamp + + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + + url = 'http://example.COM/foo' + cookie.normalize(url) + self.assertEqual(cookie.domain, 'example.com') + self.assertEqual(cookie.path, '/') + self.assertEqual(cookie.expires, self.expires) + + cookie = Cookie('color', 'blue', max_age=self.max_age) + cookie.timestamp = self.now_timestamp + + self.assertEqual(cookie.domain, None) + self.assertEqual(cookie.path, None) + + url = 'http://example.com/foo/' + cookie.normalize(url) + self.assertEqual(cookie.domain, 'example.com') + self.assertEqual(cookie.path, '/foo') + self.assertEqual(cookie.expires, self.age_expiration) + + cookie = Cookie('color', 'blue') + url = 'http://example.com/foo' + cookie.normalize(url) + self.assertEqual(cookie.domain, 'example.com') + self.assertEqual(cookie.path, '/') + + cookie = Cookie('color', 'blue') + url = 'http://example.com/foo/bar' + cookie.normalize(url) + self.assertEqual(cookie.domain, 'example.com') + self.assertEqual(cookie.path, '/foo') + + cookie = Cookie('color', 'blue') + url = 'http://example.com/foo/bar/' + cookie.normalize(url) + self.assertEqual(cookie.domain, 'example.com') + self.assertEqual(cookie.path, '/foo/bar') + + +#------------------------------------------------------------------------------- +if __name__ == '__main__': + unittest.main() diff --git a/ipatests/test_ipapython/test_dn.py b/ipatests/test_ipapython/test_dn.py new file mode 100644 index 000000000..cdeab9374 --- /dev/null +++ b/ipatests/test_ipapython/test_dn.py @@ -0,0 +1,1937 @@ +#!/usr/bin/python + +import unittest +from ipapython.dn import * + +def default_rdn_attr_arg(i): + return 'a%d' % i + +def default_rdn_value_arg(i): + return str(i) + +def alt_rdn_attr_arg(i): + return 'b%d' % i + +def alt_rdn_value_arg(i): + return str(i*10) + +def make_rdn_args(low, high, kind, attr=None, value=None): + result=[] + for i in range(low, high): + if attr is None: + new_attr = default_rdn_attr_arg(i) + elif callable(attr): + new_attr = attr(i) + else: + new_attr = attr + + if value is None: + new_value = default_rdn_value_arg(i) + elif callable(value): + new_value = value(i) + else: + new_value = value + + if kind == 'tuple': + result.append((new_attr, new_value)) + elif kind == 'list': + result.append([new_attr, new_value]) + elif kind == 'RDN': + result.append(RDN((new_attr, new_value))) + else: + raise ValueError("Unknown kind = %s" % kind) + + return result + +def expected_class(klass, component): + if klass is AVA: + if component == 'self': + return AVA + + elif klass is EditableAVA: + if component == 'self': + return EditableAVA + + elif klass is RDN: + if component == 'self': + return RDN + elif component == 'AVA': + return AVA + + elif klass is EditableRDN: + if component == 'self': + return EditableRDN + elif component == 'AVA': + return EditableAVA + + elif klass is DN: + if component == 'self': + return DN + elif component == 'AVA': + return AVA + elif component == 'RDN': + return RDN + + elif klass is EditableDN: + if component == 'self': + return EditableDN + elif component == 'AVA': + return EditableAVA + elif component == 'RDN': + return EditableRDN + + raise ValueError("class %s with component '%s' unknown" % (klass.__name__, component)) + + +class TestAVA(unittest.TestCase): + def setUp(self): + self.attr1 = 'cn' + self.value1 = 'Bob' + self.str_ava1 = '%s=%s' % (self.attr1, self.value1) + self.ava1 = AVA(self.attr1, self.value1) + + self.attr2 = 'ou' + self.value2 = 'People' + self.str_ava2 = '%s=%s' % (self.attr2, self.value2) + self.ava2 = AVA(self.attr2, self.value2) + + self.attr3 = 'c' + self.value3 = 'US' + self.str_ava3 = '%s=%s' % (self.attr3, self.value3) + self.ava3 = AVA(self.attr3, self.value3) + + def assertExpectedClass(self, klass, obj, component): + self.assertIs(obj.__class__, expected_class(klass, component)) + + def test_create(self): + for AVA_class in (AVA, EditableAVA): + # Create with attr,value pair + ava1 = AVA_class(self.attr1, self.value1) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1, self.ava1) + + # Create with "attr=value" string + ava1 = AVA_class(self.str_ava1) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1, self.ava1) + + # Create with tuple (attr, value) + ava1 = AVA_class((self.attr1, self.value1)) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1, self.ava1) + + # Create with list [attr, value] + ava1 = AVA_class([self.attr1, self.value1]) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1, self.ava1) + + # Create with no args should fail + with self.assertRaises(TypeError): + AVA_class() + + # Create with more than 2 args should fail + with self.assertRaises(TypeError): + AVA_class(self.attr1, self.value1, self.attr1) + + # Create with 1 arg which is not string should fail + with self.assertRaises(TypeError): + AVA_class(1) + + # Create with malformed AVA_class string should fail + with self.assertRaises(ValueError): + AVA_class("cn") + + # Create with non-string parameters, should convert + ava1 = AVA_class(1, self.value1) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1.attr, u'1') + + ava1 = AVA_class((1, self.value1)) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1.attr, u'1') + + ava1 = AVA_class(self.attr1, 1) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1.value, u'1') + + ava1 = AVA_class((self.attr1, 1)) + self.assertExpectedClass(AVA_class, ava1, 'self') + self.assertEqual(ava1.value, u'1') + + def test_indexing(self): + for AVA_class in (AVA, EditableAVA): + ava1 = AVA_class(self.ava1) + + self.assertEqual(ava1[self.attr1], self.value1) + + with self.assertRaises(KeyError): + ava1['foo'] + + with self.assertRaises(TypeError): + ava1[0] + + def test_properties(self): + for AVA_class in (AVA, EditableAVA): + ava1 = AVA_class(self.ava1) + + self.assertEqual(ava1.attr, self.attr1) + self.assertIsInstance(ava1.attr, unicode) + + self.assertEqual(ava1.value, self.value1) + self.assertIsInstance(ava1.value, unicode) + + def test_str(self): + for AVA_class in (AVA, EditableAVA): + ava1 = AVA_class(self.ava1) + + self.assertEqual(str(ava1), self.str_ava1) + self.assertIsInstance(str(ava1), str) + + def test_cmp(self): + for AVA_class in (AVA, EditableAVA): + # Equality + ava1 = AVA_class(self.attr1, self.value1) + + self.assertTrue(ava1 == self.ava1) + self.assertFalse(ava1 != self.ava1) + + self.assertTrue(ava1 == self.str_ava1) + self.assertFalse(ava1 != self.str_ava1) + + result = cmp(ava1, self.ava1) + self.assertEqual(result, 0) + + # Upper case attr should still be equal + ava1 = AVA_class(self.attr1.upper(), self.value1) + + self.assertFalse(ava1.attr == self.attr1) + self.assertTrue(ava1.value == self.value1) + self.assertTrue(ava1 == self.ava1) + self.assertFalse(ava1 != self.ava1) + + result = cmp(ava1, self.ava1) + self.assertEqual(result, 0) + + # Upper case value should still be equal + ava1 = AVA_class(self.attr1, self.value1.upper()) + + self.assertTrue(ava1.attr == self.attr1) + self.assertFalse(ava1.value == self.value1) + self.assertTrue(ava1 == self.ava1) + self.assertFalse(ava1 != self.ava1) + + result = cmp(ava1, self.ava1) + self.assertEqual(result, 0) + + # Make ava1's attr greater + if AVA_class.is_mutable: + ava1.attr = self.attr1 + "1" + else: + with self.assertRaises(AttributeError): + ava1.attr = self.attr1 + "1" + ava1 = AVA_class(self.attr1 + "1", self.value1.upper()) + + self.assertFalse(ava1 == self.ava1) + self.assertTrue(ava1 != self.ava1) + + result = cmp(ava1, self.ava1) + self.assertEqual(result, 1) + + result = cmp(self.ava1, ava1) + self.assertEqual(result, -1) + + # Reset ava1's attr, should be equal again + if AVA_class.is_mutable: + ava1.attr = self.attr1 + else: + with self.assertRaises(AttributeError): + ava1.attr = self.attr1 + ava1 = AVA_class(self.attr1, self.value1.upper()) + + result = cmp(ava1, self.ava1) + self.assertEqual(result, 0) + + # Make ava1's value greater + # attr will be equal, this tests secondary comparision component + if AVA_class.is_mutable: + ava1.value = self.value1 + "1" + else: + with self.assertRaises(AttributeError): + ava1.value = self.value1 + "1" + ava1 = AVA_class(self.attr1, self.value1 + "1") + + result = cmp(ava1, self.ava1) + self.assertEqual(result, 1) + + result = cmp(self.ava1, ava1) + self.assertEqual(result, -1) + + def test_hashing(self): + # create AVA's that are equal but differ in case + immutable_ava1 = AVA((self.attr1.lower(), self.value1.upper())) + immutable_ava2 = AVA((self.attr1.upper(), self.value1.lower())) + + mutable_ava1 = EditableAVA((self.attr1.lower(), self.value1.upper())) + mutable_ava2 = EditableAVA((self.attr1.upper(), self.value1.lower())) + + # Immutable AVA's that are equal should hash to the same value. + # Mutable AVA's should not be hashable. + + self.assertEqual(immutable_ava1, immutable_ava2) + self.assertEqual(immutable_ava1, mutable_ava1) + self.assertEqual(immutable_ava1, mutable_ava2) + self.assertEqual(mutable_ava1, immutable_ava2) + + # Good, everyone's equal, now verify their hash values + + self.assertEqual(hash(immutable_ava1), hash(immutable_ava2)) + with self.assertRaises(TypeError): + hash(mutable_ava1) + with self.assertRaises(TypeError): + hash(mutable_ava2) + + # Different immutable AVA objects with the same value should + # map to 1 common key and 1 member in a set. The key and + # member are based on the object's value. + # + # Mutable AVA objects should be unhashable. + + for AVA_class in (AVA, EditableAVA): + ava1_a = AVA_class(self.ava1) + ava1_b = AVA_class(self.ava1) + + ava2_a = AVA_class(self.ava2) + ava2_b = AVA_class(self.ava2) + + ava3_a = AVA_class(self.ava3) + ava3_b = AVA_class(self.ava3) + + self.assertEqual(ava1_a, ava1_b) + self.assertEqual(ava2_a, ava2_b) + self.assertEqual(ava3_a, ava3_b) + + d = dict() + s = set() + + if AVA_class.is_mutable: + with self.assertRaises(TypeError): + d[ava1_a] = str(ava1_a) + with self.assertRaises(TypeError): + d[ava1_b] = str(ava1_b) + with self.assertRaises(TypeError): + d[ava2_a] = str(ava2_a) + with self.assertRaises(TypeError): + d[ava2_b] = str(ava2_b) + + with self.assertRaises(TypeError): + s.add(ava1_a) + with self.assertRaises(TypeError): + s.add(ava1_b) + with self.assertRaises(TypeError): + s.add(ava2_a) + with self.assertRaises(TypeError): + s.add(ava2_b) + else: + d[ava1_a] = str(ava1_a) + d[ava1_b] = str(ava1_b) + d[ava2_a] = str(ava2_a) + d[ava2_b] = str(ava2_b) + + s.add(ava1_a) + s.add(ava1_b) + s.add(ava2_a) + s.add(ava2_b) + + self.assertEqual(len(d), 2) + self.assertEqual(len(s), 2) + self.assertEqual(sorted(d.keys()), sorted([ava1_a, ava2_a])) + self.assertEqual(sorted(s), sorted([ava1_a, ava2_a])) + + self.assertTrue(ava1_a in d) + self.assertTrue(ava1_b in d) + self.assertTrue(ava2_a in d) + self.assertTrue(ava2_b in d) + self.assertFalse(ava3_a in d) + self.assertFalse(ava3_b in d) + + self.assertTrue(d.has_key(ava1_a)) + self.assertTrue(d.has_key(ava1_b)) + self.assertTrue(d.has_key(ava2_a)) + self.assertTrue(d.has_key(ava2_b)) + self.assertFalse(d.has_key(ava3_a)) + self.assertFalse(d.has_key(ava3_b)) + + self.assertTrue(ava1_a in s) + self.assertTrue(ava1_b in s) + self.assertTrue(ava2_a in s) + self.assertTrue(ava2_b in s) + self.assertFalse(ava3_a in s) + self.assertFalse(ava3_b in s) + + def test_coerce(self): + # Coerce an immutable to a mutable + immutable_ava1 = AVA(self.ava1) + mutable_ava1 = EditableAVA(immutable_ava1) + self.assertEqual(mutable_ava1, self.ava1) + self.assertEqual(mutable_ava1, immutable_ava1) + + # Coerce a mutable to an immutable + mutable_ava1 = EditableAVA(self.ava1) + immutable_ava1 = AVA(mutable_ava1) + self.assertEqual(immutable_ava1, self.ava1) + self.assertEqual(immutable_ava1, mutable_ava1) + +class TestRDN(unittest.TestCase): + def setUp(self): + # ava1 must sort before ava2 + self.attr1 = 'cn' + self.value1 = 'Bob' + self.str_ava1 = '%s=%s' % (self.attr1, self.value1) + self.ava1 = AVA(self.attr1, self.value1) + + self.str_rdn1 = '%s=%s' % (self.attr1, self.value1) + self.rdn1 = RDN((self.attr1, self.value1)) + + self.attr2 = 'ou' + self.value2 = 'people' + self.str_ava2 = '%s=%s' % (self.attr2, self.value2) + self.ava2 = AVA(self.attr2, self.value2) + + self.str_rdn2 = '%s=%s' % (self.attr2, self.value2) + self.rdn2 = RDN((self.attr2, self.value2)) + + self.str_ava3 = '%s=%s+%s=%s' % (self.attr1, self.value1, self.attr2, self.value2) + + self.str_rdn3 = '%s=%s+%s=%s' % (self.attr1, self.value1, self.attr2, self.value2) + self.rdn3 = RDN(self.ava1, self.ava2) + + def assertExpectedClass(self, klass, obj, component): + self.assertIs(obj.__class__, expected_class(klass, component)) + + def test_create(self): + for RDN_class in (RDN, EditableRDN): + # Create with single attr,value pair + rdn1 = RDN_class((self.attr1, self.value1)) + + + self.assertEqual(len(rdn1), 1) + self.assertEqual(rdn1, self.rdn1) + self.assertExpectedClass(RDN_class, rdn1, 'self') + for i in range(0, len(rdn1)): + self.assertExpectedClass(RDN_class, rdn1[i], 'AVA') + self.assertEqual(rdn1[0], self.ava1) + + # Create with multiple attr,value pairs + rdn3 = RDN_class((self.attr1, self.value1), (self.attr2, self.value2)) + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[1], self.ava2) + + # Create with multiple attr,value pairs passed as lists + rdn3 = RDN_class([self.attr1, self.value1], [self.attr2, self.value2]) + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[1], self.ava2) + + # Create with multiple attr,value pairs but reverse + # constructor parameter ordering. RDN canonical ordering + # should remain the same + rdn3 = RDN_class((self.attr2, self.value2), (self.attr1, self.value1)) + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[1], self.ava2) + + # Create with single AVA object + rdn1 = RDN_class(self.ava1) + self.assertEqual(len(rdn1), 1) + self.assertEqual(rdn1, self.rdn1) + self.assertExpectedClass(RDN_class, rdn1, 'self') + for i in range(0, len(rdn1)): + self.assertExpectedClass(RDN_class, rdn1[i], 'AVA') + self.assertEqual(rdn1[0], self.ava1) + + # Create with multiple AVA objects + rdn3 = RDN_class(self.ava1, self.ava2) + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[1], self.ava2) + + + # Create with multiple AVA objects but reverse constructor + # parameter ordering. RDN canonical ordering should remain + # the same + rdn3 = RDN_class(self.ava2, self.ava1) + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[1], self.ava2) + + # Create with single string with 1 AVA + rdn1 = RDN_class(self.str_rdn1) + self.assertEqual(len(rdn1), 1) + self.assertEqual(rdn1, self.rdn1) + self.assertExpectedClass(RDN_class, rdn1, 'self') + for i in range(0, len(rdn1)): + self.assertExpectedClass(RDN_class, rdn1[i], 'AVA') + self.assertEqual(rdn1[0], self.ava1) + + # Create with single string with 2 AVA's + rdn3 = RDN_class(self.str_rdn3) + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[1], self.ava2) + + def test_properties(self): + for RDN_class in (RDN, EditableRDN): + rdn1 = RDN_class(self.rdn1) + rdn2 = RDN_class(self.rdn2) + rdn3 = RDN_class(self.rdn3) + + self.assertEqual(rdn1.attr, self.attr1) + self.assertIsInstance(rdn1.attr, unicode) + + self.assertEqual(rdn1.value, self.value1) + self.assertIsInstance(rdn1.value, unicode) + + self.assertEqual(rdn2.attr, self.attr2) + self.assertIsInstance(rdn2.attr, unicode) + + self.assertEqual(rdn2.value, self.value2) + self.assertIsInstance(rdn2.value, unicode) + + self.assertEqual(rdn3.attr, self.attr1) + self.assertIsInstance(rdn3.attr, unicode) + + self.assertEqual(rdn3.value, self.value1) + self.assertIsInstance(rdn3.value, unicode) + + def test_str(self): + for RDN_class in (RDN, EditableRDN): + rdn1 = RDN_class(self.rdn1) + rdn2 = RDN_class(self.rdn2) + rdn3 = RDN_class(self.rdn3) + + self.assertEqual(str(rdn1), self.str_rdn1) + self.assertIsInstance(str(rdn1), str) + + self.assertEqual(str(rdn2), self.str_rdn2) + self.assertIsInstance(str(rdn2), str) + + self.assertEqual(str(rdn3), self.str_rdn3) + self.assertIsInstance(str(rdn3), str) + + def test_cmp(self): + for RDN_class in (RDN, EditableRDN): + # Equality + rdn1 = RDN_class((self.attr1, self.value1)) + + self.assertTrue(rdn1 == self.rdn1) + self.assertFalse(rdn1 != self.rdn1) + + self.assertTrue(rdn1 == self.str_rdn1) + self.assertFalse(rdn1 != self.str_rdn1) + + result = cmp(rdn1, self.rdn1) + self.assertEqual(result, 0) + + # Make rdn1's attr greater + if RDN_class.is_mutable: + rdn1.attr = self.attr1 + "1" + else: + rdn1 = RDN_class((self.attr1 + "1", self.value1)) + + self.assertFalse(rdn1 == self.rdn1) + self.assertTrue(rdn1 != self.rdn1) + + result = cmp(rdn1, self.rdn1) + self.assertEqual(result, 1) + + result = cmp(self.rdn1, rdn1) + self.assertEqual(result, -1) + + # Reset rdn1's attr, should be equal again + if RDN_class.is_mutable: + rdn1.attr = self.attr1 + else: + rdn1 = RDN_class((self.attr1, self.value1)) + + result = cmp(rdn1, self.rdn1) + self.assertEqual(result, 0) + + # Make rdn1's value greater + # attr will be equal, this tests secondary comparision component + if RDN_class.is_mutable: + rdn1.value = self.value1 + "1" + else: + rdn1 = RDN_class((self.attr1, self.value1 + "1")) + + result = cmp(rdn1, self.rdn1) + self.assertEqual(result, 1) + + result = cmp(self.rdn1, rdn1) + self.assertEqual(result, -1) + + # Make sure rdn's with more ava's are greater + result = cmp(self.rdn1, self.rdn3) + self.assertEqual(result, -1) + result = cmp(self.rdn3, self.rdn1) + self.assertEqual(result, 1) + + def test_indexing(self): + for RDN_class in (RDN, EditableRDN): + rdn1 = RDN_class(self.rdn1) + rdn2 = RDN_class(self.rdn2) + rdn3 = RDN_class(self.rdn3) + + self.assertEqual(rdn1[0], self.ava1) + self.assertEqual(rdn1[self.ava1.attr], self.ava1.value) + with self.assertRaises(KeyError): + rdn1['foo'] + + self.assertEqual(rdn2[0], self.ava2) + self.assertEqual(rdn2[self.ava2.attr], self.ava2.value) + with self.assertRaises(KeyError): + rdn2['foo'] + + self.assertEqual(rdn3[0], self.ava1) + self.assertEqual(rdn3[self.ava1.attr], self.ava1.value) + self.assertEqual(rdn3[1], self.ava2) + self.assertEqual(rdn3[self.ava2.attr], self.ava2.value) + with self.assertRaises(KeyError): + rdn3['foo'] + + self.assertEqual(rdn1.attr, self.attr1) + self.assertEqual(rdn1.value, self.value1) + + with self.assertRaises(TypeError): + rdn3[1.0] + + # Slices + self.assertEqual(rdn3[0:1], [self.ava1]) + self.assertEqual(rdn3[:], [self.ava1, self.ava2]) + + def test_assignments(self): + for RDN_class in (RDN, EditableRDN): + rdn = RDN_class((self.attr1, self.value1)) + if RDN_class.is_mutable: + rdn[0] = self.ava2 + self.assertEqual(rdn, self.rdn2) + else: + with self.assertRaises(TypeError): + rdn[0] = self.ava2 + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + if RDN_class.is_mutable: + rdn[0] = (self.attr2, self.value2) + self.assertEqual(rdn, self.rdn2) + else: + with self.assertRaises(TypeError): + rdn[0] = (self.attr2, self.value2) + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + if RDN_class.is_mutable: + rdn[self.attr1] = self.str_ava2 + self.assertEqual(rdn[0], self.ava2) + else: + with self.assertRaises(TypeError): + rdn[self.attr1] = self.str_ava2 + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + # Can't assign multiples to single entry + rdn = RDN_class((self.attr1, self.value1)) + with self.assertRaises(TypeError): + rdn[self.attr1] = self.str_ava3 + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + with self.assertRaises(TypeError): + rdn[self.attr1] = (self.attr1, self.value1, self.attr2, self.value2) + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + with self.assertRaises(TypeError): + rdn[self.attr1] = [(self.attr1, self.value1), (self.attr2, self.value2)] + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + # Slices + rdn = RDN_class((self.attr1, self.value1)) + self.assertEqual(rdn, self.rdn1) + if RDN_class.is_mutable: + rdn[0:1] = [self.ava2] + self.assertEqual(rdn, self.rdn2) + else: + with self.assertRaises(TypeError): + rdn[0:1] = [self.ava2] + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + self.assertEqual(rdn, self.rdn1) + if RDN_class.is_mutable: + rdn[:] = [(self.attr2, self.value2)] + self.assertEqual(rdn, self.rdn2) + else: + with self.assertRaises(TypeError): + rdn[:] = [(self.attr2, self.value2)] + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + self.assertEqual(rdn, self.rdn1) + if RDN_class.is_mutable: + rdn[:] = [(self.attr1, self.value1),(self.attr2, self.value2)] + self.assertEqual(rdn, self.rdn3) + else: + with self.assertRaises(TypeError): + rdn[:] = [(self.attr1, self.value1),(self.attr2, self.value2)] + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + rdn = RDN_class((self.attr1, self.value1)) + self.assertEqual(rdn, self.rdn1) + if RDN_class.is_mutable: + rdn[0:1] = [(self.attr1, self.value1), (self.attr2, self.value2)] + self.assertEqual(rdn, self.rdn3) + else: + with self.assertRaises(TypeError): + rdn[0:1] = [(self.attr1, self.value1), (self.attr2, self.value2)] + self.assertExpectedClass(RDN_class, rdn, 'self') + for i in range(0, len(rdn)): + self.assertExpectedClass(RDN_class, rdn[i], 'AVA') + + + def test_iter(self): + for RDN_class in (RDN, EditableRDN): + rdn1 = RDN_class(self.rdn1) + rdn2 = RDN_class(self.rdn2) + rdn3 = RDN_class(self.rdn3) + + self.assertEqual(len(rdn1), 1) + self.assertEqual(rdn1[:], [self.ava1]) + for i, ava in enumerate(rdn1): + if i == 0: + self.assertEqual(ava, self.ava1) + else: + self.fail("got iteration index %d, but len=%d" % (i, len(rdn1))) + + self.assertEqual(len(rdn2), 1) + self.assertEqual(rdn2[:], [self.ava2]) + for i, ava in enumerate(rdn2): + if i == 0: + self.assertEqual(ava, self.ava2) + else: + self.fail("got iteration index %d, but len=%d" % (i, len(rdn2))) + + self.assertEqual(len(rdn3), 2) + self.assertEqual(rdn3[:], [self.ava1, self.ava2]) + for i, ava in enumerate(rdn3): + if i == 0: + self.assertEqual(ava, self.ava1) + elif i == 1: + self.assertEqual(ava, self.ava2) + else: + self.fail("got iteration index %d, but len=%d" % (i, len(rdn3))) + + + def test_concat(self): + for RDN_class in (RDN, EditableRDN): + rdn1 = RDN_class((self.attr1, self.value1)) + rdn2 = RDN_class((self.attr2, self.value2)) + + # in-place addtion + + # Note: If __iadd__ is not available Python will emulate += by + # replacing the lhs object with the result of __add__ (if available). + # Thus += works for both immutable and mutable RDN,DN object, the only + # difference is an immutable without __iadd__ will have a different object + # on the lhs after the operator evaluates. + + rdn1 += rdn2 + self.assertEqual(rdn1, self.rdn3) + self.assertExpectedClass(RDN_class, rdn1, 'self') + for i in range(0, len(rdn1)): + self.assertExpectedClass(RDN_class, rdn1[i], 'AVA') + + rdn1 = RDN_class((self.attr1, self.value1)) + rdn1 += self.ava2 + self.assertEqual(rdn1, self.rdn3) + self.assertExpectedClass(RDN_class, rdn1, 'self') + for i in range(0, len(rdn1)): + self.assertExpectedClass(RDN_class, rdn1[i], 'AVA') + + rdn1 = RDN_class((self.attr1, self.value1)) + rdn1 += self.str_ava2 + self.assertEqual(rdn1, self.rdn3) + self.assertExpectedClass(RDN_class, rdn1, 'self') + for i in range(0, len(rdn1)): + self.assertExpectedClass(RDN_class, rdn1[i], 'AVA') + + # concatenation + rdn1 = RDN_class((self.attr1, self.value1)) + rdn3 = rdn1 + rdn2 + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + + rdn3 = rdn1 + self.ava2 + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + + rdn3 = rdn1 + self.str_ava2 + self.assertEqual(rdn3, self.rdn3) + self.assertExpectedClass(RDN_class, rdn3, 'self') + for i in range(0, len(rdn3)): + self.assertExpectedClass(RDN_class, rdn3[i], 'AVA') + + + def test_hashing(self): + # create RDN's that are equal but differ in case + immutable_rdn1 = RDN((self.attr1.lower(), self.value1.upper())) + immutable_rdn2 = RDN((self.attr1.upper(), self.value1.lower())) + + mutable_rdn1 = EditableRDN((self.attr1.lower(), self.value1.upper())) + mutable_rdn2 = EditableRDN((self.attr1.upper(), self.value1.lower())) + + # Immutable RDN's that are equal should hash to the same value. + # Mutable RDN's should not be hashable. + + self.assertEqual(immutable_rdn1, immutable_rdn2) + self.assertEqual(immutable_rdn1, mutable_rdn1) + self.assertEqual(immutable_rdn1, mutable_rdn2) + self.assertEqual(mutable_rdn1, immutable_rdn2) + + # Good, everyone's equal, now verify their hash values + + self.assertEqual(hash(immutable_rdn1), hash(immutable_rdn2)) + with self.assertRaises(TypeError): + hash(mutable_rdn1) + with self.assertRaises(TypeError): + hash(mutable_rdn2) + + def test_coerce(self): + # Coerce an immutable to a mutable + immutable_rdn3 = RDN(self.rdn3) + mutable_rdn3 = EditableRDN(immutable_rdn3) + self.assertEqual(mutable_rdn3, self.rdn3) + self.assertEqual(mutable_rdn3, immutable_rdn3) + + # Coerce a mutable to an immutable + mutable_rdn3 = EditableRDN(self.rdn3) + immutable_rdn3 = RDN(mutable_rdn3) + self.assertEqual(immutable_rdn3, self.rdn3) + self.assertEqual(immutable_rdn3, mutable_rdn3) + +class TestDN(unittest.TestCase): + def setUp(self): + # ava1 must sort before ava2 + self.attr1 = 'cn' + self.value1 = 'Bob' + self.str_ava1 = '%s=%s' % (self.attr1, self.value1) + self.ava1 = AVA(self.attr1, self.value1) + + self.str_rdn1 = '%s=%s' % (self.attr1, self.value1) + self.rdn1 = RDN((self.attr1, self.value1)) + + self.attr2 = 'ou' + self.value2 = 'people' + self.str_ava2 = '%s=%s' % (self.attr2, self.value2) + self.ava2 = AVA(self.attr2, self.value2) + + self.str_rdn2 = '%s=%s' % (self.attr2, self.value2) + self.rdn2 = RDN((self.attr2, self.value2)) + + self.str_dn1 = self.str_rdn1 + self.dn1 = DN(self.rdn1) + + self.str_dn2 = self.str_rdn2 + self.dn2 = DN(self.rdn2) + + self.str_dn3 = '%s,%s' % (self.str_rdn1, self.str_rdn2) + self.dn3 = DN(self.rdn1, self.rdn2) + + self.base_rdn1 = RDN(('dc', 'redhat')) + self.base_rdn2 = RDN(('dc', 'com')) + self.base_dn = DN(self.base_rdn1, self.base_rdn2) + + self.container_rdn1 = RDN(('cn', 'sudorules')) + self.container_rdn2 = RDN(('cn', 'sudo')) + self.container_dn = DN(self.container_rdn1, self.container_rdn2) + + self.base_container_dn = DN((self.attr1, self.value1), + self.container_dn, self.base_dn) + + + def assertExpectedClass(self, klass, obj, component): + self.assertIs(obj.__class__, expected_class(klass, component)) + + def test_create(self): + for DN_class in (DN, EditableDN): + # Create with single attr,value pair + dn1 = DN_class((self.attr1, self.value1)) + self.assertEqual(len(dn1), 1) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[0].attr, unicode) + self.assertIsInstance(dn1[0].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + + # Create with single attr,value pair passed as a tuple + dn1 = DN_class((self.attr1, self.value1)) + self.assertEqual(len(dn1), 1) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + + # Creation with multiple attr,value string pairs should fail + with self.assertRaises(ValueError): + dn1 = DN_class(self.attr1, self.value1, self.attr2, self.value2) + + # Create with multiple attr,value pairs passed as tuples & lists + dn1 = DN_class((self.attr1, self.value1), [self.attr2, self.value2]) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + self.assertEqual(dn1[1], self.rdn2) + + # Create with multiple attr,value pairs passed as tuple and RDN + dn1 = DN_class((self.attr1, self.value1), RDN((self.attr2, self.value2))) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + self.assertEqual(dn1[1], self.rdn2) + + # Create with multiple attr,value pairs but reverse + # constructor parameter ordering. RDN ordering should also be + # reversed because DN's are a ordered sequence of RDN's + dn1 = DN_class((self.attr2, self.value2), (self.attr1, self.value1)) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn2) + self.assertEqual(dn1[1], self.rdn1) + + # Create with single RDN object + dn1 = DN_class(self.rdn1) + self.assertEqual(len(dn1), 1) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + + # Create with multiple RDN objects, assure ordering is preserved. + dn1 = DN_class(self.rdn1, self.rdn2) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + self.assertEqual(dn1[1], self.rdn2) + + # Create with multiple RDN objects in different order, assure + # ordering is preserved. + dn1 = DN_class(self.rdn2, self.rdn1) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn2) + self.assertEqual(dn1[1], self.rdn1) + + # Create with single string with 1 RDN + dn1 = DN_class(self.str_rdn1) + self.assertEqual(len(dn1), 1) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + + # Create with single string with 2 RDN's + dn1 = DN_class(self.str_dn3) + self.assertEqual(len(dn1), 2) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + self.assertIsInstance(dn1[i].attr, unicode) + self.assertIsInstance(dn1[i].value, unicode) + self.assertEqual(dn1[0], self.rdn1) + self.assertEqual(dn1[1], self.rdn2) + + # Create with RDN, and 2 DN's (e.g. attr + container + base) + dn1 = DN_class((self.attr1, self.value1), self.container_dn, self.base_dn) + self.assertEqual(len(dn1), 5) + dn_str = ','.join([str(self.rdn1), + str(self.container_rdn1), str(self.container_rdn2), + str(self.base_rdn1), str(self.base_rdn2)]) + self.assertEqual(str(dn1), dn_str) + + def test_str(self): + for DN_class in (DN, EditableDN): + dn1 = DN_class(self.dn1) + dn2 = DN_class(self.dn2) + dn3 = DN_class(self.dn3) + + self.assertEqual(str(dn1), self.str_dn1) + self.assertIsInstance(str(dn1), str) + + self.assertEqual(str(dn2), self.str_dn2) + self.assertIsInstance(str(dn2), str) + + self.assertEqual(str(dn3), self.str_dn3) + self.assertIsInstance(str(dn3), str) + + def test_cmp(self): + for DN_class in (DN, EditableDN): + # Equality + dn1 = DN_class((self.attr1, self.value1)) + + self.assertTrue(dn1 == self.dn1) + self.assertFalse(dn1 != self.dn1) + + self.assertTrue(dn1 == self.str_dn1) + self.assertFalse(dn1 != self.str_dn1) + + result = cmp(dn1, self.dn1) + self.assertEqual(result, 0) + + # Make dn1's attr greater + if DN_class.is_mutable: + dn1[0].attr = self.attr1 + "1" + else: + with self.assertRaises(AttributeError): + dn1[0].attr = self.attr1 + "1" + dn1 = DN_class((self.attr1 + "1", self.value1)) + + self.assertFalse(dn1 == self.dn1) + self.assertTrue(dn1 != self.dn1) + + result = cmp(dn1, self.dn1) + self.assertEqual(result, 1) + + result = cmp(self.dn1, dn1) + self.assertEqual(result, -1) + + # Reset dn1's attr, should be equal again + if DN_class.is_mutable: + dn1[0].attr = self.attr1 + else: + with self.assertRaises(AttributeError): + dn1[0].attr = self.attr1 + dn1 = DN_class((self.attr1, self.value1)) + + result = cmp(dn1, self.dn1) + self.assertEqual(result, 0) + + # Make dn1's value greater + # attr will be equal, this tests secondary comparision component + if DN_class.is_mutable: + dn1[0].value = self.value1 + "1" + else: + with self.assertRaises(AttributeError): + dn1[0].value = self.value1 + "1" + dn1 = DN_class((self.attr1, self.value1 + "1")) + + result = cmp(dn1, self.dn1) + self.assertEqual(result, 1) + + result = cmp(self.dn1, dn1) + self.assertEqual(result, -1) + + # Make sure dn's with more rdn's are greater + result = cmp(self.dn1, self.dn3) + self.assertEqual(result, -1) + result = cmp(self.dn3, self.dn1) + self.assertEqual(result, 1) + + + # Test startswith, endswith + container_dn = DN_class(self.container_dn) + base_container_dn = DN_class(self.base_container_dn) + + self.assertTrue(base_container_dn.startswith(self.rdn1)) + self.assertTrue(base_container_dn.startswith(self.dn1)) + self.assertTrue(base_container_dn.startswith(self.dn1 + container_dn)) + self.assertFalse(base_container_dn.startswith(self.dn2)) + self.assertFalse(base_container_dn.startswith(self.rdn2)) + self.assertTrue(base_container_dn.startswith((self.dn1))) + self.assertTrue(base_container_dn.startswith((self.rdn1))) + self.assertFalse(base_container_dn.startswith((self.rdn2))) + self.assertTrue(base_container_dn.startswith((self.rdn2, self.rdn1))) + self.assertTrue(base_container_dn.startswith((self.dn1, self.dn2))) + + self.assertTrue(base_container_dn.endswith(self.base_dn)) + self.assertTrue(base_container_dn.endswith(container_dn + self.base_dn)) + self.assertFalse(base_container_dn.endswith(DN(self.base_rdn1))) + self.assertTrue(base_container_dn.endswith(DN(self.base_rdn2))) + self.assertTrue(base_container_dn.endswith((DN(self.base_rdn1), DN(self.base_rdn2)))) + + # Test "in" membership + self.assertTrue(self.container_rdn1 in container_dn) + self.assertTrue(container_dn in container_dn) + self.assertFalse(self.base_rdn1 in container_dn) + + self.assertTrue(self.container_rdn1 in base_container_dn) + self.assertTrue(container_dn in base_container_dn) + self.assertTrue(container_dn + self.base_dn in + base_container_dn) + self.assertTrue(self.dn1 + container_dn + self.base_dn in + base_container_dn) + self.assertTrue(self.dn1 + container_dn + self.base_dn == + base_container_dn) + + self.assertFalse(self.container_rdn1 in self.base_dn) + + def test_indexing(self): + for DN_class in (DN, EditableDN): + dn1 = DN_class(self.dn1) + dn2 = DN_class(self.dn2) + dn3 = DN_class(self.dn3) + + self.assertEqual(dn1[0], self.rdn1) + self.assertEqual(dn1[self.rdn1.attr], self.rdn1.value) + with self.assertRaises(KeyError): + dn1['foo'] + + self.assertEqual(dn2[0], self.rdn2) + self.assertEqual(dn2[self.rdn2.attr], self.rdn2.value) + with self.assertRaises(KeyError): + dn2['foo'] + + self.assertEqual(dn3[0], self.rdn1) + self.assertEqual(dn3[self.rdn1.attr], self.rdn1.value) + self.assertEqual(dn3[1], self.rdn2) + self.assertEqual(dn3[self.rdn2.attr], self.rdn2.value) + with self.assertRaises(KeyError): + dn3['foo'] + + with self.assertRaises(TypeError): + dn3[1.0] + + def test_assignments(self): + for DN_class in (DN, EditableDN): + dn_low = 0 + dn_high = 6 + + rdn_args = make_rdn_args(dn_low, dn_high, 'tuple', + default_rdn_attr_arg, default_rdn_value_arg) + dn1 = DN_class(*rdn_args) + + rdn_args = make_rdn_args(dn_low, dn_high, 'list', + default_rdn_attr_arg, default_rdn_value_arg) + dn2 = DN_class(*rdn_args) + + rdn_args = make_rdn_args(dn_low, dn_high, 'RDN', + default_rdn_attr_arg, default_rdn_value_arg) + dn3 = DN_class(*rdn_args) + + self.assertEqual(dn1, dn2) + self.assertEqual(dn1, dn3) + + for i in range(dn_low, dn_high): + attr = default_rdn_attr_arg(i) + value = default_rdn_value_arg(i) + + self.assertEqual(dn1[i].attr, attr) + self.assertEqual(dn1[i].value, value) + self.assertEqual(dn1[attr], value) + self.assertExpectedClass(DN_class, dn1, 'self') + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + + self.assertEqual(dn2[i].attr, attr) + self.assertEqual(dn2[i].value, value) + self.assertEqual(dn2[attr], value) + self.assertExpectedClass(DN_class, dn2, 'self') + self.assertExpectedClass(DN_class, dn2[i], 'RDN') + for j in range(0, len(dn2[i])): + self.assertExpectedClass(DN_class, dn2[i][j], 'AVA') + + self.assertEqual(dn3[i].attr, attr) + self.assertEqual(dn3[i].value, value) + self.assertEqual(dn3[attr], value) + self.assertExpectedClass(DN_class, dn3, 'self') + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + for j in range(0, len(dn3[i])): + self.assertExpectedClass(DN_class, dn3[i][j], 'AVA') + + + for i in range(dn_low, dn_high): + if i % 2: + orig_attr = default_rdn_attr_arg(i) + attr = alt_rdn_attr_arg(i) + value = alt_rdn_value_arg(i) + + if DN_class.is_mutable: + dn1[i] = attr, value + else: + with self.assertRaises(TypeError): + dn1[i] = attr, value + + if DN_class.is_mutable: + dn2[orig_attr] = (attr, value) + else: + with self.assertRaises(TypeError): + dn2[orig_attr] = (attr, value) + + if DN_class.is_mutable: + dn3[i] = RDN((attr, value)) + else: + with self.assertRaises(TypeError): + dn3[i] = RDN((attr, value)) + + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + + self.assertExpectedClass(DN_class, dn2, 'self') + for i in range(0, len(dn2)): + self.assertExpectedClass(DN_class, dn2[i], 'RDN') + for j in range(0, len(dn2[i])): + self.assertExpectedClass(DN_class, dn2[i][j], 'AVA') + + self.assertExpectedClass(DN_class, dn3, 'self') + for i in range(0, len(dn3)): + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + for j in range(0, len(dn3[i])): + self.assertExpectedClass(DN_class, dn3[i][j], 'AVA') + + + if DN_class.is_mutable: + self.assertEqual(dn1, dn2) + self.assertEqual(dn1, dn3) + + for i in range(dn_low, dn_high): + if i % 2: + attr = alt_rdn_attr_arg(i) + value = alt_rdn_value_arg(i) + else: + attr = default_rdn_attr_arg(i) + value = default_rdn_value_arg(i) + self.assertEqual(dn1[i].attr, attr) + self.assertEqual(dn1[i].value, value) + self.assertEqual(dn1[attr], value) + + # Slices + slice_low = 2 + slice_high = 4 + slice_interval = range(slice_low, slice_high) + + # Slices + # Assign via tuple + rdn_args = make_rdn_args(dn_low, dn_high, 'tuple', + default_rdn_attr_arg, default_rdn_value_arg) + dn = DN_class(*rdn_args) + + dn_slice = make_rdn_args(slice_low, slice_high, 'tuple', + alt_rdn_attr_arg, alt_rdn_value_arg) + + if DN_class.is_mutable: + dn[slice_low:slice_high] = dn_slice + for i in range(dn_low, dn_high): + if i in slice_interval: + attr = alt_rdn_attr_arg(i) + value = alt_rdn_value_arg(i) + else: + attr = default_rdn_attr_arg(i) + value = default_rdn_value_arg(i) + self.assertEqual(dn[i].attr, attr) + self.assertEqual(dn[i].value, value) + self.assertEqual(dn[attr], value) + + query_slice = dn[slice_low:slice_high] + for i, query_rdn in enumerate(query_slice): + slice_rdn = RDN(dn_slice[i]) + self.assertEqual(slice_rdn, query_rdn) + else: + with self.assertRaises(TypeError): + dn[slice_low:slice_high] = dn_slice + + + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + # insert + dn = DN_class(self.rdn2) + + if DN_class.is_mutable: + dn.insert(0, self.rdn1) + self.assertEqual(dn, self.dn3) + else: + with self.assertRaises(AttributeError): + dn.insert(0, self.rdn1) + + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + dn = DN_class(self.rdn1) + + if DN_class.is_mutable: + dn.insert(1, (self.attr2, self.value2)) + self.assertEqual(dn, self.dn3) + else: + with self.assertRaises(AttributeError): + dn.insert(1, (self.attr2, self.value2)) + + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + # Slices + # Assign via RDN + rdn_args = make_rdn_args(dn_low, dn_high, 'tuple', + default_rdn_attr_arg, default_rdn_value_arg) + dn = DN_class(*rdn_args) + + dn_slice = make_rdn_args(slice_low, slice_high, 'RDN', + alt_rdn_attr_arg, alt_rdn_value_arg) + + if DN_class.is_mutable: + dn[slice_low:slice_high] = dn_slice + for i in range(dn_low, dn_high): + if i in slice_interval: + attr = alt_rdn_attr_arg(i) + value = alt_rdn_value_arg(i) + else: + attr = default_rdn_attr_arg(i) + value = default_rdn_value_arg(i) + self.assertEqual(dn[i].value, value) + self.assertEqual(dn[attr], value) + + query_slice = dn[slice_low:slice_high] + for i, query_rdn in enumerate(query_slice): + slice_rdn = dn_slice[i] + self.assertEqual(slice_rdn, query_rdn) + else: + with self.assertRaises(TypeError): + dn[slice_low:slice_high] = dn_slice + + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + def test_iter(self): + for DN_class in (DN, EditableDN): + dn1 = DN_class(self.dn1) + dn2 = DN_class(self.dn2) + dn3 = DN_class(self.dn3) + + self.assertEqual(len(dn1), 1) + self.assertEqual(dn1[:], [self.rdn1]) + for i, ava in enumerate(dn1): + if i == 0: + self.assertEqual(ava, self.rdn1) + else: + self.fail("got iteration index %d, but len=%d" % (i, len(self.rdn1))) + + self.assertEqual(len(dn2), 1) + self.assertEqual(dn2[:], [self.rdn2]) + for i, ava in enumerate(dn2): + if i == 0: + self.assertEqual(ava, self.rdn2) + else: + self.fail("got iteration index %d, but len=%d" % (i, len(self.rdn2))) + + self.assertEqual(len(dn3), 2) + self.assertEqual(dn3[:], [self.rdn1, self.rdn2]) + for i, ava in enumerate(dn3): + if i == 0: + self.assertEqual(ava, self.rdn1) + elif i == 1: + self.assertEqual(ava, self.rdn2) + else: + self.fail("got iteration index %d, but len=%d" % (i, len(dn3))) + + + def test_concat(self): + for DN_class in (DN, EditableDN): + dn1 = DN_class((self.attr1, self.value1)) + dn2 = DN_class([self.attr2, self.value2]) + + # in-place addtion + + # Note: If __iadd__ is not available Python will emulate += by + # replacing the lhs object with the result of __add__ (if available). + # Thus += works for both immutable and mutable RDN,DN object, the only + # difference is an immutable without __iadd__ will have a different object + # on the lhs after the operator evaluates. + + dn1 += dn2 + self.assertEqual(dn1, self.dn3) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + + + dn1 = DN_class((self.attr1, self.value1)) + dn1 += self.rdn2 + self.assertEqual(dn1, self.dn3) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + + + dn1 = DN_class((self.attr1, self.value1)) + dn1 += self.dn2 + self.assertEqual(dn1, self.dn3) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + + + dn1 = DN_class((self.attr1, self.value1)) + dn1 += self.str_dn2 + self.assertEqual(dn1, self.dn3) + self.assertExpectedClass(DN_class, dn1, 'self') + for i in range(0, len(dn1)): + self.assertExpectedClass(DN_class, dn1[i], 'RDN') + for j in range(0, len(dn1[i])): + self.assertExpectedClass(DN_class, dn1[i][j], 'AVA') + + + # concatenation + dn1 = DN_class((self.attr1, self.value1)) + dn3 = dn1 + dn2 + self.assertEqual(dn3, self.dn3) + self.assertExpectedClass(DN_class, dn3, 'self') + for i in range(0, len(dn3)): + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + for j in range(0, len(dn3[i])): + self.assertExpectedClass(DN_class, dn3[i][j], 'AVA') + + + dn1 = DN_class((self.attr1, self.value1)) + dn3 = dn1 + self.rdn2 + self.assertEqual(dn3, self.dn3) + self.assertExpectedClass(DN_class, dn3, 'self') + for i in range(0, len(dn3)): + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + for j in range(0, len(dn3[i])): + self.assertExpectedClass(DN_class, dn3[i][j], 'AVA') + + dn3 = dn1 + self.str_rdn2 + self.assertEqual(dn3, self.dn3) + self.assertExpectedClass(DN_class, dn3, 'self') + for i in range(0, len(dn3)): + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + self.assertExpectedClass(DN_class, dn3[i][0], 'AVA') + + dn3 = dn1 + self.str_dn2 + self.assertEqual(dn3, self.dn3) + self.assertExpectedClass(DN_class, dn3, 'self') + self.assertExpectedClass(DN_class, dn3, 'self') + for i in range(0, len(dn3)): + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + for j in range(0, len(dn3[i])): + self.assertExpectedClass(DN_class, dn3[i][j], 'AVA') + + dn3 = dn1 + self.dn2 + self.assertEqual(dn3, self.dn3) + self.assertExpectedClass(DN_class, dn3, 'self') + self.assertExpectedClass(DN_class, dn3, 'self') + for i in range(0, len(dn3)): + self.assertExpectedClass(DN_class, dn3[i], 'RDN') + for j in range(0, len(dn3[i])): + self.assertExpectedClass(DN_class, dn3[i][j], 'AVA') + + def test_find(self): + for DN_class in (DN, EditableDN): + # -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 + dn = DN_class('t=0,t=1,cn=bob,t=3,t=4,t=5,cn=bob,t=7,t=8,t=9') + pat = DN_class('cn=bob') + + # forward + self.assertEqual(dn.find(pat), 2) + self.assertEqual(dn.find(pat, 1), 2) + self.assertEqual(dn.find(pat, 1, 3), 2) + self.assertEqual(dn.find(pat, 2, 3), 2) + self.assertEqual(dn.find(pat, 6), 6) + + self.assertEqual(dn.find(pat, 7), -1) + self.assertEqual(dn.find(pat, 1, 2), -1) + + with self.assertRaises(ValueError): + self.assertEqual(dn.index(pat, 7), -1) + with self.assertRaises(ValueError): + self.assertEqual(dn.index(pat, 1, 2), -1) + + # reverse + self.assertEqual(dn.rfind(pat), 6) + self.assertEqual(dn.rfind(pat, -4), 6) + self.assertEqual(dn.rfind(pat, 6), 6) + self.assertEqual(dn.rfind(pat, 6, 8), 6) + self.assertEqual(dn.rfind(pat, 6, 8), 6) + self.assertEqual(dn.rfind(pat, -8), 6) + self.assertEqual(dn.rfind(pat, -8, -4), 6) + self.assertEqual(dn.rfind(pat, -8, -5), 2) + + self.assertEqual(dn.rfind(pat, 7), -1) + self.assertEqual(dn.rfind(pat, -3), -1) + + with self.assertRaises(ValueError): + self.assertEqual(dn.rindex(pat, 7), -1) + with self.assertRaises(ValueError): + self.assertEqual(dn.rindex(pat, -3), -1) + + + def test_replace(self): + for DN_class in (DN, EditableDN): + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=8,t=9') + pat = DN('cn=bob') + replacement = DN('cn=bob') + + if DN_class.is_mutable: + n_replaced = dn.replace(pat, replacement) + self.assertEqual(n_replaced, 0) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + pat = DN('t=2') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,cn=bob,t=3,t=4,t=5,t=6,t=7,t=8,t=9') + n_replaced = dn.replace(pat, replacement) + self.assertEqual(n_replaced, 1) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,cn=bob,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + n_replaced = dn.replace(pat, replacement, 1) + self.assertEqual(n_replaced, 1) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement, 1) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,cn=bob,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + n_replaced = dn.replace(pat, replacement, 1) + self.assertEqual(n_replaced, 1) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement, 1) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + replacement = DN('cn=bob,ou=people') + + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,cn=bob,ou=people,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + n_replaced = dn.replace(pat, replacement, 1) + self.assertEqual(n_replaced, 1) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement, 1) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=2,t=9') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,cn=bob,ou=people,t=3,t=4,t=5,t=6,t=7,cn=bob,ou=people,t=9') + n_replaced = dn.replace(pat, replacement) + self.assertEqual(n_replaced, 2) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + pat = DN('t=3,t=4') + replacement = DN('cn=bob') + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=8,t=9') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,t=2,cn=bob,t=5,t=6,t=7,t=8,t=9') + n_replaced = dn.replace(pat, replacement) + self.assertEqual(n_replaced, 1) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + pat = DN('t=3,t=4') + replacement = DN('cn=bob,ou=people') + dn = DN_class('t=0,t=1,t=2,t=3,t=4,t=5,t=6,t=7,t=8,t=9') + if DN_class.is_mutable: + expected_dn = DN('t=0,t=1,t=2,cn=bob,ou=people,t=5,t=6,t=7,t=8,t=9') + n_replaced = dn.replace(pat, replacement) + self.assertEqual(n_replaced, 1) + self.assertEqual(dn, expected_dn) + else: + with self.assertRaises(AttributeError): + n_replaced = dn.replace(pat, replacement) + self.assertExpectedClass(DN_class, dn, 'self') + for i in range(0, len(dn)): + self.assertExpectedClass(DN_class, dn[i], 'RDN') + for j in range(0, len(dn[i])): + self.assertExpectedClass(DN_class, dn[i][j], 'AVA') + + def test_hashing(self): + # create DN's that are equal but differ in case + immutable_dn1 = DN((self.attr1.lower(), self.value1.upper())) + immutable_dn2 = DN((self.attr1.upper(), self.value1.lower())) + + mutable_dn1 = EditableDN((self.attr1.lower(), self.value1.upper())) + mutable_dn2 = EditableDN((self.attr1.upper(), self.value1.lower())) + + # Immutable DN's that are equal should hash to the same value. + # Mutable DN's should not be hashable. + + self.assertEqual(immutable_dn1, immutable_dn2) + self.assertEqual(immutable_dn1, mutable_dn1) + self.assertEqual(immutable_dn1, mutable_dn2) + self.assertEqual(mutable_dn1, immutable_dn2) + + # Good, everyone's equal, now verify their hash values + + self.assertEqual(hash(immutable_dn1), hash(immutable_dn2)) + with self.assertRaises(TypeError): + hash(mutable_dn1) + with self.assertRaises(TypeError): + hash(mutable_dn2) + + # Different immutable DN objects with the same value should + # map to 1 common key and 1 member in a set. The key and + # member are based on the object's value. + # + # Mutable DN objects should be unhashable. + + for DN_class in (DN, EditableDN): + dn1_a = DN_class(self.dn1) + dn1_b = DN_class(self.dn1) + + dn2_a = DN_class(self.dn2) + dn2_b = DN_class(self.dn2) + + dn3_a = DN_class(self.dn3) + dn3_b = DN_class(self.dn3) + + self.assertEqual(dn1_a, dn1_b) + self.assertEqual(dn2_a, dn2_b) + self.assertEqual(dn3_a, dn3_b) + + d = dict() + s = set() + + if DN_class.is_mutable: + with self.assertRaises(TypeError): + d[dn1_a] = str(dn1_a) + with self.assertRaises(TypeError): + d[dn1_b] = str(dn1_b) + with self.assertRaises(TypeError): + d[dn2_a] = str(dn2_a) + with self.assertRaises(TypeError): + d[dn2_b] = str(dn2_b) + + with self.assertRaises(TypeError): + s.add(dn1_a) + with self.assertRaises(TypeError): + s.add(dn1_b) + with self.assertRaises(TypeError): + s.add(dn2_a) + with self.assertRaises(TypeError): + s.add(dn2_b) + else: + d[dn1_a] = str(dn1_a) + d[dn1_b] = str(dn1_b) + d[dn2_a] = str(dn2_a) + d[dn2_b] = str(dn2_b) + + s.add(dn1_a) + s.add(dn1_b) + s.add(dn2_a) + s.add(dn2_b) + + self.assertEqual(len(d), 2) + self.assertEqual(len(s), 2) + self.assertEqual(sorted(d.keys()), sorted([dn1_a, dn2_a])) + self.assertEqual(sorted(s), sorted([dn1_a, dn2_a])) + + self.assertTrue(dn1_a in d) + self.assertTrue(dn1_b in d) + self.assertTrue(dn2_a in d) + self.assertTrue(dn2_b in d) + self.assertFalse(dn3_a in d) + self.assertFalse(dn3_b in d) + + self.assertTrue(d.has_key(dn1_a)) + self.assertTrue(d.has_key(dn1_b)) + self.assertTrue(d.has_key(dn2_a)) + self.assertTrue(d.has_key(dn2_b)) + self.assertFalse(d.has_key(dn3_a)) + self.assertFalse(d.has_key(dn3_b)) + + self.assertTrue(dn1_a in s) + self.assertTrue(dn1_b in s) + self.assertTrue(dn2_a in s) + self.assertTrue(dn2_b in s) + self.assertFalse(dn3_a in s) + self.assertFalse(dn3_b in s) + + def test_coerce(self): + # Coerce an immutable to a mutable + immutable_dn3 = DN(self.dn3) + mutable_dn3 = EditableDN(immutable_dn3) + self.assertEqual(mutable_dn3, self.dn3) + self.assertEqual(mutable_dn3, immutable_dn3) + + # Coerce a mutable to an immutable + mutable_dn3 = EditableDN(self.dn3) + immutable_dn3 = DN(mutable_dn3) + self.assertEqual(immutable_dn3, self.dn3) + self.assertEqual(immutable_dn3, mutable_dn3) + +class TestEscapes(unittest.TestCase): + def setUp(self): + self.privilege = 'R,W privilege' + self.dn_str_hex_escape = 'cn=R\\2cW privilege,cn=privileges,cn=pbac,dc=idm,dc=lab,dc=bos,dc=redhat,dc=com' + self.dn_str_backslash_escape = 'cn=R\\,W privilege,cn=privileges,cn=pbac,dc=idm,dc=lab,dc=bos,dc=redhat,dc=com' + + def test_escape(self): + for DN_class in (DN, EditableDN): + dn = DN_class(self.dn_str_hex_escape) + self.assertEqual(dn['cn'], self.privilege) + self.assertEqual(dn[0].value, self.privilege) + + dn = DN_class(self.dn_str_backslash_escape) + self.assertEqual(dn['cn'], self.privilege) + self.assertEqual(dn[0].value, self.privilege) + +class TestInternationalization(unittest.TestCase): + def setUp(self): + # Hello in Arabic + self.arabic_hello_utf8 = '\xd9\x85\xd9\x83\xd9\x8a\xd9\x84' + \ + '\xd8\xb9\x20\xd9\x85\xd8\xa7\xd9' + \ + '\x84\xd9\x91\xd8\xb3\xd9\x84\xd8\xa7' + + self.arabic_hello_unicode = self.arabic_hello_utf8.decode('utf-8') + + def test_i18n(self): + self.assertEqual(self.arabic_hello_utf8, + self.arabic_hello_unicode.encode('utf-8')) + + # AVA's + # test attr i18n + for AVA_class in (AVA, EditableAVA): + ava1 = AVA_class(self.arabic_hello_unicode, 'foo') + self.assertIsInstance(ava1.attr, unicode) + self.assertIsInstance(ava1.value, unicode) + self.assertEqual(ava1.attr, self.arabic_hello_unicode) + self.assertEqual(str(ava1), self.arabic_hello_utf8+'=foo') + + ava1 = AVA_class(self.arabic_hello_utf8, 'foo') + self.assertIsInstance(ava1.attr, unicode) + self.assertIsInstance(ava1.value, unicode) + self.assertEqual(ava1.attr, self.arabic_hello_unicode) + self.assertEqual(str(ava1), self.arabic_hello_utf8+'=foo') + + # test value i18n + ava1 = AVA_class('cn', self.arabic_hello_unicode) + self.assertIsInstance(ava1.attr, unicode) + self.assertIsInstance(ava1.value, unicode) + self.assertEqual(ava1.value, self.arabic_hello_unicode) + self.assertEqual(str(ava1), 'cn='+self.arabic_hello_utf8) + + ava1 = AVA_class('cn', self.arabic_hello_utf8) + self.assertIsInstance(ava1.attr, unicode) + self.assertIsInstance(ava1.value, unicode) + self.assertEqual(ava1.value, self.arabic_hello_unicode) + self.assertEqual(str(ava1), 'cn='+self.arabic_hello_utf8) + + # RDN's + # test attr i18n + for RDN_class in (RDN, EditableRDN): + rdn1 = RDN_class((self.arabic_hello_unicode, 'foo')) + self.assertIsInstance(rdn1.attr, unicode) + self.assertIsInstance(rdn1.value, unicode) + self.assertEqual(rdn1.attr, self.arabic_hello_unicode) + self.assertEqual(str(rdn1), self.arabic_hello_utf8+'=foo') + + rdn1 = RDN_class((self.arabic_hello_utf8, 'foo')) + self.assertIsInstance(rdn1.attr, unicode) + self.assertIsInstance(rdn1.value, unicode) + self.assertEqual(rdn1.attr, self.arabic_hello_unicode) + self.assertEqual(str(rdn1), self.arabic_hello_utf8+'=foo') + + # test value i18n + rdn1 = RDN_class(('cn', self.arabic_hello_unicode)) + self.assertIsInstance(rdn1.attr, unicode) + self.assertIsInstance(rdn1.value, unicode) + self.assertEqual(rdn1.value, self.arabic_hello_unicode) + self.assertEqual(str(rdn1), 'cn='+self.arabic_hello_utf8) + + rdn1 = RDN_class(('cn', self.arabic_hello_utf8)) + self.assertIsInstance(rdn1.attr, unicode) + self.assertIsInstance(rdn1.value, unicode) + self.assertEqual(rdn1.value, self.arabic_hello_unicode) + self.assertEqual(str(rdn1), 'cn='+self.arabic_hello_utf8) + + # DN's + # test attr i18n + for DN_class in (DN, EditableDN): + dn1 = DN_class((self.arabic_hello_unicode, 'foo')) + self.assertIsInstance(dn1[0].attr, unicode) + self.assertIsInstance(dn1[0].value, unicode) + self.assertEqual(dn1[0].attr, self.arabic_hello_unicode) + self.assertEqual(str(dn1), self.arabic_hello_utf8+'=foo') + + dn1 = DN_class((self.arabic_hello_utf8, 'foo')) + self.assertIsInstance(dn1[0].attr, unicode) + self.assertIsInstance(dn1[0].value, unicode) + self.assertEqual(dn1[0].attr, self.arabic_hello_unicode) + self.assertEqual(str(dn1), self.arabic_hello_utf8+'=foo') + + # test value i18n + dn1 = DN_class(('cn', self.arabic_hello_unicode)) + self.assertIsInstance(dn1[0].attr, unicode) + self.assertIsInstance(dn1[0].value, unicode) + self.assertEqual(dn1[0].value, self.arabic_hello_unicode) + self.assertEqual(str(dn1), 'cn='+self.arabic_hello_utf8) + + dn1 = DN_class(('cn', self.arabic_hello_utf8)) + self.assertIsInstance(dn1[0].attr, unicode) + self.assertIsInstance(dn1[0].value, unicode) + self.assertEqual(dn1[0].value, self.arabic_hello_unicode) + self.assertEqual(str(dn1), 'cn='+self.arabic_hello_utf8) + +if __name__ == '__main__': + unittest.main() diff --git a/ipatests/test_ipapython/test_ipautil.py b/ipatests/test_ipapython/test_ipautil.py new file mode 100644 index 000000000..650e1ce95 --- /dev/null +++ b/ipatests/test_ipapython/test_ipautil.py @@ -0,0 +1,69 @@ +# Authors: +# Jan Cholasta <jcholast@redhat.com> +# +# Copyright (C) 2011 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +""" +Test the `ipapython/ipautil.py` module. +""" + +import nose + +from ipapython import ipautil + +class CheckIPAddress: + def __init__(self, addr): + self.description = "Test IP address parsing and verification (%s)" % addr + + def __call__(self, addr, words=None, prefixlen=None): + try: + ip = ipautil.CheckedIPAddress(addr, match_local=False) + assert ip.words == words and ip.prefixlen == prefixlen + except: + assert words is None and prefixlen is None + +def test_ip_address(): + addrs = [ + ('10.11.12.13', (10, 11, 12, 13), 8), + ('10.11.12.13/14', (10, 11, 12, 13), 14), + ('10.11.12.13%zoneid',), + ('10.11.12.13%zoneid/14',), + ('10.11.12.1337',), + ('10.11.12.13/33',), + ('127.0.0.1',), + ('241.1.2.3',), + ('169.254.1.2',), + ('10.11.12.0/24',), + ('224.5.6.7',), + ('10.11.12.255/24',), + + ('2001::1', (0x2001, 0, 0, 0, 0, 0, 0, 1), 64), + ('2001::1/72', (0x2001, 0, 0, 0, 0, 0, 0, 1), 72), + ('2001::1%zoneid', (0x2001, 0, 0, 0, 0, 0, 0, 1), 64), + ('2001::1%zoneid/72',), + ('2001::1beef',), + ('2001::1/129',), + ('::1',), + ('6789::1',), + ('fe89::1',), + ('2001::/64',), + ('ff01::1',), + + ('junk',) + ] + + for addr in addrs: + yield (CheckIPAddress(addr[0]),) + addr diff --git a/ipatests/test_ipapython/test_keyring.py b/ipatests/test_ipapython/test_keyring.py new file mode 100644 index 000000000..568fd5ee1 --- /dev/null +++ b/ipatests/test_ipapython/test_keyring.py @@ -0,0 +1,147 @@ +# Authors: +# Rob Crittenden <rcritten@redhat.com> +# +# Copyright (C) 2012 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +""" +Test the `kernel_keyring.py` module. +""" + +from nose.tools import raises, assert_raises # pylint: disable=E0611 +from ipapython import kernel_keyring + +TEST_KEY = 'ipa_test' +TEST_VALUE = 'abc123' +UPDATE_VALUE = '123abc' + +SIZE_256 = 'abcdefgh' * 32 +SIZE_512 = 'abcdefgh' * 64 +SIZE_1024 = 'abcdefgh' * 128 + +class test_keyring(object): + """ + Test the kernel keyring interface + """ + + def setUp(self): + try: + kernel_keyring.del_key(TEST_KEY) + except ValueError: + pass + try: + kernel_keyring.del_key(SIZE_256) + except ValueError: + pass + + def test_01(self): + """ + Add a new key and value, then remove it + """ + kernel_keyring.add_key(TEST_KEY, TEST_VALUE) + result = kernel_keyring.read_key(TEST_KEY) + assert(result == TEST_VALUE) + + kernel_keyring.del_key(TEST_KEY) + + # Make sure it is gone + try: + result = kernel_keyring.read_key(TEST_KEY) + except ValueError, e: + assert e.message == 'key %s not found' % TEST_KEY + + def test_02(self): + """ + Delete a non_existent key + """ + try: + kernel_keyring.del_key(TEST_KEY) + raise AssertionError('key should not have been deleted') + except ValueError: + pass + + @raises(ValueError) + def test_03(self): + """ + Add a duplicate key + """ + kernel_keyring.add_key(TEST_KEY, TEST_VALUE) + kernel_keyring.add_key(TEST_KEY, TEST_VALUE) + + def test_04(self): + """ + Update the value in a key + """ + kernel_keyring.update_key(TEST_KEY, UPDATE_VALUE) + result = kernel_keyring.read_key(TEST_KEY) + assert(result == UPDATE_VALUE) + + # Now update it 10 times + for i in xrange(10): + kernel_keyring.update_key(TEST_KEY, 'test %d' % i) + result = kernel_keyring.read_key(TEST_KEY) + assert(result == 'test %d' % i) + + kernel_keyring.del_key(TEST_KEY) + + @raises(ValueError) + def test_05(self): + """ + Read a non-existent key + """ + result = kernel_keyring.read_key(TEST_KEY) + + def test_06(self): + """ + See if a key is available + """ + kernel_keyring.add_key(TEST_KEY, TEST_VALUE) + + result = kernel_keyring.has_key(TEST_KEY) + assert(result == True) + kernel_keyring.del_key(TEST_KEY) + + result = kernel_keyring.has_key(TEST_KEY) + assert(result == False) + + def test_07(self): + """ + Test a 256-byte key + """ + kernel_keyring.add_key(SIZE_256, TEST_VALUE) + result = kernel_keyring.read_key(SIZE_256) + assert(result == TEST_VALUE) + + kernel_keyring.del_key(SIZE_256) + + def test_08(self): + """ + Test 512-bytes of data + """ + kernel_keyring.add_key(TEST_KEY, SIZE_512) + result = kernel_keyring.read_key(TEST_KEY) + assert(result == SIZE_512) + + kernel_keyring.del_key(TEST_KEY) + + def test_09(self): + """ + Test 1k bytes of data + """ + kernel_keyring.add_key(TEST_KEY, SIZE_1024) + result = kernel_keyring.read_key(TEST_KEY) + assert(result == SIZE_1024) + + kernel_keyring.del_key(TEST_KEY) diff --git a/ipatests/test_ipapython/test_ssh.py b/ipatests/test_ipapython/test_ssh.py new file mode 100644 index 000000000..2640af50d --- /dev/null +++ b/ipatests/test_ipapython/test_ssh.py @@ -0,0 +1,76 @@ +# Authors: +# Jan Cholasta <jcholast@redhat.com> +# +# Copyright (C) 2011 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +""" +Test the `ipapython/ssh.py` module. +""" + +import base64 +import nose + +from ipapython import ssh + +class CheckPublicKey: + def __init__(self, pk): + self.description = "Test SSH public key parsing (%s)" % repr(pk) + + def __call__(self, pk, out): + try: + parsed = ssh.SSHPublicKey(pk) + assert parsed.openssh() == out + except Exception, e: + assert type(e) is out + +def test_public_key_parsing(): + b64 = 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDGAX3xAeLeaJggwTqMjxNwa6XHBUAikXPGMzEpVrlLDCZtv00djsFTBi38PkgxBJVkgRWMrcBsr/35lq7P6w8KGIwA8GI48Z0qBS2NBMJ2u9WQ2hjLN6GdMlo77O0uJY3251p12pCVIS/bHRSq8kHO2No8g7KA9fGGcagPfQH+ee3t7HUkpbQkFTmbPPN++r3V8oVUk5LxbryB3UIIVzNmcSIn3JrXynlvui4MixvrtX6zx+O/bBo68o8/eZD26QrahVbA09fivrn/4h3TM019Eu/c2jOdckfU3cHUV/3Tno5d6JicibyaoDDK7S/yjdn5jhaz8MSEayQvFkZkiF0L' + raw = base64.b64decode(b64) + openssh = 'ssh-rsa %s' % b64 + + pks = [ + ('\xff', UnicodeDecodeError), + + (raw, openssh), + ('\0\0\0\x04none', u'none AAAABG5vbmU='), + ('\0\0\0', ValueError), + ('\0\0\0\0', ValueError), + ('\0\0\0\x01', ValueError), + ('\0\0\0\x01\xff', ValueError), + + (b64, openssh), + (unicode(b64), openssh), + (u'\n%s\n\n' % b64, openssh), + (u'AAAABG5vbmU=', u'none AAAABG5vbmU='), + (u'AAAAB', ValueError), + + (openssh, openssh), + (unicode(openssh), openssh), + (u'none AAAABG5vbmU=', u'none AAAABG5vbmU='), + (u'\t \t ssh-rsa \t \t%s\t \tthis is a comment\t \t ' % b64, + u'%s this is a comment' % openssh), + (u'opt3,opt2="\tx ",opt1,opt2="\\"x " %s comment ' % openssh, + u'opt1,opt2="\\"x ",opt3 %s comment' % openssh), + (u'ssh-rsa\n%s' % b64, ValueError), + (u'ssh-rsa\t%s' % b64, ValueError), + (u'vanitas %s' % b64, ValueError), + (u'@opt %s' % openssh, ValueError), + (u'opt=val %s' % openssh, ValueError), + (u'opt, %s' % openssh, ValueError), + ] + + for pk in pks: + yield (CheckPublicKey(pk[0]),) + pk |