6 commits - kolabd.py pykolab/auth pykolab/wap_client tests/functional tests/__init__.py tests/test-000-imports.py tests/test-001-contact_reference.py tests/test-002-attendee.py tests/test-003-event.py tests/test-004-icalendar.py tests/test-005-timezone.py tests/unit

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Fri Jul 20 11:21:05 CEST 2012


 kolabd.py                                             |    2 
 pykolab/auth/ldap/__init__.py                         |    4 
 pykolab/wap_client/__init__.py                        |   35 +-
 tests/__init__.py                                     |    4 
 tests/functional/__init__.py                          |    4 
 tests/functional/test_kolabd/__init__.py              |    6 
 tests/functional/test_kolabd/test_001_user_sync.py    |   97 +++++++
 tests/functional/test_postfix/__init__.py             |    6 
 tests/functional/test_wap_client/__init__.py          |    6 
 tests/functional/test_wap_client/test_001_connect.py  |   38 +++
 tests/functional/test_wap_client/test_002_user_add.py |  101 ++++++++
 tests/functional/user_add.py                          |   68 +++++
 tests/test-000-imports.py                             |   23 -
 tests/test-001-contact_reference.py                   |   30 --
 tests/test-002-attendee.py                            |  105 --------
 tests/test-003-event.py                               |  108 --------
 tests/test-004-icalendar.py                           |  220 ------------------
 tests/test-005-timezone.py                            |   50 ----
 tests/unit/__init__.py                                |    4 
 tests/unit/test-000-imports.py                        |   23 +
 tests/unit/test-001-contact_reference.py              |   30 ++
 tests/unit/test-002-attendee.py                       |  105 ++++++++
 tests/unit/test-003-event.py                          |  108 ++++++++
 tests/unit/test-004-icalendar.py                      |  220 ++++++++++++++++++
 tests/unit/test-005-timezone.py                       |   50 ++++
 25 files changed, 893 insertions(+), 554 deletions(-)

New commits:
commit bde1f6b4d468c6f9e9f7586280c79736ca07ca91
Merge: a91c6f3 2c9f4f4
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 20 09:20:59 2012 +0200

    Merge branch 'master' of ssh://git.kolab.org/git/pykolab



commit a91c6f384436c910e7f98dfbec5765026fe4e0c5
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 20 09:20:19 2012 +0200

    Add some functional tests, move unit tests to separate directory

diff --git a/tests/__init__.py b/tests/__init__.py
index b5e7094..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,4 +0,0 @@
-import sys
-
-sys.path = [ '.', '..' ] + sys.path
-
diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py
index b9b864f..1020893 100644
--- a/tests/functional/__init__.py
+++ b/tests/functional/__init__.py
@@ -1 +1,4 @@
-from user_add import user_add
+import sys
+
+def setup_module():
+    sys.path = ['.'] + sys.path
diff --git a/tests/functional/test_auth/__init__.py b/tests/functional/test_auth/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional/test_auth/test_001_ldap.py b/tests/functional/test_auth/test_001_ldap.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional/test_auth/test_002_sql.py b/tests/functional/test_auth/test_002_sql.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional/test_auth/test_003_pam.py b/tests/functional/test_auth/test_003_pam.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional/test_auth/test_004_saslauthd.py b/tests/functional/test_auth/test_004_saslauthd.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional/test_kolabd/__init__.py b/tests/functional/test_kolabd/__init__.py
new file mode 100644
index 0000000..d7bf27f
--- /dev/null
+++ b/tests/functional/test_kolabd/__init__.py
@@ -0,0 +1,6 @@
+import pykolab
+
+def setup_package():
+    conf = pykolab.getConf()
+    conf.finalize_conf(fatal=False)
+
diff --git a/tests/functional/test_kolabd/test_001_user_sync.py b/tests/functional/test_kolabd/test_001_user_sync.py
new file mode 100644
index 0000000..2c3a144
--- /dev/null
+++ b/tests/functional/test_kolabd/test_001_user_sync.py
@@ -0,0 +1,97 @@
+import time
+import unittest
+
+import pykolab
+from pykolab import wap_client
+from pykolab.auth import Auth
+from pykolab.imap import IMAP
+
+conf = pykolab.getConf()
+
+class TestKolabDaemon(unittest.TestCase):
+    @classmethod
+    def setup_class(self, *args, **kw):
+        self.user = {
+                'local': 'john.doe',
+                'domain': 'example.org'
+            }
+
+        from tests.functional.user_add import user_add
+        user_add("John", "Doe")
+
+    def test_001_user_recipient_policy(self):
+        auth = Auth()
+        auth.connect()
+        recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user))
+        if hasattr(self, 'assertIsInstance'):
+            self.assertIsInstance(recipient, str)
+
+        self.assertEqual(recipient, "uid=doe,ou=People,dc=example,dc=org")
+
+        result = wap_client.user_info(recipient)
+        self.assertEqual(result['mail'], 'john.doe at example.org')
+        self.assertEqual(result['alias'], ['doe at example.org', 'j.doe at example.org'])
+
+    def test_002_user_mailbox_created(self):
+        time.sleep(2)
+        imap = IMAP()
+        imap.connect()
+
+        folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
+        self.assertEqual(len(folders), 1)
+
+    def test_003_user_additional_folders_created(self):
+        time.sleep(2)
+        imap = IMAP()
+        imap.connect()
+
+        exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
+
+        folders = imap.lm('user/%(local)s/*@%(domain)s' % (self.user))
+
+        self.assertEqual(len(folders), len(ac_folders.keys()))
+
+    def test_004_user_folder_annotations_set(self):
+        imap = IMAP()
+        imap.connect()
+
+        exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
+
+        folders = []
+        folders.extend(imap.lm('user/%(local)s@%(domain)s' % (self.user)))
+        folders.extend(imap.lm('user/%(local)s/*@%(domain)s' % (self.user)))
+
+        for folder in folders:
+            annotation = imap.getannotation(folder)
+            print annotation
+
+            folder_name = '/'.join(folder.split('/')[2:]).split('@')[0]
+            if ac_folders.has_key(folder_name):
+                if ac_folders[folder_name].has_key('annotations'):
+                    for _annotation in ac_folders[folder_name]['annotations'].keys():
+                        _annotation_value = ac_folders[folder_name]['annotations'][_annotation]
+                        self.assertTrue(annotation[annotation.keys().pop()].has_key(_annotation))
+                        self.assertEqual(_annotation_value, annotation[annotation.keys().pop()][_annotation])
+
+    def test_005_user_subscriptions(self):
+        imap = IMAP()
+        imap.connect(login=False)
+        login = conf.get('cyrus-imap', 'admin_login')
+        password = conf.get('cyrus-imap', 'admin_password')
+        imap.login_plain(login, password, 'john.doe at example.org')
+
+        folders = imap.lm()
+        self.assertTrue("INBOX" in folders)
+
+        #folders = imap.imap.lsub()
+        #self.assertTrue("Calendar" in folders)
+
+    def test_011_resource_add(self):
+        pass
+
+    def test_012_resource_mailbox_created(self):
+        pass
+
+    def test_013_resource_mailbox_annotation(self):
+        pass
+
diff --git a/tests/functional/test_postfix/__init__.py b/tests/functional/test_postfix/__init__.py
new file mode 100644
index 0000000..d7bf27f
--- /dev/null
+++ b/tests/functional/test_postfix/__init__.py
@@ -0,0 +1,6 @@
+import pykolab
+
+def setup_package():
+    conf = pykolab.getConf()
+    conf.finalize_conf(fatal=False)
+
diff --git a/tests/functional/test_wap_client/test_002_user_add.py b/tests/functional/test_wap_client/test_002_user_add.py
index 55d030d..84eeeb2 100644
--- a/tests/functional/test_wap_client/test_002_user_add.py
+++ b/tests/functional/test_wap_client/test_002_user_add.py
@@ -16,62 +16,15 @@ class TestUserAdd(unittest.TestCase):
         self.password = conf.get('ldap', 'bind_pw')
         self.domain = conf.get('kolab', 'primary_domain')
 
-        self.user_details = {
-                'givenname': "John",
-                'sn': "Doe",
-                'preferredlanguage': 'en_US',
-                'ou': 'ou=People,dc=example,dc=org',
-                'userpassword': 'Welcome2KolabSystems'
-            }
-
         self.user = {
                 'local': 'john.doe',
                 'domain': 'example.org'
             }
 
-    def test_001_user_add(self):
-        user_type_id = 0
-
-        result = wap_client.authenticate(self.login, self.password, self.domain)
-
-        user_types = wap_client.user_types_list()
-
-        for key in user_types['list'].keys():
-            if user_types['list'][key]['key'] == 'kolab':
-                user_type_id = key
-
-        self.assertTrue(user_type_id > 0, "No 'kolab' user type found")
-
-        user_type_info = user_types['list'][user_type_id]['attributes']
-
-        params = {
-                'user_type_id': user_type_id,
-            }
-
-        for attribute in user_type_info['form_fields'].keys():
-            attr_details = user_type_info['form_fields'][attribute]
-
-            if isinstance(attr_details, dict):
-                if not attr_details.has_key('optional') or attr_details['optional'] == False:
-                    self.assertTrue(self.user_details.has_key(attribute), "No attribute %r in user details" % (attribute))
-                    params[attribute] = self.user_details[attribute]
-            elif isinstance(attr_details, list):
-                self.assertTrue(self.user_details.has_key(attribute), "No attribute %r in user details" % (attribute))
-                params[attribute] = self.user_details[attribute]
-                
-        fvg_params = params
-        fvg_params['object_type'] = 'user'
-        fvg_params['type_id'] = user_type_id
-        fvg_params['attributes'] = [attr for attr in user_type_info['auto_form_fields'].keys() if not attr in params.keys()]
-
-        exec("retval = wap_client.form_value_generate(%r)" % (params))
-
-        for attribute in user_type_info['auto_form_fields'].keys():
-            params[attribute] = retval[attribute]
-
-        result = wap_client.user_add(params)
+        from tests.functional.user_add import user_add
+        user_add("John", "Doe")
 
-    def test_003_inbox_created(self):
+    def test_001_inbox_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
@@ -79,7 +32,7 @@ class TestUserAdd(unittest.TestCase):
         folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
         self.assertEqual(len(folders), 1)
 
-    def test_004_autocreate_folders_created(self):
+    def test_002_autocreate_folders_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
@@ -93,7 +46,7 @@ class TestUserAdd(unittest.TestCase):
 
         self.assertEqual(len(folders), len(ac_folders.keys()))
 
-    def test_005_folder_types_set(self):
+    def test_003_folder_types_set(self):
         imap = IMAP()
         imap.connect()
 
diff --git a/tests/functional/user_add.py b/tests/functional/user_add.py
new file mode 100644
index 0000000..0271d43
--- /dev/null
+++ b/tests/functional/user_add.py
@@ -0,0 +1,68 @@
+import pykolab
+
+from pykolab import wap_client
+
+conf = pykolab.getConf()
+
+def user_add(givenname, sn, preferredlanguage='en_US'):
+    if givenname == None:
+        raise Exception
+
+    if givenname == '':
+        raise Exception
+
+    if sn == None:
+        raise Exception
+
+    if sn == '':
+        raise Exception
+
+    user_details = {
+            'givenname': givenname,
+            'sn': sn,
+            'preferredlanguage': preferredlanguage,
+            'ou': 'ou=People,dc=example,dc=org',
+            'userpassword': 'Welcome2KolabSystems'
+        }
+
+    login = conf.get('ldap', 'bind_dn')
+    password = conf.get('ldap', 'bind_pw')
+    domain = conf.get('kolab', 'primary_domain')
+
+    user_type_id = 0
+
+    result = wap_client.authenticate(login, password, domain)
+
+    user_types = wap_client.user_types_list()
+
+    for key in user_types['list'].keys():
+        if user_types['list'][key]['key'] == 'kolab':
+            user_type_id = key
+
+    user_type_info = user_types['list'][user_type_id]['attributes']
+
+    params = {
+            'user_type_id': user_type_id,
+        }
+
+    for attribute in user_type_info['form_fields'].keys():
+        attr_details = user_type_info['form_fields'][attribute]
+
+        if isinstance(attr_details, dict):
+            if not attr_details.has_key('optional') or attr_details['optional'] == False:
+                params[attribute] = user_details[attribute]
+        elif isinstance(attr_details, list):
+            params[attribute] = user_details[attribute]
+
+    fvg_params = params
+    fvg_params['object_type'] = 'user'
+    fvg_params['type_id'] = user_type_id
+    fvg_params['attributes'] = [attr for attr in user_type_info['auto_form_fields'].keys() if not attr in params.keys()]
+
+    exec("retval = wap_client.form_value_generate(%r)" % (params))
+
+    for attribute in user_type_info['auto_form_fields'].keys():
+        params[attribute] = retval[attribute]
+
+    result = wap_client.user_add(params)
+
diff --git a/tests/test-000-imports.py b/tests/test-000-imports.py
deleted file mode 100644
index 17e1c57..0000000
--- a/tests/test-000-imports.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import unittest
-
-class TestImports(unittest.TestCase):
-    def test_pykolab(self):
-        import pykolab
-
-    def test_pykolab_xml(self):
-        import pykolab.xml
-
-    def test_pykolab_xml_attendee(self):
-        from pykolab.xml import Attendee
-
-    def test_pykolab_xml_contact(self):
-        from pykolab.xml import Contact
-
-    def test_pykolab_xml_contactReference(self):
-        from pykolab.xml import ContactReference
-
-    def test_pykolab_xml_event(self):
-        from pykolab.xml import Event
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/test-001-contact_reference.py b/tests/test-001-contact_reference.py
deleted file mode 100644
index 7c8f128..0000000
--- a/tests/test-001-contact_reference.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import datetime
-import unittest
-
-from pykolab.xml import ContactReference
-
-class TestEventXML(unittest.TestCase):
-    contact_reference = ContactReference("jane at doe.org")
-
-    def assertIsInstance(self, _value, _type):
-        if hasattr(unittest.TestCase, 'assertIsInstance'):
-            return unittest.TestCase.assertIsInstance(self, _value, _type)
-        else:
-            if (type(_value)) == _type:
-                return True
-            else:
-                raise AssertionError, "%s != %s" % (type(_value), _type)
-
-    def test_001_minimal(self):
-        self.assertIsInstance(self.contact_reference.__str__(), str)
-
-    def test_002_empty_name(self):
-        self.assertEqual(self.contact_reference.get_name(), "")
-
-    def test_003_set_name(self):
-        name = "Doe, Jane"
-        self.contact_reference.set_name(name)
-        self.assertEqual(self.contact_reference.get_name(), name)
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/test-002-attendee.py b/tests/test-002-attendee.py
deleted file mode 100644
index 4298761..0000000
--- a/tests/test-002-attendee.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import datetime
-import unittest
-
-from pykolab.xml import Attendee
-
-class TestEventXML(unittest.TestCase):
-    attendee = Attendee("jane at doe.org")
-
-    def assertIsInstance(self, _value, _type):
-        if hasattr(unittest.TestCase, 'assertIsInstance'):
-            return unittest.TestCase.assertIsInstance(self, _value, _type)
-        else:
-            if (type(_value)) == _type:
-                return True
-            else:
-                raise AssertionError, "%s != %s" % (type(_value), _type)
-
-    def test_001_minimal(self):
-        self.assertIsInstance(self.attendee.__str__(), str)
-
-    def test_002_empty_name(self):
-        self.assertEqual(self.attendee.get_name(), "")
-
-    def test_003_set_name(self):
-        name = "Doe, Jane"
-        self.attendee.set_name(name)
-        self.assertEqual(self.attendee.get_name(), name)
-
-    def test_004_default_participant_status(self):
-        self.assertEqual(self.attendee.get_participant_status(), 0)
-
-    def test_005_participant_status_map_length(self):
-        self.assertEqual(len(self.attendee.participant_status_map.keys()), 5)
-
-    def test_006_participant_status_map_forward_lookup(self):
-        # Forward lookups
-        self.assertEqual(self.attendee.participant_status_map["NEEDS-ACTION"], 0)
-        self.assertEqual(self.attendee.participant_status_map["ACCEPTED"], 1)
-        self.assertEqual(self.attendee.participant_status_map["DECLINED"], 2)
-        self.assertEqual(self.attendee.participant_status_map["TENTATIVE"], 3)
-        self.assertEqual(self.attendee.participant_status_map["DELEGATED"], 4)
-
-    def test_007_participant_status_map_reverse_lookup(self):
-        # Reverse lookups
-        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 0][0], "NEEDS-ACTION")
-        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 1][0], "ACCEPTED")
-        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 2][0], "DECLINED")
-        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 3][0], "TENTATIVE")
-        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 4][0], "DELEGATED")
-
-    def test_008_default_rsvp(self):
-        self.assertEqual(self.attendee.get_rsvp(), 0)
-
-    def test_009_rsvp_map_length(self):
-        self.assertEqual(len(self.attendee.rsvp_map.keys()), 2)
-
-    def test_010_rsvp_map_forward_lookup_boolean(self):
-        self.assertEqual(self.attendee.rsvp_map["TRUE"], True)
-        self.assertEqual(self.attendee.rsvp_map["FALSE"], False)
-
-    def test_011_rsvp_map_forward_lookup_integer(self):
-        self.assertEqual(self.attendee.rsvp_map["TRUE"], 1)
-        self.assertEqual(self.attendee.rsvp_map["FALSE"], 0)
-
-    def test_012_rsvp_map_reverse_lookup_boolean(self):
-        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == True][0], "TRUE")
-        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == False][0], "FALSE")
-
-    def test_013_rsvp_map_reverse_lookup_integer(self):
-        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == 1][0], "TRUE")
-        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == 0][0], "FALSE")
-
-    def test_014_default_role(self):
-        self.assertEqual(self.attendee.get_role(), 0)
-
-    def test_015_role_map_length(self):
-        self.assertEqual(len(self.attendee.role_map.keys()), 4)
-
-    def test_016_role_map_forward_lookup(self):
-        self.assertEqual(self.attendee.role_map["REQ-PARTICIPANT"], 0)
-        self.assertEqual(self.attendee.role_map["CHAIR"], 1)
-        self.assertEqual(self.attendee.role_map["OPTIONAL"], 2)
-        self.assertEqual(self.attendee.role_map["NONPARTICIPANT"], 3)
-
-    def test_017_role_map_reverse_lookup(self):
-        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 0][0], "REQ-PARTICIPANT")
-        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 1][0], "CHAIR")
-        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 2][0], "OPTIONAL")
-        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 3][0], "NONPARTICIPANT")
-
-    def test_015_cutype_map_length(self):
-        self.assertEqual(len(self.attendee.cutype_map.keys()), 3)
-
-    def test_016_cutype_map_forward_lookup(self):
-        self.assertEqual(self.attendee.cutype_map["GROUP"], 1)
-        self.assertEqual(self.attendee.cutype_map["INDIVIDUAL"], 2)
-        self.assertEqual(self.attendee.cutype_map["RESOURCE"], 3)
-
-    def test_017_cutype_map_reverse_lookup(self):
-        self.assertEqual([k for k,v in self.attendee.cutype_map.iteritems() if v == 1][0], "GROUP")
-        self.assertEqual([k for k,v in self.attendee.cutype_map.iteritems() if v == 2][0], "INDIVIDUAL")
-        self.assertEqual([k for k,v in self.attendee.cutype_map.iteritems() if v == 3][0], "RESOURCE")
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/test-003-event.py b/tests/test-003-event.py
deleted file mode 100644
index 8f76397..0000000
--- a/tests/test-003-event.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import datetime
-import pytz
-import sys
-import unittest
-
-from pykolab.xml import Attendee
-from pykolab.xml import Event
-from pykolab.xml import EventIntegrityError
-from pykolab.xml import InvalidAttendeeParticipantStatusError
-from pykolab.xml import InvalidEventDateError
-
-class TestEventXML(unittest.TestCase):
-    event = Event()
-
-    def assertIsInstance(self, _value, _type):
-        if hasattr(unittest.TestCase, 'assertIsInstance'):
-            return unittest.TestCase.assertIsInstance(self, _value, _type)
-        else:
-            if (type(_value)) == _type:
-                return True
-            else:
-                raise AssertionError, "%s != %s" % (type(_value), _type)
-
-    def test_000_no_start_date(self):
-        self.assertRaises(EventIntegrityError, self.event.__str__)
-
-    def test_001_minimal(self):
-        self.event.set_start(datetime.datetime.now(pytz.timezone("Europe/London")))
-        self.assertIsInstance(self.event.get_start(), datetime.datetime)
-        self.assertIsInstance(self.event.__str__(), str)
-
-    def test_002_attendees_list(self):
-        self.assertIsInstance(self.event.get_attendees(), list)
-
-    def test_003_attendees_no_default(self):
-        self.assertEqual(len(self.event.get_attendees()), 0)
-
-    def test_004_attendee_add(self):
-        self.event.add_attendee("john at doe.org")
-        self.assertIsInstance(self.event.get_attendees(), list)
-        self.assertEqual(len(self.event.get_attendees()), 1)
-
-    def test_005_attendee_add_name(self):
-        self.event.add_attendee("jane at doe.org", "Doe, Jane")
-        self.assertIsInstance(self.event.get_attendees(), list)
-        self.assertEqual(len(self.event.get_attendees()), 2)
-
-    def test_006_get_attendees(self):
-        self.assertEqual([x.get_email() for x in self.event.get_attendees()], ["john at doe.org", "jane at doe.org"])
-
-    def test_007_get_attendee_by_email(self):
-        self.assertIsInstance(self.event.get_attendee_by_email("jane at doe.org"), Attendee)
-        self.assertIsInstance(self.event.get_attendee("jane at doe.org"), Attendee)
-
-    def test_007_get_nonexistent_attendee_by_email(self):
-        self.assertRaises(ValueError, self.event.get_attendee_by_email, "nosuchattendee at invalid.domain")
-        self.assertRaises(ValueError, self.event.get_attendee, "nosuchattendee at invalid.domain")
-
-    def test_008_get_attendee_by_name(self):
-        self.assertIsInstance(self.event.get_attendee_by_name("Doe, Jane"), Attendee)
-        self.assertIsInstance(self.event.get_attendee("Doe, Jane"), Attendee)
-
-    def test_008_get_nonexistent_attendee_by_name(self):
-        self.assertRaises(ValueError, self.event.get_attendee_by_name, "Houdini, Harry")
-        self.assertRaises(ValueError, self.event.get_attendee, "Houdini, Harry")
-
-    def test_009_invalid_participant_status(self):
-        self.assertRaises(InvalidAttendeeParticipantStatusError, self.event.set_attendee_participant_status, "jane at doe.org", "INVALID")
-
-    def test_010_datetime_from_string(self):
-        self.assertRaises(InvalidEventDateError, self.event.set_start, "2012-05-23 11:58:00")
-
-    def test_011_attendee_equality(self):
-        self.assertEqual(self.event.get_attendee("jane at doe.org").get_email(), "jane at doe.org")
-
-    def test_012_delegate_new_attendee(self):
-        self.event.delegate("jane at doe.org", "max at imum.com")
-
-    def test_013_delegatee_is_now_attendee(self):
-        self.assertIsInstance(self.event.get_attendee("max at imum.com"), Attendee)
-
-    def test_014_delegate_attendee_adds(self):
-        self.assertEqual(len(self.event.get_attendee("jane at doe.org").get_delegated_to()), 1)
-        self.event.delegate("jane at doe.org", "john at doe.org")
-        self.assertEqual(len(self.event.get_attendee("jane at doe.org").get_delegated_to()), 2)
-
-    def test_015_timezone(self):
-        _tz = self.event.get_start()
-        self.assertIsInstance(_tz.tzinfo, datetime.tzinfo)
-
-    def test_016_start_with_timezone(self):
-        _start = datetime.datetime(2012, 05, 23, 11, 58, 00, tzinfo=pytz.timezone("Europe/Zurich"))
-        _start_utc = _start.astimezone(pytz.utc)
-        self.assertEqual(_start.__str__(), "2012-05-23 11:58:00+01:00")
-        self.assertEqual(_start_utc.__str__(), "2012-05-23 10:58:00+00:00")
-        self.event.set_start(_start)
-        self.assertIsInstance(_start.tzinfo, datetime.tzinfo)
-        self.assertEqual(_start.tzinfo, pytz.timezone("Europe/Zurich"))
-
-    def test_017_allday_without_timezone(self):
-        _start = datetime.date(2012, 05, 23)
-        self.assertEqual(_start.__str__(), "2012-05-23")
-        self.event.set_start(_start)
-        self.assertEqual(hasattr(_start,'tzinfo'), False)
-        self.assertEqual(self.event.get_start().__str__(), "2012-05-23")
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/tests/test-004-icalendar.py b/tests/test-004-icalendar.py
deleted file mode 100644
index 6033a33..0000000
--- a/tests/test-004-icalendar.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from email import message_from_string
-import icalendar
-import unittest
-
-class TestICalendar(unittest.TestCase):
-
-    def test_001_from_message_recurrence(self):
-        message = message_from_string("""Received: from localhost (localhost [127.0.0.1])
-    by kolab.example.org (Postfix) with ESMTP id 513B942E10
-    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:16 +0200 (CEST)
-X-Virus-Scanned: amavisd-new at example.org
-X-Spam-Flag: NO
-X-Spam-Score: 0.551
-X-Spam-Level:
-X-Spam-Status: No, score=0.551 tagged_above=-10 required=6.2
-    tests=[ALL_TRUSTED=-1, DNS_FROM_RFC_DSN=0.001,
-    NORMAL_HTTP_TO_IP=0.001, TVD_RCVD_IP=0.054, TVD_RCVD_IP4=1.495]
-    autolearn=no
-Received: from kolab.example.org ([127.0.0.1])
-    by localhost (kolab.example.org [127.0.0.1]) (amavisd-new, port 10024)
-    with ESMTP id KNJgv841fj-1 for <resource-collection-car at example.org>;
-    Fri, 13 Jul 2012 14:54:15 +0200 (CEST)
-Received: from 192.168.122.228 (localhost [127.0.0.1])
-    (Authenticated sender: john.doe at example.org)
-    by kolab.example.org (Postfix) with ESMTPSA id 0EBDA42E39
-    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:14 +0200 (CEST)
-MIME-Version: 1.0
-Content-Type: multipart/mixed;
- boundary="=_c8894dbdb8baeedacae836230e3436fd"
-From: "Doe, John" <john.doe at example.org>
-Date: Fri, 13 Jul 2012 13:54:14 +0100
-Message-ID: <240fe7ae7e139129e9eb95213c1016d7 at example.org>
-X-Sender: john.doe at example.org
-User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0
-To: resource-collection-car at example.org
-Subject: "test" has been updated
-
---=_c8894dbdb8baeedacae836230e3436fd
-Content-Transfer-Encoding: quoted-printable
-Content-Type: text/plain; charset=UTF-8;
- format=flowed
-
-*test*
-
-When: 2012-07-13 10:00 - 11:00 (Europe/London)
-
-Invitees: Doe, John <john.doe at example.org>,=20
-resource-collection-car at example.org
-
-Please find attached an iCalendar file with the updated event details which=
-=20
-you can import to your calendar application.
-
-In case your email client doesn't support iTip requests you can use the=20
-following link to either accept or decline this invitation:
-http://192.168.122.228/roundcubemail/?_task=3Dcalendar&_t=3D9febd7562df0f5b=
-ca7646a7bf6696801a394dd5a.cmVzb3VyY2UtY29sbGVjdGlvbi1jYXJAZXhhbXBsZS5vcmc%3=
-D.726d2f&_action=3Dattend
---=_c8894dbdb8baeedacae836230e3436fd
-Content-Type: text/calendar; charset=UTF-8; method=REQUEST;
- name=event.ics
-Content-Disposition: attachment;
- filename=event.ics
-Content-Transfer-Encoding: quoted-printable
-
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Roundcube Webmail 0.9-0.3.el6.kolab_3.0//NONSGML Calendar//EN
-CALSCALE:GREGORIAN
-METHOD:REQUEST
-BEGIN:VEVENT
-UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
-DTSTAMP:20120713T1254140
-DTSTART;TZID=3DEurope/London:20120713T100000
-DTEND;TZID=3DEurope/London:20120713T110000
-SUMMARY:test
-DESCRIPTION:test
-ORGANIZER;CN=3D"Doe, John":mailto:john.doe at example.org
-ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt=
-o:resourc
- e-collection-car at example.org
-RRULE:FREQ=3DWEEKLY;INTERVAL=3D1;BYDAY=3DFR
-TRANSP:OPAQUE
-END:VEVENT
-END:VCALENDAR
-
---=_c8894dbdb8baeedacae836230e3436fd--
-""")
-
-        self.assertTrue(message.is_multipart())
-
-        itip_methods = [ "REQUEST" ]
-
-        # Check each part
-        for part in message.walk():
-
-            # The iTip part MUST be Content-Type: text/calendar (RFC 6047,
-            # section 2.4)
-            if part.get_content_type() == "text/calendar":
-                if not part.get_param('method') in itip_methods:
-                    raise Exception, "method not interesting"
-
-                # Get the itip_payload
-                itip_payload = part.get_payload(decode=True)
-
-                # Python iCalendar prior to 3.0 uses "from_string".
-                if hasattr(icalendar.Calendar, 'from_ical'):
-                    cal = icalendar.Calendar.from_ical(itip_payload)
-                elif hasattr(icalendar.Calendar, 'from_string'):
-                    cal = icalendar.Calendar.from_string(itip_payload)
-                # If we can't read it, we're out
-                else:
-                    return []
-
-    def test_002_from_message(self):
-        message = message_from_string("""Received: from localhost (localhost [127.0.0.1])
-    by kolab.example.org (Postfix) with ESMTP id 513B942E10
-    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:16 +0200 (CEST)
-X-Virus-Scanned: amavisd-new at example.org
-X-Spam-Flag: NO
-X-Spam-Score: 0.551
-X-Spam-Level:
-X-Spam-Status: No, score=0.551 tagged_above=-10 required=6.2
-    tests=[ALL_TRUSTED=-1, DNS_FROM_RFC_DSN=0.001,
-    NORMAL_HTTP_TO_IP=0.001, TVD_RCVD_IP=0.054, TVD_RCVD_IP4=1.495]
-    autolearn=no
-Received: from kolab.example.org ([127.0.0.1])
-    by localhost (kolab.example.org [127.0.0.1]) (amavisd-new, port 10024)
-    with ESMTP id KNJgv841fj-1 for <resource-collection-car at example.org>;
-    Fri, 13 Jul 2012 14:54:15 +0200 (CEST)
-Received: from 192.168.122.228 (localhost [127.0.0.1])
-    (Authenticated sender: john.doe at example.org)
-    by kolab.example.org (Postfix) with ESMTPSA id 0EBDA42E39
-    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:14 +0200 (CEST)
-MIME-Version: 1.0
-Content-Type: multipart/mixed;
- boundary="=_c8894dbdb8baeedacae836230e3436fd"
-From: "Doe, John" <john.doe at example.org>
-Date: Fri, 13 Jul 2012 13:54:14 +0100
-Message-ID: <240fe7ae7e139129e9eb95213c1016d7 at example.org>
-X-Sender: john.doe at example.org
-User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0
-To: resource-collection-car at example.org
-Subject: "test" has been updated
-
---=_c8894dbdb8baeedacae836230e3436fd
-Content-Transfer-Encoding: quoted-printable
-Content-Type: text/plain; charset=UTF-8;
- format=flowed
-
-*test*
-
-When: 2012-07-13 10:00 - 11:00 (Europe/London)
-
-Invitees: Doe, John <john.doe at example.org>,=20
-resource-collection-car at example.org
-
-Please find attached an iCalendar file with the updated event details which=
-=20
-you can import to your calendar application.
-
-In case your email client doesn't support iTip requests you can use the=20
-following link to either accept or decline this invitation:
-http://192.168.122.228/roundcubemail/?_task=3Dcalendar&_t=3D9febd7562df0f5b=
-ca7646a7bf6696801a394dd5a.cmVzb3VyY2UtY29sbGVjdGlvbi1jYXJAZXhhbXBsZS5vcmc%3=
-D.726d2f&_action=3Dattend
---=_c8894dbdb8baeedacae836230e3436fd
-Content-Type: text/calendar; charset=UTF-8; method=REQUEST;
- name=event.ics
-Content-Disposition: attachment;
- filename=event.ics
-Content-Transfer-Encoding: quoted-printable
-
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Roundcube Webmail 0.9-0.3.el6.kolab_3.0//NONSGML Calendar//EN
-CALSCALE:GREGORIAN
-METHOD:REQUEST
-BEGIN:VEVENT
-UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
-DTSTAMP:20120713T1254140
-DTSTART;TZID=3DEurope/London:20120713T100000
-DTEND;TZID=3DEurope/London:20120713T110000
-SUMMARY:test
-DESCRIPTION:test
-ORGANIZER;CN=3D"Doe, John":mailto:john.doe at example.org
-ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt=
-o:resourc
- e-collection-car at example.org
-TRANSP:OPAQUE
-END:VEVENT
-END:VCALENDAR
-
---=_c8894dbdb8baeedacae836230e3436fd--
-""")
-
-        self.assertTrue(message.is_multipart())
-
-        itip_methods = [ "REQUEST" ]
-
-        # Check each part
-        for part in message.walk():
-
-            # The iTip part MUST be Content-Type: text/calendar (RFC 6047,
-            # section 2.4)
-            if part.get_content_type() == "text/calendar":
-                if not part.get_param('method') in itip_methods:
-                    raise Exception, "method not interesting"
-
-                # Get the itip_payload
-                itip_payload = part.get_payload(decode=True)
-
-                # Python iCalendar prior to 3.0 uses "from_string".
-                if hasattr(icalendar.Calendar, 'from_ical'):
-                    cal = icalendar.Calendar.from_ical(itip_payload)
-                elif hasattr(icalendar.Calendar, 'from_string'):
-                    cal = icalendar.Calendar.from_string(itip_payload)
-                # If we can't read it, we're out
-                else:
-                    return []
diff --git a/tests/test-005-timezone.py b/tests/test-005-timezone.py
deleted file mode 100644
index 9b12c66..0000000
--- a/tests/test-005-timezone.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import datetime
-import icalendar
-import pytz
-import unittest
-
-from pykolab.xml import Attendee
-from pykolab.xml import Event
-from pykolab.xml import EventIntegrityError
-from pykolab.xml import InvalidAttendeeParticipantStatusError
-from pykolab.xml import InvalidEventDateError
-from pykolab.xml import event_from_ical
-
-class TestTimezone(unittest.TestCase):
-
-    def test_001_timezone_conflict(self):
-        london = Event()
-        london.set_organizer("john.doe at example.org", "Doe, John")
-        london.add_attendee("resource-car-vw at example.org", cutype="RESOURCE")
-        london.set_start(datetime.datetime.now(pytz.timezone("Europe/London")))
-        london.set_end(datetime.datetime.now(pytz.timezone("Europe/London")))
-
-        zurich = Event()
-        zurich.set_organizer("john.doe at example.org", "Doe, John")
-        zurich.add_attendee("resource-car-vw at example.org", cutype="RESOURCE")
-        zurich.set_start(datetime.datetime.now(pytz.timezone("Europe/Zurich")))
-        zurich.set_end(datetime.datetime.now(pytz.timezone("Europe/Zurich")))
-
-        london_xml = london.__str__()
-        zurich_xml = zurich.__str__()
-
-        #print london_xml
-        #print zurich_xml
-
-        london_itip = london.as_string_itip()
-        zurich_itip = zurich.as_string_itip()
-
-        del london, zurich
-
-        #print london_itip
-        #print zurich_itip
-
-        london_cal = icalendar.Calendar.from_ical(london_itip)
-        london = event_from_ical(london_cal.walk('VEVENT')[0].to_ical())
-
-        zurich_cal = icalendar.Calendar.from_ical(zurich_itip)
-        zurich = event_from_ical(zurich_cal.walk('VEVENT')[0].to_ical())
-
-        self.assertEqual(london_xml, london.__str__())
-        self.assertEqual(zurich_xml, zurich.__str__())
-
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
new file mode 100644
index 0000000..b5e7094
--- /dev/null
+++ b/tests/unit/__init__.py
@@ -0,0 +1,4 @@
+import sys
+
+sys.path = [ '.', '..' ] + sys.path
+
diff --git a/tests/unit/test-000-imports.py b/tests/unit/test-000-imports.py
new file mode 100644
index 0000000..17e1c57
--- /dev/null
+++ b/tests/unit/test-000-imports.py
@@ -0,0 +1,23 @@
+import unittest
+
+class TestImports(unittest.TestCase):
+    def test_pykolab(self):
+        import pykolab
+
+    def test_pykolab_xml(self):
+        import pykolab.xml
+
+    def test_pykolab_xml_attendee(self):
+        from pykolab.xml import Attendee
+
+    def test_pykolab_xml_contact(self):
+        from pykolab.xml import Contact
+
+    def test_pykolab_xml_contactReference(self):
+        from pykolab.xml import ContactReference
+
+    def test_pykolab_xml_event(self):
+        from pykolab.xml import Event
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/unit/test-001-contact_reference.py b/tests/unit/test-001-contact_reference.py
new file mode 100644
index 0000000..7c8f128
--- /dev/null
+++ b/tests/unit/test-001-contact_reference.py
@@ -0,0 +1,30 @@
+import datetime
+import unittest
+
+from pykolab.xml import ContactReference
+
+class TestEventXML(unittest.TestCase):
+    contact_reference = ContactReference("jane at doe.org")
+
+    def assertIsInstance(self, _value, _type):
+        if hasattr(unittest.TestCase, 'assertIsInstance'):
+            return unittest.TestCase.assertIsInstance(self, _value, _type)
+        else:
+            if (type(_value)) == _type:
+                return True
+            else:
+                raise AssertionError, "%s != %s" % (type(_value), _type)
+
+    def test_001_minimal(self):
+        self.assertIsInstance(self.contact_reference.__str__(), str)
+
+    def test_002_empty_name(self):
+        self.assertEqual(self.contact_reference.get_name(), "")
+
+    def test_003_set_name(self):
+        name = "Doe, Jane"
+        self.contact_reference.set_name(name)
+        self.assertEqual(self.contact_reference.get_name(), name)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/unit/test-002-attendee.py b/tests/unit/test-002-attendee.py
new file mode 100644
index 0000000..4298761
--- /dev/null
+++ b/tests/unit/test-002-attendee.py
@@ -0,0 +1,105 @@
+import datetime
+import unittest
+
+from pykolab.xml import Attendee
+
+class TestEventXML(unittest.TestCase):
+    attendee = Attendee("jane at doe.org")
+
+    def assertIsInstance(self, _value, _type):
+        if hasattr(unittest.TestCase, 'assertIsInstance'):
+            return unittest.TestCase.assertIsInstance(self, _value, _type)
+        else:
+            if (type(_value)) == _type:
+                return True
+            else:
+                raise AssertionError, "%s != %s" % (type(_value), _type)
+
+    def test_001_minimal(self):
+        self.assertIsInstance(self.attendee.__str__(), str)
+
+    def test_002_empty_name(self):
+        self.assertEqual(self.attendee.get_name(), "")
+
+    def test_003_set_name(self):
+        name = "Doe, Jane"
+        self.attendee.set_name(name)
+        self.assertEqual(self.attendee.get_name(), name)
+
+    def test_004_default_participant_status(self):
+        self.assertEqual(self.attendee.get_participant_status(), 0)
+
+    def test_005_participant_status_map_length(self):
+        self.assertEqual(len(self.attendee.participant_status_map.keys()), 5)
+
+    def test_006_participant_status_map_forward_lookup(self):
+        # Forward lookups
+        self.assertEqual(self.attendee.participant_status_map["NEEDS-ACTION"], 0)
+        self.assertEqual(self.attendee.participant_status_map["ACCEPTED"], 1)
+        self.assertEqual(self.attendee.participant_status_map["DECLINED"], 2)
+        self.assertEqual(self.attendee.participant_status_map["TENTATIVE"], 3)
+        self.assertEqual(self.attendee.participant_status_map["DELEGATED"], 4)
+
+    def test_007_participant_status_map_reverse_lookup(self):
+        # Reverse lookups
+        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 0][0], "NEEDS-ACTION")
+        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 1][0], "ACCEPTED")
+        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 2][0], "DECLINED")
+        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 3][0], "TENTATIVE")
+        self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 4][0], "DELEGATED")
+
+    def test_008_default_rsvp(self):
+        self.assertEqual(self.attendee.get_rsvp(), 0)
+
+    def test_009_rsvp_map_length(self):
+        self.assertEqual(len(self.attendee.rsvp_map.keys()), 2)
+
+    def test_010_rsvp_map_forward_lookup_boolean(self):
+        self.assertEqual(self.attendee.rsvp_map["TRUE"], True)
+        self.assertEqual(self.attendee.rsvp_map["FALSE"], False)
+
+    def test_011_rsvp_map_forward_lookup_integer(self):
+        self.assertEqual(self.attendee.rsvp_map["TRUE"], 1)
+        self.assertEqual(self.attendee.rsvp_map["FALSE"], 0)
+
+    def test_012_rsvp_map_reverse_lookup_boolean(self):
+        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == True][0], "TRUE")
+        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == False][0], "FALSE")
+
+    def test_013_rsvp_map_reverse_lookup_integer(self):
+        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == 1][0], "TRUE")
+        self.assertEqual([k for k,v in self.attendee.rsvp_map.iteritems() if v == 0][0], "FALSE")
+
+    def test_014_default_role(self):
+        self.assertEqual(self.attendee.get_role(), 0)
+
+    def test_015_role_map_length(self):
+        self.assertEqual(len(self.attendee.role_map.keys()), 4)
+
+    def test_016_role_map_forward_lookup(self):
+        self.assertEqual(self.attendee.role_map["REQ-PARTICIPANT"], 0)
+        self.assertEqual(self.attendee.role_map["CHAIR"], 1)
+        self.assertEqual(self.attendee.role_map["OPTIONAL"], 2)
+        self.assertEqual(self.attendee.role_map["NONPARTICIPANT"], 3)
+
+    def test_017_role_map_reverse_lookup(self):
+        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 0][0], "REQ-PARTICIPANT")
+        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 1][0], "CHAIR")
+        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 2][0], "OPTIONAL")
+        self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 3][0], "NONPARTICIPANT")
+
+    def test_015_cutype_map_length(self):
+        self.assertEqual(len(self.attendee.cutype_map.keys()), 3)
+
+    def test_016_cutype_map_forward_lookup(self):
+        self.assertEqual(self.attendee.cutype_map["GROUP"], 1)
+        self.assertEqual(self.attendee.cutype_map["INDIVIDUAL"], 2)
+        self.assertEqual(self.attendee.cutype_map["RESOURCE"], 3)
+
+    def test_017_cutype_map_reverse_lookup(self):
+        self.assertEqual([k for k,v in self.attendee.cutype_map.iteritems() if v == 1][0], "GROUP")
+        self.assertEqual([k for k,v in self.attendee.cutype_map.iteritems() if v == 2][0], "INDIVIDUAL")
+        self.assertEqual([k for k,v in self.attendee.cutype_map.iteritems() if v == 3][0], "RESOURCE")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py
new file mode 100644
index 0000000..8f76397
--- /dev/null
+++ b/tests/unit/test-003-event.py
@@ -0,0 +1,108 @@
+import datetime
+import pytz
+import sys
+import unittest
+
+from pykolab.xml import Attendee
+from pykolab.xml import Event
+from pykolab.xml import EventIntegrityError
+from pykolab.xml import InvalidAttendeeParticipantStatusError
+from pykolab.xml import InvalidEventDateError
+
+class TestEventXML(unittest.TestCase):
+    event = Event()
+
+    def assertIsInstance(self, _value, _type):
+        if hasattr(unittest.TestCase, 'assertIsInstance'):
+            return unittest.TestCase.assertIsInstance(self, _value, _type)
+        else:
+            if (type(_value)) == _type:
+                return True
+            else:
+                raise AssertionError, "%s != %s" % (type(_value), _type)
+
+    def test_000_no_start_date(self):
+        self.assertRaises(EventIntegrityError, self.event.__str__)
+
+    def test_001_minimal(self):
+        self.event.set_start(datetime.datetime.now(pytz.timezone("Europe/London")))
+        self.assertIsInstance(self.event.get_start(), datetime.datetime)
+        self.assertIsInstance(self.event.__str__(), str)
+
+    def test_002_attendees_list(self):
+        self.assertIsInstance(self.event.get_attendees(), list)
+
+    def test_003_attendees_no_default(self):
+        self.assertEqual(len(self.event.get_attendees()), 0)
+
+    def test_004_attendee_add(self):
+        self.event.add_attendee("john at doe.org")
+        self.assertIsInstance(self.event.get_attendees(), list)
+        self.assertEqual(len(self.event.get_attendees()), 1)
+
+    def test_005_attendee_add_name(self):
+        self.event.add_attendee("jane at doe.org", "Doe, Jane")
+        self.assertIsInstance(self.event.get_attendees(), list)
+        self.assertEqual(len(self.event.get_attendees()), 2)
+
+    def test_006_get_attendees(self):
+        self.assertEqual([x.get_email() for x in self.event.get_attendees()], ["john at doe.org", "jane at doe.org"])
+
+    def test_007_get_attendee_by_email(self):
+        self.assertIsInstance(self.event.get_attendee_by_email("jane at doe.org"), Attendee)
+        self.assertIsInstance(self.event.get_attendee("jane at doe.org"), Attendee)
+
+    def test_007_get_nonexistent_attendee_by_email(self):
+        self.assertRaises(ValueError, self.event.get_attendee_by_email, "nosuchattendee at invalid.domain")
+        self.assertRaises(ValueError, self.event.get_attendee, "nosuchattendee at invalid.domain")
+
+    def test_008_get_attendee_by_name(self):
+        self.assertIsInstance(self.event.get_attendee_by_name("Doe, Jane"), Attendee)
+        self.assertIsInstance(self.event.get_attendee("Doe, Jane"), Attendee)
+
+    def test_008_get_nonexistent_attendee_by_name(self):
+        self.assertRaises(ValueError, self.event.get_attendee_by_name, "Houdini, Harry")
+        self.assertRaises(ValueError, self.event.get_attendee, "Houdini, Harry")
+
+    def test_009_invalid_participant_status(self):
+        self.assertRaises(InvalidAttendeeParticipantStatusError, self.event.set_attendee_participant_status, "jane at doe.org", "INVALID")
+
+    def test_010_datetime_from_string(self):
+        self.assertRaises(InvalidEventDateError, self.event.set_start, "2012-05-23 11:58:00")
+
+    def test_011_attendee_equality(self):
+        self.assertEqual(self.event.get_attendee("jane at doe.org").get_email(), "jane at doe.org")
+
+    def test_012_delegate_new_attendee(self):
+        self.event.delegate("jane at doe.org", "max at imum.com")
+
+    def test_013_delegatee_is_now_attendee(self):
+        self.assertIsInstance(self.event.get_attendee("max at imum.com"), Attendee)
+
+    def test_014_delegate_attendee_adds(self):
+        self.assertEqual(len(self.event.get_attendee("jane at doe.org").get_delegated_to()), 1)
+        self.event.delegate("jane at doe.org", "john at doe.org")
+        self.assertEqual(len(self.event.get_attendee("jane at doe.org").get_delegated_to()), 2)
+
+    def test_015_timezone(self):
+        _tz = self.event.get_start()
+        self.assertIsInstance(_tz.tzinfo, datetime.tzinfo)
+
+    def test_016_start_with_timezone(self):
+        _start = datetime.datetime(2012, 05, 23, 11, 58, 00, tzinfo=pytz.timezone("Europe/Zurich"))
+        _start_utc = _start.astimezone(pytz.utc)
+        self.assertEqual(_start.__str__(), "2012-05-23 11:58:00+01:00")
+        self.assertEqual(_start_utc.__str__(), "2012-05-23 10:58:00+00:00")
+        self.event.set_start(_start)
+        self.assertIsInstance(_start.tzinfo, datetime.tzinfo)
+        self.assertEqual(_start.tzinfo, pytz.timezone("Europe/Zurich"))
+
+    def test_017_allday_without_timezone(self):
+        _start = datetime.date(2012, 05, 23)
+        self.assertEqual(_start.__str__(), "2012-05-23")
+        self.event.set_start(_start)
+        self.assertEqual(hasattr(_start,'tzinfo'), False)
+        self.assertEqual(self.event.get_start().__str__(), "2012-05-23")
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/unit/test-004-icalendar.py b/tests/unit/test-004-icalendar.py
new file mode 100644
index 0000000..6033a33
--- /dev/null
+++ b/tests/unit/test-004-icalendar.py
@@ -0,0 +1,220 @@
+from email import message_from_string
+import icalendar
+import unittest
+
+class TestICalendar(unittest.TestCase):
+
+    def test_001_from_message_recurrence(self):
+        message = message_from_string("""Received: from localhost (localhost [127.0.0.1])
+    by kolab.example.org (Postfix) with ESMTP id 513B942E10
+    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:16 +0200 (CEST)
+X-Virus-Scanned: amavisd-new at example.org
+X-Spam-Flag: NO
+X-Spam-Score: 0.551
+X-Spam-Level:
+X-Spam-Status: No, score=0.551 tagged_above=-10 required=6.2
+    tests=[ALL_TRUSTED=-1, DNS_FROM_RFC_DSN=0.001,
+    NORMAL_HTTP_TO_IP=0.001, TVD_RCVD_IP=0.054, TVD_RCVD_IP4=1.495]
+    autolearn=no
+Received: from kolab.example.org ([127.0.0.1])
+    by localhost (kolab.example.org [127.0.0.1]) (amavisd-new, port 10024)
+    with ESMTP id KNJgv841fj-1 for <resource-collection-car at example.org>;
+    Fri, 13 Jul 2012 14:54:15 +0200 (CEST)
+Received: from 192.168.122.228 (localhost [127.0.0.1])
+    (Authenticated sender: john.doe at example.org)
+    by kolab.example.org (Postfix) with ESMTPSA id 0EBDA42E39
+    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:14 +0200 (CEST)
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_c8894dbdb8baeedacae836230e3436fd"
+From: "Doe, John" <john.doe at example.org>
+Date: Fri, 13 Jul 2012 13:54:14 +0100
+Message-ID: <240fe7ae7e139129e9eb95213c1016d7 at example.org>
+X-Sender: john.doe at example.org
+User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0
+To: resource-collection-car at example.org
+Subject: "test" has been updated
+
+--=_c8894dbdb8baeedacae836230e3436fd
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain; charset=UTF-8;
+ format=flowed
+
+*test*
+
+When: 2012-07-13 10:00 - 11:00 (Europe/London)
+
+Invitees: Doe, John <john.doe at example.org>,=20
+resource-collection-car at example.org
+
+Please find attached an iCalendar file with the updated event details which=
+=20
+you can import to your calendar application.
+
+In case your email client doesn't support iTip requests you can use the=20
+following link to either accept or decline this invitation:
+http://192.168.122.228/roundcubemail/?_task=3Dcalendar&_t=3D9febd7562df0f5b=
+ca7646a7bf6696801a394dd5a.cmVzb3VyY2UtY29sbGVjdGlvbi1jYXJAZXhhbXBsZS5vcmc%3=
+D.726d2f&_action=3Dattend
+--=_c8894dbdb8baeedacae836230e3436fd
+Content-Type: text/calendar; charset=UTF-8; method=REQUEST;
+ name=event.ics
+Content-Disposition: attachment;
+ filename=event.ics
+Content-Transfer-Encoding: quoted-printable
+
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Roundcube Webmail 0.9-0.3.el6.kolab_3.0//NONSGML Calendar//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
+DTSTAMP:20120713T1254140
+DTSTART;TZID=3DEurope/London:20120713T100000
+DTEND;TZID=3DEurope/London:20120713T110000
+SUMMARY:test
+DESCRIPTION:test
+ORGANIZER;CN=3D"Doe, John":mailto:john.doe at example.org
+ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt=
+o:resourc
+ e-collection-car at example.org
+RRULE:FREQ=3DWEEKLY;INTERVAL=3D1;BYDAY=3DFR
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+
+--=_c8894dbdb8baeedacae836230e3436fd--
+""")
+
+        self.assertTrue(message.is_multipart())
+
+        itip_methods = [ "REQUEST" ]
+
+        # Check each part
+        for part in message.walk():
+
+            # The iTip part MUST be Content-Type: text/calendar (RFC 6047,
+            # section 2.4)
+            if part.get_content_type() == "text/calendar":
+                if not part.get_param('method') in itip_methods:
+                    raise Exception, "method not interesting"
+
+                # Get the itip_payload
+                itip_payload = part.get_payload(decode=True)
+
+                # Python iCalendar prior to 3.0 uses "from_string".
+                if hasattr(icalendar.Calendar, 'from_ical'):
+                    cal = icalendar.Calendar.from_ical(itip_payload)
+                elif hasattr(icalendar.Calendar, 'from_string'):
+                    cal = icalendar.Calendar.from_string(itip_payload)
+                # If we can't read it, we're out
+                else:
+                    return []
+
+    def test_002_from_message(self):
+        message = message_from_string("""Received: from localhost (localhost [127.0.0.1])
+    by kolab.example.org (Postfix) with ESMTP id 513B942E10
+    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:16 +0200 (CEST)
+X-Virus-Scanned: amavisd-new at example.org
+X-Spam-Flag: NO
+X-Spam-Score: 0.551
+X-Spam-Level:
+X-Spam-Status: No, score=0.551 tagged_above=-10 required=6.2
+    tests=[ALL_TRUSTED=-1, DNS_FROM_RFC_DSN=0.001,
+    NORMAL_HTTP_TO_IP=0.001, TVD_RCVD_IP=0.054, TVD_RCVD_IP4=1.495]
+    autolearn=no
+Received: from kolab.example.org ([127.0.0.1])
+    by localhost (kolab.example.org [127.0.0.1]) (amavisd-new, port 10024)
+    with ESMTP id KNJgv841fj-1 for <resource-collection-car at example.org>;
+    Fri, 13 Jul 2012 14:54:15 +0200 (CEST)
+Received: from 192.168.122.228 (localhost [127.0.0.1])
+    (Authenticated sender: john.doe at example.org)
+    by kolab.example.org (Postfix) with ESMTPSA id 0EBDA42E39
+    for <resource-collection-car at example.org>; Fri, 13 Jul 2012 14:54:14 +0200 (CEST)
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_c8894dbdb8baeedacae836230e3436fd"
+From: "Doe, John" <john.doe at example.org>
+Date: Fri, 13 Jul 2012 13:54:14 +0100
+Message-ID: <240fe7ae7e139129e9eb95213c1016d7 at example.org>
+X-Sender: john.doe at example.org
+User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0
+To: resource-collection-car at example.org
+Subject: "test" has been updated
+
+--=_c8894dbdb8baeedacae836230e3436fd
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain; charset=UTF-8;
+ format=flowed
+
+*test*
+
+When: 2012-07-13 10:00 - 11:00 (Europe/London)
+
+Invitees: Doe, John <john.doe at example.org>,=20
+resource-collection-car at example.org
+
+Please find attached an iCalendar file with the updated event details which=
+=20
+you can import to your calendar application.
+
+In case your email client doesn't support iTip requests you can use the=20
+following link to either accept or decline this invitation:
+http://192.168.122.228/roundcubemail/?_task=3Dcalendar&_t=3D9febd7562df0f5b=
+ca7646a7bf6696801a394dd5a.cmVzb3VyY2UtY29sbGVjdGlvbi1jYXJAZXhhbXBsZS5vcmc%3=
+D.726d2f&_action=3Dattend
+--=_c8894dbdb8baeedacae836230e3436fd
+Content-Type: text/calendar; charset=UTF-8; method=REQUEST;
+ name=event.ics
+Content-Disposition: attachment;
+ filename=event.ics
+Content-Transfer-Encoding: quoted-printable
+
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Roundcube Webmail 0.9-0.3.el6.kolab_3.0//NONSGML Calendar//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
+DTSTAMP:20120713T1254140
+DTSTART;TZID=3DEurope/London:20120713T100000
+DTEND;TZID=3DEurope/London:20120713T110000
+SUMMARY:test
+DESCRIPTION:test
+ORGANIZER;CN=3D"Doe, John":mailto:john.doe at example.org
+ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt=
+o:resourc
+ e-collection-car at example.org
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+
+--=_c8894dbdb8baeedacae836230e3436fd--
+""")
+
+        self.assertTrue(message.is_multipart())
+
+        itip_methods = [ "REQUEST" ]
+
+        # Check each part
+        for part in message.walk():
+
+            # The iTip part MUST be Content-Type: text/calendar (RFC 6047,
+            # section 2.4)
+            if part.get_content_type() == "text/calendar":
+                if not part.get_param('method') in itip_methods:
+                    raise Exception, "method not interesting"
+
+                # Get the itip_payload
+                itip_payload = part.get_payload(decode=True)
+
+                # Python iCalendar prior to 3.0 uses "from_string".
+                if hasattr(icalendar.Calendar, 'from_ical'):
+                    cal = icalendar.Calendar.from_ical(itip_payload)
+                elif hasattr(icalendar.Calendar, 'from_string'):
+                    cal = icalendar.Calendar.from_string(itip_payload)
+                # If we can't read it, we're out
+                else:
+                    return []
diff --git a/tests/unit/test-005-timezone.py b/tests/unit/test-005-timezone.py
new file mode 100644
index 0000000..9b12c66
--- /dev/null
+++ b/tests/unit/test-005-timezone.py
@@ -0,0 +1,50 @@
+import datetime
+import icalendar
+import pytz
+import unittest
+
+from pykolab.xml import Attendee
+from pykolab.xml import Event
+from pykolab.xml import EventIntegrityError
+from pykolab.xml import InvalidAttendeeParticipantStatusError
+from pykolab.xml import InvalidEventDateError
+from pykolab.xml import event_from_ical
+
+class TestTimezone(unittest.TestCase):
+
+    def test_001_timezone_conflict(self):
+        london = Event()
+        london.set_organizer("john.doe at example.org", "Doe, John")
+        london.add_attendee("resource-car-vw at example.org", cutype="RESOURCE")
+        london.set_start(datetime.datetime.now(pytz.timezone("Europe/London")))
+        london.set_end(datetime.datetime.now(pytz.timezone("Europe/London")))
+
+        zurich = Event()
+        zurich.set_organizer("john.doe at example.org", "Doe, John")
+        zurich.add_attendee("resource-car-vw at example.org", cutype="RESOURCE")
+        zurich.set_start(datetime.datetime.now(pytz.timezone("Europe/Zurich")))
+        zurich.set_end(datetime.datetime.now(pytz.timezone("Europe/Zurich")))
+
+        london_xml = london.__str__()
+        zurich_xml = zurich.__str__()
+
+        #print london_xml
+        #print zurich_xml
+
+        london_itip = london.as_string_itip()
+        zurich_itip = zurich.as_string_itip()
+
+        del london, zurich
+
+        #print london_itip
+        #print zurich_itip
+
+        london_cal = icalendar.Calendar.from_ical(london_itip)
+        london = event_from_ical(london_cal.walk('VEVENT')[0].to_ical())
+
+        zurich_cal = icalendar.Calendar.from_ical(zurich_itip)
+        zurich = event_from_ical(zurich_cal.walk('VEVENT')[0].to_ical())
+
+        self.assertEqual(london_xml, london.__str__())
+        self.assertEqual(zurich_xml, zurich.__str__())
+


commit 30ea265c66630f7961aa957ea8448086977f7a4f
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Jul 18 19:11:27 2012 +0200

    Add some tests for the client-side of the WAP API.

diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py
new file mode 100644
index 0000000..b9b864f
--- /dev/null
+++ b/tests/functional/__init__.py
@@ -0,0 +1 @@
+from user_add import user_add
diff --git a/tests/functional/test_wap_client/__init__.py b/tests/functional/test_wap_client/__init__.py
new file mode 100644
index 0000000..d7bf27f
--- /dev/null
+++ b/tests/functional/test_wap_client/__init__.py
@@ -0,0 +1,6 @@
+import pykolab
+
+def setup_package():
+    conf = pykolab.getConf()
+    conf.finalize_conf(fatal=False)
+
diff --git a/tests/functional/test_wap_client/test_001_connect.py b/tests/functional/test_wap_client/test_001_connect.py
new file mode 100644
index 0000000..42b8aa3
--- /dev/null
+++ b/tests/functional/test_wap_client/test_001_connect.py
@@ -0,0 +1,38 @@
+import unittest
+
+import pykolab
+from pykolab import wap_client
+
+class TestConnect(unittest.TestCase):
+    @classmethod
+    def setup_class(self, *args, **kw):
+        conf = pykolab.getConf()
+        conf.finalize_conf(fatal=False)
+
+        self.login = conf.get('ldap', 'bind_dn')
+        self.password = conf.get('ldap', 'bind_pw')
+        self.domain = conf.get('kolab', 'primary_domain')
+
+    def test_001_authenticate(self):
+        result = wap_client.authenticate(self.login, self.password, self.domain)
+
+    def test_002_response_ok(self):
+        result = wap_client.request_raw('POST', 'domains.list')
+        self.assertTrue(result.has_key('status'))
+        self.assertTrue(result.has_key('result'))
+        self.assertEqual(result['status'], "OK")
+
+    def test_003_response_fail(self):
+        result = wap_client.request_raw('POST', 'service.method')
+        self.assertTrue(result.has_key('status'))
+        self.assertTrue(result.has_key('reason'))
+        self.assertTrue(result.has_key('code'))
+        self.assertEqual(result['status'], "ERROR")
+        self.assertEqual(result['reason'], "Unknown service")
+        self.assertEqual(result['code'], 400)
+
+    def test_003_domains_list(self):
+        result = wap_client.domains_list()
+        self.assertTrue(result.has_key('count'))
+        self.assertTrue(result.has_key('list'))
+        self.assertEqual(result['count'], len(result['list']))
diff --git a/tests/functional/test_wap_client/test_002_user_add.py b/tests/functional/test_wap_client/test_002_user_add.py
new file mode 100644
index 0000000..55d030d
--- /dev/null
+++ b/tests/functional/test_wap_client/test_002_user_add.py
@@ -0,0 +1,148 @@
+import time
+import unittest
+
+import pykolab
+from pykolab import wap_client
+from pykolab.imap import IMAP
+
+conf = pykolab.getConf()
+
+class TestUserAdd(unittest.TestCase):
+    @classmethod
+    def setup_class(self, *args, **kw):
+        conf.finalize_conf(fatal=False)
+
+        self.login = conf.get('ldap', 'bind_dn')
+        self.password = conf.get('ldap', 'bind_pw')
+        self.domain = conf.get('kolab', 'primary_domain')
+
+        self.user_details = {
+                'givenname': "John",
+                'sn': "Doe",
+                'preferredlanguage': 'en_US',
+                'ou': 'ou=People,dc=example,dc=org',
+                'userpassword': 'Welcome2KolabSystems'
+            }
+
+        self.user = {
+                'local': 'john.doe',
+                'domain': 'example.org'
+            }
+
+    def test_001_user_add(self):
+        user_type_id = 0
+
+        result = wap_client.authenticate(self.login, self.password, self.domain)
+
+        user_types = wap_client.user_types_list()
+
+        for key in user_types['list'].keys():
+            if user_types['list'][key]['key'] == 'kolab':
+                user_type_id = key
+
+        self.assertTrue(user_type_id > 0, "No 'kolab' user type found")
+
+        user_type_info = user_types['list'][user_type_id]['attributes']
+
+        params = {
+                'user_type_id': user_type_id,
+            }
+
+        for attribute in user_type_info['form_fields'].keys():
+            attr_details = user_type_info['form_fields'][attribute]
+
+            if isinstance(attr_details, dict):
+                if not attr_details.has_key('optional') or attr_details['optional'] == False:
+                    self.assertTrue(self.user_details.has_key(attribute), "No attribute %r in user details" % (attribute))
+                    params[attribute] = self.user_details[attribute]
+            elif isinstance(attr_details, list):
+                self.assertTrue(self.user_details.has_key(attribute), "No attribute %r in user details" % (attribute))
+                params[attribute] = self.user_details[attribute]
+                
+        fvg_params = params
+        fvg_params['object_type'] = 'user'
+        fvg_params['type_id'] = user_type_id
+        fvg_params['attributes'] = [attr for attr in user_type_info['auto_form_fields'].keys() if not attr in params.keys()]
+
+        exec("retval = wap_client.form_value_generate(%r)" % (params))
+
+        for attribute in user_type_info['auto_form_fields'].keys():
+            params[attribute] = retval[attribute]
+
+        result = wap_client.user_add(params)
+
+    def test_003_inbox_created(self):
+        time.sleep(2)
+        imap = IMAP()
+        imap.connect()
+
+        folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
+        self.assertEqual(len(folders), 1)
+
+    def test_004_autocreate_folders_created(self):
+        time.sleep(2)
+        imap = IMAP()
+        imap.connect()
+
+        exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
+
+        folders = imap.lm('user/%(local)s/*@%(domain)s' % (self.user))
+
+        print folders
+        print ac_folders.keys()
+
+        self.assertEqual(len(folders), len(ac_folders.keys()))
+
+    def test_005_folder_types_set(self):
+        imap = IMAP()
+        imap.connect()
+
+        exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
+
+        folders = []
+        folders.extend(imap.lm('user/%(local)s@%(domain)s' % (self.user)))
+        folders.extend(imap.lm('user/%(local)s/*@%(domain)s' % (self.user)))
+
+        for folder in folders:
+            annotation = imap.getannotation(folder)
+            print annotation
+
+            folder_name = '/'.join(folder.split('/')[2:]).split('@')[0]
+            if ac_folders.has_key(folder_name):
+                if ac_folders[folder_name].has_key('annotations'):
+                    for _annotation in ac_folders[folder_name]['annotations'].keys():
+                        _annotation_value = ac_folders[folder_name]['annotations'][_annotation]
+                        self.assertTrue(annotation[annotation.keys().pop()].has_key(_annotation))
+                        self.assertEqual(_annotation_value, annotation[annotation.keys().pop()][_annotation])
+
+    @classmethod
+    def teardown_class(self):
+        time.sleep(2)
+
+        res_attr = conf.get('cyrus-sasl', 'result_attribute')
+
+        exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
+        expected_number_of_folders = len(ac_folders.keys()) + 1
+
+        users = []
+
+        result = wap_client.users_list()
+        for user in result['list'].keys():
+            user_info = wap_client.user_info(user)
+            users.append(user_info)
+            result = wap_client.user_delete({'user': user})
+
+        imap = IMAP()
+        imap.connect()
+
+        for user in users:
+            if len(user[res_attr].split('@')) > 1:
+                localpart = user[res_attr].split('@')[0]
+                domain = user[res_attr].split('@')[1]
+
+            folders = []
+            folders.extend(imap.lm('user/%s' % (user[res_attr])))
+            folders.extend(imap.lm('user/%s/*@%s' % (localpart,domain)))
+
+            # Expect folders length to be 0
+


commit cdb841b97a6e0a563c9227e08c9001d58acf2658
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Jul 18 19:08:56 2012 +0200

    Accept user argument to user_info() function
    Add a request_raw() function and make the request() function use it
    Add function form_value_generate()
    Accept a domain argument to the authenticate function

diff --git a/pykolab/wap_client/__init__.py b/pykolab/wap_client/__init__.py
index 381e946..d5b0f50 100644
--- a/pykolab/wap_client/__init__.py
+++ b/pykolab/wap_client/__init__.py
@@ -41,9 +41,8 @@ session_id = None
 conn = None
 
 from connect import connect
-from request import request
 
-def authenticate(username=None, password=None):
+def authenticate(username=None, password=None, domain=None):
     global session_id
 
     conf_username = conf.get('ldap', 'service_bind_dn')
@@ -61,10 +60,14 @@ def authenticate(username=None, password=None):
     if password == None:
         password = utils.ask_question("Password", default=conf_password, password=True)
 
+    if domain == None:
+        domain = conf.get('kolab', 'primary_domain')
+
     params = json.dumps(
             {
                     'username': username,
-                    'password': password
+                    'password': password,
+                    'domain': domain
                 }
         )
 
@@ -123,6 +126,11 @@ def domains_capabilities():
 def domains_list():
     return request('GET', 'domains.list')
 
+def form_value_generate(params):
+    params = json.dumps(params)
+
+    return request('POST', 'form_value.generate', params)
+
 def get_group_input():
     group_types = group_types_list()
 
@@ -231,6 +239,15 @@ def groups_list():
     return request('GET', 'groups.list')
 
 def request(method, api_uri, params=None, headers={}):
+    response_data = request_raw(method, api_uri, params, headers)
+
+    if response_data['status'] == "OK":
+        del response_data['status']
+        return response_data['result']
+    else:
+        return response_data['result']
+
+def request_raw(method, api_uri, params=None, headers={}):
     global session_id
 
     if not session_id == None:
@@ -249,13 +266,8 @@ def request(method, api_uri, params=None, headers={}):
     except ValueError, e:
         # Some data is not JSON
         log.error(_("Response data is not JSON"))
-        sys.exit(1)
 
-    if response_data['status'] == "OK":
-        del response_data['status']
-        return response_data['result']
-    else:
-        return response_data['result']
+    return response_data
 
 def role_capabilities():
     return request('GET', 'role.capabilities')
@@ -410,8 +422,9 @@ def user_form_value_generate_userpassword(*args, **kw):
     result = form_value_generate_password()
     return { 'userpassword': result['password'] }
 
-def user_info():
-    user = utils.ask_question("User email address")
+def user_info(user=None):
+    if user == None:
+        user = utils.ask_question("User email address")
     user = request('GET', 'user.info?user=%s' % (user))
     return user
 


commit abb12c868b4cb138b8bbd1af67b1dd31fb883aa7
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Jul 18 19:08:09 2012 +0200

    Apply the correct sys.path extension

diff --git a/kolabd.py b/kolabd.py
index ec77767..a4acb4a 100755
--- a/kolabd.py
+++ b/kolabd.py
@@ -21,7 +21,7 @@
 import sys
 
 # For development purposes
-sys.path.extend(['.', '..'])
+sys.path = [ '.' ] + sys.path
 
 from pykolab.translate import _
 


commit d44815b60ab21da0f6d04e0d9f218828aa27cdb4
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Jul 18 19:07:35 2012 +0200

    Execute the correct change notification callback function

diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index 26b4438..71f16ce 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -1069,6 +1069,7 @@ class LDAP(pykolab.base.Base):
             the entry was - user, group, role or sharedfolder.
         """
         result_attribute = conf.get('cyrus-sasl', 'result_attribute')
+
         if not entry.has_key(result_attribute):
             return None
 
@@ -1078,7 +1079,7 @@ class LDAP(pykolab.base.Base):
         success = True
         for _type in ['user','group','role','sharedfolder']:
             try:
-                eval("_change_delete_%s(entry, change)" % (_type))
+                eval("self._change_delete_%s(entry, change)" % (_type))
             except:
                 success = False
 
@@ -1089,7 +1090,6 @@ class LDAP(pykolab.base.Base):
         """
             An entry of type user was deleted.
         """
-
         result_attribute = conf.get('cyrus-sasl', 'result_attribute')
 
         if not entry.has_key(result_attribute):





More information about the commits mailing list