diff --git a/conf.py b/conf.py index c74a0ff..8e5ef43 100755 --- a/conf.py +++ b/conf.py @@ -1,47 +1,45 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # """ Kolab configuration utility. """ - import logging import os import sys sys.path.append('.') from pykolab.translate import _ try: import pykolab.logger except ImportError, e: print >> sys.stderr, _("Cannot load pykolab/logger.py:") print >> sys.stderr, "%s" % e sys.exit(1) import pykolab if __name__ == "__main__": pykolab = pykolab.Conf() pykolab.finalize_conf() pykolab.run() - diff --git a/kolab-cli.py b/kolab-cli.py index 760c902..fbcfac5 100755 --- a/kolab-cli.py +++ b/kolab-cli.py @@ -1,41 +1,40 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # import logging import os import sys # For development purposes sys.path = [ '.' ] + sys.path from pykolab.translate import _ from pykolab.cli import Cli try: import pykolab.logger except ImportError, e: print >> sys.stderr, _("Cannot load pykolab/logger.py:") print >> sys.stderr, "%s" % e sys.exit(1) if __name__ == "__main__": kolab = Cli() kolab.run() - diff --git a/kolabd.py b/kolabd.py index 6ca2975..ef4500a 100755 --- a/kolabd.py +++ b/kolabd.py @@ -1,40 +1,39 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # import sys # For development purposes sys.path = [ '.' ] + sys.path from pykolab.translate import _ try: from pykolab.constants import * except ImportError, e: print >> sys.stderr, _("Cannot load pykolab/constants.py:") print >> sys.stderr, "%s" % e sys.exit(1) import kolabd if __name__ == "__main__": kolabd = kolabd.KolabDaemon() kolabd.run() - diff --git a/kolabd/__init__.py b/kolabd/__init__.py index b21bd80..586ce70 100644 --- a/kolabd/__init__.py +++ b/kolabd/__init__.py @@ -1,371 +1,371 @@ -# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2016 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # """ The Kolab daemon. """ import grp import os import pwd import shutil import sys import time import traceback import pykolab from pykolab.auth import Auth from pykolab import constants from pykolab import utils from pykolab.translate import _ from process import KolabdProcess as Process log = pykolab.getLogger('pykolab.daemon') conf = pykolab.getConf() + class KolabDaemon(object): def __init__(self): """ The main Kolab Groupware daemon process. """ daemon_group = conf.add_cli_parser_option_group(_("Daemon Options")) daemon_group.add_option( "--fork", dest = "fork_mode", action = "store_true", default = False, help = _("Fork to the background.") ) daemon_group.add_option( "-p", "--pid-file", dest = "pidfile", action = "store", default = "/var/run/kolabd/kolabd.pid", help = _("Path to the PID file to use.") ) daemon_group.add_option( "-u", "--user", dest = "process_username", action = "store", default = "kolab", help = _("Run as user USERNAME"), metavar = "USERNAME" ) daemon_group.add_option( "-g", "--group", dest = "process_groupname", action = "store", default = "kolab", help = _("Run as group GROUPNAME"), metavar = "GROUPNAME" ) conf.finalize_conf() def run(self): """Run Forest, RUN!""" exitcode = 0 utils.ensure_directory( os.path.dirname(conf.pidfile), conf.process_username, conf.process_groupname ) try: try: (ruid, euid, suid) = os.getresuid() (rgid, egid, sgid) = os.getresgid() except AttributeError, errmsg: ruid = os.getuid() rgid = os.getgid() if ruid == 0: # Means we can setreuid() / setregid() / setgroups() if rgid == 0: # Get group entry details try: ( group_name, group_password, group_gid, group_members ) = grp.getgrnam(conf.process_groupname) except KeyError: print >> sys.stderr, _("Group %s does not exist") % ( conf.process_groupname ) sys.exit(1) # Set real and effective group if not the same as current. if not group_gid == rgid: log.debug( _("Switching real and effective group id to %d") % ( group_gid ), level=8 ) os.setregid(group_gid, group_gid) if ruid == 0: # Means we haven't switched yet. try: ( user_name, user_password, user_uid, user_gid, user_gecos, user_homedir, user_shell ) = pwd.getpwnam(conf.process_username) except KeyError: print >> sys.stderr, _("User %s does not exist") % ( conf.process_username ) sys.exit(1) - # Set real and effective user if not the same as current. if not user_uid == ruid: log.debug( _("Switching real and effective user id to %d") % ( user_uid ), level=8 ) os.setreuid(user_uid, user_uid) except: log.error(_("Could not change real and effective uid and/or gid")) try: pid = os.getpid() if conf.fork_mode: pid = os.fork() if pid > 0 and not conf.fork_mode: self.do_sync() elif pid > 0: sys.exit(0) else: # Give up the session, all control, # all open file descriptors, see #5151 os.chdir("/") os.umask(0) os.setsid() pid = os.fork() if pid > 0: sys.exit(0) sys.stderr.flush() sys.stdout.flush() os.close(0) os.close(1) os.close(2) log.remove_stdout_handler() self.set_signal_handlers() self.write_pid() self.do_sync() except SystemExit, errcode: exitcode = errcode except KeyboardInterrupt: exitcode = 1 log.info(_("Interrupted by user")) except AttributeError, errmsg: exitcode = 1 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a " + \ - "bug at https://issues.kolab.org") + print >> sys.stderr, _("Traceback occurred, please report a " + + "bug at https://issues.kolab.org") except TypeError, errmsg: exitcode = 1 traceback.print_exc() log.error(_("Type Error: %s") % errmsg) except: exitcode = 2 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a " + \ - "bug at https://issues.kolab.org") + print >> sys.stderr, _("Traceback occurred, please report a " + + "bug at https://issues.kolab.org") sys.exit(exitcode) def do_sync(self): domain_auth = {} pid = os.getpid() primary_domain = conf.get('kolab', 'primary_domain') while 1: primary_auth = Auth(primary_domain) connected = False while not connected: try: primary_auth.connect() connected = True except Exception, errmsg: connected = False log.error(_("Could not connect to LDAP, is it running?")) time.sleep(5) log.debug(_("Listing domains..."), level=5) start = time.time() try: domains = primary_auth.list_domains() except: time.sleep(60) continue if isinstance(domains, list) and len(domains) < 1: log.error(_("No domains. Not syncing")) time.sleep(5) continue # domains now is a list of tuples in the format of # ('primary',[secondaries]), we want the primary_domains domain_base_dns = [] primary_domains = [] for primary_domain in list(set(domains.values())): domain_base_dn = primary_auth.domain_naming_context(primary_domain) log.debug(_("Domain Base DN for domain %r is %r") % (primary_domain, domain_base_dn), level=8) - if not domain_base_dn == None: - if not domain_base_dn in domain_base_dns: + if domain_base_dn is not None: + if domain_base_dn not in domain_base_dns: domain_base_dns.append(domain_base_dn) primary_domain = primary_auth.primary_domain_for_naming_context(domain_base_dn) primary_domains.append(primary_domain) log.debug(_("Naming contexts to synchronize: %r") % (primary_domains), level=8) # Now we can check if any changes happened. added_domains = [] removed_domains = [] # Combine the domains from LDAP with the domain processes # accounted for locally. all_domains = list(set(primary_domains + domain_auth.keys())) log.debug(_("All naming contexts: %r") % (all_domains), level=8) for domain in all_domains: log.debug(_("Checking for domain %s") % (domain), level=8) if domain in domain_auth.keys() and domain in primary_domains: if not domain_auth[domain].is_alive(): log.debug(_("Domain %s isn't alive anymore.") % (domain), level=8) domain_auth[domain].terminate() added_domains.append(domain) else: log.debug(_("Domain %s already there and alive.") % (domain), level=8) continue elif domain in domain_auth.keys(): log.debug(_("Domain %s should not exist any longer.") % (domain), level=8) removed_domains.append(domain) else: log.debug(_("Domain %s does not have a process yet.") % (domain), level=8) added_domains.append(domain) if len(removed_domains) == 0 and len(added_domains) == 0: try: sleep_between_domain_operations_in_seconds = (float)(conf.get('kolab', 'domain_sync_interval')) time.sleep(sleep_between_domain_operations_in_seconds) except ValueError: time.sleep(600) log.debug( _("added domains: %r, removed domains: %r") % ( added_domains, removed_domains ), level=8 ) for domain in added_domains: domain_auth[domain] = Process(domain) domain_auth[domain].start() # Pause or hammer your LDAP server to death if len(added_domains) >= 5: time.sleep(10) for domain in removed_domains: domain_auth[domain].terminate() del domain_auth[domain] def reload_config(self, *args, **kw): pass def remove_pid(self, *args, **kw): """ Remove our PID file. Note that multiple processes can attempt to do this very same thing at the same time, and therefore we need to test if the PID file exists, and only try/except removing it. """ if os.access(conf.pidfile, os.R_OK): try: os.remove(conf.pidfile) except: pass raise SystemExit def set_signal_handlers(self): import signal signal.signal(signal.SIGHUP, self.reload_config) signal.signal(signal.SIGTERM, self.remove_pid) def write_pid(self): pid = os.getpid() - fp = open(conf.pidfile,'w') + fp = open(conf.pidfile, 'w') fp.write("%d\n" % (pid)) fp.close() diff --git a/kolabd/process.py b/kolabd/process.py index 384c394..e4d93ba 100644 --- a/kolabd/process.py +++ b/kolabd/process.py @@ -1,62 +1,63 @@ # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # import multiprocessing import os import time import pykolab from pykolab.auth import Auth from pykolab.translate import _ log = pykolab.getLogger('pykolab.daemon') conf = pykolab.getConf() + class KolabdProcess(multiprocessing.Process): def __init__(self, domain): self.domain = domain log.debug(_("Process created for domain %s") % (domain), level=8) multiprocessing.Process.__init__( self, target=self.synchronize, args=(domain,), name="Kolab(%s)" % domain ) def synchronize(self, domain): log.debug(_("Synchronizing for domain %s") % (domain), level=8) sync_interval = conf.get('kolab', 'sync_interval') - if sync_interval == None or sync_interval == 0: + if sync_interval is None or sync_interval == 0: sync_interval = 300 else: sync_interval = (int)(sync_interval) while True: try: auth = Auth(domain) auth.connect(domain) auth.synchronize() time.sleep(sync_interval) except KeyboardInterrupt: break except Exception, errmsg: log.error(_("Error in process %r, terminating:\n\t%r") % (self.name, errmsg)) import traceback traceback.print_exc() time.sleep(1) diff --git a/saslauthd.py b/saslauthd.py index 8db7c58..9d4beab 100755 --- a/saslauthd.py +++ b/saslauthd.py @@ -1,42 +1,41 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # import logging import os import sys # For development purposes sys.path.extend(['.', '..']) from pykolab.translate import _ try: import pykolab.logger except ImportError, e: print >> sys.stderr, _("Cannot load pykolab/logger.py:") print >> sys.stderr, "%s" % e sys.exit(1) import saslauthd if __name__ == "__main__": saslauthd = saslauthd.SASLAuthDaemon() saslauthd.run() - diff --git a/saslauthd/__init__.py b/saslauthd/__init__.py index c5d9cfa..2cb76d6 100644 --- a/saslauthd/__init__.py +++ b/saslauthd/__init__.py @@ -1,374 +1,376 @@ -# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2016 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # """ SASL authentication daemon for multi-domain Kolab deployments. The SASL authentication daemon can use the domain name space or realm in the login credentials to determine the backend authentication database, and authenticate the credentials supplied against that backend. """ from optparse import OptionParser from ConfigParser import SafeConfigParser import grp import os import pwd import shutil import sys import time import traceback import pykolab from pykolab import utils from pykolab.auth import Auth from pykolab.constants import * from pykolab.translate import _ log = pykolab.getLogger('saslauthd') conf = pykolab.getConf() + class SASLAuthDaemon(object): def __init__(self): daemon_group = conf.add_cli_parser_option_group(_("Daemon Options")) daemon_group.add_option( "--fork", dest = "fork_mode", action = "store_true", default = False, help = _("Fork to the background.") ) daemon_group.add_option( "-p", "--pid-file", dest = "pidfile", action = "store", default = "/var/run/kolab-saslauthd/kolab-saslauthd.pid", help = _("Path to the PID file to use.") ) daemon_group.add_option( "-s", "--socket", dest = "socketfile", action = "store", default = "/var/run/saslauthd/mux", help = _("Socket file to bind to.") ) daemon_group.add_option( "-u", "--user", dest = "process_username", action = "store", default = "kolab", help = _("Run as user USERNAME"), metavar = "USERNAME" ) daemon_group.add_option( "-g", "--group", dest = "process_groupname", action = "store", default = "kolab", help = _("Run as group GROUPNAME"), metavar = "GROUPNAME" ) conf.finalize_conf() try: utils.ensure_directory( os.path.dirname(conf.pidfile), conf.process_username, conf.process_groupname ) except Exception, errmsg: log.error(_("Could not create %r: %r") % (os.path.dirname(conf.pidfile), errmsg)) sys.exit(1) self.thread_count = 0 def run(self): """ Run the SASL authentication daemon. """ exitcode = 0 self._ensure_socket_dir() self._drop_privileges() try: pid = os.getpid() if conf.fork_mode: pid = os.fork() if pid > 0 and not conf.fork_mode: self.do_saslauthd() elif pid > 0: sys.exit(0) else: # Give up the session, all control, # all open file descriptors, see #5151 os.chdir("/") os.umask(0) os.setsid() pid = os.fork() if pid > 0: sys.exit(0) sys.stderr.flush() sys.stdout.flush() os.close(0) os.close(1) os.close(2) self.thread_count += 1 log.remove_stdout_handler() self.set_signal_handlers() self.write_pid() self.do_saslauthd() except SystemExit, e: exitcode = e except KeyboardInterrupt: exitcode = 1 log.info(_("Interrupted by user")) except AttributeError, e: exitcode = 1 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a bug at https://issues.kolab.org") + print >> sys.stderr, _("Traceback occurred, please report a " + + "bug at https://issues.kolab.org") except TypeError, e: exitcode = 1 traceback.print_exc() log.error(_("Type Error: %s") % e) except: exitcode = 2 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a bug at https://issues.kolab.org") + print >> sys.stderr, _("Traceback occurred, please report a " + + "bug at https://issues.kolab.org") sys.exit(exitcode) def do_saslauthd(self): """ Create the actual listener socket, and handle the authentication. The actual authentication handling is passed on to the appropriate backend authentication classes through the more generic Auth(). """ import binascii import socket import struct s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # TODO: The saslauthd socket path could be a setting. try: os.remove(conf.socketfile) except: # TODO: Do the "could not remove, could not start" dance pass s.bind(conf.socketfile) os.chmod(conf.socketfile, 0777) s.listen(5) while 1: max_tries = 20 cur_tries = 0 bound = False while not bound: cur_tries += 1 try: (clientsocket, address) = s.accept() bound = True except Exception, errmsg: log.error( - _("kolab-saslauthd could not accept " + \ - "connections on socket: %r") % (errmsg) + _("kolab-saslauthd could not accept " + + "connections on socket: %r") % (errmsg) ) if cur_tries >= max_tries: log.fatal(_("Maximum tries exceeded, exiting")) sys.exit(1) time.sleep(1) received = clientsocket.recv(4096) login = [] start = 0 end = 2 while end < len(received): (length,) = struct.unpack("!H", received[start:end]) start += 2 end += length (value,) = struct.unpack("!%ds" % (length), received[start:end]) start += length end = start + 2 login.append(value) if len(login) == 4: realm = login[3] elif len(login[0].split('@')) > 1: realm = login[0].split('@')[1] else: realm = conf.get('kolab', 'primary_domain') auth = Auth(domain=realm) auth.connect() success = False try: success = auth.authenticate(login) except: success = False if success: # #1170: Catch broken pipe error (incomplete authentication request) try: clientsocket.send(struct.pack("!H2s", 2, "OK")) except: pass else: # #1170: Catch broken pipe error (incomplete authentication request) try: clientsocket.send(struct.pack("!H2s", 2, "NO")) except: pass clientsocket.close() auth.disconnect() def reload_config(self, *args, **kw): pass def remove_pid(self, *args, **kw): if os.access(conf.pidfile, os.R_OK): os.remove(conf.pidfile) raise SystemExit def set_signal_handlers(self): import signal signal.signal(signal.SIGHUP, self.reload_config) signal.signal(signal.SIGTERM, self.remove_pid) def write_pid(self): pid = os.getpid() - fp = open(conf.pidfile,'w') + fp = open(conf.pidfile, 'w') fp.write("%d\n" % (pid)) fp.close() def _ensure_socket_dir(self): utils.ensure_directory( os.path.dirname(conf.socketfile), conf.process_username, conf.process_groupname ) def _drop_privileges(self): try: try: (ruid, euid, suid) = os.getresuid() (rgid, egid, sgid) = os.getresgid() except AttributeError, errmsg: ruid = os.getuid() rgid = os.getgid() if ruid == 0: # Means we can setreuid() / setregid() / setgroups() if rgid == 0: # Get group entry details try: ( group_name, group_password, group_gid, group_members ) = grp.getgrnam(conf.process_groupname) except KeyError: print >> sys.stderr, _("Group %s does not exist") % ( conf.process_groupname ) sys.exit(1) # Set real and effective group if not the same as current. if not group_gid == rgid: log.debug( _("Switching real and effective group id to %d") % ( group_gid ), level=8 ) os.setregid(group_gid, group_gid) if ruid == 0: # Means we haven't switched yet. try: ( user_name, user_password, user_uid, user_gid, user_gecos, user_homedir, user_shell ) = pwd.getpwnam(conf.process_username) except KeyError: print >> sys.stderr, _("User %s does not exist") % ( conf.process_username ) sys.exit(1) - # Set real and effective user if not the same as current. if not user_uid == ruid: log.debug( _("Switching real and effective user id to %d") % ( user_uid ), level=8 ) os.setreuid(user_uid, user_uid) except: log.error(_("Could not change real and effective uid and/or gid")) diff --git a/test-wallace.py b/test-wallace.py index 7ba7ac6..92d69f4 100755 --- a/test-wallace.py +++ b/test-wallace.py @@ -1,102 +1,103 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # import smtplib import socket import sys # For development purposes sys.path.extend(['.', '..']) from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText from email.Utils import COMMASPACE, formatdate from email import Encoders + def send_mail(send_from, send_to, send_with=None): smtp = smtplib.SMTP("localhost", 10026) smtp.set_debuglevel(True) subject = "This is a Kolab load test mail" text = """Hi there, I am a Kolab Groupware test email, generated by a script that makes me send lots of email to lots of people using one account and a bunch of delegation blah. Your response, though completely unnecessary, would be appreciated, because being a fictitious character doesn't do my address book of friends any good. Kind regards, Lucy Meier. """ msg = MIMEMultipart() msg['From'] = send_from msg['To'] = COMMASPACE.join(send_to) msg['Date'] = formatdate(localtime=True) msg['Subject'] = subject msg.attach( MIMEText(text) ) - #msg.attach( MIMEBase('application', open('/boot/initrd-plymouth.img').read()) ) + # msg.attach( MIMEBase('application', open('/boot/initrd-plymouth.img').read()) ) smtp.sendmail(send_from, send_to, msg.as_string()) if __name__ == "__main__": #send_to = [ #'Jeroen van Meeuwen ', #'Aleksander Machniak ', #'Georg Greve ', #'Paul Adams ', #'Thomas Broderli ', #'Christoph Wickert ', #'Lucy Meier ', #] #send_mail( #'Jeroen van Meeuwen ', #send_to #) #send_mail( #'Lucy Meier on behalf of Paul Adams ', #send_to #) #send_mail( #'Lucy Meier on behalf of Georg Greve ', #send_to #) send_to = [ 'Jeroen van Meeuwen (REJECT) ', 'Jeroen van Meeuwen (HOLD) ', 'Jeroen van Meeuwen (DEFER) ', 'Jeroen van Meeuwen (ACCEPT) ', 'Jeroen "kanarip" van Meeuwen (ACCEPT) ', 'Jeroen "kanarip" van Meeuwen (REJECT) ', 'Lucy Meier (REJECT) ', 'Georg Greve (REJECT) ', ] send_mail('Jeroen van Meeuwen ', send_to) diff --git a/tests/functional/purge_imap.py b/tests/functional/purge_imap.py index 67d4aec..f4d62e0 100644 --- a/tests/functional/purge_imap.py +++ b/tests/functional/purge_imap.py @@ -1,20 +1,21 @@ import time import pykolab from pykolab import wap_client from pykolab.imap import IMAP conf = pykolab.getConf() + def purge_imap(): time.sleep(2) imap = IMAP() imap.connect() for folder in imap.lm(): try: imap.dm(folder) except: pass diff --git a/tests/functional/purge_users.py b/tests/functional/purge_users.py index 17db9d7..713ea46 100644 --- a/tests/functional/purge_users.py +++ b/tests/functional/purge_users.py @@ -1,17 +1,18 @@ import pykolab from pykolab import wap_client from pykolab.auth import Auth from pykolab.imap import IMAP conf = pykolab.getConf() + def purge_users(): wap_client.authenticate(conf.get("ldap", "bind_dn"), conf.get("ldap", "bind_pw")) users = wap_client.users_list() for user in users['list']: wap_client.user_delete({'id': user}) from tests.functional.purge_imap import purge_imap purge_imap() diff --git a/tests/functional/resource_func.py b/tests/functional/resource_func.py index a7e4d90..36bd1bd 100644 --- a/tests/functional/resource_func.py +++ b/tests/functional/resource_func.py @@ -1,69 +1,75 @@ import pykolab from pykolab import wap_client conf = pykolab.getConf() + def resource_add(type, cn, members=None, owner=None, **kw): - if type == None or type == '': + if type is None or type == '': raise Exception - if cn == None or cn == '': + if cn is None or cn == '': raise Exception resource_details = { 'cn': cn, 'kolabtargetfolder': "shared/Resources/" + cn + "@example.org", 'uniquemember': members, 'owner': owner, 'ou': 'ou=resources,dc=example,dc=org' } resource_details.update(kw) - result = wap_client.authenticate(conf.get('ldap', 'bind_dn'), conf.get('ldap', 'bind_pw'), conf.get('kolab', 'primary_domain')) + bind_dn = conf.get('ldap', 'bind_dn') + bind_pw = conf.get('ldap', 'bind_pw') + domain = conf.get('kolab', 'primary_domain') + result = wap_client.authenticate(bind_dn, bind_pw, domain) type_id = 0 resource_types = wap_client.resource_types_list() for key in resource_types['list'].keys(): if resource_types['list'][key]['key'] == type: type_id = key if type_id == 0: raise Exception resource_type_info = resource_types['list'][type_id]['attributes'] params = {} for attribute in resource_type_info['form_fields'].keys(): attr_details = resource_type_info['form_fields'][attribute] if isinstance(attr_details, dict): - if not attr_details.has_key('optional') or attr_details['optional'] == False or resource_details.has_key(attribute): + if 'optional' not in attr_details or attr_details['optional'] is False or attribute in resource_details: params[attribute] = resource_details[attribute] elif isinstance(attr_details, list): params[attribute] = resource_details[attribute] fvg_params = params fvg_params['object_type'] = 'resource' fvg_params['type_id'] = type_id - fvg_params['attributes'] = [attr for attr in resource_type_info['auto_form_fields'].keys() if not attr in params.keys()] + fvg_params['attributes'] = [attr for attr in resource_type_info['auto_form_fields'].keys() if attr not in params] result = wap_client.resource_add(params) result['dn'] = "cn=" + result['cn'] + ",ou=Resources,dc=example,dc=org" return result def purge_resources(): - wap_client.authenticate(conf.get("ldap", "bind_dn"), conf.get("ldap", "bind_pw"), conf.get('kolab', 'primary_domain')) + bind_dn = conf.get('ldap', 'bind_dn') + bind_pw = conf.get('ldap', 'bind_pw') + domain = conf.get('kolab', 'primary_domain') + result = wap_client.authenticate(bind_dn, bind_pw, domain) resources = wap_client.resources_list() for resource in resources['list']: wap_client.resource_delete({'id': resource}) - #from tests.functional.purge_imap import purge_imap - #purge_imap() - + # from tests.functional.purge_imap import purge_imap + # purge_imap() diff --git a/tests/functional/synchronize.py b/tests/functional/synchronize.py index 7046db2..428ea74 100644 --- a/tests/functional/synchronize.py +++ b/tests/functional/synchronize.py @@ -1,6 +1,7 @@ from pykolab.auth import Auth + def synchronize_once(): auth = Auth() auth.connect() auth.synchronize(mode='_paged_search') diff --git a/tests/functional/test_kolabd/__init__.py b/tests/functional/test_kolabd/__init__.py index d7bf27f..d6cdb57 100644 --- a/tests/functional/test_kolabd/__init__.py +++ b/tests/functional/test_kolabd/__init__.py @@ -1,6 +1,7 @@ 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 index 671188a..e8c804d 100644 --- a/tests/functional/test_kolabd/test_001_user_sync.py +++ b/tests/functional/test_kolabd/test_001_user_sync.py @@ -1,140 +1,140 @@ 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): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'john.doe', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("John", "Doe") @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() 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@example.org') self.assertEqual(result['alias'], ['doe@example.org', 'j.doe@example.org']) def test_002_user_recipient_policy_duplicate(self): from tests.functional.user_add import user_add user = { 'local': 'jane.doe', 'domain': 'example.org' } user_add("Jane", "Doe") time.sleep(3) auth = Auth() auth.connect() recipient = auth.find_recipient("%(local)s@%(domain)s" % (user)) if hasattr(self, 'assertIsInstance'): self.assertIsInstance(recipient, str) self.assertEqual(recipient, "uid=doe2,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) - if not result.has_key('mailhost'): + if 'mailhost' not in result: from tests.functional.synchronize import synchronize_once synchronize_once() result = wap_client.user_info(recipient) self.assertEqual(result['mail'], 'jane.doe@example.org') self.assertEqual(result['alias'], ['doe2@example.org', 'j.doe2@example.org']) def test_003_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_004_user_additional_folders_created(self): time.sleep(2) imap = IMAP() imap.connect() ac_folders = conf.get_raw('kolab', 'autocreate_folders') exec("ac_folders = %s" % (ac_folders)) folders = imap.lm('user/%(local)s/*@%(domain)s' % (self.user)) self.assertEqual(len(folders), len(ac_folders.keys())) def test_005_user_folders_metadata_set(self): imap = IMAP() imap.connect() ac_folders = conf.get_raw('kolab', 'autocreate_folders') exec("ac_folders = %s" % (ac_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: metadata = imap.get_metadata(folder) print metadata folder_name = '/'.join(folder.split('/')[2:]).split('@')[0] - if ac_folders.has_key(folder_name): - if ac_folders[folder_name].has_key('annotations'): + if folder_name in ac_folders: + if 'annotations' in ac_folders[folder_name]: for _annotation in ac_folders[folder_name]['annotations'].keys(): if _annotation.startswith('/private'): continue _annotation_value = ac_folders[folder_name]['annotations'][_annotation] - self.assertTrue(metadata[metadata.keys().pop()].has_key(_annotation)) + self.assertTrue(_annotation in metadata[metadata.keys().pop()]) self.assertEqual(_annotation_value, metadata[metadata.keys().pop()][_annotation]) def test_006_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@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_kolabd/test_002_user_rename.py b/tests/functional/test_kolabd/test_002_user_rename.py index f526ade..3ac4a02 100644 --- a/tests/functional/test_kolabd/test_002_user_rename.py +++ b/tests/functional/test_kolabd/test_002_user_rename.py @@ -1,80 +1,80 @@ 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): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'john.doe', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("John", "Doe") time.sleep(2) @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_user_rename(self): """ Rename user "Doe, John" to "Sixpack, Joe" and verify the recipient policy is applied, and the IMAP INBOX folder for the user is renamed. """ auth = Auth() auth.connect() recipient = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(recipient) - if not user_info.has_key('mailhost'): + if 'mailhost' not in user_info: from tests.functional.synchronize import synchronize_once synchronize_once() imap = IMAP() imap.connect() folders = imap.lm('user/john.doe@example.org') self.assertEqual(len(folders), 1) auth = Auth() auth.connect() recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user)) user_info = wap_client.user_info(recipient) user_info['sn'] = 'Sixpack' user_info['givenname'] = 'Joe' user_info['uid'] = 'sixpack' user_edit = wap_client.user_edit(recipient, user_info) time.sleep(2) print imap.lm() user_info = wap_client.user_info('uid=sixpack,ou=People,dc=example,dc=org') if not user_info['mail'] == 'joe.sixpack@example.org': from tests.functional.synchronize import synchronize_once synchronize_once() user_info = wap_client.user_info('uid=sixpack,ou=People,dc=example,dc=org') self.assertEqual(user_info['mail'], 'joe.sixpack@example.org') print imap.lm() folders = imap.lm('user/john.doe@example.org') self.assertEqual(len(folders), 0, "INBOX for john.doe still exists") folders = imap.lm('user/joe.sixpack@example.org') self.assertEqual(len(folders), 1, "INBOX for joe.sixpack does not exist") - diff --git a/tests/functional/test_kolabd/test_003_two_johns.py b/tests/functional/test_kolabd/test_003_two_johns.py index b2bfb46..0f93969 100644 --- a/tests/functional/test_kolabd/test_003_two_johns.py +++ b/tests/functional/test_kolabd/test_003_two_johns.py @@ -1,51 +1,51 @@ 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): from tests.functional.purge_users import purge_users purge_users() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_two_johns(self): from tests.functional.user_add import user_add user_add("John", "Doe") user_add("John", "Doe") time.sleep(3) auth = Auth() auth.connect() max_tries = 20 while max_tries > 0: recipient1 = auth.find_recipient('john.doe@example.org') recipient2 = auth.find_recipient('john.doe2@example.org') if not recipient1 or not recipient2: time.sleep(1) max_tries -= 1 else: break imap = IMAP() imap.connect() folders = imap.lm('user/john.doe@example.org') self.assertEqual(len(folders), 1, "No INBOX found for first John") folders = imap.lm('user/john.doe2@example.org') self.assertEqual(len(folders), 1, "No INBOX found for second John") - diff --git a/tests/functional/test_postfix/__init__.py b/tests/functional/test_postfix/__init__.py index d7bf27f..d6cdb57 100644 --- a/tests/functional/test_postfix/__init__.py +++ b/tests/functional/test_postfix/__init__.py @@ -1,6 +1,7 @@ import pykolab + def setup_package(): conf = pykolab.getConf() conf.finalize_conf(fatal=False) diff --git a/tests/functional/test_wallace/__init__.py b/tests/functional/test_wallace/__init__.py index d7bf27f..d6cdb57 100644 --- a/tests/functional/test_wallace/__init__.py +++ b/tests/functional/test_wallace/__init__.py @@ -1,6 +1,7 @@ import pykolab + def setup_package(): conf = pykolab.getConf() conf.finalize_conf(fatal=False) diff --git a/tests/functional/test_wallace/test_001_user_add.py b/tests/functional/test_wallace/test_001_user_add.py index d4e620a..a6945c9 100644 --- a/tests/functional/test_wallace/test_001_user_add.py +++ b/tests/functional/test_wallace/test_001_user_add.py @@ -1,96 +1,96 @@ - from email import message_from_string 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 TestUserAdd(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.john = { 'local': 'john.doe', 'domain': 'example.org' } self.jane = { 'local': 'jane.doe', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("John", "Doe") user_add("Jane", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_inbox_created(self): imap = IMAP() imap.connect() folders = imap.lm('user/%(local)s@%(domain)s' % (self.john)) self.assertEqual(len(folders), 1) - + folders = imap.lm('user/%(local)s@%(domain)s' % (self.jane)) self.assertEqual(len(folders), 1) def test_002_send_forwarded_email(self): import smtplib from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText from email.Utils import COMMASPACE, formatdate from email import Encoders smtp = smtplib.SMTP('localhost', 10026) subject = "%s" % (time.time()) body = "This is a test message" msg = MIMEMultipart() msg['From'] = '"Doe, Jane" ' msg['To'] = '"Doe, John" ' msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) msg.attach(MIMEText(body)) send_to = 'jane.doe@example.org' send_from = 'john.doe@example.org' smtp.sendmail(send_from, send_to, msg.as_string()) imap = IMAP() imap.connect() imap.set_acl("user/jane.doe@example.org", "cyrus-admin", "lrs") imap.imap.m.select("user/jane.doe@example.org") found = False max_tries = 20 while not found and max_tries > 0: max_tries -= 1 typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') _msg = message_from_string(msg[0][1]) if _msg['Subject'] == subject: found = True time.sleep(1) if not found: raise Exception diff --git a/tests/functional/test_wallace/test_002_footer.py b/tests/functional/test_wallace/test_002_footer.py index 4c1599c..d6e5836 100644 --- a/tests/functional/test_wallace/test_002_footer.py +++ b/tests/functional/test_wallace/test_002_footer.py @@ -1,293 +1,294 @@ from email import message_from_string from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEImage import MIMEImage from email.MIMEText import MIMEText from email.Utils import COMMASPACE, formatdate from email import Encoders import os import smtplib 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 TestWallaceFooter(unittest.TestCase): user = None @classmethod def setUp(self): """ Compatibility for twisted.trial.unittest """ if not self.user: self.setup_class() @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'john.doe', 'domain': 'example.org' } self.footer = {} footer_html_file = conf.get('wallace', 'footer_html') footer_text_file = conf.get('wallace', 'footer_text') if os.path.isfile(footer_text_file): self.footer['plain'] = open(footer_text_file, 'r').read() if not os.path.isfile(footer_html_file): self.footer['html'] = '

' + self.footer['plain'] + '

' else: self.footer['html'] = open(footer_html_file, 'r').read() self.send_to = 'john.doe@example.org' self.send_from = 'john.doe@example.org' self.message_to = '"Doe, John" <%s>' % (self.send_to) self.message_from = '"Doe, John" <%s>' % (self.send_from) from tests.functional.user_add import user_add user_add("John", "Doe") time.sleep(2) from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def check_message_delivered(self, subject): imap = IMAP() imap.connect() imap.set_acl("user/john.doe@example.org", "cyrus-admin", "lrs") imap.imap.m.select("user/john.doe@example.org") found = False max_tries = 20 while not found and max_tries > 0: max_tries -= 1 typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') _msg = message_from_string(msg[0][1]) if _msg['Subject'] == subject: found = True time.sleep(1) return found def html_attachment(self): html_body = "

This is an HTML attachment

" html_part = MIMEBase("text", "html") html_part.add_header("Content-Disposition", "attachment", filename="html_attachment.html") html_part.set_payload(html_body) return html_part def image_attachment(self): image_file = '/usr/share/kolab-webadmin/public_html/skins/default/images/logo_kolab.png' image_part = MIMEImage(open(image_file, 'r').read()) image_part.add_header("Content-Disposition", "attachment", filename=os.path.basename(image_file)) return image_part def message_standard_params(self, subject, msg): msg['From'] = self.message_from msg['To'] = self.message_to msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) return msg def send_message(self, msg, _to=None, _from=None): smtp = smtplib.SMTP('localhost', 10026) - if _to == None: + if _to is None: _to = self.send_to - if _from == None: + if _from is None: _from = self.send_from smtp.sendmail(_from, _to, msg.as_string()) def test_001_inbox_created(self): imap = IMAP() imap.connect() folders = imap.lm('user/%(local)s@%(domain)s' % (self.user)) self.assertEqual(len(folders), 1) def test_002_send_plaintext(self): subject = "test_002_send_plaintext" body = "This is a test message" msg = MIMEBase("text", "plain") msg = self.message_standard_params(subject, msg) msg.set_payload(body) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_003_send_plaintext_with_attachment(self): subject = "test_003_send_plaintext_with_attachment" body = "This is a test message" msg = MIMEMultipart() msg = self.message_standard_params(subject, msg) msg.attach(MIMEText(body)) msg.attach(self.image_attachment()) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_004_send_html(self): subject = "test_004_send_html" body = "

This is a test message

" msg = MIMEBase("text", "html") msg = self.message_standard_params(subject, msg) msg.set_payload(body) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_005_send_html_with_plaintext_alternative(self): subject = "test_005_send_html_with_plaintext_alternative" html_body = "

This is the HTML part

" plain_body = "This is the plaintext part" msg = MIMEMultipart("alternative") msg = self.message_standard_params(subject, msg) html_part = MIMEBase("text", "html") html_part.set_payload(html_body) msg.attach(html_part) plain_part = MIMEText(plain_body) msg.attach(plain_part) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_006_send_html_with_attachment(self): subject = "test_006_send_html_with_attachment" html_body = "

This is the HTML part

" plain_body = "This is the plaintext part" msg = MIMEMultipart() msg = self.message_standard_params(subject, msg) html_part = MIMEBase("text", "html") html_part.set_payload(html_body) msg.attach(html_part) msg.attach(self.image_attachment()) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_007_send_html_with_plaintext_alternative_and_attachment(self): subject = "test_007_send_html_with_plaintext_alternative_and_attachment" html_body = "

This is the HTML part

" plain_body = "This is the plaintext part" msg = MIMEMultipart("mixed") msg = self.message_standard_params(subject, msg) message_part = MIMEMultipart("alternative") html_part = MIMEBase("text", "html") html_part.set_payload(html_body) message_part.attach(html_part) plain_part = MIMEText(plain_body) message_part.attach(plain_part) msg.attach(message_part) msg.attach(self.image_attachment()) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_008_send_plaintext_with_html_attachment(self): subject = "test_008_send_plaintext_with_html_attachment" body = "This is a plaintext message" msg = MIMEMultipart() msg = self.message_standard_params(subject, msg) msg.attach(MIMEText(body)) msg.attach(self.html_attachment()) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_009_send_plaintext_forwarded(self): subject = "test_009_send_plaintext_forwarded" body = "This is a plaintext message" from tests.functional.user_add import user_add user_add("Jane", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() admin_login = conf.get('cyrus-imap', 'admin_login') admin_password = conf.get('cyrus-imap', 'admin_password') import sievelib.factory script = sievelib.factory.FiltersSet("test_wallace_test_009_forward") script.require("copy") script.addfilter("forward", ["true"], [("redirect", ":copy", "john.doe@example.org")]) import sievelib.managesieve sieveclient = sievelib.managesieve.Client('localhost', 4190, True) sieveclient.connect(None, None, True) sieveclient._plain_authentication(admin_login, admin_password, 'jane.doe@example.org') sieveclient.authenticated = True script_str = script.__str__() print script_str sieveclient.putscript("test_wallace_test_009_forward", script_str) sieveclient.setactive("test_wallace_test_009_forward") msg = MIMEText(body) msg['From'] = self.message_from msg['To'] = '"Doe, Jane" ' msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) self.send_message(msg, _to='jane.doe@example.org', _from='john.doe@example.org') raise Exception diff --git a/tests/functional/test_wallace/test_003_nonascii_subject.py b/tests/functional/test_wallace/test_003_nonascii_subject.py index ff21aee..87d163a 100644 --- a/tests/functional/test_wallace/test_003_nonascii_subject.py +++ b/tests/functional/test_wallace/test_003_nonascii_subject.py @@ -1,126 +1,126 @@ # *-* encoding: utf-8 *-* from email.header import Header from email import message_from_string from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEImage import MIMEImage from email.MIMEText import MIMEText from email.Utils import COMMASPACE, formatdate from email import Encoders import os import smtplib 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 TestWallaceNonASCIISubject(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'john.doe', 'domain': 'example.org' } self.send_to = 'john.doe@example.org' self.send_from = 'john.doe@example.org' self.message_to = '"Doe, John" <%s>' % (self.send_to) self.message_from = '"Doe, John" <%s>' % (self.send_from) from tests.functional.user_add import user_add user_add("John", "Doe") time.sleep(2) from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def check_message_delivered(self, subject): imap = IMAP() imap.connect() imap.set_acl("user/john.doe@example.org", "cyrus-admin", "lrs") imap.imap.m.select("user/john.doe@example.org") found = False max_tries = 20 while not found and max_tries > 0: max_tries -= 1 typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') _msg = message_from_string(msg[0][1]) if _msg['Subject'] == subject: found = True time.sleep(1) return found def message_standard_params(self, subject, msg): msg['From'] = self.message_from msg['To'] = self.message_to msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) return msg def send_message(self, msg, _to=None, _from=None): smtp = smtplib.SMTP('localhost', 10026) - if _to == None: + if _to is None: _to = self.send_to - if _from == None: + if _from is None: _from = self.send_from smtp.sendmail(_from, _to, msg.as_string()) def test_001_inbox_created(self): imap = IMAP() imap.connect() folders = imap.lm('user/%(local)s@%(domain)s' % (self.user)) self.assertEqual(len(folders), 1) - + def test_002_send_nonascii_subject(self): subject = Header(u"test_002_nonascii_subject chwała") body = "This is a test message" msg = MIMEBase("text", "plain") msg = self.message_standard_params(subject, msg) msg.set_payload(body) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_003_send_nonascii_subject(self): subject = Header(u"test_003_nonascii_subject Тест") body = "This is a test message" msg = MIMEBase("text", "plain") msg = self.message_standard_params(subject, msg) msg.set_payload(body) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception - diff --git a/tests/functional/test_wallace/test_004_nonascii_addresses.py b/tests/functional/test_wallace/test_004_nonascii_addresses.py index 45df30e..42239c4 100644 --- a/tests/functional/test_wallace/test_004_nonascii_addresses.py +++ b/tests/functional/test_wallace/test_004_nonascii_addresses.py @@ -1,126 +1,126 @@ # *-* encoding: utf-8 *-* from email.header import Header from email import message_from_string from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEImage import MIMEImage from email.MIMEText import MIMEText from email.Utils import COMMASPACE, formatdate from email import Encoders import os import smtplib 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 TestWallaceNonASCIIAddresses(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'nikolaj.rimskij-korsakov', 'domain': 'example.org' } self.send_to = 'nikolaj.rimskij-korsakov@example.org' self.send_from = 'nikolaj.rimskij-korsakov@example.org' self.message_to = '"Римский-Корсаков, Николай" <%s>' % (self.send_to) self.message_from = '"Римский-Корсаков, Николай" <%s>' % (self.send_from) from tests.functional.user_add import user_add user_add("Николай", "Римский-Корсаков", preferredlanguage='ru_RU') time.sleep(2) from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def check_message_delivered(self, subject): imap = IMAP() imap.connect() imap.set_acl("user/nikolaj.rimskij-korsakov@example.org", "cyrus-admin", "lrs") imap.imap.m.select("user/nikolaj.rimskij-korsakov@example.org") found = False max_tries = 20 while not found and max_tries > 0: max_tries -= 1 typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') _msg = message_from_string(msg[0][1]) if _msg['Subject'] == subject: found = True time.sleep(1) return found def message_standard_params(self, subject, msg): msg['From'] = Header(self.message_from) msg['To'] = Header(self.message_to) msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) return msg def send_message(self, msg, _to=None, _from=None): smtp = smtplib.SMTP('localhost', 10026) - if _to == None: + if _to is None: _to = self.send_to - if _from == None: + if _from is None: _from = self.send_from smtp.sendmail(_from, _to, msg.as_string()) def test_001_inbox_created(self): imap = IMAP() imap.connect() folders = imap.lm('user/%(local)s@%(domain)s' % (self.user)) self.assertEqual(len(folders), 1) - + def test_002_send_nonascii_addresses(self): subject = Header(u"test_002_nonascii_addresses") body = "This is a test message" msg = MIMEBase("text", "plain") msg = self.message_standard_params(subject, msg) msg.set_payload(body) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception def test_003_send_nonascii_subject(self): subject = Header(u"test_003_nonascii_subject Тест") body = "This is a test message" msg = MIMEBase("text", "plain") msg = self.message_standard_params(subject, msg) msg.set_payload(body) self.send_message(msg) if not self.check_message_delivered(subject): raise Exception - diff --git a/tests/functional/test_wallace/test_005_resource_add.py b/tests/functional/test_wallace/test_005_resource_add.py index fc7f3ed..a378d9c 100644 --- a/tests/functional/test_wallace/test_005_resource_add.py +++ b/tests/functional/test_wallace/test_005_resource_add.py @@ -1,71 +1,72 @@ import time import pykolab from pykolab import wap_client from pykolab.auth import Auth from pykolab.imap import IMAP from wallace import module_resources from twisted.trial import unittest import tests.functional.resource_func as funcs conf = pykolab.getConf() + class TestResourceAdd(unittest.TestCase): @classmethod def setUp(self): from tests.functional.purge_users import purge_users - #purge_users() + # purge_users() self.john = { 'local': 'john.doe', 'domain': 'example.org' } from tests.functional.user_add import user_add - #user_add("John", "Doe") + # user_add("John", "Doe") funcs.purge_resources() self.audi = funcs.resource_add("car", "Audi A4") self.passat = funcs.resource_add("car", "VW Passat") self.boxter = funcs.resource_add("car", "Porsche Boxter S", kolabinvitationpolicy='ACT_ACCEPT_AND_NOTIFY') - self.cars = funcs.resource_add("collection", "Company Cars", [ self.audi['dn'], self.passat['dn'], self.boxter['dn'] ], kolabinvitationpolicy='ACT_ACCEPT') + self.cars = funcs.resource_add("collection", "Company Cars", [self.audi['dn'], self.passat['dn'], self.boxter['dn']], kolabinvitationpolicy='ACT_ACCEPT') from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def tearDown(self): from tests.functional.purge_users import purge_users - #funcs.purge_resources() - #purge_users() + # funcs.purge_resources() + # purge_users() def test_001_resource_created(self): resource = module_resources.resource_record_from_email_address(self.audi['mail']) self.assertEqual(len(resource), 1) self.assertEqual(resource[0], self.audi['dn']) collection = module_resources.resource_record_from_email_address(self.cars['mail']) self.assertEqual(len(collection), 1) self.assertEqual(collection[0], self.cars['dn']) def test_002_resource_collection(self): auth = Auth() auth.connect() attrs = auth.get_entry_attributes(None, self.cars['dn'], ['*']) self.assertIn('groupofuniquenames', attrs['objectclass']) self.assertEqual(len(attrs['uniquemember']), 3) self.assertEqual(attrs['kolabinvitationpolicy'], 'ACT_ACCEPT') def test_003_get_resource_records(self): resource_dns = module_resources.resource_record_from_email_address(self.cars['mail']) self.assertEqual(resource_dns[0], self.cars['dn']) resources = module_resources.get_resource_records(resource_dns) self.assertEqual(len(resources), 4) # check for (inherited) kolabinvitationpolicy values (bitmasks) self.assertEqual(resources[self.cars['dn']]['kolabinvitationpolicy'], [module_resources.ACT_ACCEPT]) self.assertEqual(resources[self.audi['dn']]['kolabinvitationpolicy'], [module_resources.ACT_ACCEPT]) self.assertEqual(resources[self.boxter['dn']]['kolabinvitationpolicy'], [module_resources.ACT_ACCEPT_AND_NOTIFY]) diff --git a/tests/functional/test_wallace/test_005_resource_invitation.py b/tests/functional/test_wallace/test_005_resource_invitation.py index af84fa6..5429331 100644 --- a/tests/functional/test_wallace/test_005_resource_invitation.py +++ b/tests/functional/test_wallace/test_005_resource_invitation.py @@ -1,1017 +1,1004 @@ import time import pykolab import smtplib import email import datetime import uuid import re from pykolab.imap import IMAP from wallace import module_resources from pykolab.translate import _ from pykolab.xml import utils as xmlutils from pykolab.xml import event_from_message from pykolab.xml import participant_status_label from pykolab.itip import events_from_message from email import message_from_string from twisted.trial import unittest import tests.functional.resource_func as funcs conf = pykolab.getConf() itip_invitation = """ 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:%s%s DTSTAMP:20140213T125414Z DTSTART;TZID=Europe/London:%s DTEND;TZID=Europe/London:%s SUMMARY:test DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=Somebody Else:mailto:somebody@else.com TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ itip_update = """ 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:%s%s DTSTAMP:20140215T125414Z DTSTART;TZID=Europe/London:%s DTEND;TZID=Europe/London:%s SEQUENCE:2 SUMMARY:test DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ itip_delegated = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.0-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT UID:%s%s DTSTAMP;VALUE=DATE-TIME:20140227T141939Z DTSTART;VALUE=DATE-TIME;TZID=Europe/London:%s DTEND;VALUE=DATE-TIME;TZID=Europe/London:%s SUMMARY:test SEQUENCE:4 ATTENDEE;CN=Company Cars;PARTSTAT=DELEGATED;ROLE=NON-PARTICIPANT;CUTYPE=IND IVIDUAL;RSVP=TRUE;DELEGATED-TO=resource-car-audia4@example.org:mailto:reso urce-collection-companycars@example.org ATTENDEE;CN=The Delegate;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT;CUTYPE=INDI VIDUAL;RSVP=TRUE;DELEGATED-FROM=resource-collection-companycars@example.or g:mailto:resource-car-audia4@example.org ORGANIZER;CN=:mailto:john.doe@example.org DESCRIPTION:Sent to %s END:VEVENT END:VCALENDAR """ itip_cancellation = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube Webmail 0.9-0.3.el6.kolab_3.0//NONSGML Calendar//EN CALSCALE:GREGORIAN METHOD:CANCEL BEGIN:VEVENT UID:%s%s DTSTAMP:20140218T125414Z DTSTART;TZID=Europe/London:20120713T100000 DTEND;TZID=Europe/London:20120713T110000 SUMMARY:test DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE:mailt= o:%s TRANSP:OPAQUE SEQUENCE:3 END:VEVENT END:VCALENDAR """ itip_allday = """ 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:%s%s DTSTAMP:20140213T125414Z DTSTART;VALUE=DATE:%s DTEND;VALUE=DATE:%s SUMMARY:test DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ - itip_recurring = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//Mac OS X 10.9.2//EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT UID:%s%s DTSTAMP:20140213T125414Z DTSTART;TZID=Europe/London:%s DTEND;TZID=Europe/London:%s RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10 SUMMARY:test DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:%s TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ mime_message = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Tue, 25 Feb 2014 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: %s Subject: "test" --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable *test* --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/calendar; charset=UTF-8; method=REQUEST; name=event.ics Content-Disposition: attachment; filename=event.ics Content-Transfer-Encoding: 8bit %s --=_c8894dbdb8baeedacae836230e3436fd-- """ -class TestResourceInvitation(unittest.TestCase): +class TestResourceInvitation(unittest.TestCase): john = None @classmethod def setUp(self): """ Compatibility for twisted.trial.unittest """ if not self.john: self.setup_class() @classmethod def setup_class(self, *args, **kw): # set language to default - pykolab.translate.setUserLanguage(conf.get('kolab','default_locale')) + pykolab.translate.setUserLanguage(conf.get('kolab', 'default_locale')) self.itip_reply_subject = _("Reservation Request for %(summary)s was %(status)s") from tests.functional.purge_users import purge_users purge_users() self.john = { 'displayname': 'John Doe', 'mail': 'john.doe@example.org', 'sender': 'John Doe ', 'mailbox': 'user/john.doe@example.org', 'dn': 'uid=doe,ou=People,dc=example,dc=org' } self.jane = { 'displayname': 'Jane Manager', 'mail': 'jane.manager@example.org', 'sender': 'Jane Manager ', 'mailbox': 'user/jane.manager@example.org', 'dn': 'uid=manager,ou=People,dc=example,dc=org' } from tests.functional.user_add import user_add user_add("John", "Doe", kolabinvitationpolicy='ALL_MANUAL') user_add("Jane", "Manager", kolabinvitationpolicy='ALL_MANUAL') funcs.purge_resources() self.audi = funcs.resource_add("car", "Audi A4") self.passat = funcs.resource_add("car", "VW Passat") self.boxter = funcs.resource_add("car", "Porsche Boxter S") - self.cars = funcs.resource_add("collection", "Company Cars", [ self.audi['dn'], self.passat['dn'], self.boxter['dn'] ]) + self.cars = funcs.resource_add("collection", "Company Cars", [self.audi['dn'], self.passat['dn'], self.boxter['dn']]) self.room1 = funcs.resource_add("confroom", "Room 101", owner=self.jane['dn'], kolabinvitationpolicy='ACT_ACCEPT_AND_NOTIFY') self.room2 = funcs.resource_add("confroom", "Conference Room B-222") - self.rooms = funcs.resource_add("collection", "Rooms", [ self.room1['dn'], self.room2['dn'] ], self.jane['dn'], kolabinvitationpolicy='ACT_ACCEPT_AND_NOTIFY') + self.rooms = funcs.resource_add("collection", "Rooms", [self.room1['dn'], self.room2['dn']], self.jane['dn'], kolabinvitationpolicy='ACT_ACCEPT_AND_NOTIFY') self.room3 = funcs.resource_add("confroom", "CEOs Office 303") - self.viprooms = funcs.resource_add("collection", "VIP Rooms", [ self.room3['dn'] ], self.jane['dn'], kolabinvitationpolicy='ACT_MANUAL') + self.viprooms = funcs.resource_add("collection", "VIP Rooms", [self.room3['dn']], self.jane['dn'], kolabinvitationpolicy='ACT_MANUAL') time.sleep(1) from tests.functional.synchronize import synchronize_once synchronize_once() def send_message(self, itip_payload, to_addr, from_addr=None): if from_addr is None: from_addr = self.john['mail'] smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(from_addr, to_addr, mime_message % (to_addr, itip_payload)) smtp.quit() def send_itip_invitation(self, resource_email, start=None, allday=False, template=None, uid=None, instance=None): if start is None: start = datetime.datetime.now() if uid is None: uid = str(uuid.uuid4()) if allday: default_template = itip_allday end = start + datetime.timedelta(days=1) date_format = '%Y%m%d' else: end = start + datetime.timedelta(hours=4) default_template = itip_invitation date_format = '%Y%m%dT%H%M%S' recurrence_id = '' if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/London:" + instance.strftime(date_format) self.send_message((template if template is not None else default_template) % ( uid, recurrence_id, start.strftime(date_format), end.strftime(date_format), resource_email ), resource_email) return uid def send_itip_update(self, resource_email, uid, start=None, template=None, sequence=None, instance=None): if start is None: start = datetime.datetime.now() end = start + datetime.timedelta(hours=4) recurrence_id = '' if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/London:" + instance.strftime('%Y%m%dT%H%M%S') if sequence is not None: if not template: template = itip_update template = re.sub(r'SEQUENCE:\d+', 'SEQUENCE:' + str(sequence), template) self.send_message((template if template is not None else itip_update) % ( uid, recurrence_id, start.strftime('%Y%m%dT%H%M%S'), end.strftime('%Y%m%dT%H%M%S'), resource_email ), resource_email) return uid def send_itip_cancel(self, resource_email, uid, instance=None): recurrence_id = '' if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/London:" + instance.strftime('%Y%m%dT%H%M%S') self.send_message(itip_cancellation % ( uid, recurrence_id, resource_email ), resource_email) return uid - def send_owner_response(self, event, partstat, from_addr=None): if from_addr is None: from_addr = self.jane['mail'] - itip_reply = event.to_message_itip(from_addr, - method="REPLY", - participant_status=partstat, - message_text="Request " + partstat, - subject="Booking has been %s" % (partstat) + itip_reply = event.to_message_itip( + from_addr, + method="REPLY", + participant_status=partstat, + message_text="Request " + partstat, + subject="Booking has been %s" % (partstat) ) smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(from_addr, str(event.get_organizer().email()), str(itip_reply)) smtp.quit() def check_message_received(self, subject, from_addr=None, mailbox=None): if mailbox is None: mailbox = self.john['mailbox'] imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrs") imap.imap.m.select(mailbox) found = None retries = 15 while not found and retries > 0: retries -= 1 typ, data = imap.imap.m.search(None, '(UNDELETED HEADER FROM "%s")' % (from_addr) if from_addr else 'UNDELETED') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') message = message_from_string(msg[0][1]) if message['Subject'] == subject: found = message break time.sleep(1) imap.disconnect() return found def check_resource_calendar_event(self, mailbox, uid=None): imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrs") imap.imap.m.select(imap.folder_quote(mailbox)) found = None retries = 10 while not found and retries > 0: retries -= 1 typ, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid) if uid else '(UNDELETED HEADER X-Kolab-Type "application/x-vnd.kolab.event")') for num in data[0].split(): typ, data = imap.imap.m.fetch(num, '(RFC822)') event_message = message_from_string(data[0][1]) # return matching UID or first event found if uid and event_message['subject'] != uid: continue found = event_from_message(event_message) if found: break time.sleep(1) imap.disconnect() return found def purge_mailbox(self, mailbox): imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrwcdest") imap.imap.m.select(u'"'+mailbox+'"') typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): imap.imap.m.store(num, '+FLAGS', '\\Deleted') imap.imap.m.expunge() imap.disconnect() - def find_resource_by_email(self, email): resource = None for r in [self.audi, self.passat, self.boxter, self.room1, self.room2]: if (email.find(r['mail']) >= 0): resource = r break return resource - def test_001_resource_from_email_address(self): resource = module_resources.resource_record_from_email_address(self.audi['mail']) self.assertEqual(len(resource), 1) self.assertEqual(resource[0], self.audi['dn']) collection = module_resources.resource_record_from_email_address(self.cars['mail']) self.assertEqual(len(collection), 1) self.assertEqual(collection[0], self.cars['dn']) - def test_002_invite_resource(self): - uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,7,13, 10,0,0)) + uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 7, 13, 10, 0, 0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.audi['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.audi['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test") - # @depends test_002_invite_resource def test_003_invite_resource_conflict(self): - uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,7,13, 12,0,0)) + uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 7, 13, 12, 0, 0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.audi['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.audi['mail']) self.assertIsInstance(response, email.message.Message) self.assertEqual(self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid), None) - def test_004_invite_resource_collection(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.cars['mail'], datetime.datetime(2014,7,13, 12,0,0)) + uid = self.send_itip_invitation(self.cars['mail'], datetime.datetime(2014, 7, 13, 12, 0, 0)) # one of the collection members accepted the reservation - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) delegatee = self.find_resource_by_email(accept['from']) self.assertIn(delegatee['mail'], accept['from']) # check booking in the delegatee's resource calendar self.assertIsInstance(self.check_resource_calendar_event(delegatee['kolabtargetfolder'], uid), pykolab.xml.Event) # resource collection responds with a DELEGATED message - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DELEGATED') }, self.cars['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DELEGATED')}, self.cars['mail']) self.assertIsInstance(response, email.message.Message) self.assertIn("ROLE=NON-PARTICIPANT;RSVP=FALSE", str(response)) - def test_005_rescheduling_reservation(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,4,1, 10,0,0)) + uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 4, 1, 10, 0, 0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.audi['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.audi['mail']) self.assertIsInstance(response, email.message.Message) self.purge_mailbox(self.john['mailbox']) - self.send_itip_update(self.audi['mail'], uid, datetime.datetime(2014,4,1, 12,0,0)) # conflict with myself + self.send_itip_update(self.audi['mail'], uid, datetime.datetime(2014, 4, 1, 12, 0, 0)) # conflict with myself - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.audi['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.audi['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_start().hour, 12) self.assertEqual(event.get_sequence(), 2) - def test_005_rescheduling_collection(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.cars['mail'], datetime.datetime(2014,4,24, 12,0,0)) + uid = self.send_itip_invitation(self.cars['mail'], datetime.datetime(2014, 4, 24, 12, 0, 0)) # one of the collection members accepted the reservation - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) delegatee = self.find_resource_by_email(accept['from']) # book that resource for the next day - self.send_itip_invitation(delegatee['mail'], datetime.datetime(2014,4,25, 14,0,0)) - accept2 = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + self.send_itip_invitation(delegatee['mail'], datetime.datetime(2014, 4, 25, 14, 0, 0)) + accept2 = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) # re-schedule first booking to a conflicting date self.purge_mailbox(self.john['mailbox']) update_template = itip_delegated.replace("resource-car-audia4@example.org", delegatee['mail']) - self.send_itip_update(delegatee['mail'], uid, datetime.datetime(2014,4,25, 12,0,0), template=update_template) + self.send_itip_update(delegatee['mail'], uid, datetime.datetime(2014, 4, 25, 12, 0, 0), template=update_template) # expect response from another member of the initially delegated collection - new_accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + new_accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(new_accept, email.message.Message) new_delegatee = self.find_resource_by_email(new_accept['from']) self.assertNotEqual(delegatee['mail'], new_delegatee['mail']) # event now booked into new delegate's calendar event = self.check_resource_calendar_event(new_delegatee['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # old resource responds with a DELEGATED message - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DELEGATED') }, delegatee['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DELEGATED')}, delegatee['mail']) self.assertIsInstance(response, email.message.Message) # old reservation was removed from old delegate's calendar self.assertEqual(self.check_resource_calendar_event(delegatee['kolabtargetfolder'], uid), None) - def test_006_cancelling_revervation(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.boxter['mail'], datetime.datetime(2014,5,1, 10,0,0)) + uid = self.send_itip_invitation(self.boxter['mail'], datetime.datetime(2014, 5, 1, 10, 0, 0)) self.assertIsInstance(self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid), pykolab.xml.Event) self.send_itip_cancel(self.boxter['mail'], uid) time.sleep(2) # wait for IMAP to update self.assertEqual(self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid), None) # make new reservation to the now free'd slot - self.send_itip_invitation(self.boxter['mail'], datetime.datetime(2014,5,1, 9,0,0)) + self.send_itip_invitation(self.boxter['mail'], datetime.datetime(2014, 5, 1, 9, 0, 0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.boxter['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.boxter['mail']) self.assertIsInstance(response, email.message.Message) - def test_007_update_delegated(self): self.purge_mailbox(self.john['mailbox']) - dt = datetime.datetime(2014,8,1, 12,0,0) + dt = datetime.datetime(2014, 8, 1, 12, 0, 0) uid = self.send_itip_invitation(self.cars['mail'], dt) # wait for accept notification - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) delegatee = self.find_resource_by_email(accept['from']) # send update message to all attendees (collection and delegatee) self.purge_mailbox(self.john['mailbox']) update_template = itip_delegated.replace("resource-car-audia4@example.org", delegatee['mail']) self.send_itip_update(self.cars['mail'], uid, dt, template=update_template) self.send_itip_update(delegatee['mail'], uid, dt, template=update_template) # get response from delegatee - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) self.assertIn(delegatee['mail'], accept['from']) # no delegation response on updates - self.assertEqual(self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DELEGATED') }, self.cars['mail']), None) - + self.assertEqual(self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DELEGATED')}, self.cars['mail']), None) def test_008_allday_reservation(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,6,2), True) + uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 6, 2), True) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) event = self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertIsInstance(event.get_start(), datetime.date) - uid2 = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,6,2, 16,0,0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.audi['mail']) + uid2 = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 6, 2, 16, 0, 0)) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.audi['mail']) self.assertIsInstance(response, email.message.Message) - def test_009_recurring_events(self): self.purge_mailbox(self.john['mailbox']) # register an infinitely recurring resource invitation - uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,2,20, 12,0,0), + uid = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 2, 20, 12, 0, 0), template=itip_recurring.replace(";COUNT=10", "")) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) # check non-recurring against recurring - uid2 = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,3,13, 10,0,0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.audi['mail']) + uid2 = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 3, 13, 10, 0, 0)) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.audi['mail']) self.assertIsInstance(response, email.message.Message) self.purge_mailbox(self.john['mailbox']) # check recurring against recurring - uid3 = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,2,22, 8,0,0), template=itip_recurring) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + uid3 = self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 2, 22, 8, 0, 0), template=itip_recurring) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) self.assertIsInstance(accept, email.message.Message) - def test_010_invalid_bookings(self): self.purge_mailbox(self.john['mailbox']) itip_other = itip_invitation.replace("mailto:%s", "mailto:some-other-resource@example.org\nCOMMENT: Sent to %s") - self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,3,22, 8,0,0), template=itip_other) + self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 3, 22, 8, 0, 0), template=itip_other) time.sleep(1) itip_invalid = itip_invitation.replace("DTSTART;", "X-DTSTART;") - self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014,3,24, 19,30,0), template=itip_invalid) - - self.assertEqual(self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.audi['mail']), None) + self.send_itip_invitation(self.audi['mail'], datetime.datetime(2014, 3, 24, 19, 30, 0), template=itip_invalid) + self.assertEqual(self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.audi['mail']), None) def test_011_owner_info(self): self.purge_mailbox(self.john['mailbox']) - self.send_itip_invitation(self.room1['mail'], datetime.datetime(2014,6,19, 16,0,0)) + self.send_itip_invitation(self.room1['mail'], datetime.datetime(2014, 6, 19, 16, 0, 0)) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.room1['mail']) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.room1['mail']) self.assertIsInstance(accept, email.message.Message) respose_text = str(accept.get_payload(0)) self.assertIn(self.jane['mail'], respose_text) self.assertIn(self.jane['displayname'], respose_text) - def test_011_owner_info_from_collection(self): self.purge_mailbox(self.john['mailbox']) - self.send_itip_invitation(self.room2['mail'], datetime.datetime(2014,6,19, 16,0,0)) + self.send_itip_invitation(self.room2['mail'], datetime.datetime(2014, 6, 19, 16, 0, 0)) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.room2['mail']) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.room2['mail']) self.assertIsInstance(accept, email.message.Message) respose_text = str(accept.get_payload(0)) self.assertIn(self.jane['mail'], respose_text) self.assertIn(self.jane['displayname'], respose_text) - def test_012_owner_notification(self): self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.jane['mailbox']) - self.send_itip_invitation(self.room1['mail'], datetime.datetime(2014,8,4, 13,0,0)) + self.send_itip_invitation(self.room1['mail'], datetime.datetime(2014, 8, 4, 13, 0, 0)) # check notification message sent to resource owner (jane) notify = self.check_message_received(_('Booking for %s has been %s') % (self.room1['cn'], participant_status_label('ACCEPTED')), self.room1['mail'], self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) notification_text = str(notify.get_payload()) self.assertIn(self.john['mail'], notification_text) self.assertIn(participant_status_label('ACCEPTED'), notification_text) self.purge_mailbox(self.john['mailbox']) # check notification sent to collection owner (jane) - self.send_itip_invitation(self.rooms['mail'], datetime.datetime(2014,8,4, 12,30,0)) + self.send_itip_invitation(self.rooms['mail'], datetime.datetime(2014, 8, 4, 12, 30, 0)) # one of the collection members accepted the reservation - accepted = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accepted = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}) delegatee = self.find_resource_by_email(accepted['from']) notify = self.check_message_received(_('Booking for %s has been %s') % (delegatee['cn'], participant_status_label('ACCEPTED')), delegatee['mail'], self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) self.assertIn(self.john['mail'], notification_text) - def test_013_owner_confirmation_accept(self): self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.jane['mailbox']) - uid = self.send_itip_invitation(self.room3['mail'], datetime.datetime(2014,9,12, 14,0,0)) + uid = self.send_itip_invitation(self.room3['mail'], datetime.datetime(2014, 9, 12, 14, 0, 0)) # requester (john) gets a TENTATIVE confirmation - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_resource_calendar_event(self.room3['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test") self.assertEqual(event.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'TENTATIVE') # check confirmation message sent to resource owner (jane) notify = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) # resource owner confirms reservation request itip_event = events_from_message(notify)[0] self.send_owner_response(itip_event['xml'], 'ACCEPTED', from_addr=self.jane['mail']) # requester (john) now gets the ACCEPTED response - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_resource_calendar_event(self.room3['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_status(True), 'CONFIRMED') self.assertEqual(event.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'ACCEPTED') - def test_014_owner_confirmation_decline(self): self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.jane['mailbox']) - uid = self.send_itip_invitation(self.room3['mail'], datetime.datetime(2014,9,14, 9,0,0)) + uid = self.send_itip_invitation(self.room3['mail'], datetime.datetime(2014, 9, 14, 9, 0, 0)) # requester (john) gets a TENTATIVE confirmation - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) # check confirmation message sent to resource owner (jane) notify = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) itip_event = events_from_message(notify)[0] # resource owner declines reservation request - itip_reply = itip_event['xml'].to_message_itip(self.jane['mail'], - method="REPLY", - participant_status='DECLINED', - message_text="Request declined", - subject=_('Booking for %s has been %s') % (self.room3['cn'], participant_status_label('DECLINED')) + itip_reply = itip_event['xml'].to_message_itip( + self.jane['mail'], + method="REPLY", + participant_status='DECLINED', + message_text="Request declined", + subject=_('Booking for %s has been %s') % (self.room3['cn'], participant_status_label('DECLINED')) ) smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(self.jane['mail'], str(itip_event['organizer']), str(itip_reply)) smtp.quit() # requester (john) now gets the DECLINED response - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) # tentative reservation was set to cancelled event = self.check_resource_calendar_event(self.room3['kolabtargetfolder'], uid) self.assertEqual(event, None) - #self.assertEqual(event.get_status(True), 'CANCELLED') - #self.assertEqual(event.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'DECLINED') - + # self.assertEqual(event.get_status(True), 'CANCELLED') + # self.assertEqual(event.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'DECLINED') def test_015_owner_confirmation_update(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.room3['mail'], datetime.datetime(2014,8,19, 9,0,0), uid="http://a-totally.stupid/?uid") + uid = self.send_itip_invitation(self.room3['mail'], datetime.datetime(2014, 8, 19, 9, 0, 0), uid="http://a-totally.stupid/?uid") # requester (john) gets a TENTATIVE confirmation - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) # check first confirmation message sent to resource owner (jane) notify1 = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify1, email.message.Message) itip_event1 = events_from_message(notify1)[0] self.assertEqual(itip_event1['start'].hour, 9) self.purge_mailbox(self.jane['mailbox']) self.purge_mailbox(self.john['mailbox']) # send update with new date (and sequence) - self.send_itip_update(self.room3['mail'], uid, datetime.datetime(2014,8,19, 16,0,0)) + self.send_itip_update(self.room3['mail'], uid, datetime.datetime(2014, 8, 19, 16, 0, 0)) event = self.check_resource_calendar_event(self.room3['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'TENTATIVE') # check second confirmation message sent to resource owner (jane) notify2 = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify2, email.message.Message) itip_event2 = events_from_message(notify2)[0] self.assertEqual(itip_event2['start'].hour, 16) # resource owner declines the first reservation request - itip_reply = itip_event1['xml'].to_message_itip(self.jane['mail'], - method="REPLY", - participant_status='DECLINED', - message_text="Request declined", - subject=_('Booking for %s has been %s') % (self.room3['cn'], participant_status_label('DECLINED')) + itip_reply = itip_event1['xml'].to_message_itip( + self.jane['mail'], + method="REPLY", + participant_status='DECLINED', + message_text="Request declined", + subject=_('Booking for %s has been %s') % (self.room3['cn'], participant_status_label('DECLINED')) ) smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(self.jane['mail'], str(itip_event1['organizer']), str(itip_reply)) smtp.quit() time.sleep(5) # resource owner accpets the second reservation request - itip_reply = itip_event2['xml'].to_message_itip(self.jane['mail'], - method="REPLY", - participant_status='ACCEPTED', - message_text="Request accepred", - subject=_('Booking for %s has been %s') % (self.room3['cn'], participant_status_label('ACCEPTED')) + itip_reply = itip_event2['xml'].to_message_itip( + self.jane['mail'], + method="REPLY", + participant_status='ACCEPTED', + message_text="Request accepred", + subject=_('Booking for %s has been %s') % (self.room3['cn'], participant_status_label('ACCEPTED')) ) smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(self.jane['mail'], str(itip_event2['organizer']), str(itip_reply)) smtp.quit() # requester (john) now gets the ACCEPTED response - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_resource_calendar_event(self.room3['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'ACCEPTED') - def test_016_collection_owner_confirmation(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.viprooms['mail'], datetime.datetime(2014,8,15, 17,0,0)) + uid = self.send_itip_invitation(self.viprooms['mail'], datetime.datetime(2014, 8, 15, 17, 0, 0)) # resource collection responds with a DELEGATED message - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DELEGATED') }, self.viprooms['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DELEGATED')}, self.viprooms['mail']) self.assertIsInstance(response, email.message.Message) # the collection member tentatively accepted the reservation - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }) + accept = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}) self.assertIsInstance(accept, email.message.Message) self.assertIn(self.room3['mail'], accept['from']) # check confirmation message sent to resource owner (jane) notify = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) - def test_017_reschedule_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) + subject = {'summary': 'test', 'status': participant_status_label('ACCEPTED')} + subject = self.itip_reply_subject % (subject) + # register a recurring resource invitation - start = datetime.datetime(2015,2,10, 12,0,0) + start = datetime.datetime(2015, 2, 10, 12, 0, 0) uid = self.send_itip_invitation(self.audi['mail'], start, template=itip_recurring) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.purge_mailbox(self.john['mailbox']) # send rescheduling request to a single instance exdate = start + datetime.timedelta(days=14) exstart = exdate + datetime.timedelta(hours=5) self.send_itip_update(self.audi['mail'], uid, exstart, instance=exdate) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + exdate.strftime('%Y%m%dT%H%M%S'), str(accept)) event = self.check_resource_calendar_event(self.audi['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) # send new invitation for now free slot uid = self.send_itip_invitation(self.audi['mail'], exdate, template=itip_invitation.replace('SUMMARY:test', 'SUMMARY:new')) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'new', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) # send rescheduling request to that single instance again: now conflicting exdate = start + datetime.timedelta(days=14) exstart = exdate + datetime.timedelta(hours=2) self.send_itip_update(self.audi['mail'], uid, exstart, instance=exdate, sequence=3) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}) self.assertIsInstance(response, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:", str(response)) - def test_018_invite_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.boxter['kolabtargetfolder']) - start = datetime.datetime(2015,3,2, 18,30,0) + subject = {'summary': 'test', 'status': participant_status_label('ACCEPTED')} + subject = self.itip_reply_subject % (subject) + + start = datetime.datetime(2015, 3, 2, 18, 30, 0) uid = self.send_itip_invitation(self.boxter['mail'], start, instance=start) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + start.strftime('%Y%m%dT%H%M%S'), str(accept)) self.purge_mailbox(self.john['mailbox']) # send a second invitation for another instance with the same UID - nextstart = datetime.datetime(2015,3,9, 18,30,0) + nextstart = datetime.datetime(2015, 3, 9, 18, 30, 0) self.send_itip_invitation(self.boxter['mail'], nextstart, uid=uid, instance=nextstart) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + nextstart.strftime('%Y%m%dT%H%M%S'), str(accept)) self.purge_mailbox(self.john['mailbox']) # send rescheduling request to the first instance exstart = start + datetime.timedelta(hours=2) self.send_itip_update(self.boxter['mail'], uid, exstart, instance=start) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + start.strftime('%Y%m%dT%H%M%S'), str(accept)) # the resource calendar now has two reservations stored in one object one = self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid) self.assertIsInstance(one, pykolab.xml.Event) self.assertIsInstance(one.get_recurrence_id(), datetime.datetime) self.assertEqual(one.get_start().hour, exstart.hour) two = one.get_instance(nextstart) self.assertIsInstance(two, pykolab.xml.Event) self.assertIsInstance(two.get_recurrence_id(), datetime.datetime) self.purge_mailbox(self.john['mailbox']) # send rescheduling request to the 2nd instance self.send_itip_update(self.boxter['mail'], uid, nextstart + datetime.timedelta(hours=2), sequence=2, instance=nextstart) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + nextstart.strftime('%Y%m%dT%H%M%S'), str(accept)) event = self.check_resource_calendar_event(self.boxter['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) two = event.get_instance(nextstart) self.assertIsInstance(two, pykolab.xml.Event) self.assertEqual(two.get_sequence(), 2) self.assertEqual(two.get_start().hour, 20) - def test_019_cancel_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) + subject = {'summary': 'test', 'status': participant_status_label('ACCEPTED')} + subject = self.itip_reply_subject % (subject) + # register a recurring resource invitation - start = datetime.datetime(2015,2,12, 14,0,0) + start = datetime.datetime(2015, 2, 12, 14, 0, 0) uid = self.send_itip_invitation(self.passat['mail'], start, template=itip_recurring) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) exdate = start + datetime.timedelta(days=7) self.send_itip_cancel(self.passat['mail'], uid, instance=exdate) time.sleep(5) # wait for IMAP to update event = self.check_resource_calendar_event(self.passat['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertEqual(exception.get_status(True), 'CANCELLED') self.assertTrue(exception.get_transparency()) self.purge_mailbox(self.john['mailbox']) # store a single occurrence with recurrence-id - start = datetime.datetime(2015,3,2, 18,30,0) + start = datetime.datetime(2015, 3, 2, 18, 30, 0) uid = self.send_itip_invitation(self.passat['mail'], start, instance=start) - accept = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }) + accept = self.check_message_received(subject) self.assertIsInstance(accept, email.message.Message) self.send_itip_cancel(self.passat['mail'], uid, instance=start) time.sleep(5) # wait for IMAP to update self.assertEqual(self.check_resource_calendar_event(self.passat['kolabtargetfolder'], uid), None) - def test_020_owner_confirmation_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.jane['mailbox']) - start = datetime.datetime(2015,4,18, 14,0,0) + start = datetime.datetime(2015, 4, 18, 14, 0, 0) uid = self.send_itip_invitation(self.room3['mail'], start, template=itip_recurring) notify = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) # resource owner confirms reservation request (entire series) itip_event = events_from_message(notify)[0] self.send_owner_response(itip_event['xml'], 'ACCEPTED', from_addr=self.jane['mail']) self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.jane['mailbox']) # send rescheduling request to a single instance exdate = start + datetime.timedelta(days=14) exstart = exdate + datetime.timedelta(hours=4) self.send_itip_update(self.room3['mail'], uid, exstart, instance=exdate) # check confirmation message sent to resource owner (jane) notify = self.check_message_received(_('Booking request for %s requires confirmation') % (self.room3['cn']), mailbox=self.jane['mailbox']) self.assertIsInstance(notify, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + exdate.strftime('%Y%m%dT%H%M%S'), str(notify)) itip_event = events_from_message(notify)[0] self.assertIsInstance(itip_event['xml'].get_recurrence_id(), datetime.datetime) # resource owner declines reservation request self.send_owner_response(itip_event['xml'], 'DECLINED', from_addr=self.jane['mail']) # requester (john) now gets the DECLINED response - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.room3['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.room3['mail']) self.assertIsInstance(response, email.message.Message) self.assertIn("RECURRENCE-ID;TZID=Europe/London:" + exdate.strftime('%Y%m%dT%H%M%S'), str(response)) event = self.check_resource_calendar_event(self.room3['kolabtargetfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertEqual(exception.get_attendee_by_email(self.room3['mail']).get_participant_status(True), 'DECLINED') - diff --git a/tests/functional/test_wallace/test_006_resource_performance.py b/tests/functional/test_wallace/test_006_resource_performance.py index ed83611..814a111 100644 --- a/tests/functional/test_wallace/test_006_resource_performance.py +++ b/tests/functional/test_wallace/test_006_resource_performance.py @@ -1,145 +1,142 @@ import time import datetime import pykolab import pytz import uuid import string import random from pykolab.xml import Event from pykolab.xml import Attendee from pykolab.imap import IMAP from wallace import module_resources from twisted.trial import unittest from tests.functional import resource_func as funcs from tests.functional.synchronize import synchronize_once conf = pykolab.getConf() class TestWallacePerformance(unittest.TestCase): rooms = None @classmethod def setUp(self): """ Compatibility for twisted.trial.unittest """ if not self.rooms: self.setup_class() @classmethod def setup_class(self, *args, **kw): funcs.purge_resources() self.room1 = funcs.resource_add("confroom", "Room 101") self.room2 = funcs.resource_add("confroom", "Conference Room B-222") - self.rooms = funcs.resource_add("collection", "Rooms", [ self.room1['dn'], self.room2['dn'] ]) + self.rooms = funcs.resource_add("collection", "Rooms", [self.room1['dn'], self.room2['dn']]) time.sleep(1) synchronize_once() module_resources.imap = IMAP() module_resources.imap.connect() def purge_mailbox(self, mailbox): imap = IMAP() imap.connect() imap.set_acl(mailbox, "cyrus-admin", "lrwcdest") imap.imap.m.select(imap.folder_quote(mailbox)) typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): imap.imap.m.store(num, '+FLAGS', '\\Deleted') imap.imap.m.expunge() imap.disconnect() def populate_calendar(self, resource, num=10, date=None): if date is None: date = datetime.datetime.now(pytz.timezone("Europe/London")) i = 0 while i < num: offset = random.randint(-3200, 3200) * 10 duration = random.randint(3, 72) * 10 summary = ''.join(random.sample((string.ascii_uppercase + string.digits) * 12, random.randint(6, 18))) start = date + datetime.timedelta(minutes=offset) event = Event() event.set_summary(summary) event.set_start(start) event.set_end(start + datetime.timedelta(minutes=duration)) saved = module_resources.save_resource_event(dict(xml=event), resource) i += 1 - def test_001_save_resource_event(self): event = Event() event.set_summary("test") date = datetime.datetime.now(pytz.timezone("Europe/London")) event.set_start(date) event.set_end(date + datetime.timedelta(hours=2)) saved = module_resources.save_resource_event(dict(xml=event), self.room1) self.assertTrue(saved) - def test_002_read_resource_calendar(self): self.purge_mailbox(self.room1['kolabtargetfolder']) event = Event() event.set_summary("test") - event.set_start(datetime.datetime(2014,4,1, 12,0,0, tzinfo=pytz.timezone("Europe/London"))) - event.set_end(datetime.datetime(2014,4,1, 14,0,0, tzinfo=pytz.timezone("Europe/London"))) + event.set_start(datetime.datetime(2014, 4, 1, 12, 0, 0, tzinfo=pytz.timezone("Europe/London"))) + event.set_end(datetime.datetime(2014, 4, 1, 14, 0, 0, tzinfo=pytz.timezone("Europe/London"))) saved = module_resources.save_resource_event(dict(xml=event), self.room1) self.assertTrue(saved) uid = event.get_uid() itip = dict( - uid = str(uuid.uuid4()), - sequence = 0, - start = datetime.datetime(2014,4,1, 13,0,0, tzinfo=pytz.timezone("Europe/London")), - end = datetime.datetime(2014,4,1, 14,30,0, tzinfo=pytz.timezone("Europe/London")) + uid=str(uuid.uuid4()), + sequence=0, + start=datetime.datetime(2014, 4, 1, 13, 0, 0, tzinfo=pytz.timezone("Europe/London")), + end=datetime.datetime(2014, 4, 1, 14, 30, 0, tzinfo=pytz.timezone("Europe/London")) ) event.set_uid(itip['uid']) event.set_start(itip['start']) event.set_end(itip['end']) itip['xml'] = event - res = module_resources.read_resource_calendar(self.room1, [ itip ]) + res = module_resources.read_resource_calendar(self.room1, [itip]) self.assertEqual(res, 1) self.assertTrue(self.room1['conflict']) self.assertIn(uid, self.room1['conflicting_events']) - def test_003_read_time(self): self.purge_mailbox(self.room1['kolabtargetfolder']) # populate 5K random events num = 5000 date = datetime.datetime.now(pytz.timezone("Europe/London")).replace(hour=10, minute=0, second=0, microsecond=0) self.populate_calendar(self.room1, num, date) itip = dict( - uid = str(uuid.uuid4()), - sequence = 0, - start = date, - end = date + datetime.timedelta(minutes=90) + uid=str(uuid.uuid4()), + sequence=0, + start=date, + end=date + datetime.timedelta(minutes=90) ) event = Event() event.set_uid(itip['uid']) event.set_start(itip['start']) event.set_end(itip['end']) itip['xml'] = event start = time.time() - res = module_resources.read_resource_calendar(self.room1, [ itip ]) + res = module_resources.read_resource_calendar(self.room1, [itip]) self.assertEqual(res, num) print "\nREAD TIME:", time.time() - start print "CONFLICTS:", self.room1['conflicting_events'] diff --git a/tests/functional/test_wallace/test_007_invitationpolicy.py b/tests/functional/test_wallace/test_007_invitationpolicy.py index f019a91..e11e906 100644 --- a/tests/functional/test_wallace/test_007_invitationpolicy.py +++ b/tests/functional/test_wallace/test_007_invitationpolicy.py @@ -1,1423 +1,1397 @@ import time import pykolab import smtplib import email import datetime import pytz import uuid import kolabformat from pykolab.imap import IMAP from wallace import module_resources from pykolab.translate import _ from pykolab.xml import event_from_message from pykolab.xml import todo_from_message from pykolab.xml import participant_status_label from pykolab.itip import events_from_message from email import message_from_string from twisted.trial import unittest import tests.functional.resource_func as funcs conf = pykolab.getConf() itip_invitation = """ 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:%(uid)s%(recurrenceid)s DTSTAMP:20140213T125414Z DTSTART;TZID=Europe/Berlin:%(start)s DTEND;TZID=Europe/Berlin:%(end)s SUMMARY:%(summary)s DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=%(partstat)s;RSVP=TRUE:mailto:%(mailto)s ATTENDEE;ROLE=OPT-PARTICIPANT;PARTSTAT=TENTATIVE;RSVP=FALSE:mailto:somebody@else.com TRANSP:OPAQUE SEQUENCE:%(sequence)d END:VEVENT END:VCALENDAR """ itip_cancellation = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube Webmail 0.9-0.3.el6.kolab_3.0//NONSGML Calendar//EN CALSCALE:GREGORIAN METHOD:CANCEL BEGIN:VEVENT UID:%(uid)s%(recurrenceid)s DTSTAMP:20140218T125414Z DTSTART;TZID=Europe/Berlin:20120713T100000 DTEND;TZID=Europe/Berlin:20120713T110000 SUMMARY:%(summary)s DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE:mailto:%(mailto)s TRANSP:OPAQUE SEQUENCE:%(sequence)d END:VEVENT END:VCALENDAR """ itip_recurring = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//Mac OS X 10.9.2//EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT UID:%(uid)s DTSTAMP:20140213T125414Z DTSTART;TZID=Europe/Berlin:%(start)s DTEND;TZID=Europe/Berlin:%(end)s RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10 SUMMARY:%(summary)s DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=%(partstat)s;RSVP=TRUE:mailto:%(mailto)s TRANSP:OPAQUE SEQUENCE:%(sequence)d END:VEVENT END:VCALENDAR """ itip_reply = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//pykolab-0.6.9-1//kolab.org// CALSCALE:GREGORIAN METHOD:REPLY BEGIN:VEVENT SUMMARY:%(summary)s UID:%(uid)s%(recurrenceid)s DTSTART;TZID=Europe/Berlin;VALUE=DATE-TIME:%(start)s DTEND;TZID=Europe/Berlin;VALUE=DATE-TIME:%(end)s DTSTAMP;VALUE=DATE-TIME:20140706T171038Z ORGANIZER;CN="Doe, John":MAILTO:%(organizer)s ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=%(partstat)s;ROLE=REQ-PARTICIPANT:mailto:%(mailto)s PRIORITY:0 SEQUENCE:%(sequence)d END:VEVENT END:VCALENDAR """ itip_delegated = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//pykolab-0.6.9-1//kolab.org// CALSCALE:GREGORIAN METHOD:REPLY BEGIN:VEVENT SUMMARY:%(summary)s UID:%(uid)s%(recurrenceid)s DTSTART;TZID=Europe/Berlin;VALUE=DATE-TIME:%(start)s DTEND;TZID=Europe/Berlin;VALUE=DATE-TIME:%(end)s DTSTAMP;VALUE=DATE-TIME:20140706T171038Z ORGANIZER;CN="Doe, John":MAILTO:%(organizer)s ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO=jack@ripper.com;ROLE=NON-PARTICIPANT:mailto:%(mailto)s ATTENDEE;PARTSTAT=%(partstat)s;DELEGATED-FROM=%(mailto)s;ROLE=REQ-PARTICIPANT:mailto:jack@ripper.com PRIORITY:0 SEQUENCE:%(sequence)d END:VEVENT END:VCALENDAR """ itip_todo = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VTODO UID:%(uid)s CREATED;VALUE=DATE-TIME:20140731T100704Z DTSTAMP;VALUE=DATE-TIME:20140820T101333Z DTSTART;VALUE=DATE-TIME;TZID=Europe/Berlin:%(start)s DUE;VALUE=DATE-TIME;TZID=Europe/Berlin:%(end)s SUMMARY:%(summary)s SEQUENCE:%(sequence)d PRIORITY:1 STATUS:NEEDS-ACTION PERCENT-COMPLETE:0 ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;PARTSTAT=%(partstat)s;ROLE=REQ-PARTICIPANT:mailto:%(mailto)s END:VTODO END:VCALENDAR """ itip_todo_reply = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN METHOD:REPLY BEGIN:VTODO UID:%(uid)s CREATED;VALUE=DATE-TIME:20140731T100704Z DTSTAMP;VALUE=DATE-TIME:20140821T085424Z DTSTART;VALUE=DATE-TIME;TZID=Europe/Berlin:%(start)s DUE;VALUE=DATE-TIME;TZID=Europe/Berlin:%(end)s SUMMARY:%(summary)s SEQUENCE:%(sequence)d PRIORITY:1 STATUS:NEEDS-ACTION PERCENT-COMPLETE:40 ATTENDEE;PARTSTAT=%(partstat)s;ROLE=REQ-PARTICIPANT;CUTYPE=INDIVIDUAL:mailto:%(mailto)s ORGANIZER;CN="Doe, John":mailto:%(organizer)s END:VTODO END:VCALENDAR """ itip_todo_cancel = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN METHOD:CANCEL BEGIN:VTODO UID:%(uid)s CREATED;VALUE=DATE-TIME:20140731T100704Z DTSTAMP;VALUE=DATE-TIME:20140820T101333Z SUMMARY:%(summary)s SEQUENCE:%(sequence)d PRIORITY:1 STATUS:CANCELLED ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:%(mailto)s END:VTODO END:VCALENDAR """ mime_message = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Tue, 25 Feb 2014 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> To: %s Subject: "test" --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable *test* --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/calendar; charset=UTF-8; method=%s; name=event.ics Content-Disposition: attachment; filename=event.ics Content-Transfer-Encoding: 8bit %s --=_c8894dbdb8baeedacae836230e3436fd-- """ + class TestWallaceInvitationpolicy(unittest.TestCase): john = None itip_reply_subject = None @classmethod def setUp(self): """ Compatibility for twisted.trial.unittest """ if not self.john: self.setup_class() @classmethod def setup_class(self, *args, **kw): # set language to default - pykolab.translate.setUserLanguage(conf.get('kolab','default_locale')) + pykolab.translate.setUserLanguage(conf.get('kolab', 'default_locale')) self.itip_reply_subject = _('"%(summary)s" has been %(status)s') from tests.functional.purge_users import purge_users purge_users() self.john = { 'displayname': 'John Doe', 'mail': 'john.doe@example.org', 'dn': 'uid=doe,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/john.doe@example.org', 'kolabcalendarfolder': 'user/john.doe/Calendar@example.org', 'kolabtasksfolder': 'user/john.doe/Tasks@example.org', - 'kolabinvitationpolicy': ['ACT_UPDATE_AND_NOTIFY','ACT_MANUAL'] + 'kolabinvitationpolicy': ['ACT_UPDATE_AND_NOTIFY', 'ACT_MANUAL'] } self.jane = { 'displayname': 'Jane Manager', 'mail': 'jane.manager@example.org', 'dn': 'uid=manager,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/jane.manager@example.org', 'kolabcalendarfolder': 'user/jane.manager/Calendar@example.org', 'kolabtasksfolder': 'user/jane.manager/Tasks@example.org', 'kolabconfidentialcalendar': 'user/jane.manager/Calendar/Confidential@example.org', - 'kolabinvitationpolicy': ['ACT_ACCEPT_IF_NO_CONFLICT','ACT_REJECT_IF_CONFLICT','TASK_ACCEPT','TASK_UPDATE_AND_NOTIFY','ACT_UPDATE'] + 'kolabinvitationpolicy': ['ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_REJECT_IF_CONFLICT', 'TASK_ACCEPT', 'TASK_UPDATE_AND_NOTIFY', 'ACT_UPDATE'] } self.jack = { 'displayname': 'Jack Tentative', 'mail': 'jack.tentative@example.org', 'dn': 'uid=tentative,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/jack.tentative@example.org', 'kolabcalendarfolder': 'user/jack.tentative/Calendar@example.org', 'kolabtasksfolder': 'user/jack.tentative/Tasks@example.org', - 'kolabinvitationpolicy': ['ACT_TENTATIVE_IF_NO_CONFLICT','ALL_SAVE_TO_FOLDER','ACT_UPDATE'] + 'kolabinvitationpolicy': ['ACT_TENTATIVE_IF_NO_CONFLICT', 'ALL_SAVE_TO_FOLDER', 'ACT_UPDATE'] } self.mark = { 'displayname': 'Mark German', 'mail': 'mark.german@example.org', 'dn': 'uid=german,ou=People,dc=example,dc=org', 'preferredlanguage': 'de_DE', 'mailbox': 'user/mark.german@example.org', 'kolabcalendarfolder': 'user/mark.german/Calendar@example.org', 'kolabtasksfolder': 'user/mark.german/Tasks@example.org', - 'kolabinvitationpolicy': ['ACT_ACCEPT','ACT_UPDATE_AND_NOTIFY'] + 'kolabinvitationpolicy': ['ACT_ACCEPT', 'ACT_UPDATE_AND_NOTIFY'] } self.lucy = { 'displayname': 'Lucy Meyer', 'mail': 'lucy.meyer@example.org', 'dn': 'uid=meyer,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/lucy.meyer@example.org', 'kolabcalendarfolder': 'user/lucy.meyer/Calendar@example.org', 'kolabtasksfolder': 'user/lucy.meyer/Tasks@example.org', - 'kolabinvitationpolicy': ['ALL_SAVE_AND_FORWARD','ACT_CANCEL_DELETE_AND_NOTIFY','ACT_UPDATE_AND_NOTIFY'] + 'kolabinvitationpolicy': ['ALL_SAVE_AND_FORWARD', 'ACT_CANCEL_DELETE_AND_NOTIFY', 'ACT_UPDATE_AND_NOTIFY'] } self.bill = { 'displayname': 'Bill Mayor', 'mail': 'bill.mayor@example.org', 'dn': 'uid=mayor,ou=People,dc=example,dc=org', 'preferredlanguage': 'en_US', 'mailbox': 'user/bill.mayor@example.org', 'kolabcalendarfolder': 'user/bill.mayor/Calendar@example.org', 'kolabtasksfolder': 'user/bill.mayor/Tasks@example.org', - 'kolabinvitationpolicy': ['ALL_SAVE_TO_FOLDER:lucy.meyer@example.org','ALL_REJECT'] + 'kolabinvitationpolicy': ['ALL_SAVE_TO_FOLDER:lucy.meyer@example.org', 'ALL_REJECT'] } self.external = { 'displayname': 'Bob External', 'mail': 'bob.external@gmail.com' } from tests.functional.user_add import user_add user_add("John", "Doe", kolabinvitationpolicy=self.john['kolabinvitationpolicy'], preferredlanguage=self.john['preferredlanguage']) user_add("Jane", "Manager", kolabinvitationpolicy=self.jane['kolabinvitationpolicy'], preferredlanguage=self.jane['preferredlanguage'], kolabdelegate=[self.mark['dn']]) user_add("Jack", "Tentative", kolabinvitationpolicy=self.jack['kolabinvitationpolicy'], preferredlanguage=self.jack['preferredlanguage']) user_add("Mark", "German", kolabinvitationpolicy=self.mark['kolabinvitationpolicy'], preferredlanguage=self.mark['preferredlanguage']) user_add("Lucy", "Meyer", kolabinvitationpolicy=self.lucy['kolabinvitationpolicy'], preferredlanguage=self.lucy['preferredlanguage']) user_add("Bill", "Mayor", kolabinvitationpolicy=self.bill['kolabinvitationpolicy'], preferredlanguage=self.bill['preferredlanguage']) time.sleep(1) from tests.functional.synchronize import synchronize_once synchronize_once() time.sleep(4) # create confidential calendar folder for jane imap = IMAP() - imap.connect(domain='example.org') # sets self.domain + imap.connect(domain='example.org') # sets self.domain imap.user_mailbox_create_additional_folders(self.jane['mail'], { 'Calendar/Confidential': { 'annotations': { '/shared/vendor/kolab/folder-type': "event", '/private/vendor/kolab/folder-type': "event.confidential" } } }) # grant full access for Mark to Jane's calendar imap.set_acl(imap.folder_quote(self.jane['kolabcalendarfolder']), self.mark['mail'], "lrswipkxtecda") imap.disconnect() - def send_message(self, itip_payload, to_addr, from_addr=None, method="REQUEST"): if from_addr is None: from_addr = self.john['mail'] smtp = smtplib.SMTP('localhost', 10026) smtp.sendmail(from_addr, to_addr, mime_message % (to_addr, method, itip_payload)) def send_itip_invitation(self, attendee_email, start=None, allday=False, template=None, summary="test", sequence=0, partstat='NEEDS-ACTION', from_addr=None, uid=None, instance=None): if start is None: start = datetime.datetime.now() if uid is None: uid = str(uuid.uuid4()) recurrence_id = '' if allday: default_template = itip_allday end = start + datetime.timedelta(days=1) date_format = '%Y%m%d' else: end = start + datetime.timedelta(hours=4) default_template = itip_invitation date_format = '%Y%m%dT%H%M%S' if from_addr is not None: if template: template = template.replace("john.doe@example.org", from_addr) else: default_template = default_template.replace("john.doe@example.org", from_addr) if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin:" + instance.strftime(date_format) self.send_message((template if template is not None else default_template) % { 'uid': uid, 'recurrenceid': recurrence_id, 'start': start.strftime(date_format), 'end': end.strftime(date_format), 'mailto': attendee_email, 'summary': summary, 'sequence': sequence, 'partstat': partstat }, attendee_email, from_addr=from_addr) return uid def send_itip_update(self, attendee_email, uid, start=None, template=None, summary="test", sequence=1, partstat='ACCEPTED', instance=None): if start is None: start = datetime.datetime.now() end = start + datetime.timedelta(hours=4) date_format = '%Y%m%dT%H%M%S' recurrence_id = '' if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin:" + instance.strftime(date_format) self.send_message((template if template is not None else itip_invitation) % { 'uid': uid, 'recurrenceid': recurrence_id, 'start': start.strftime(date_format), 'end': end.strftime(date_format), 'mailto': attendee_email, 'summary': summary, 'sequence': sequence, 'partstat': partstat }, attendee_email) return uid def send_itip_reply(self, uid, attendee_email, mailto, start=None, template=None, summary="test", sequence=0, partstat='ACCEPTED', instance=None): if start is None: start = datetime.datetime.now() end = start + datetime.timedelta(hours=4) date_format = '%Y%m%dT%H%M%S' recurrence_id = '' if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin:" + instance.strftime(date_format) self.send_message((template if template is not None else itip_reply) % { 'uid': uid, 'recurrenceid': recurrence_id, 'start': start.strftime(date_format), 'end': end.strftime(date_format), 'mailto': attendee_email, 'organizer': mailto, 'summary': summary, 'sequence': sequence, 'partstat': partstat }, mailto, attendee_email, method='REPLY') return uid def send_itip_cancel(self, attendee_email, uid, template=None, summary="test", sequence=1, instance=None, thisandfuture=False): recurrence_id = '' if instance is not None: recurrence_id = "\nRECURRENCE-ID;TZID=Europe/Berlin%s:%s" % ( ';RANGE=THISANDFUTURE' if thisandfuture else '', instance.strftime('%Y%m%dT%H%M%S') ) self.send_message((template if template is not None else itip_cancellation) % { 'uid': uid, 'recurrenceid': recurrence_id, 'mailto': attendee_email, 'summary': summary, 'sequence': sequence, }, attendee_email, method='CANCEL') return uid def create_calendar_event(self, start=None, summary="test", sequence=0, user=None, attendees=None, folder=None, recurring=False, uid=None): if start is None: start = datetime.datetime.now(pytz.timezone("Europe/Berlin")) if user is None: user = self.john if attendees is None: attendees = [self.jane] if folder is None: folder = user['kolabcalendarfolder'] end = start + datetime.timedelta(hours=4) event = pykolab.xml.Event() event.set_start(start) event.set_end(end) event.set_organizer(user['mail'], user['displayname']) if uid: event.set_uid(uid) for attendee in attendees: event.add_attendee(attendee['mail'], attendee['displayname'], role="REQ-PARTICIPANT", participant_status="NEEDS-ACTION", rsvp=True) event.set_summary(summary) event.set_sequence(sequence) if recurring and isinstance(recurring, kolabformat.RecurrenceRule): event.set_recurrence(rrule) else: rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Daily) rrule.setCount(10) event.set_recurrence(rrule) # create event with attachment vattach = event.get_attachments() attachment = kolabformat.Attachment() attachment.setLabel('attach.txt') attachment.setData('This is a text attachment', 'text/plain') vattach.append(attachment) event.event.setAttachments(vattach) imap = IMAP() imap.connect() mailbox = imap.folder_quote(folder) imap.set_acl(mailbox, "cyrus-admin", "lrswipkxtecda") imap.imap.m.select(mailbox) result = imap.imap.m.append( mailbox, None, None, event.to_message().as_string() ) return event.get_uid() def create_task_assignment(self, due=None, summary="test", sequence=0, user=None, attendees=None): if due is None: due = datetime.datetime.now(pytz.timezone("Europe/Berlin")) + datetime.timedelta(days=2) if user is None: user = self.john if attendees is None: attendees = [self.jane] todo = pykolab.xml.Todo() todo.set_due(due) todo.set_organizer(user['mail'], user['displayname']) for attendee in attendees: todo.add_attendee(attendee['mail'], attendee['displayname'], role="REQ-PARTICIPANT", participant_status="NEEDS-ACTION", rsvp=True) todo.set_summary(summary) todo.set_sequence(sequence) imap = IMAP() imap.connect() mailbox = imap.folder_quote(user['kolabtasksfolder']) imap.set_acl(mailbox, "cyrus-admin", "lrswipkxtecda") imap.imap.m.select(mailbox) result = imap.imap.m.append( mailbox, None, None, todo.to_message().as_string() ) return todo.get_uid() def update_calendar_event(self, uid, start=None, summary=None, sequence=0, user=None): if user is None: user = self.john event = self.check_user_calendar_event(user['kolabcalendarfolder'], uid) if event: if start is not None: event.set_start(start) if summary is not None: event.set_summary(summary) if sequence is not None: event.set_sequence(sequence) imap = IMAP() imap.connect() mailbox = imap.folder_quote(user['kolabcalendarfolder']) imap.set_acl(mailbox, "cyrus-admin", "lrswipkxtecda") imap.imap.m.select(mailbox) return imap.imap.m.append( mailbox, None, None, event.to_message().as_string() ) return False def check_message_received(self, subject, from_addr=None, mailbox=None): if mailbox is None: mailbox = self.john['mailbox'] imap = IMAP() imap.connect() mailbox = imap.folder_quote(mailbox) imap.set_acl(mailbox, "cyrus-admin", "lrs") imap.imap.m.select(mailbox) found = None retries = 15 while not found and retries > 0: retries -= 1 typ, data = imap.imap.m.search(None, '(UNDELETED HEADER FROM "%s")' % (from_addr) if from_addr else 'UNDELETED') for num in data[0].split(): typ, msg = imap.imap.m.fetch(num, '(RFC822)') message = message_from_string(msg[0][1]) if message['Subject'] == subject: found = message break time.sleep(1) imap.disconnect() return found def check_user_calendar_event(self, mailbox, uid=None): return self.check_user_imap_object(mailbox, uid) def check_user_imap_object(self, mailbox, uid=None, type='event'): imap = IMAP() imap.connect() mailbox = imap.folder_quote(mailbox) imap.set_acl(mailbox, "cyrus-admin", "lrs") imap.imap.m.select(mailbox) found = None retries = 15 while not found and retries > 0: retries -= 1 typ, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid) if uid else '(UNDELETED HEADER X-Kolab-Type "application/x-vnd.kolab.' + type + '")') for num in data[0].split(): typ, data = imap.imap.m.fetch(num, '(RFC822)') object_message = message_from_string(data[0][1]) # return matching UID or first event found if uid and object_message['subject'] != uid: continue if type == 'task': found = todo_from_message(object_message) else: found = event_from_message(object_message) if found: break time.sleep(1) return found def purge_mailbox(self, mailbox): imap = IMAP() imap.connect() mailbox = imap.folder_quote(mailbox) imap.set_acl(mailbox, "cyrus-admin", "lrwcdest") imap.imap.m.select(mailbox) typ, data = imap.imap.m.search(None, 'ALL') for num in data[0].split(): imap.imap.m.store(num, '+FLAGS', '\\Deleted') imap.imap.m.expunge() imap.disconnect() - def test_001_invite_accept_udate(self): - start = datetime.datetime(2014,8,13, 10,0,0) + start = datetime.datetime(2014, 8, 13, 10, 0, 0) uid = self.send_itip_invitation(self.jane['mail'], start) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test") # send update with the same sequence: no re-scheduling self.send_itip_update(self.jane['mail'], uid, start, summary="test updated", sequence=0, partstat='ACCEPTED') time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test updated") self.assertEqual(event.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) - # @depends on test_001_invite_user def test_002_invite_conflict_reject(self): - uid = self.send_itip_invitation(self.jane['mail'], datetime.datetime(2014,8,13, 11,0,0), summary="test2") + uid = self.send_itip_invitation(self.jane['mail'], datetime.datetime(2014, 8, 13, 11, 0, 0), summary="test2") - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test2', 'status':participant_status_label('DECLINED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test2', 'status': participant_status_label('DECLINED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test2") - def test_003_invite_accept_tentative(self): self.purge_mailbox(self.john['mailbox']) - uid = self.send_itip_invitation(self.jack['mail'], datetime.datetime(2014,7,24, 8,0,0)) + uid = self.send_itip_invitation(self.jack['mail'], datetime.datetime(2014, 7, 24, 8, 0, 0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }, self.jack['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}, self.jack['mail']) self.assertIsInstance(response, email.message.Message) - def test_004_copy_to_calendar(self): self.purge_mailbox(self.john['mailbox']) - self.send_itip_invitation(self.jack['mail'], datetime.datetime(2014,7,29, 8,0,0)) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }, self.jack['mail']) + self.send_itip_invitation(self.jack['mail'], datetime.datetime(2014, 7, 29, 8, 0, 0)) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}, self.jack['mail']) self.assertIsInstance(response, email.message.Message) # send conflicting request to jack - uid = self.send_itip_invitation(self.jack['mail'], datetime.datetime(2014,7,29, 10,0,0), summary="test2") - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test2', 'status':participant_status_label('DECLINED') }, self.jack['mail']) + uid = self.send_itip_invitation(self.jack['mail'], datetime.datetime(2014, 7, 29, 10, 0, 0), summary="test2") + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test2', 'status': participant_status_label('DECLINED')}, self.jack['mail']) self.assertEqual(response, None, "No reply expected") event = self.check_user_calendar_event(self.jack['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test2") self.assertEqual(event.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartNeedsAction) - def test_004_copy_to_calendar_and_forward(self): - uid = self.send_itip_invitation(self.lucy['mail'], datetime.datetime(2015,2,11, 14,0,0), summary="test forward") - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test forward', 'status':participant_status_label('ACCEPTED') }, self.lucy['mail'], self.lucy['mailbox']) + uid = self.send_itip_invitation(self.lucy['mail'], datetime.datetime(2015, 2, 11, 14, 0, 0), summary="test forward") + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test forward', 'status': participant_status_label('ACCEPTED')}, self.lucy['mail'], self.lucy['mailbox']) self.assertEqual(response, None, "No reply expected") event = self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test forward") self.assertEqual(event.get_attendee(self.lucy['mail']).get_participant_status(), kolabformat.PartNeedsAction) # find original itip invitation in user's inbox message = self.check_message_received('"test"', 'john.doe@example.org', self.lucy['mailbox']) self.assertIsInstance(message, email.message.Message) itips = events_from_message(message) - self.assertEqual(len(itips), 1); - self.assertEqual(itips[0]['method'], 'REQUEST'); - self.assertEqual(itips[0]['uid'], uid); - + self.assertEqual(len(itips), 1) + self.assertEqual(itips[0]['method'], 'REQUEST') + self.assertEqual(itips[0]['uid'], uid) def test_005_invite_rescheduling_accept(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2014,8,14, 9,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 14, 9, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.send_itip_invitation(self.jane['mail'], start) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test") self.purge_mailbox(self.john['mailbox']) # send update with new date and incremented sequence - new_start = pytz.timezone("Europe/Berlin").localize(datetime.datetime(2014,8,15, 15,0,0)) + new_start = pytz.timezone("Europe/Berlin").localize(datetime.datetime(2014, 8, 15, 15, 0, 0)) self.send_itip_update(self.jane['mail'], uid, new_start, summary="test", sequence=1) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_start(), new_start) self.assertEqual(event.get_sequence(), 1) - def test_005_invite_rescheduling_reject(self): self.purge_mailbox(self.john['mailbox']) self.purge_mailbox(self.jack['kolabcalendarfolder']) - start = datetime.datetime(2014,8,9, 17,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 9, 17, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.send_itip_invitation(self.jack['mail'], start) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('TENTATIVE') }, self.jack['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('TENTATIVE')}, self.jack['mail']) self.assertIsInstance(response, email.message.Message) # send update with new but conflicting date and incremented sequence - self.create_calendar_event(datetime.datetime(2014,8,10, 10,30,0, tzinfo=pytz.timezone("Europe/Berlin")), user=self.jack) - new_start = pytz.timezone("Europe/Berlin").localize(datetime.datetime(2014,8,10, 9,30,0)) + self.create_calendar_event(datetime.datetime(2014, 8, 10, 10, 30, 0, tzinfo=pytz.timezone("Europe/Berlin")), user=self.jack) + new_start = pytz.timezone("Europe/Berlin").localize(datetime.datetime(2014, 8, 10, 9, 30, 0)) self.send_itip_update(self.jack['mail'], uid, new_start, summary="test (updated)", sequence=1) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.jack['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.jack['mail']) self.assertEqual(response, None) # verify re-scheduled copy in jack's calendar with NEEDS-ACTION event = self.check_user_calendar_event(self.jack['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_start(), new_start) self.assertEqual(event.get_sequence(), 1) attendee = event.get_attendee(self.jack['mail']) self.assertTrue(attendee.get_rsvp()) self.assertEqual(attendee.get_participant_status(), kolabformat.PartNeedsAction) - def test_006_invitation_reply(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2014,8,18, 14,30,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 18, 14, 30, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.john) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # send a reply from jane to john self.send_itip_reply(uid, self.jane['mail'], self.john['mail'], start=start) # check for the updated event in john's calendar time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) attendee = event.get_attendee(self.jane['mail']) self.assertIsInstance(attendee, pykolab.xml.Attendee) self.assertEqual(attendee.get_participant_status(), kolabformat.PartAccepted) # check attachments in update event attachments = event.get_attachments() self.assertEqual(len(attachments), 1) self.assertEqual(event.get_attachment_data(0), 'This is a text attachment') - def test_006_invitation_reply_delegated(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2014,8,28, 14,30,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 28, 14, 30, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.john) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # send a reply from jane to john self.send_itip_reply(uid, self.jane['mail'], self.john['mail'], start=start, template=itip_delegated, partstat='NEEDS-ACTION') # check for the updated event in john's calendar time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) attendee = event.get_attendee(self.jane['mail']) self.assertIsInstance(attendee, pykolab.xml.Attendee) self.assertEqual(attendee.get_participant_status(), kolabformat.PartDelegated) self.assertEqual(len(attendee.get_delegated_to()), 1) self.assertEqual(attendee.get_delegated_to(True)[0], 'jack@ripper.com') delegatee = event.get_attendee('jack@ripper.com') self.assertIsInstance(delegatee, pykolab.xml.Attendee) self.assertEqual(delegatee.get_participant_status(), kolabformat.PartNeedsAction) self.assertEqual(len(delegatee.get_delegated_from()), 1) self.assertEqual(delegatee.get_delegated_from(True)[0], self.jane['mail']) - def test_007_invitation_cancel(self): self.purge_mailbox(self.john['mailbox']) uid = self.send_itip_invitation(self.jane['mail'], summary="cancelled") - response = self.check_message_received(self.itip_reply_subject % { 'summary':'cancelled', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'cancelled', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) self.send_itip_cancel(self.jane['mail'], uid, summary="cancelled") time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "cancelled") self.assertEqual(event.get_status(True), 'CANCELLED') self.assertTrue(event.get_transparency()) - def test_007_invitation_cancel_and_delete(self): self.purge_mailbox(self.john['mailbox']) uid = self.send_itip_invitation(self.lucy['mail'], summary="cancel-delete") event = self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.send_itip_cancel(self.lucy['mail'], uid, summary="cancel-delete") response = self.check_message_received(_('"%s" has been cancelled') % ('cancel-delete'), self.john['mail'], mailbox=self.lucy['mailbox']) self.assertIsInstance(response, email.message.Message) # verify event was removed from the user's calendar self.assertEqual(self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid), None) - def test_008_inivtation_reply_notify(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2014,8,12, 16,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 12, 16, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.john, attendees=[self.jane, self.mark, self.jack]) # send a reply from jane to john self.send_itip_reply(uid, self.jane['mail'], self.john['mail'], start=start) # check for notification message # this notification should be suppressed until mark has replied, too notification = self.check_message_received(_('"%s" has been updated') % ('test'), self.john['mail']) self.assertEqual(notification, None) # send a reply from mark to john self.send_itip_reply(uid, self.mark['mail'], self.john['mail'], start=start, partstat='ACCEPTED') notification = self.check_message_received(_('"%s" has been updated') % ('test'), self.john['mail']) self.assertIsInstance(notification, email.message.Message) - notification_text = str(notification.get_payload()); + notification_text = str(notification.get_payload()) self.assertIn(self.jane['mail'], notification_text) self.assertIn(_("PENDING"), notification_text) self.purge_mailbox(self.john['mailbox']) # send a reply from mark to john self.send_itip_reply(uid, self.jack['mail'], self.john['mail'], start=start, partstat='ACCEPTED') # this triggers an additional notification notification = self.check_message_received(_('"%s" has been updated') % ('test'), self.john['mail']) self.assertIsInstance(notification, email.message.Message) - notification_text = str(notification.get_payload()); + notification_text = str(notification.get_payload()) self.assertNotIn(_("PENDING"), notification_text) - def test_008_notify_translated(self): self.purge_mailbox(self.mark['mailbox']) - start = datetime.datetime(2014,8,12, 16,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 12, 16, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.mark, attendees=[self.jane]) # send a reply from jane to mark self.send_itip_reply(uid, self.jane['mail'], self.mark['mail'], start=start) # change translations to de_DE pykolab.translate.setUserLanguage(self.mark['preferredlanguage']) notification = self.check_message_received(_('"%s" has been updated') % ('test'), self.mark['mail'], self.mark['mailbox']) self.assertIsInstance(notification, email.message.Message) - notification_text = str(notification.get_payload()); + notification_text = str(notification.get_payload()) self.assertIn(self.jane['mail'], notification_text) self.assertIn(participant_status_label("ACCEPTED")+":", notification_text) # reset localization - pykolab.translate.setUserLanguage(conf.get('kolab','default_locale')) - + pykolab.translate.setUserLanguage(conf.get('kolab', 'default_locale')) def test_009_outdated_reply(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2014,9,2, 11,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 9, 2, 11, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.john, sequence=2) # send a reply from jane to john self.send_itip_reply(uid, self.jane['mail'], self.john['mail'], start=start, sequence=1) # verify jane's attendee status was not updated time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_sequence(), 2) self.assertEqual(event.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartNeedsAction) - def test_010_partstat_update_propagation(self): # ATTENTION: this test requires wallace.invitationpolicy_autoupdate_other_attendees_on_reply to be enabled in config - start = datetime.datetime(2014,8,21, 13,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 8, 21, 13, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.john, attendees=[self.jane, self.jack, self.external]) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # send invitations to jack and jane event_itip = event.as_string_itip() self.send_itip_invitation(self.jane['mail'], start, template=event_itip) self.send_itip_invitation(self.jack['mail'], start, template=event_itip) # wait for replies from jack and jane to be processed and propagated time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # check updated event in organizer's calendar self.assertEqual(event.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) self.assertEqual(event.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartTentative) # check updated partstats in jane's calendar janes = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertEqual(janes.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) self.assertEqual(janes.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartTentative) # check updated partstats in jack's calendar jacks = self.check_user_calendar_event(self.jack['kolabcalendarfolder'], uid) self.assertEqual(jacks.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) self.assertEqual(jacks.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartTentative) # PART 2: create conflicting event in jack's calendar - new_start = datetime.datetime(2014,8,21, 6,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + new_start = datetime.datetime(2014, 8, 21, 6, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) self.create_calendar_event(new_start, user=self.jack, attendees=[], summary="blocker") # re-schedule initial event to new date self.update_calendar_event(uid, start=new_start, sequence=1, user=self.john) self.send_itip_update(self.jane['mail'], uid, new_start, summary="test (updated)", sequence=1) self.send_itip_update(self.jack['mail'], uid, new_start, summary="test (updated)", sequence=1) # wait for replies to be processed and propagated time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # check updated event in organizer's calendar (jack didn't reply yet) self.assertEqual(event.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) self.assertEqual(event.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartTentative) # check partstats in jack's calendar: jack's status should remain needs-action jacks = self.check_user_calendar_event(self.jack['kolabcalendarfolder'], uid) self.assertEqual(jacks.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) self.assertEqual(jacks.get_attendee(self.jack['mail']).get_participant_status(), kolabformat.PartNeedsAction) - def test_011_manual_schedule_auto_update(self): self.purge_mailbox(self.john['mailbox']) # create an event in john's calendar as it was manually accepted - start = datetime.datetime(2014,9,2, 11,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2014, 9, 2, 11, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.jane, sequence=1, folder=self.john['kolabcalendarfolder']) # send update with the same sequence: no re-scheduling templ = itip_invitation.replace("RSVP=TRUE", "RSVP=FALSE").replace("Doe, John", self.jane['displayname']).replace("john.doe@example.org", self.jane['mail']) self.send_itip_update(self.john['mail'], uid, start, summary="test updated", sequence=1, partstat='ACCEPTED', template=templ) time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "test updated") self.assertEqual(event.get_attendee(self.john['mail']).get_participant_status(), kolabformat.PartAccepted) # this should also trigger an update notification notification = self.check_message_received(_('"%s" has been updated') % ('test updated'), self.jane['mail'], mailbox=self.john['mailbox']) self.assertIsInstance(notification, email.message.Message) # send outdated update: should not be saved self.send_itip_update(self.john['mail'], uid, start, summary="old test", sequence=0, partstat='NEEDS-ACTION', template=templ) notification = self.check_message_received(_('"%s" has been updated') % ('old test'), self.jane['mail'], mailbox=self.john['mailbox']) self.assertEqual(notification, None) - def test_012_confidential_invitation(self): - start = datetime.datetime(2014,9,21, 9,30,0) + start = datetime.datetime(2014, 9, 21, 9, 30, 0) uid = self.send_itip_invitation(self.jane['mail'], start, summary='confidential', template=itip_invitation.replace("DESCRIPTION:test", "CLASS:CONFIDENTIAL")) # check event being stored in the folder annotared with event.confidential event = self.check_user_calendar_event(self.jane['kolabconfidentialcalendar'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "confidential") - def test_013_update_shared_folder(self): # create an event organized by Mark (a delegate of Jane) into Jane's calendar - start = datetime.datetime(2015,3,10, 9,30,0, tzinfo=pytz.timezone("Europe/Berlin")) + start = datetime.datetime(2015, 3, 10, 9, 30, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_calendar_event(start, user=self.mark, attendees=[self.jane, self.john], folder=self.jane['kolabcalendarfolder']) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # send a reply from john to mark self.send_itip_reply(uid, self.john['mail'], self.mark['mail'], start=start) # check for the updated event in jane's calendar time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_attendee(self.john['mail']).get_participant_status(), kolabformat.PartAccepted) def test_014_per_sender_policy(self): # send invitation from john => REJECT - start = datetime.datetime(2015,2,28, 14,0,0) + start = datetime.datetime(2015, 2, 28, 14, 0, 0) uid = self.send_itip_invitation(self.bill['mail'], start) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('DECLINED') }, self.bill['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('DECLINED')}, self.bill['mail']) self.assertIsInstance(response, email.message.Message) # send invitation from lucy => SAVE - start = datetime.datetime(2015,3,11, 10,0,0) + start = datetime.datetime(2015, 3, 11, 10, 0, 0) templ = itip_invitation.replace("Doe, John", self.lucy['displayname']) uid = self.send_itip_invitation(self.bill['mail'], start, template=templ, from_addr=self.lucy['mail']) event = self.check_user_calendar_event(self.bill['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) - def test_015_update_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,4,2, 14,0,0) + start = datetime.datetime(2015, 4, 2, 14, 0, 0) uid = self.send_itip_invitation(self.jane['mail'], start, template=itip_recurring) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertTrue(event.is_recurring()) # send update to a single instance with the same sequence: no re-scheduling exdate = start + datetime.timedelta(days=14) self.send_itip_update(self.jane['mail'], uid, exdate, summary="test exception", sequence=0, partstat='ACCEPTED', instance=exdate) time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertEqual(exception.get_summary(), "test exception") self.assertEqual(exception.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) - def test_015_reschedule_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,4,10, 9,0,0) + start = datetime.datetime(2015, 4, 10, 9, 0, 0) uid = self.send_itip_invitation(self.jane['mail'], start, template=itip_recurring) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) # send update to a single instance with the same sequence: no re-scheduling exdate = start + datetime.timedelta(days=14) exstart = exdate + datetime.timedelta(hours=5) self.send_itip_update(self.jane['mail'], uid, exstart, summary="test resceduled", sequence=1, partstat='NEEDS-ACTION', instance=exdate) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test resceduled', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test resceduled', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) # re-schedule again, conflicts with itself exstart = exdate + datetime.timedelta(hours=6) self.send_itip_update(self.jane['mail'], uid, exstart, summary="test new", sequence=2, partstat='NEEDS-ACTION', instance=exdate) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'test new', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'test new', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) # check for updated excaption time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertIsInstance(exception, pykolab.xml.Event) self.assertEqual(exception.get_start().strftime('%Y%m%dT%H%M%S'), exstart.strftime('%Y%m%dT%H%M%S')) - def test_016_reply_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,3,7, 10,0,0, tzinfo=pytz.timezone("Europe/Zurich")) + start = datetime.datetime(2015, 3, 7, 10, 0, 0, tzinfo=pytz.timezone("Europe/Zurich")) uid = self.create_calendar_event(start, attendees=[self.jane, self.mark], recurring=True) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # store a copy in mark's calendar, too self.create_calendar_event(start, attendees=[self.jane, self.mark], recurring=True, folder=self.mark['kolabcalendarfolder'], uid=uid) # send a reply for a single occurrence from jane exdate = start + datetime.timedelta(days=7) self.send_itip_reply(uid, self.jane['mail'], self.john['mail'], start=exdate, instance=exdate) # check for the updated event in john's calendar time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertEqual(exception.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) # check mark's copy for partstat update being stored in an exception, too marks = self.check_user_calendar_event(self.mark['kolabcalendarfolder'], uid) self.assertIsInstance(marks, pykolab.xml.Event) self.assertEqual(len(marks.get_exceptions()), 1) exception = marks.get_instance(exdate) self.assertEqual(exception.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) # send a reply for a the entire series from mark self.send_itip_reply(uid, self.mark['mail'], self.john['mail'], start=start) # check for the updated event in john's calendar time.sleep(10) event = self.check_user_calendar_event(self.john['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertEqual(exception.get_attendee(self.mark['mail']).get_participant_status(), kolabformat.PartAccepted) def test_017_cancel_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,3,20, 19,0,0, tzinfo=pytz.timezone("Europe/Zurich")) + start = datetime.datetime(2015, 3, 20, 19, 0, 0, tzinfo=pytz.timezone("Europe/Zurich")) uid = self.send_itip_invitation(self.jane['mail'], summary="recurring", start=start, template=itip_recurring) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'recurring', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'recurring', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) exdate = start + datetime.timedelta(days=14) self.send_itip_cancel(self.jane['mail'], uid, summary="recurring cancelled", instance=exdate) time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(exdate) self.assertEqual(exception.get_status(True), 'CANCELLED') self.assertTrue(exception.get_transparency()) # send a new invitation for the cancelled slot uid = self.send_itip_invitation(self.jane['mail'], summary="new booking", start=exdate) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'new booking', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'new booking', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) def test_017_cancel_delete_single_occurrence(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,3,24, 13,0,0, tzinfo=pytz.timezone("Europe/Zurich")) + start = datetime.datetime(2015, 3, 24, 13, 0, 0, tzinfo=pytz.timezone("Europe/Zurich")) uid = self.send_itip_invitation(self.lucy['mail'], summary="recurring cancel-delete", start=start, template=itip_recurring) event = self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) # send update to a single instance with the same sequence: no re-scheduling exdate = start + datetime.timedelta(days=14) exstart = exdate + datetime.timedelta(hours=5) self.send_itip_update(self.lucy['mail'], uid, exstart, summary="recurring rescheduled", sequence=1, partstat='NEEDS-ACTION', instance=exdate) time.sleep(10) event = self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) # send cancellation for exception self.send_itip_cancel(self.lucy['mail'], uid, summary="recurring rescheduled", instance=exdate) response = self.check_message_received(_('"%s" has been cancelled') % ('recurring rescheduled'), self.john['mail'], mailbox=self.lucy['mailbox']) self.assertIsInstance(response, email.message.Message) event = self.check_user_calendar_event(self.lucy['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exception_dates()), 1) self.assertEqual(event.get_exception_dates()[0].strftime("%Y-%m-%d"), exdate.strftime("%Y-%m-%d")) self.assertEqual(len(event.get_exceptions()), 0) def test_017_cancel_thisandfuture(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,5,4, 6,30,0) + start = datetime.datetime(2015, 5, 4, 6, 30, 0) uid = self.send_itip_invitation(self.mark['mail'], summary="recurring", start=start, template=itip_recurring) pykolab.translate.setUserLanguage(self.mark['preferredlanguage']) - response = self.check_message_received(_('"%(summary)s" has been %(status)s') % { 'summary':'recurring', 'status':participant_status_label('ACCEPTED') }, self.mark['mail']) + response = self.check_message_received(_('"%(summary)s" has been %(status)s') % {'summary': 'recurring', 'status': participant_status_label('ACCEPTED')}, self.mark['mail']) self.assertIsInstance(response, email.message.Message) pykolab.translate.setUserLanguage(conf.get('kolab','default_locale')) event = self.check_user_calendar_event(self.mark['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) exdate = start + datetime.timedelta(days=14) self.send_itip_cancel(self.mark['mail'], uid, summary="recurring ended", instance=exdate, thisandfuture=True) time.sleep(10) event = self.check_user_calendar_event(self.mark['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) rrule = event.get_recurrence().to_dict() self.assertIsInstance(rrule['until'], datetime.datetime) self.assertEqual(rrule['until'].strftime('%Y%m%d'), (exdate - datetime.timedelta(days=1)).strftime('%Y%m%d')) - def test_018_invite_individual_occurrences(self): self.purge_mailbox(self.john['mailbox']) - start = datetime.datetime(2015,1,30, 17,0,0, tzinfo=pytz.timezone("Europe/Zurich")) + start = datetime.datetime(2015, 1, 30, 17, 0, 0, tzinfo=pytz.timezone("Europe/Zurich")) uid = self.send_itip_invitation(self.jane['mail'], summary="single", start=start, instance=start) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'single', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'single', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) self.assertIn("RECURRENCE-ID", str(response)) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertIsInstance(event.get_recurrence_id(), datetime.datetime) # send another invitation with the same UID for different RECURRENCE-ID - newstart = datetime.datetime(2015,2,6, 17,0,0, tzinfo=pytz.timezone("Europe/Zurich")) + newstart = datetime.datetime(2015, 2, 6, 17, 0, 0, tzinfo=pytz.timezone("Europe/Zurich")) self.send_itip_invitation(self.jane['mail'], summary="single #2", uid=uid, start=newstart, instance=newstart) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'single #2', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'single #2', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) self.assertIn("RECURRENCE-ID", str(response)) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_instance(newstart) self.assertEqual(exception.get_summary(), "single #2") self.assertEqual(exception.get_recurrence_id(), newstart) # send an update occurrence #1 self.send_itip_invitation(self.jane['mail'], summary="updated #1", uid=uid, start=start, instance=start) time.sleep(5) # send an update occurrence #2 self.send_itip_invitation(self.jane['mail'], summary="updated #2", uid=uid, start=newstart, instance=newstart) time.sleep(10) event = self.check_user_calendar_event(self.jane['kolabcalendarfolder'], uid) self.assertIsInstance(event, pykolab.xml.Event) self.assertEqual(event.get_summary(), "updated #1") exception = event.get_instance(newstart) self.assertEqual(exception.get_summary(), "updated #2") - def test_020_task_assignment_accept(self): - start = datetime.datetime(2014,9,10, 19,0,0) + start = datetime.datetime(2014, 9, 10, 19, 0, 0) uid = self.send_itip_invitation(self.jane['mail'], start, summary='work', template=itip_todo) - response = self.check_message_received(self.itip_reply_subject % { 'summary':'work', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'work', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertIsInstance(response, email.message.Message) todo = self.check_user_imap_object(self.jane['kolabtasksfolder'], uid, 'task') self.assertIsInstance(todo, pykolab.xml.Todo) self.assertEqual(todo.get_summary(), "work") # send update with the same sequence: no re-scheduling self.send_itip_update(self.jane['mail'], uid, start, summary='work updated', template=itip_todo, sequence=0, partstat='ACCEPTED') - response = self.check_message_received(self.itip_reply_subject % { 'summary':'work updated', 'status':participant_status_label('ACCEPTED') }, self.jane['mail']) + response = self.check_message_received(self.itip_reply_subject % {'summary': 'work updated', 'status': participant_status_label('ACCEPTED')}, self.jane['mail']) self.assertEqual(response, None) time.sleep(10) todo = self.check_user_imap_object(self.jane['kolabtasksfolder'], uid, 'task') self.assertIsInstance(todo, pykolab.xml.Todo) self.assertEqual(todo.get_summary(), "work updated") self.assertEqual(todo.get_attendee(self.jane['mail']).get_participant_status(), kolabformat.PartAccepted) - def test_021_task_assignment_reply(self): self.purge_mailbox(self.john['mailbox']) - due = datetime.datetime(2014,9,12, 14,0,0, tzinfo=pytz.timezone("Europe/Berlin")) + due = datetime.datetime(2014, 9, 12, 14, 0, 0, tzinfo=pytz.timezone("Europe/Berlin")) uid = self.create_task_assignment(due, user=self.john) todo = self.check_user_imap_object(self.john['kolabtasksfolder'], uid, 'task') self.assertIsInstance(todo, pykolab.xml.Todo) # send a reply from jane to john partstat = 'COMPLETED' self.send_itip_reply(uid, self.jane['mail'], self.john['mail'], start=due, template=itip_todo_reply, partstat=partstat) # check for the updated task in john's tasklist time.sleep(10) todo = self.check_user_imap_object(self.john['kolabtasksfolder'], uid, 'task') self.assertIsInstance(todo, pykolab.xml.Todo) attendee = todo.get_attendee(self.jane['mail']) self.assertIsInstance(attendee, pykolab.xml.Attendee) self.assertEqual(attendee.get_participant_status(True), partstat) # this should trigger an update notification notification = self.check_message_received(_('"%s" has been updated') % ('test'), self.john['mail']) self.assertIsInstance(notification, email.message.Message) - notification_text = str(notification.get_payload()); + notification_text = str(notification.get_payload()) self.assertIn(participant_status_label(partstat), notification_text) - def test_022_task_cancellation(self): uid = self.send_itip_invitation(self.jane['mail'], summary='more work', template=itip_todo) time.sleep(10) self.send_itip_cancel(self.jane['mail'], uid, template=itip_todo_cancel, summary="cancelled") time.sleep(10) todo = self.check_user_imap_object(self.jane['kolabtasksfolder'], uid, 'task') self.assertIsInstance(todo, pykolab.xml.Todo) self.assertEqual(todo.get_summary(), "more work") self.assertEqual(todo.get_status(True), 'CANCELLED') # this should trigger a notification message notification = self.check_message_received(_('"%s" has been cancelled') % ('more work'), self.john['mail'], mailbox=self.jane['mailbox']) self.assertIsInstance(notification, email.message.Message) - diff --git a/tests/functional/test_wap_client/__init__.py b/tests/functional/test_wap_client/__init__.py index d7bf27f..d6cdb57 100644 --- a/tests/functional/test_wap_client/__init__.py +++ b/tests/functional/test_wap_client/__init__.py @@ -1,6 +1,7 @@ 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 index aa0c069..783d996 100644 --- a/tests/functional/test_wap_client/test_001_connect.py +++ b/tests/functional/test_wap_client/test_001_connect.py @@ -1,42 +1,43 @@ 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.assertTrue('status' in result) + self.assertTrue('result' in 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.assertTrue('status' in result) + self.assertTrue('reason' in result) + self.assertTrue('code' in result) self.assertEqual(result['status'], "ERROR") self.assertEqual(result['reason'], "Unknown service") self.assertEqual(result['code'], 400) def test_004_domains_list(self): result = wap_client.domains_list() - self.assertTrue(result.has_key('count')) - self.assertTrue(result.has_key('list')) + self.assertTrue('count' in result) + self.assertTrue('list' in result) self.assertEqual(result['count'], len(result['list'])) def test_005_get_domain(self): result = wap_client.request_raw('GET', 'system.get_domain') self.assertEqual(result, {u'status': u'OK', u'result': {u'domain': u'example.org'}}) 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 96c3de1..771e700 100644 --- a/tests/functional/test_wap_client/test_002_user_add.py +++ b/tests/functional/test_wap_client/test_002_user_add.py @@ -1,80 +1,78 @@ 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 TestUserAdd(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'john.doe', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_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_002_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_003_folders_metadata_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: metadata = imap.get_metadata(folder) 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(): + if folder_name in ac_folders: + if 'annotations' in ac_folders[folder_name]: + for _annotation in ac_folders[folder_name]['annotations']: if _annotation.startswith('/private/'): continue _annotation_value = ac_folders[folder_name]['annotations'][_annotation] - self.assertTrue(metadata[metadata.keys().pop()].has_key(_annotation)) + self.assertTrue(_annotation in metadata[metadata.keys().pop()]) self.assertEqual(_annotation_value, metadata[metadata.keys().pop()][_annotation]) - - diff --git a/tests/functional/test_wap_client/test_003_user_add_fr_FR.py b/tests/functional/test_wap_client/test_003_user_add_fr_FR.py index b9ef510..72cf84b 100644 --- a/tests/functional/test_wap_client/test_003_user_add_fr_FR.py +++ b/tests/functional/test_wap_client/test_003_user_add_fr_FR.py @@ -1,58 +1,56 @@ # -*- coding: utf-8 -*- 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 TestUserAddFrFR(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'etienne-nicolas.mehul', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("Étienne-Nicolas", "Méhul", 'fr_FR') from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_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_002_fr_FR_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=mehul,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) print result self.assertEqual(result['mail'], 'etienne-nicolas.mehul@example.org') self.assertEqual(sorted(result['alias']), ['e.mehul@example.org', 'mehul@example.org']) - - diff --git a/tests/functional/test_wap_client/test_004_user_add_es_ES.py b/tests/functional/test_wap_client/test_004_user_add_es_ES.py index a681a2d..7fc5d83 100644 --- a/tests/functional/test_wap_client/test_004_user_add_es_ES.py +++ b/tests/functional/test_wap_client/test_004_user_add_es_ES.py @@ -1,58 +1,56 @@ # -*- coding: utf-8 -*- 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 TestUserAddEsES(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'alvaro.fuentes', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("Álvaro", "Fuentes", 'es_ES') from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_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_002_fr_FR_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=fuentes,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) self.assertEqual(result['mail'], 'alvaro.fuentes@example.org') self.assertEqual(sorted(result['alias']), ['a.fuentes@example.org', 'fuentes@example.org']) - - diff --git a/tests/functional/test_wap_client/test_005_user_add_de_CH.py b/tests/functional/test_wap_client/test_005_user_add_de_CH.py index dd38383..c2d22c6 100644 --- a/tests/functional/test_wap_client/test_005_user_add_de_CH.py +++ b/tests/functional/test_wap_client/test_005_user_add_de_CH.py @@ -1,57 +1,56 @@ # -*- coding: utf-8 -*- 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 TestUserAddDeCH(unittest.TestCase): @classmethod def setup_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'thomas.bruederli', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("Thomas", "Brüderli", 'de_CH') from tests.functional.synchronize import synchronize_once synchronize_once() @classmethod def teardown_class(self, *args, **kw): from tests.functional.purge_users import purge_users purge_users() def test_001_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_002_fr_FR_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=bruederli,ou=People,dc=example,dc=org") result = wap_client.user_info(recipient) self.assertEqual(result['mail'], 'thomas.bruederli@example.org') self.assertEqual(sorted(result['alias']), ['bruederli@example.org', 't.bruederli@example.org']) - - diff --git a/tests/functional/test_wap_client/test_006_form_value_select_options.py b/tests/functional/test_wap_client/test_006_form_value_select_options.py index 92b4992..0f2bbdd 100644 --- a/tests/functional/test_wap_client/test_006_form_value_select_options.py +++ b/tests/functional/test_wap_client/test_006_form_value_select_options.py @@ -1,31 +1,31 @@ import time import unittest import pykolab from pykolab import wap_client conf = pykolab.getConf() + class TestFormValueListOptions(unittest.TestCase): def test_001_list_options_user_preferredlanguage(self): 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') result = wap_client.authenticate(self.login, self.password, self.domain) attribute_values = wap_client.form_value_select_options( 'user', 1, 'preferredlanguage' ) - self.assertTrue(attribute_values['preferredlanguage'].has_key('default')) - self.assertTrue(attribute_values['preferredlanguage'].has_key('list')) + self.assertTrue('default' in attribute_values['preferredlanguage']) + self.assertTrue('list' in attribute_values['preferredlanguage']) self.assertTrue(len(attribute_values['preferredlanguage']['list']) > 1) self.assertTrue(attribute_values['preferredlanguage']['default'] in attribute_values['preferredlanguage']['list']) - diff --git a/tests/functional/test_wap_client/test_007_policy_uid.py b/tests/functional/test_wap_client/test_007_policy_uid.py index 534ebda..a10eeb9 100644 --- a/tests/functional/test_wap_client/test_007_policy_uid.py +++ b/tests/functional/test_wap_client/test_007_policy_uid.py @@ -1,176 +1,175 @@ from ConfigParser import RawConfigParser 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 TestPolicyUid(unittest.TestCase): def remove_option(self, section, option): self.config.remove_option(section, option) fp = open(conf.config_file, "w") self.config.write(fp) fp.close() def set(self, section, option, value): self.config.set(section, option, value) fp = open(conf.config_file, "w") self.config.write(fp) fp.close() @classmethod def setup_class(self, *args, **kw): self.config = RawConfigParser() self.config.read(conf.config_file) from tests.functional.purge_users import purge_users purge_users() self.user = { 'local': 'john.doe', 'domain': 'example.org' } self.login = conf.get('ldap', 'bind_dn') self.password = conf.get('ldap', 'bind_pw') self.domain = conf.get('kolab', 'primary_domain') result = wap_client.authenticate(self.login, self.password, self.domain) @classmethod def teardown_class(self, *args, **kw): self.config.remove_option('example.org', 'policy_uid') fp = open(conf.config_file, "w") self.config.write(fp) fp.close() from tests.functional.purge_users import purge_users purge_users() def test_001_default(self): from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "doe") from tests.functional.purge_users import purge_users purge_users() def test_002_givenname_dot_surname(self): self.set('example.org', 'policy_uid', '%(givenname)s.%(surname)s') from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "John.Doe") from tests.functional.purge_users import purge_users purge_users() def test_003_givenname_fc_dot_surname(self): self.set('example.org', 'policy_uid', "'%(givenname)s'[0:1].%(surname)s") from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "J.Doe") from tests.functional.purge_users import purge_users purge_users() def test_004_givenname(self): self.set('example.org', 'policy_uid', '%(givenname)s') from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "John") from tests.functional.purge_users import purge_users purge_users() def test_005_lowercase_givenname(self): self.set('example.org', 'policy_uid', '%(givenname)s.lower()') from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "john") from tests.functional.purge_users import purge_users purge_users() def test_006_lowercase_givenname_surname(self): self.set('example.org', 'policy_uid', "%(givenname)s.lower().%(surname)s.lower()") from tests.functional.user_add import user_add user_add("John", "Doe") from tests.functional.synchronize import synchronize_once synchronize_once() auth = Auth() auth.connect() user = auth.find_recipient('john.doe@example.org') user_info = wap_client.user_info(user) self.assertEqual(user_info['uid'], "john.doe") from tests.functional.purge_users import purge_users purge_users() - - diff --git a/tests/functional/test_wap_client/test_008_resource_add.py b/tests/functional/test_wap_client/test_008_resource_add.py index 5647d0c..f4b4c20 100644 --- a/tests/functional/test_wap_client/test_008_resource_add.py +++ b/tests/functional/test_wap_client/test_008_resource_add.py @@ -1,51 +1,52 @@ import time import pykolab from pykolab import wap_client from pykolab.auth import Auth from pykolab.imap import IMAP from twisted.trial import unittest import tests.functional.resource_func as funcs conf = pykolab.getConf() + class TestResourceAdd(unittest.TestCase): @classmethod def setUp(self): from tests.functional.purge_users import purge_users purge_users() self.john = { 'local': 'john.doe', 'domain': 'example.org' } from tests.functional.user_add import user_add user_add("John", "Doe") funcs.purge_resources() self.audi = funcs.resource_add("car", "Audi A4") self.passat = funcs.resource_add("car", "VW Passat") self.boxter = funcs.resource_add("car", "Porsche Boxter S") - self.cars = funcs.resource_add("collection", "Company Cars", [ self.audi['dn'], self.passat['dn'], self.boxter['dn'] ]) + self.cars = funcs.resource_add("collection", "Company Cars", [self.audi['dn'], self.passat['dn'], self.boxter['dn']]) from tests.functional.synchronize import synchronize_once synchronize_once() def test_001_resource_created(self): auth = Auth() auth.connect() resource = auth.find_resource(self.audi['mail']) self.assertEqual(resource, self.audi['dn']) collection = auth.find_resource(self.cars['mail']) self.assertEqual(collection, self.cars['dn']) def test_002_resource_collection(self): auth = Auth() auth.connect() attrs = auth.get_entry_attributes(None, self.cars['dn'], ['*']) self.assertIn('groupofuniquenames', attrs['objectclass']) self.assertEqual(len(attrs['uniquemember']), 3) diff --git a/tests/functional/user_add.py b/tests/functional/user_add.py index b1b37f1..35e2b6b 100644 --- a/tests/functional/user_add.py +++ b/tests/functional/user_add.py @@ -1,65 +1,59 @@ import pykolab from pykolab import wap_client conf = pykolab.getConf() -def user_add(givenname, sn, preferredlanguage='en_US', **kw): - if givenname == None: - raise Exception - - if givenname == '': - raise Exception - if sn == None: +def user_add(givenname, sn, preferredlanguage='en_US', **kw): + if givenname is None or givenname == '': raise Exception - if sn == '': + if sn is None or sn == '': raise Exception user_details = { 'givenname': givenname, 'sn': sn, 'preferredlanguage': preferredlanguage, 'ou': 'ou=People,dc=example,dc=org', 'userpassword': 'Welcome2KolabSystems' } user_details.update(kw) 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 or user_details.has_key(attribute): + if 'optional' not in attr_details or attr_details['optional'] is False or attribute in user_details: 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()] + fvg_params['attributes'] = [attr for attr in user_type_info['auto_form_fields'].keys() if attr not in params] result = wap_client.user_add(params) - diff --git a/tests/unit/test-000-imports.py b/tests/unit/test-000-imports.py index 17e1c57..9c65e6d 100644 --- a/tests/unit/test-000-imports.py +++ b/tests/unit/test-000-imports.py @@ -1,23 +1,24 @@ 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 index 7c8f128..a566f9e 100644 --- a/tests/unit/test-001-contact_reference.py +++ b/tests/unit/test-001-contact_reference.py @@ -1,30 +1,31 @@ import datetime import unittest from pykolab.xml import ContactReference + class TestEventXML(unittest.TestCase): contact_reference = ContactReference("jane@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) + 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 index ccde3ac..c90c48e 100644 --- a/tests/unit/test-002-attendee.py +++ b/tests/unit/test-002-attendee.py @@ -1,136 +1,137 @@ import datetime import unittest import kolabformat from pykolab.xml import Attendee from pykolab.xml import participant_status_label + class TestEventXML(unittest.TestCase): attendee = Attendee("jane@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) + 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()), 7) 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) self.assertEqual(self.attendee.participant_status_map["IN-PROCESS"], 5) self.assertEqual(self.attendee.participant_status_map["COMPLETED"], 6) 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") - self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 5][0], "IN-PROCESS") - self.assertEqual([k for k,v in self.attendee.participant_status_map.iteritems() if v == 6][0], "COMPLETED") + 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") + self.assertEqual([k for k, v in self.attendee.participant_status_map.iteritems() if v == 5][0], "IN-PROCESS") + self.assertEqual([k for k, v in self.attendee.participant_status_map.iteritems() if v == 6][0], "COMPLETED") 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") + self.assertEqual([k for k, v in self.attendee.rsvp_map.iteritems() if v is True][0], "TRUE") + self.assertEqual([k for k, v in self.attendee.rsvp_map.iteritems() if v is 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") + 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["OPT-PARTICIPANT"], 2) self.assertEqual(self.attendee.role_map["NON-PARTICIPANT"], 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], "OPT-PARTICIPANT") - self.assertEqual([k for k,v in self.attendee.role_map.iteritems() if v == 3][0], "NON-PARTICIPANT") + 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], "OPT-PARTICIPANT") + self.assertEqual([k for k, v in self.attendee.role_map.iteritems() if v == 3][0], "NON-PARTICIPANT") 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") + 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") def test_018_partstat_label(self): self.assertEqual(participant_status_label('NEEDS-ACTION'), "Needs Action") self.assertEqual(participant_status_label(kolabformat.PartTentative), "Tentatively Accepted") self.assertEqual(participant_status_label('UNKNOWN'), "UNKNOWN") def test_020_to_dict(self): name = "Doe, Jane" role = 'OPT-PARTICIPANT' cutype = 'RESOURCE' partstat = 'ACCEPTED' self.attendee.set_name(name) self.attendee.set_rsvp(True) self.attendee.set_role(role) self.attendee.set_cutype(cutype) self.attendee.set_participant_status(partstat) data = self.attendee.to_dict() self.assertIsInstance(data, dict) self.assertEqual(data['role'], role) self.assertEqual(data['cutype'], cutype) self.assertEqual(data['partstat'], partstat) self.assertEqual(data['name'], name) self.assertEqual(data['email'], 'jane@doe.org') self.assertTrue(data['rsvp']) if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py index 61fd888..18b186b 100644 --- a/tests/unit/test-003-event.py +++ b/tests/unit/test-003-event.py @@ -1,976 +1,972 @@ import re import datetime import pytz import sys import unittest import kolabformat import icalendar import pykolab from pykolab.xml import Attendee from pykolab.xml import Event from pykolab.xml import RecurrenceRule from pykolab.xml import EventIntegrityError from pykolab.xml import InvalidAttendeeParticipantStatusError from pykolab.xml import InvalidEventDateError from pykolab.xml import event_from_ical from pykolab.xml import event_from_string from pykolab.xml import event_from_message from pykolab.xml import compute_diff from pykolab.xml import property_to_string from collections import OrderedDict ical_event = """ BEGIN:VEVENT UID:7a35527d-f783-4b58-b404-b1389bd2fc57 DTSTAMP;VALUE=DATE-TIME:20140407T122311Z CREATED;VALUE=DATE-TIME:20140407T122245Z LAST-MODIFIED;VALUE=DATE-TIME:20140407T122311Z DTSTART;TZID=Europe/Zurich;VALUE=DATE-TIME:20140523T110000 DURATION:PT1H30M0S RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10 EXDATE;TZID=Europe/Zurich;VALUE=DATE-TIME:20140530T110000 EXDATE;TZID=Europe/Zurich;VALUE=DATE-TIME:20140620T110000 SUMMARY:Summary LOCATION:Location DESCRIPTION:Description\\n2 lines CATEGORIES:Personal TRANSP:OPAQUE PRIORITY:2 SEQUENCE:2 CLASS:PUBLIC ATTENDEE;CN="Manager, Jane";PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;CUTYP E=INDIVIDUAL;RSVP=TRUE:mailto:jane.manager@example.org ATTENDEE;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;ROLE=OPT-PARTICIPANT;RSVP=FA LSE:MAILTO:max@imum.com ORGANIZER;CN=Doe\, John:mailto:john.doe@example.org URL:http://somelink.com/foo ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=image/png;X-LABEL=silhouette.pn g:iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAIAAADY27xgAAAAGXRFWHRTb2Z0d2FyZQBBZG9i ZSBJbWFnZVJlYWR5ccllPAAAAsRJREFUeNrsmeluKjEMhTswrAWB4P3fECGx79CjsTDmOKRkpF xxpfoHSmchX7ybFrfb7eszpPH1MfKH8ofyH6KUtd/c7/en0wmfWBdF0Wq1Op1Ou91uNGoer6iX V1ar1Xa7xUJeB4qsr9frdyVlWWZH2VZyPp+xPXHIAoK70+m02+1m9JXj8bhcLi+Xi3J4xUCazS bUltdtd7ud7ldUIhC3u+iTwF0sFhlR4Kds4LtRZK1w4te5UM6V6JaqhqC3CQ28OAsKggJfbZ3U eozCqZ4koHIZCGmD9ivuos9YONFirmxrI0UNZG1kbZeUXdJQNJNa91RlqMn0ekYUMZDup6dXVV m+1OSZhqLx6bVCELJGSsyFQtFrF15JGYMZgoxubWGDSDVhvTipDKWhoBOIpFobxtlbJ0Gh0/tg lgXal4woUHi/36fQoBQncDAlupa8DeVwOPRe4lUyGAwQ+dl7W+xBXkJBhEUqR32UoJfYIKrR4d ZBgcdIRqfEqn+mekl9FNRbSTA249la3ev1/kXHD47ZbEYR5L9kMplkd9vNZqMFyIYxxfN8Pk8q QGlagT5QDtfrNYUMlWW9LiGNPPSmC/+OgpK2r4RO6dOatZd+4gAAemdIi6Fg9EKLD4vASWkzv3 ew06NSCiA40CumAIoaIrhrcAwjF7aDo58gUchgNV+0n1BAcDgcoAZrXV9mI4qkhtK6FJFhi9Fo ZKPsgQI1ACJieH/Kd570t+xFoIzHYzl5Q40CFGrSqGuks3qmYIKJfIl0nPKLxAMFw7Dv1+2QYf vFSOBQubbOFDSc7ZcfWvHv6DzhOzT6IeOVPuz8Roex0f6EgsE/2IL4qdg7hIXz7/pBie7q1uWr tp66xrif0l1KwUE4P7Y9Gci/ZgtNRFX+Rw06Q2RigsjuDc3urwKHxuNITaaxyD9mT2WvSDAXn/ Pvhh8BBgBjyfPSGbSYcwAAAABJRU5ErkJggg== ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=text/plain;X-LABEL=text.txt:VGh pcyBpcyBhIHRleHQgZmlsZQo= BEGIN:VALARM ACTION:DISPLAY TRIGGER:-PT30M END:VALARM END:VEVENT """ ical_exception = """ BEGIN:VEVENT UID:7a35527d-f783-4b58-b404-b1389bd2fc57 DTSTAMP;VALUE=DATE-TIME:20140407T122311Z CREATED;VALUE=DATE-TIME:20140407T122245Z LAST-MODIFIED;VALUE=DATE-TIME:20140407T122311Z RECURRENCE-ID;TZID=Europe/Zurich;RANGE=THISANDFUTURE:20140606T110000 DTSTART;TZID=Europe/Zurich;VALUE=DATE-TIME:20140607T120000 DTEND;TZID=Europe/Zurich;VALUE=DATE-TIME:20140607T143000 SUMMARY:Exception CATEGORIES:Personal TRANSP:TRANSPARENT PRIORITY:2 SEQUENCE:3 STATUS:CANCELLED ORGANIZER;CN=Doe\, John:mailto:john.doe@example.org END:VEVENT """ xml_event = """ Libkolabxml-1.1 2.0 3.1.0 75c740bb-b3c6-442c-8021-ecbaeb0a025e 2013-07-07T01:28:23Z 2013-07-07T01:28:23Z 1 PUBLIC /kolab.org/Europe/London 2013-08-13T10:00:00 /kolab.org/Europe/London 2013-08-13T14:00:00 DAILY 2015-07-25 /kolab.org/Europe/Berlin 2014-07-19 2014-07-26 2014-07-12 2014-07-13 2014-07-20 2014-07-27 2014-07-05 2014-07-06 test test 5 CANCELLED Room 101 Doe, John mailto:%3Cjohn%40example.org%3E ACCEPTED REQ-PARTICIPANT true mailto:%3Csomebody%40else.com%3E mailto:%3Cjane%40example.org%3E DELEGATED NON-PARTICIPANT mailto:%3Cjane%40example.org%3E mailto:%3Csomebody%40else.com%3E text/html noname.1395223627.5555 cid:noname.1395223627.5555 X-MOZ-RECEIVED-DTSTAMP 20140224T155612Z X-GWSHOW-AS BUSY DISPLAY alarm 1 START -PT2H EMAIL test alarm 2 mailto:%3Cjohn.doe%40example.org%3E START -P1D 75c740bb-b3c6-442c-8021-ecbaeb0a025e 2014-07-07T01:28:23Z 2014-07-07T01:28:23Z 2 PUBLIC /kolab.org/Europe/London 2014-08-16T13:00:00 /kolab.org/Europe/London 2014-08-16T16:00:00 /kolab.org/Europe/London THISANDFUTURE 2014-08-16T10:00:00 exception exception Room 101 Doe, John mailto:%3Cjohn%40example.org%3E DECLINED REQ-PARTICIPANT mailto:%3Cjane%40example.org%3E """ + class TestEventXML(unittest.TestCase): event = Event() @classmethod def setUp(self): """ Compatibility for twisted.trial.unittest """ self.setup_class() @classmethod def setup_class(self, *args, **kw): # set language to default pykolab.translate.setUserLanguage('en_US') def assertIsInstance(self, _value, _type, _msg=None): if hasattr(unittest.TestCase, 'assertIsInstance'): return unittest.TestCase.assertIsInstance(self, _value, _type, _msg) else: if (type(_value)) == _type: return True else: - if not _msg == None: - raise AssertionError, "%s != %s: %r" % (type(_value), _type, _msg) + if _msg is not None: + raise AssertionError("%s != %s: %r" % (type(_value), _type, _msg)) else: - raise AssertionError, "%s != %s" % (type(_value), _type) + 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@doe.org") self.assertIsInstance(self.event.get_attendees(), list) self.assertEqual(len(self.event.get_attendees()), 1) def test_005_attendee_add_name_and_props(self): self.event.add_attendee("jane@doe.org", "Doe, Jane", role="OPT-PARTICIPANT", cutype="RESOURCE") 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@doe.org", "jane@doe.org"]) def test_007_get_attendee_by_email(self): self.assertIsInstance(self.event.get_attendee_by_email("jane@doe.org"), Attendee) self.assertIsInstance(self.event.get_attendee("jane@doe.org"), Attendee) def test_007_get_attendee_props(self): self.assertEqual(self.event.get_attendee("jane@doe.org").get_cutype(), kolabformat.CutypeResource) self.assertEqual(self.event.get_attendee("jane@doe.org").get_role(), kolabformat.Optional) def test_007_get_nonexistent_attendee_by_email(self): self.assertRaises(ValueError, self.event.get_attendee_by_email, "nosuchattendee@invalid.domain") self.assertRaises(ValueError, self.event.get_attendee, "nosuchattendee@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@doe.org", "INVALID") def test_009_update_attendees(self): jane = self.event.get_attendee("jane@doe.org") jane.set_name("Jane (GI) Doe") self.event.update_attendees([jane]) self.assertEqual(len(self.event.get_attendees()), 2) self.assertEqual(self.event.get_attendee("jane@doe.org").get_name(), "Jane (GI) Doe") 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@doe.org").get_email(), "jane@doe.org") def test_012_delegate_new_attendee(self): self.event.delegate("jane@doe.org", "max@imum.com") def test_013_delegatee_is_now_attendee(self): delegatee = self.event.get_attendee("max@imum.com") self.assertIsInstance(delegatee, Attendee) self.assertEqual(delegatee.get_role(), kolabformat.Optional) self.assertEqual(delegatee.get_cutype(), kolabformat.CutypeResource) def test_014_delegate_attendee_adds(self): self.assertEqual(len(self.event.get_attendee("jane@doe.org").get_delegated_to()), 1) self.event.delegate("jane@doe.org", "john@doe.org") self.assertEqual(len(self.event.get_attendee("jane@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.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(hasattr(_start, 'tzinfo'), False) self.assertEqual(self.event.get_start().__str__(), "2012-05-23") def test_018_load_from_ical(self): ical_str = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN METHOD:REQUEST """ + ical_event + ical_exception + "END:VCALENDAR" ical = icalendar.Calendar.from_ical(ical_str) event = event_from_ical(ical.walk('VEVENT')[0], ical_str) self.assertEqual(event.get_location(), "Location") self.assertEqual(str(event.get_lastmodified()), "2014-04-07 12:23:11+00:00") self.assertEqual(event.get_description(), "Description\n2 lines") self.assertEqual(event.get_url(), "http://somelink.com/foo") self.assertEqual(event.get_transparency(), False) self.assertEqual(event.get_categories(), ["Personal"]) self.assertEqual(event.get_priority(), '2') self.assertEqual(event.get_classification(), kolabformat.ClassPublic) self.assertEqual(event.get_attendee_by_email("max@imum.com").get_cutype(), kolabformat.CutypeResource) self.assertEqual(event.get_sequence(), 2) self.assertTrue(event.is_recurring()) self.assertIsInstance(event.get_duration(), datetime.timedelta) self.assertIsInstance(event.get_end(), datetime.datetime) self.assertEqual(str(event.get_end()), "2014-05-23 12:30:00+02:00") self.assertEqual(len(event.get_exception_dates()), 2) self.assertIsInstance(event.get_exception_dates()[0], datetime.datetime) self.assertEqual(len(event.get_alarms()), 1) self.assertEqual(len(event.get_attachments()), 2) self.assertEqual(len(event.get_exceptions()), 1) exception = event.get_exceptions()[0] self.assertIsInstance(exception.get_recurrence_id(), datetime.datetime) # self.assertEqual(exception.thisandfuture, True) self.assertEqual(str(exception.get_start()), "2014-06-07 12:00:00+02:00") def test_018_ical_to_message(self): event = event_from_ical(ical_event) message = event.to_message() self.assertTrue(message.is_multipart()) self.assertEqual(message['Subject'], event.uid) self.assertEqual(message['X-Kolab-Type'], 'application/x-vnd.kolab.event') parts = [p for p in message.walk()] - attachments = event.get_attachments(); + attachments = event.get_attachments() self.assertEqual(len(parts), 5) self.assertEqual(parts[1].get_content_type(), 'text/plain') self.assertEqual(parts[2].get_content_type(), 'application/calendar+xml') self.assertEqual(parts[3].get_content_type(), 'image/png') self.assertEqual(parts[4].get_content_type(), 'text/plain') self.assertEqual(parts[2]['Content-ID'], None) self.assertEqual(parts[3]['Content-ID'].strip('<>'), attachments[0].uri()[4:]) self.assertEqual(parts[4]['Content-ID'].strip('<>'), attachments[1].uri()[4:]) def test_018_ical_allday_events(self): ical = """BEGIN:VEVENT UID:ffffffff-f783-4b58-b404-b1389bd2ffff DTSTAMP;VALUE=DATE-TIME:20140407T122311Z CREATED;VALUE=DATE-TIME:20140407T122245Z DTSTART;VALUE=DATE:20140823 DTEND;VALUE=DATE:20140824 SUMMARY:All day DESCRIPTION:One single day TRANSP:OPAQUE CLASS:PUBLIC END:VEVENT """ event = event_from_ical(ical) self.assertEqual(str(event.get_start()), "2014-08-23") self.assertEqual(str(event.get_end()), "2014-08-23") self.assertEqual(str(event.get_ical_dtend()), "2014-08-24") self.assertTrue(re.match('.*\s*2014-08-23', str(event), re.DOTALL)) def test_019_as_string_itip(self): self.event.set_summary("test") self.event.set_start(datetime.datetime(2014, 05, 23, 11, 00, 00, tzinfo=pytz.timezone("Europe/London"))) self.event.set_end(datetime.datetime(2014, 05, 23, 12, 30, 00, tzinfo=pytz.timezone("Europe/London"))) self.event.set_sequence(3) self.event.set_classification('CONFIDENTIAL') self.event.add_custom_property('X-Custom', 'check') self.event.set_recurrence_id(datetime.datetime(2014, 05, 23, 11, 0, 0), True) rrule = RecurrenceRule() rrule.set_frequency(kolabformat.RecurrenceRule.Weekly) - rrule.set_byday(['2WE','-1SU']) + rrule.set_byday(['2WE', '-1SU']) rrule.setBymonth([2]) rrule.set_count(10) - rrule.set_until(datetime.datetime(2014,7,23, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) - self.event.set_recurrence(rrule); + rrule.set_until(datetime.datetime(2014, 7, 23, 11, 0, 0, tzinfo=pytz.timezone("Europe/London"))) + self.event.set_recurrence(rrule) ical = icalendar.Calendar.from_ical(self.event.as_string_itip()) event = ical.walk('VEVENT')[0] self.assertEqual(event['uid'], self.event.get_uid()) self.assertEqual(event['summary'], "test") self.assertEqual(event['sequence'], 3) self.assertEqual(event['X-CUSTOM'], "check") self.assertIsInstance(event['dtstamp'].dt, datetime.datetime) self.assertEqual(event['class'], "CONFIDENTIAL") self.assertIsInstance(event['recurrence-id'].dt, datetime.datetime) self.assertEqual(event['recurrence-id'].params.get('RANGE'), 'THISANDFUTURE') - self.assertTrue(event.has_key('rrule')) + self.assertTrue('rrule' in event) self.assertEqual(event['rrule']['FREQ'][0], 'WEEKLY') self.assertEqual(event['rrule']['INTERVAL'][0], 1) self.assertEqual(event['rrule']['COUNT'][0], 10) self.assertEqual(event['rrule']['BYMONTH'][0], 2) - self.assertEqual(event['rrule']['BYDAY'], ['2WE','-1SU']) + self.assertEqual(event['rrule']['BYDAY'], ['2WE', '-1SU']) self.assertIsInstance(event['rrule']['UNTIL'][0], datetime.datetime) self.assertEquals(event['rrule']['UNTIL'][0].tzinfo, pytz.utc) def test_019_to_message_itip(self): self.event = Event() self.event.set_summary("test") self.event.set_start(datetime.datetime(2014, 05, 23, 11, 00, 00, tzinfo=pytz.timezone("Europe/London"))) self.event.set_end(datetime.datetime(2014, 05, 23, 12, 30, 00, tzinfo=pytz.timezone("Europe/London"))) self.event.set_organizer("me@kolab.org") self.event.add_attendee("john@doe.org") self.event.add_attendee("jane@doe.org") message = self.event.to_message_itip("john@doe.org", method="REPLY", participant_status="ACCEPTED") itip_event = None for part in message.walk(): if part.get_content_type() == "text/calendar": ical = icalendar.Calendar.from_ical(part.get_payload(decode=True)) itip_event = ical.walk('VEVENT')[0] break self.assertEqual(itip_event['uid'], self.event.get_uid()) self.assertEqual(itip_event['attendee'].lower(), 'mailto:john@doe.org') # delegate jane => jack self.event.delegate("jane@doe.org", "jack@ripper.com", "Jack") message = self.event.to_message_itip("jane@doe.org", method="REPLY", participant_status="DELEGATED") itip_event = None for part in message.walk(): if part.get_content_type() == "text/calendar": ical = icalendar.Calendar.from_ical(part.get_payload(decode=True)) itip_event = ical.walk('VEVENT')[0] break self.assertEqual(len(itip_event['attendee']), 2) self.assertEqual(str(itip_event['attendee'][0]).lower(), 'mailto:jane@doe.org') self.assertEqual(str(itip_event['attendee'][1]).lower(), 'mailto:jack@ripper.com') self.assertEqual(itip_event['attendee'][0].params['delegated-to'], 'jack@ripper.com') self.assertEqual(itip_event['attendee'][1].params['delegated-from'], 'jane@doe.org') - def test_020_calendaring_recurrence(self): rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Monthly) rrule.setCount(10) self.event = Event() - self.event.set_recurrence(rrule); + self.event.set_recurrence(rrule) _start = datetime.datetime(2014, 5, 1, 11, 30, 00, tzinfo=pytz.timezone("Europe/London")) self.event.set_start(_start) self.event.set_end(_start + datetime.timedelta(hours=2)) self.assertTrue(self.event.is_recurring()) next_date = self.event.get_next_occurence(_start) self.assertIsInstance(next_date, datetime.datetime) self.assertEqual(next_date.month, 6) self.assertEqual(next_date.day, 1) end_date = self.event.get_occurence_end_date(next_date) self.assertIsInstance(end_date, datetime.datetime) self.assertEqual(end_date.month, 6) self.assertEqual(end_date.hour, 13) self.assertEqual(self.event.get_next_occurence(next_date).month, 7) last_date = self.event.get_last_occurrence() self.assertIsInstance(last_date, datetime.datetime) self.assertEqual(last_date.year, 2015) self.assertEqual(last_date.month, 2) self.assertEqual(self.event.get_next_occurence(last_date), None) # check infinite recurrence rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Monthly) - self.event.set_recurrence(rrule); + self.event.set_recurrence(rrule) self.assertEqual(self.event.get_last_occurrence(), None) self.assertIsInstance(self.event.get_last_occurrence(force=True), datetime.datetime) # check get_next_instance() which returns a clone of the base event next_instance = self.event.get_next_instance(next_date) self.assertIsInstance(next_instance, Event) self.assertIsInstance(next_instance.get_recurrence_id(), datetime.datetime) self.assertEqual(self.event.get_summary(), next_instance.get_summary()) self.assertEqual(next_instance.get_start().month, 7) self.assertFalse(next_instance.is_recurring()) # check get_next_occurence() with an infinitely recurring all-day event rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Yearly) - self.event.set_recurrence(rrule); + self.event.set_recurrence(rrule) self.event.set_start(datetime.date(2014, 5, 1)) self.event.set_end(datetime.date(2014, 5, 1)) next_date = self.event.get_next_occurence(datetime.date(2015, 1, 1)) self.assertIsInstance(next_date, datetime.date) self.assertEqual(next_date.year, 2015) self.assertEqual(next_date.month, 5) def test_021_calendaring_no_recurrence(self): _start = datetime.datetime(2014, 2, 1, 14, 30, 00, tzinfo=pytz.timezone("Europe/London")) self.event = Event() self.event.set_start(_start) self.event.set_end(_start + datetime.timedelta(hours=2)) self.assertEqual(self.event.get_next_occurence(_start), None) self.assertEqual(self.event.get_last_occurrence(), None) def test_021_add_exceptions(self): event = event_from_ical(ical_event) exception = event_from_ical(ical_exception) self.assertIsInstance(event, Event) self.assertIsInstance(exception, Event) event.add_exception(exception) self.assertEquals(len(event.get_exceptions()), 1) # second call shall replace the existing exception event.add_exception(exception) self.assertEquals(len(event.get_exceptions()), 1) # first real occurrence should be our exception occurrence = event.get_next_instance(event.get_start()) self.assertEqual(occurrence.get_summary(), "Exception") def test_021_allday_recurrence(self): rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Daily) rrule.setCount(10) self.event = Event() self.event.set_summary('alldays') - self.event.set_recurrence(rrule); + self.event.set_recurrence(rrule) - _start = datetime.date(2015,1,1) + _start = datetime.date(2015, 1, 1) self.event.set_start(_start) self.event.set_end(_start) - exdate = datetime.date(2015,1,5) + exdate = datetime.date(2015, 1, 5) xmlexception = Event(from_string=str(self.event)) xmlexception.set_start(exdate) xmlexception.set_end(exdate) xmlexception.set_recurrence_id(exdate, False) xmlexception.set_status('CANCELLED') self.event.add_exception(xmlexception) - inst3 = self.event.get_instance(datetime.date(2015,1,3)) - self.assertEqual(inst3.get_start(), datetime.date(2015,1,3)) + inst3 = self.event.get_instance(datetime.date(2015, 1, 3)) + self.assertEqual(inst3.get_start(), datetime.date(2015, 1, 3)) inst5 = self.event.get_instance(exdate) self.assertEqual(inst5.get_status(True), 'CANCELLED') def test_021_ical_exceptions(self): self.event.set_summary("test") self.event.set_start(datetime.datetime(2014, 05, 23, 11, 00, 00, tzinfo=pytz.timezone("Europe/London"))) self.event.set_end(datetime.datetime(2014, 05, 23, 12, 30, 00, tzinfo=pytz.timezone("Europe/London"))) rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Weekly) self.event.set_recurrence(rrule) xmlexception = Event(from_string=str(self.event)) xmlexception.set_start(datetime.datetime(2014, 05, 30, 14, 00, 00, tzinfo=pytz.timezone("Europe/London"))) xmlexception.set_end(datetime.datetime(2014, 05, 30, 16, 00, 00, tzinfo=pytz.timezone("Europe/London"))) xmlexception.set_recurrence_id(datetime.datetime(2014, 05, 30, 11, 0, 0), False) self.event.add_exception(xmlexception) ical = icalendar.Calendar.from_ical(self.event.as_string_itip()) vevents = ical.walk('VEVENT') event = vevents[0] exception = vevents[1] self.assertEqual(event['uid'], self.event.get_uid()) self.assertEqual(event['summary'], "test") self.assertEqual(exception['uid'], self.event.get_uid()) self.assertIsInstance(exception['recurrence-id'].dt, datetime.datetime) self.assertEqual(exception['recurrence-id'].params.get('RANGE'), None) def test_021_single_instances(self): self.event = Event() self.event.set_summary('singles') - _start = datetime.datetime(2015,3,1, 14,0,0, tzinfo=pytz.timezone("Europe/London")) + _start = datetime.datetime(2015, 3, 1, 14, 0, 0, tzinfo=pytz.timezone("Europe/London")) self.event.set_start(_start) self.event.set_end(_start + datetime.timedelta(hours=1)) self.event.set_recurrence_id(_start) - _start2 = datetime.datetime(2015,3,5, 15,0,0, tzinfo=pytz.timezone("Europe/London")) + _start2 = datetime.datetime(2015, 3, 5, 15, 0, 0, tzinfo=pytz.timezone("Europe/London")) xmlexception = Event(from_string=str(self.event)) xmlexception.set_start(_start2) xmlexception.set_end(_start2 + datetime.timedelta(hours=1)) xmlexception.set_summary('singles #2') xmlexception.set_recurrence_id(_start2) self.event.add_exception(xmlexception) self.assertEqual(self.event.has_exceptions(), True) first = self.event.get_instance(_start) self.assertIsInstance(first, Event) self.assertEqual(first.get_summary(), "singles") second = self.event.get_instance(_start2) self.assertIsInstance(second, Event) self.assertEqual(second.get_summary(), "singles #2") # update main instance first.set_status('CANCELLED') first.set_summary("singles #1") self.event.add_exception(first) event = event_from_string(str(self.event)) self.assertEqual(self.event.has_exceptions(), True) self.assertEqual(event.get_status(True), 'CANCELLED') self.assertEqual(event.get_summary(), "singles #1") - def test_022_load_from_xml(self): event = event_from_string(xml_event) self.assertEqual(event.uid, '75c740bb-b3c6-442c-8021-ecbaeb0a025e') self.assertEqual(event.get_attendee_by_email("jane@example.org").get_participant_status(), kolabformat.PartAccepted) self.assertEqual(len(event.get_attendee_by_email("jane@example.org").get_delegated_from()), 1) self.assertEqual(len(event.get_attendee_by_email("somebody@else.com").get_delegated_to()), 1) self.assertEqual(event.get_sequence(), 1) self.assertIsInstance(event.get_start(), datetime.datetime) self.assertEqual(str(event.get_start()), "2013-08-13 10:00:00+01:00") self.assertEqual(str(event.get_end()), "2013-08-13 14:00:00+01:00") self.assertTrue(event.is_recurring()) exceptions = event.get_exceptions() self.assertEqual(len(exceptions), 1) exception = exceptions[0] self.assertIsInstance(exception.get_recurrence_id(), datetime.datetime) self.assertTrue(exception.thisandfuture) self.assertEqual(str(exception.get_start()), "2014-08-16 13:00:00+01:00") self.assertEqual(exception.get_attendee_by_email("jane@example.org").get_participant_status(), kolabformat.PartDeclined) self.assertRaises(ValueError, exception.get_attendee, "somebody@else.com") # get instances with exception data occurrence = event.get_next_instance(exception.get_start() - datetime.timedelta(days=1)) self.assertEqual(occurrence.get_start(), exception.get_start()) self.assertEqual(occurrence.get_summary(), "exception") # find instance directly by date _recurrence_id = datetime.datetime(2014, 8, 15, 10, 0, 0) occurrence = event.get_instance(_recurrence_id) self.assertIsInstance(occurrence, Event) self.assertEqual(str(occurrence.get_recurrence_id()), "2014-08-15 10:00:00+01:00") # set invalid date-only recurrence-id - exception.set_recurrence_id(datetime.date(2014,8,16)) + exception.set_recurrence_id(datetime.date(2014, 8, 16)) event.add_exception(exception) - inst = event.get_next_instance(_recurrence_id); + inst = event.get_next_instance(_recurrence_id) self.assertIsInstance(inst, Event) self.assertIsInstance(inst.get_recurrence_id(), datetime.datetime) def test_023_load_from_message(self): event = event_from_message(event_from_ical(ical_event).to_message()) event.set_sequence(3) message = event.to_message() self.assertTrue(message.is_multipart()) # check attachment MIME parts are kept parts = [p for p in message.walk()] - attachments = event.get_attachments(); + attachments = event.get_attachments() self.assertEqual(len(parts), 5) self.assertEqual(parts[3].get_content_type(), 'image/png') self.assertEqual(parts[3]['Content-ID'].strip('<>'), attachments[0].uri()[4:]) self.assertEqual(parts[4].get_content_type(), 'text/plain') self.assertEqual(parts[4]['Content-ID'].strip('<>'), attachments[1].uri()[4:]) self.assertEqual(event.get_attachment_data(1), 'This is a text file\n') def test_024_bogus_itip_data(self): # DTSTAMP contains an invalid date/time value vevent = """BEGIN:VEVENT UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271 DTSTAMP:20120713T1254140 DTSTART;TZID=Europe/London:20120713T100000 DTEND;TZID=Europe/London:20120713T110000 SUMMARY:test DESCRIPTION:test ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailt o:jane.doe@example.org ATTENDEE;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailt o:user.external@example.com SEQUENCE:1 TRANSP:OPAQUE END:VEVENT """ event = event_from_ical(vevent) self.assertRaises(EventIntegrityError, event.to_message) def test_025_to_dict(self): data = event_from_string(xml_event).to_dict() self.assertIsInstance(data, dict) self.assertIsInstance(data['start'], datetime.datetime) - #self.assertIsInstance(data['end'], datetime.datetime) + # self.assertIsInstance(data['end'], datetime.datetime) self.assertIsInstance(data['created'], datetime.datetime) self.assertIsInstance(data['lastmodified-date'], datetime.datetime) self.assertEqual(data['uid'], '75c740bb-b3c6-442c-8021-ecbaeb0a025e') self.assertEqual(data['summary'], 'test') self.assertEqual(data['location'], 'Room 101') self.assertEqual(data['description'], 'test') self.assertEqual(data['priority'], 5) self.assertEqual(data['status'], 'CANCELLED') self.assertEqual(data['sequence'], 1) self.assertEqual(data['transparency'], False) self.assertEqual(data['X-GWSHOW-AS'], 'BUSY') self.assertIsInstance(data['organizer'], dict) self.assertEqual(data['organizer']['email'], 'john@example.org') self.assertEqual(len(data['attendee']), 2) self.assertIsInstance(data['attendee'][0], dict) self.assertEqual(len(data['attach']), 1) self.assertIsInstance(data['attach'][0], dict) self.assertEqual(data['attach'][0]['fmttype'], 'text/html') self.assertIsInstance(data['rrule'], dict) self.assertEqual(data['rrule']['freq'], 'DAILY') self.assertEqual(data['rrule']['interval'], 1) self.assertEqual(data['rrule']['wkst'], 'MO') self.assertIsInstance(data['rrule']['until'], datetime.date) self.assertIsInstance(data['alarm'], list) self.assertEqual(len(data['alarm']), 2) self.assertEqual(data['alarm'][0]['action'], 'DISPLAY') self.assertEqual(data['alarm'][1]['action'], 'EMAIL') self.assertEqual(data['alarm'][1]['trigger']['value'], '-P1D') self.assertEqual(len(data['alarm'][1]['attendee']), 1) def test_026_compute_diff(self): e1 = event_from_string(xml_event) e2 = event_from_string(xml_event) e2.set_summary("test2") e2.set_end(e1.get_end() + datetime.timedelta(hours=3)) e2.set_sequence(e1.get_sequence() + 1) e2.set_attendee_participant_status("jane@example.org", "DECLINED") e2.set_lastmodified() diff = compute_diff(e1.to_dict(), e2.to_dict(), True) self.assertEqual(len(diff), 4, "Diff: (length: %d):\r\n%r\r\n%r" % (len(diff), diff, e2.__str__())) ps = self._find_prop_in_list(diff, 'summary') self.assertIsInstance(ps, OrderedDict) self.assertEqual(ps['new'], "test2") pa = self._find_prop_in_list(diff, 'attendee') self.assertIsInstance(pa, OrderedDict) self.assertEqual(pa['index'], 0) self.assertEqual(pa['new'], dict(partstat='DECLINED')) - def test_026_property_to_string(self): data = event_from_string(xml_event).to_dict() self.assertEqual(property_to_string('sequence', data['sequence']), "1") self.assertEqual(property_to_string('start', data['start']), "2013-08-13 10:00 (BST)") self.assertEqual(property_to_string('organizer', data['organizer']), "Doe, John") self.assertEqual(property_to_string('attendee', data['attendee'][0]), "jane@example.org, Accepted") self.assertEqual(property_to_string('rrule', data['rrule']), "Every 1 day(s) until 2015-07-25") self.assertEqual(property_to_string('exdate', data['exdate'][0]), "2014-07-19") self.assertEqual(property_to_string('alarm', data['alarm'][0]), "Display message 2 hour(s) before") self.assertEqual(property_to_string('attach', data['attach'][0]), "noname.1395223627.5555") - def test_027_merge_attendee_data(self): event = event_from_string(xml_event) jane = event.get_attendee("jane@example.org") jane.set_participant_status('TENTATIVE') jack = Attendee("jack@example.org", name="Jack", role='OPT-PARTICIPANT') some = event.set_attendee_participant_status("somebody@else.com", 'ACCEPTED') # update jane + add jack - event.update_attendees([jane,jack]) + event.update_attendees([jane, jack]) self.assertEqual(len(event.get_attendees()), 3) self.assertEqual(event.get_attendee("jane@example.org").get_participant_status(), kolabformat.PartTentative) self.assertEqual(event.get_attendee("somebody@else.com").get_participant_status(), kolabformat.PartAccepted) # test write + read event = event_from_string(str(event)) exception = event.get_exceptions()[0] self.assertEqual(len(exception.get_attendees()), 2) self.assertEqual(event.get_attendee("jane@example.org").get_participant_status(), kolabformat.PartTentative) self.assertEqual(event.get_attendee("jack@example.org").get_name(), "Jack") self.assertRaises(ValueError, exception.get_attendee, "somebody@else.com") # not addded to exception - def _find_prop_in_list(self, diff, name): for prop in diff: if prop['property'] == name: return prop return None if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test-004-icalendar.py b/tests/unit/test-004-icalendar.py index 6033a33..519599b 100644 --- a/tests/unit/test-004-icalendar.py +++ b/tests/unit/test-004-icalendar.py @@ -1,220 +1,219 @@ 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 ; 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 ; Fri, 13 Jul 2012 14:54:15 +0200 (CEST) Received: from 192.168.122.228 (localhost [127.0.0.1]) (Authenticated sender: john.doe@example.org) by kolab.example.org (Postfix) with ESMTPSA id 0EBDA42E39 for ; Fri, 13 Jul 2012 14:54:14 +0200 (CEST) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Fri, 13 Jul 2012 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> X-Sender: john.doe@example.org User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: resource-collection-car@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 ,=20 resource-collection-car@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@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= o:resourc e-collection-car@example.org RRULE:FREQ=3DWEEKLY;INTERVAL=3D1;BYDAY=3DFR TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """) self.assertTrue(message.is_multipart()) - itip_methods = [ "REQUEST" ] + 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" + 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 ; 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 ; Fri, 13 Jul 2012 14:54:15 +0200 (CEST) Received: from 192.168.122.228 (localhost [127.0.0.1]) (Authenticated sender: john.doe@example.org) by kolab.example.org (Postfix) with ESMTPSA id 0EBDA42E39 for ; Fri, 13 Jul 2012 14:54:14 +0200 (CEST) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Fri, 13 Jul 2012 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> X-Sender: john.doe@example.org User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: resource-collection-car@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 ,=20 resource-collection-car@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@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= o:resourc e-collection-car@example.org TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """) self.assertTrue(message.is_multipart()) - itip_methods = [ "REQUEST" ] + 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" + 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 index fc67eaa..47a6947 100644 --- a/tests/unit/test-005-timezone.py +++ b/tests/unit/test-005-timezone.py @@ -1,75 +1,75 @@ import datetime import icalendar import os 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): - #class datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]]) + # class datetime.timedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]]) tdelta = datetime.timedelta(0, 0, 0, 0, 0, 1) event_start = datetime.datetime.now(pytz.timezone("UTC")) event_end = datetime.datetime.now(pytz.timezone("UTC")) + tdelta london = Event() london.set_organizer("john.doe@example.org", "Doe, John") london.add_attendee("resource-car-vw@example.org", cutype="RESOURCE") london.set_dtstamp(datetime.datetime.now(pytz.timezone("UTC"))) london.set_start(event_start.replace(tzinfo=pytz.timezone("Europe/London"))) london.set_end(event_end.replace(tzinfo=pytz.timezone("Europe/London"))) zurich = Event() zurich.set_organizer("john.doe@example.org", "Doe, John") zurich.add_attendee("resource-car-vw@example.org", cutype="RESOURCE") zurich.set_dtstamp(datetime.datetime.now(pytz.timezone("Europe/Zurich"))) zurich.set_start(event_start.replace(tzinfo=pytz.timezone("Europe/Zurich"))) zurich.set_end(event_end.replace(tzinfo=pytz.timezone("Europe/Zurich"))) london_xml = london.__str__() zurich_xml = zurich.__str__() - #print london_xml - #print zurich_xml + # 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 + # 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()) - #fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-london1')), 'w') - #fp.write(london_xml) - #fp.close() + # fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-london1')), 'w') + # fp.write(london_xml) + # fp.close() - #fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-london2')), 'w') - #fp.write(london.__str__()) - #fp.close() + # fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-london2')), 'w') + # fp.write(london.__str__()) + # fp.close() - #fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-zurich1')), 'w') - #fp.write(zurich_xml) - #fp.close() + # fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-zurich1')), 'w') + # fp.write(zurich_xml) + # fp.close() - #fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-zurich2')), 'w') - #fp.write(zurich.__str__()) - #fp.close() + # fp = open(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'event-zurich2')), 'w') + # fp.write(zurich.__str__()) + # fp.close() self.assertEqual(london_xml, london.__str__()) self.assertEqual(zurich_xml, zurich.__str__()) - diff --git a/tests/unit/test-006-ldap_psearch.py b/tests/unit/test-006-ldap_psearch.py index 9b4e0cd..5e082b3 100644 --- a/tests/unit/test-006-ldap_psearch.py +++ b/tests/unit/test-006-ldap_psearch.py @@ -1,7 +1,8 @@ import unittest + class TestLDAPPsearch(unittest.TestCase): def test_001_import_psearch(self): from ldap.controls import psearch diff --git a/tests/unit/test-007-ldap_syncrepl.py b/tests/unit/test-007-ldap_syncrepl.py index e09579f..d80efea 100644 --- a/tests/unit/test-007-ldap_syncrepl.py +++ b/tests/unit/test-007-ldap_syncrepl.py @@ -1,7 +1,8 @@ import unittest + class TestLDAPSyncrepl(unittest.TestCase): def test_001_import_syncrepl(self): from ldap import syncrepl diff --git a/tests/unit/test-008-sievelib.py b/tests/unit/test-008-sievelib.py index cd4eb3b..9226ad6 100644 --- a/tests/unit/test-008-sievelib.py +++ b/tests/unit/test-008-sievelib.py @@ -1,56 +1,57 @@ import sys import unittest sieve_scripts = [ # You're average vacation script. """ require [ "vacation" ]; if anyof (true) { vacation :days 1 :subject "Out of Office" "I'm out of the office"; } """, # A non-any/allof if (control) header (test) structure """ require ["fileinto"]; if header :contains "X-Spam-Flag" "YES" { fileinto "Spam"; stop; } """, # The same, all on the same line """ require ["fileinto"]; if header :contains "X-Spam-Flag" "YES" { fileinto "Spam"; stop; } """, # A little more of a complex list of tests """ require ["fileinto"]; if allof (header :contains "X-Mailer" "OTRS", header :contains "X-Powered-By" "OTRS", header :contains "Organization" "Example, Inc.") { fileinto "OTRS"; stop; } """, ] + class TestSievelib(unittest.TestCase): def test_001_import_sievelib(self): from sievelib.parser import Parser def test_002_parse_tests(self): from sievelib.parser import Parser sieve_parser = Parser(debug=True) i = 0 for sieve_str in sieve_scripts: i += 1 result = sieve_parser.parse(sieve_str) if not result: print "Sieve line: %r" % (sieve_parser.lexer.text.split('\n')[(sieve_parser.lexer.text[:sieve_parser.lexer.pos].count('\n'))]) - raise Exception, "Failed parsing Sieve script #%d: %s" % (i, sieve_parser.error) + raise Exception("Failed parsing Sieve script #%d: %s" % (i, sieve_parser.error)) diff --git a/tests/unit/test-009-parse_ldap_uri.py b/tests/unit/test-009-parse_ldap_uri.py index d020f8d..2596ddd 100644 --- a/tests/unit/test-009-parse_ldap_uri.py +++ b/tests/unit/test-009-parse_ldap_uri.py @@ -1,17 +1,16 @@ import unittest from pykolab import utils + class TestParseLdapUri(unittest.TestCase): def test_001_ldap_uri(self): ldap_uri = "ldap://localhost" result = utils.parse_ldap_uri(ldap_uri) self.assertEqual(result, ("ldap", "localhost", "389", None, None, None, None)) def test_002_ldap_uri_port(self): ldap_uri = "ldap://localhost:389" result = utils.parse_ldap_uri(ldap_uri) self.assertEqual(result, ("ldap", "localhost", "389", None, None, None, None)) - - diff --git a/tests/unit/test-010-transliterate.py b/tests/unit/test-010-transliterate.py index e6b9000..87e4b84 100644 --- a/tests/unit/test-010-transliterate.py +++ b/tests/unit/test-010-transliterate.py @@ -1,110 +1,111 @@ # -*- coding: utf-8 -*- import unittest + class TestTransliteration(unittest.TestCase): def test_001_raw_fr_FR(self): """ The special thing about this case is that the givenname starts with a special character. """ from pykolab import utils givenname = r'Étienne-Nicolas' surname = r'Méhul' preferredlanguage = 'fr_FR' self.assertEqual('Etienne-Nicolas', utils.translate(givenname, preferredlanguage)) self.assertEqual('Mehul', utils.translate(surname, preferredlanguage)) def test_002_unicode_fr_FR(self): """ The special thing about this case is that the givenname starts with a special character. """ from pykolab import utils givenname = 'Étienne-Nicolas' surname = 'Méhul' preferredlanguage = 'fr_FR' self.assertEqual('Etienne-Nicolas', utils.translate(givenname, preferredlanguage)) self.assertEqual('Mehul', utils.translate(surname, preferredlanguage)) def test_003_raw_es_ES(self): """ The special thing about this case is that the givenname starts with a special character. """ from pykolab import utils givenname = r'Álvaro' surname = r'Fuentes' preferredlanguage = 'es_ES' self.assertEqual('Alvaro', utils.translate(givenname, preferredlanguage)) self.assertEqual('Fuentes', utils.translate(surname, preferredlanguage)) def test_004_unicode_es_ES(self): """ The special thing about this case is that the givenname starts with a special character. """ from pykolab import utils givenname = 'Álvaro' surname = 'Fuentes' preferredlanguage = 'es_ES' self.assertEqual('Alvaro', utils.translate(givenname, preferredlanguage)) self.assertEqual('Fuentes', utils.translate(surname, preferredlanguage)) def test_005_raw_ru_RU(self): from pykolab import utils givenname = r'Николай' surname = r'Римский-Корсаков' preferredlanguage = 'ru_RU' self.assertEqual('Nikolaj', utils.translate(givenname, preferredlanguage)) self.assertEqual('Rimskij-Korsakov', utils.translate(surname, preferredlanguage)) def test_006_unicode_ru_RU(self): from pykolab import utils givenname = u'Николай' surname = u'Римский-Корсаков' preferredlanguage = 'ru_RU' self.assertEqual('Nikolaj', utils.translate(givenname, preferredlanguage)) self.assertEqual('Rimskij-Korsakov', utils.translate(surname, preferredlanguage)) def test_007_raw_ru_RU(self): from pykolab import utils givenname = r'Юлия' surname = r'Ёлкина' preferredlanguage = 'ru_RU' self.assertEqual('Yuliya', utils.translate(givenname, preferredlanguage)) self.assertEqual('Yolkina', utils.translate(surname, preferredlanguage)) def test_008_unicode_ru_RU(self): from pykolab import utils givenname = u'Юлия' surname = u'Ёлкина' preferredlanguage = 'ru_RU' self.assertEqual('Yuliya', utils.translate(givenname, preferredlanguage)) self.assertEqual('Yolkina', utils.translate(surname, preferredlanguage)) def test_009_raw_decode(self): raw_str = r"Николай" self.assertEqual('Николай', raw_str.decode("string_escape")) raw_str = r"raw" self.assertEqual('raw', raw_str.decode("string_escape")) if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test-011-itip.py b/tests/unit/test-011-itip.py index 08d9949..38e00b8 100644 --- a/tests/unit/test-011-itip.py +++ b/tests/unit/test-011-itip.py @@ -1,544 +1,541 @@ # -*- coding: utf-8 -*- import pykolab import datetime import pytz import kolabformat from pykolab import itip from pykolab.xml import Event from pykolab.xml import participant_status_label from pykolab.translate import _ from icalendar import Calendar from email import message from email import message_from_string from wallace import module_resources from twisted.trial import unittest # define some iTip MIME messages itip_multipart = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Fri, 13 Jul 2012 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: resource-collection-car@example.org Subject: "test" has been updated --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable *test* --=_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@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= o:resource-collection-car@example.org ATTENDEE;ROLE=3DOPT-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailto:anoth= er-resource@example.org TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """ itip_non_multipart = """Return-Path: Sender: john.doe@example.org Content-Type: text/calendar; method=REQUEST; charset=UTF-8 Content-Transfer-Encoding: quoted-printable To: resource-collection-car@example.org From: john.doe@example.org Date: Mon, 24 Feb 2014 11:27:28 +0100 Message-ID: <1a3aa8995e83dd24cf9247e538ac913a@example.org> Subject: test 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:20120713T125414Z DTSTART;TZID=3DEurope/London:20120713T100000 DTEND;TZID=3DEurope/London:20120713T110000 SUMMARY:test DESCRIPTION:test ORGANIZER;CN=3D"Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DACCEPTED;RSVP=3DTRUE:mailt= o:resource-collection-car@example.org TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ itip_google_multipart = """MIME-Version: 1.0 Message-ID: <001a11c2ad84243e0604f3246bae@google.com> Date: Mon, 24 Feb 2014 10:27:28 +0000 Subject: =?ISO-8859-1?Q?Invitation=3A_iTip_from_Apple_=40_Mon_Feb_24=2C_2014_12pm_?= - =?ISO-8859-1?Q?=2D_1pm_=28Tom_=26_T=E4m=29?= + =?ISO-8859-1?Q?=2D_1pm_=28Tom_=26_T=E4m=29?= From: "john.doe" To: Content-Type: multipart/mixed; boundary=001a11c2ad84243df004f3246bad --001a11c2ad84243df004f3246bad Content-Type: multipart/alternative; boundary=001a11c2ad84243dec04f3246bab --001a11c2ad84243dec04f3246bab Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes --001a11c2ad84243dec04f3246bab Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable
--001a11c2ad84243dec04f3246bab Content-Type: text/calendar; charset=UTF-8; method=REQUEST Content-Transfer-Encoding: 7bit BEGIN:VCALENDAR PRODID:-//Google Inc//Google Calendar 70.9054//EN VERSION:2.0 CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT DTSTART:20140224T110000Z DTEND:20140224T120000Z DTSTAMP:20140224T102728Z ORGANIZER:mailto:kepjllr6mcq7d0959u4cdc7000@group.calendar.google.com UID:0BE2F640-5814-47C9-ABAE-E7E959204E76 ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE ;X-NUM-GUESTS=0:mailto:kepjllr6mcq7d0959u4cdc7000@group.calendar.google.com ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP= TRUE;CN=John Sample;X-NUM-GUESTS=0:mailto:john.sample@example.org CREATED:20140224T102728Z DESCRIPTION:Testing Multipart structure\\nView your event at http://www.goog le.com/calendar/event?action=VIEW&eid=XzYxMTRhY2k2Nm9xMzBiOWw3MG9qOGI5azZ0M WppYmExODkwa2FiYTU2dDJqaWQ5cDY4bzM4aDluNm8gdGhvbWFzQGJyb3RoZXJsaS5jaA&tok=N TIja2VwamxscjZtY3E3ZDA5NTl1NGNkYzcwMDBAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbTkz NTcyYTU2YmUwNWMxNjY0Zjc3OTU0MzhmMDcwY2FhN2NjZjIzYWM&ctz=Europe/Zurich&hl=en . LAST-MODIFIED:20140224T102728Z LOCATION: SEQUENCE:5 STATUS:CONFIRMED SUMMARY:iTip from Apple TRANSP:OPAQUE END:VEVENT END:VCALENDAR --001a11c2ad84243dec04f3246bab-- --001a11c2ad84243df004f3246bad Content-Type: application/ics; name="invite.ics" Content-Disposition: attachment; filename="invite.ics" Content-Transfer-Encoding: base64 QkVHSU46VkNBTEVOREFSDQpQUk9ESUQ6LS8vR29vZ2xlIEluYy8vR29vZ2xlIENhbGVuZGFyIDcw LjkwNTQvL0VODQpWRVJTSU9OOjIuMA0KQ0FMU0NBTEU6R1JFR09SSUFODQpNRVRIT0Q6UkVRVUVT VA0KQkVHSU46VkVWRU5UDQpEVFNUQVJUOjIwMTQwMjI0VDExMDAwMFoNCkRURU5EOjIwMTQwMjI0 VDEyMDAwMFoNCkRUU1RBTVA6MjAxNDAyMjRUMTAyNzI4Wg0KT1JHQU5JWkVSOm1haWx0bzprZXBq bGxyNm1jcTdkMDk1OXU0Y2RjNzAwMEBncm91cC5jYWxlbmRhci5nb29nbGUuY29tDQpVSUQ6MEJF MkY2NDAtNTgxNC00N0M5LUFCQUUtRTdFOTU5MjA0RTc2DQpBVFRFTkRFRTtDVVRZUEU9SU5ESVZJ RFVBTDtST0xFPVJFUS1QQVJUSUNJUEFOVDtQQVJUU1RBVD1BQ0NFUFRFRDtSU1ZQPVRSVUUNCiA7 WC1OVU0tR1VFU1RTPTA6bWFpbHRvOmtlcGpsbHI2bWNxN2QwOTU5dTRjZGM3MDAwQGdyb3VwLmNh bGVuZGFyLmdvb2dsZS5jb20NCkFUVEVOREVFO0NVVFlQRT1JTkRJVklEVUFMO1JPTEU9UkVRLVBB UlRJQ0lQQU5UO1BBUlRTVEFUPU5FRURTLUFDVElPTjtSU1ZQPQ0KIFRSVUU7WC1OVU0tR1VFU1RT PTA6bWFpbHRvOnRob21hc0Bicm90aGVybGkuY2gNCkFUVEVOREVFO0NVVFlQRT1JTkRJVklEVUFM O1JPTEU9UkVRLVBBUlRJQ0lQQU5UO1BBUlRTVEFUPU5FRURTLUFDVElPTjtSU1ZQPQ0KIFRSVUU7 Q049VGhvbWFzIEJydWVkZXJsaTtYLU5VTS1HVUVTVFM9MDptYWlsdG86cm91bmRjdWJlQGdtYWls LmNvbQ0KQ1JFQVRFRDoyMDE0MDIyNFQxMDI3MjhaDQpERVNDUklQVElPTjpUZXN0aW5nIE11bHRp cGFydCBzdHJ1Y3R1cmVcblZpZXcgeW91ciBldmVudCBhdCBodHRwOi8vd3d3Lmdvb2cNCiBsZS5j b20vY2FsZW5kYXIvZXZlbnQ/YWN0aW9uPVZJRVcmZWlkPVh6WXhNVFJoWTJrMk5tOXhNekJpT1d3 M01HOXFPR0k1YXpaME0NCiBXcHBZbUV4T0Rrd2EyRmlZVFUyZERKcWFXUTVjRFk0YnpNNGFEbHVO bThnZEdodmJXRnpRR0p5YjNSb1pYSnNhUzVqYUEmdG9rPU4NCiBUSWphMlZ3YW14c2NqWnRZM0Uz WkRBNU5UbDFOR05rWXpjd01EQkFaM0p2ZFhBdVkyRnNaVzVrWVhJdVoyOXZaMnhsTG1OdmJUa3oN CiBOVGN5WVRVMlltVXdOV014TmpZMFpqYzNPVFUwTXpobU1EY3dZMkZoTjJOalpqSXpZV00mY3R6 PUV1cm9wZS9adXJpY2gmaGw9ZW4NCiAuDQpMQVNULU1PRElGSUVEOjIwMTQwMjI0VDEwMjcyOFoN CkxPQ0FUSU9OOg0KU0VRVUVOQ0U6NQ0KU1RBVFVTOkNPTkZJUk1FRA0KU1VNTUFSWTppVGlwIGZy b20gQXBwbGUNClRSQU5TUDpPUEFRVUUNCkVORDpWRVZFTlQNCkVORDpWQ0FMRU5EQVINCg== --001a11c2ad84243df004f3246bad-- """ itip_application_ics = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Fri, 13 Jul 2012 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c101622@example.org> User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: resource-collection-car@example.org Subject: "test" has been updated --=_c8894dbdb8baeedacae836230e3436fd Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8; format=flowed --=_c8894dbdb8baeedacae836230e3436fd Content-Type: application/ics; charset=UTF-8; method=REQUEST; name=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@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= o:resource-collection-car@example.org TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """ itip_recurring = """Return-Path: Sender: john.doe@example.org Content-Type: text/calendar; method=REQUEST; charset=UTF-8 Content-Transfer-Encoding: 8bit From: john.doe@example.org Date: Mon, 24 Feb 2014 11:27:28 +0100 Message-ID: <1a3aa8995e83dd24cf9247e538ac913a@example.org> Subject: Recurring BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//Mac OS X 10.9.2//EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT UID:dbdb8baeedacae836230e3436fd-5e83dd24cf92 DTSTAMP:20140213T1254140 DTSTART;TZID=Europe/London:20120709T100000 DTEND;TZID=Europe/London:20120709T120000 RRULE:FREQ=DAILY;INTERVAL=1;COUNT=5 SUMMARY:Recurring ORGANIZER;CN="Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=REQ-PARTICIPANT;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:jane@example.com TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ itip_unicode = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Tue, 25 Feb 2014 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: resource-car-audia4@example.org Subject: "test" --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable *test* --=_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=20Webmail=200.9-0.3.el6.kolab_3.0//NONSGML=20Calendar//= EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT UID:eea25142-fb1c-4831-a02d-ac9fb4c16b70 DTSTAMP:20140213T125414Z DTSTART;TZID=3DEurope/London:20140713T100000 DTEND;TZID=3DEurope/London:20140713T140000 SUMMARY:Testing =C3=9Cmlauts DESCRIPTION:Testing =C3=9Cmlauts LOCATION:Rue the Gen=C3=A8ve ORGANIZER;CN=3D"D=C3=BE,=20John":mailto:john.doe@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;CUTYPE=3DRESOURCE;PARTSTAT=3DNEEDS-ACTION;R= SVP=3DTRUE:mailto:resource-car-audia4@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DTENTATIVE;CN=3DSomebody=20Else:m= ailto:somebody@else.com TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """ itip_empty = """MIME-Version: 1.0 Date: Fri, 17 Jan 2014 13:51:50 +0100 From: User-Agent: Roundcube Webmail/0.9.5 To: john.sample@example.org Subject: "test" has been sent Message-ID: <52D92766.5040508@somedomain.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Message plain text goes here... """ conf = pykolab.getConf() if not hasattr(conf, 'defaults'): conf.finalize_conf() + class TestITip(unittest.TestCase): def setUp(self): # intercept calls to smtplib.SMTP.sendmail() import smtplib self.patch(smtplib.SMTP, "__init__", self._mock_smtp_init) self.patch(smtplib.SMTP, "quit", self._mock_nop) self.patch(smtplib.SMTP, "sendmail", self._mock_smtp_sendmail) - self.smtplog = []; + self.smtplog = [] def _mock_nop(self, domain=None): pass def _mock_smtp_init(self, host=None, port=None, local_hostname=None, timeout=0): pass def _mock_smtp_sendmail(self, from_addr, to_addr, message, mail_options=None, rcpt_options=None): self.smtplog.append((from_addr, to_addr, message)) - def test_001_itip_events_from_message(self): itips1 = itip.events_from_message(message_from_string(itip_multipart)) self.assertEqual(len(itips1), 1, "Multipart iTip message with text/calendar") self.assertEqual(itips1[0]['method'], "REQUEST", "iTip request method property") itips2 = itip.events_from_message(message_from_string(itip_non_multipart)) self.assertEqual(len(itips2), 1, "Detect non-multipart iTip messages") itips3 = itip.events_from_message(message_from_string(itip_application_ics)) self.assertEqual(len(itips3), 1, "Multipart iTip message with application/ics attachment") itips4 = itip.events_from_message(message_from_string(itip_google_multipart)) self.assertEqual(len(itips4), 1, "Multipart iTip message from Google") itips5 = itip.events_from_message(message_from_string(itip_empty)) self.assertEqual(len(itips5), 0, "Simple plain text message") # invalid itip blocks self.assertRaises(Exception, itip.events_from_message, message_from_string(itip_multipart.replace("BEGIN:VEVENT", ""))) itips6 = itip.events_from_message(message_from_string(itip_multipart.replace("DTSTART;", "X-DTSTART;"))) self.assertEqual(len(itips6), 0, "Event with not DTSTART") itips7 = itip.events_from_message(message_from_string(itip_non_multipart.replace("METHOD:REQUEST", "METHOD:PUBLISH").replace("method=REQUEST", "method=PUBLISH"))) self.assertEqual(len(itips7), 0, "Invalid METHOD") # iTips with unicode data itips8 = itip.events_from_message(message_from_string(itip_unicode)) self.assertEqual(len(itips8), 1) xml = itips8[0]['xml'] self.assertEqual(xml.get_summary(), "Testing Ümlauts") self.assertEqual(xml.get_location(), "Rue the Genève") - def test_002_check_date_conflict(self): - astart = datetime.datetime(2014,7,13, 10,0,0) - aend = astart + datetime.timedelta(hours=2) + astart = datetime.datetime(2014, 7, 13, 10, 0, 0) + aend = astart + datetime.timedelta(hours=2) - bstart = datetime.datetime(2014,7,13, 10,0,0) - bend = astart + datetime.timedelta(hours=1) + bstart = datetime.datetime(2014, 7, 13, 10, 0, 0) + bend = astart + datetime.timedelta(hours=1) self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) - bstart = datetime.datetime(2014,7,13, 11,0,0) - bend = astart + datetime.timedelta(minutes=30) + bstart = datetime.datetime(2014, 7, 13, 11, 0, 0) + bend = astart + datetime.timedelta(minutes=30) self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) - bend = astart + datetime.timedelta(hours=2) + bend = astart + datetime.timedelta(hours=2) self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) - bstart = datetime.datetime(2014,7,13, 12,0,0) - bend = astart + datetime.timedelta(hours=1) + bstart = datetime.datetime(2014, 7, 13, 12, 0, 0) + bend = astart + datetime.timedelta(hours=1) self.assertFalse(itip.check_date_conflict(astart, aend, bstart, bend)) self.assertFalse(itip.check_date_conflict(bstart, bend, astart, aend)) - bstart = datetime.datetime(2014,6,13, 10,0,0) - bend = datetime.datetime(2014,6,14, 12,0,0) + bstart = datetime.datetime(2014, 6, 13, 10, 0, 0) + bend = datetime.datetime(2014, 6, 14, 12, 0, 0) self.assertFalse(itip.check_date_conflict(astart, aend, bstart, bend)) - bstart = datetime.datetime(2014,7,10, 12,0,0) - bend = datetime.datetime(2014,7,14, 14,0,0) + bstart = datetime.datetime(2014, 7, 10, 12, 0, 0) + bend = datetime.datetime(2014, 7, 14, 14, 0, 0) self.assertTrue(itip.check_date_conflict(astart, aend, bstart, bend)) - def test_002_check_event_conflict(self): itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] event = Event() - event.set_start(datetime.datetime(2012,7,13, 9,30,0, tzinfo=itip_event['start'].tzinfo)) - event.set_end(datetime.datetime(2012,7,13, 10,30,0, tzinfo=itip_event['start'].tzinfo)) + event.set_start(datetime.datetime(2012, 7, 13, 9, 30, 0, tzinfo=itip_event['start'].tzinfo)) + event.set_end(datetime.datetime(2012, 7, 13, 10, 30, 0, tzinfo=itip_event['start'].tzinfo)) self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates") event.set_uid(itip_event['uid']) self.assertFalse(itip.check_event_conflict(event, itip_event), "No conflict for same UID") allday = Event() - allday.set_start(datetime.date(2012,7,13)) - allday.set_end(datetime.date(2012,7,13)) + allday.set_start(datetime.date(2012, 7, 13)) + allday.set_end(datetime.date(2012, 7, 13)) self.assertTrue(itip.check_event_conflict(allday, itip_event), "Conflicting allday event") allday.set_transparency(True) self.assertFalse(itip.check_event_conflict(allday, itip_event), "No conflict if event is set to transparent") event2 = Event() - event2.set_start(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("US/Central"))) - event2.set_end(datetime.datetime(2012,7,13, 11,0,0, tzinfo=pytz.timezone("US/Central"))) + event2.set_start(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("US/Central"))) + event2.set_end(datetime.datetime(2012, 7, 13, 11, 0, 0, tzinfo=pytz.timezone("US/Central"))) self.assertFalse(itip.check_event_conflict(event, itip_event), "No conflict with timezone shift") rrule = kolabformat.RecurrenceRule() rrule.setFrequency(kolabformat.RecurrenceRule.Weekly) rrule.setCount(10) event3 = Event() - event3.set_recurrence(rrule); - event3.set_start(datetime.datetime(2012,6,29, 9,30,0, tzinfo=pytz.utc)) - event3.set_end(datetime.datetime(2012,6,29, 10,30,0, tzinfo=pytz.utc)) + event3.set_recurrence(rrule) + event3.set_start(datetime.datetime(2012, 6, 29, 9, 30, 0, tzinfo=pytz.utc)) + event3.set_end(datetime.datetime(2012, 6, 29, 10, 30, 0, tzinfo=pytz.utc)) self.assertTrue(itip.check_event_conflict(event3, itip_event), "Conflict in (3rd) recurring event instance") itip_event = itip.events_from_message(message_from_string(itip_recurring))[0] self.assertTrue(itip.check_event_conflict(event3, itip_event), "Conflict in two recurring events") event4 = Event() - event4.set_recurrence(rrule); - event4.set_start(datetime.datetime(2012,7,1, 9,30,0, tzinfo=pytz.utc)) - event4.set_end(datetime.datetime(2012,7,1, 10,30,0, tzinfo=pytz.utc)) + event4.set_recurrence(rrule) + event4.set_start(datetime.datetime(2012, 7, 1, 9, 30, 0, tzinfo=pytz.utc)) + event4.set_end(datetime.datetime(2012, 7, 1, 10, 30, 0, tzinfo=pytz.utc)) self.assertFalse(itip.check_event_conflict(event4, itip_event), "No conflict in two recurring events") itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] rrule.setFrequency(kolabformat.RecurrenceRule.Daily) rrule.setCount(10) event5 = Event() - event5.set_recurrence(rrule); - event5.set_start(datetime.datetime(2012,7,9, 10,0,0, tzinfo=pytz.timezone("Europe/London"))) - event5.set_end(datetime.datetime(2012,7,9, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) + event5.set_recurrence(rrule) + event5.set_start(datetime.datetime(2012, 7, 9, 10, 0, 0, tzinfo=pytz.timezone("Europe/London"))) + event5.set_end(datetime.datetime(2012, 7, 9, 11, 0, 0, tzinfo=pytz.timezone("Europe/London"))) event_xml = str(event5) exception = Event(from_string=event_xml) - exception.set_start(datetime.datetime(2012,7,13, 14,0,0, tzinfo=pytz.timezone("Europe/London"))) - exception.set_end(datetime.datetime(2012,7,13, 16,0,0, tzinfo=pytz.timezone("Europe/London"))) - exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False) + exception.set_start(datetime.datetime(2012, 7, 13, 14, 0, 0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_end(datetime.datetime(2012, 7, 13, 16, 0, 0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_recurrence_id(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("Europe/London")), False) event5.add_exception(exception) self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with exception date") exception = Event(from_string=event_xml) - exception.set_start(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London"))) - exception.set_end(datetime.datetime(2012,7,13, 11,0,0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_start(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("Europe/London"))) + exception.set_end(datetime.datetime(2012, 7, 13, 11, 0, 0, tzinfo=pytz.timezone("Europe/London"))) exception.set_status('CANCELLED') - exception.set_recurrence_id(datetime.datetime(2012,7,13, 10,0,0, tzinfo=pytz.timezone("Europe/London")), False) + exception.set_recurrence_id(datetime.datetime(2012, 7, 13, 10, 0, 0, tzinfo=pytz.timezone("Europe/London")), False) event5.add_exception(exception) self.assertFalse(itip.check_event_conflict(event5, itip_event), "No conflict with cancelled exception") def test_002_check_event_conflict_single(self): itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] event = Event() - event.set_start(datetime.datetime(2012,7,10, 9,30,0, tzinfo=itip_event['start'].tzinfo)) - event.set_end(datetime.datetime(2012,7,10, 10,30,0, tzinfo=itip_event['start'].tzinfo)) + event.set_start(datetime.datetime(2012, 7, 10, 9, 30, 0, tzinfo=itip_event['start'].tzinfo)) + event.set_end(datetime.datetime(2012, 7, 10, 10, 30, 0, tzinfo=itip_event['start'].tzinfo)) event.set_recurrence_id(event.get_start()) - dtstart = datetime.datetime(2012,7,13, 9,30,0, tzinfo=itip_event['start'].tzinfo) + dtstart = datetime.datetime(2012, 7, 13, 9, 30, 0, tzinfo=itip_event['start'].tzinfo) second = Event(from_string=str(event)) second.set_start(dtstart) second.set_end(dtstart + datetime.timedelta(hours=1)) second.set_recurrence_id(dtstart) event.add_exception(second) self.assertTrue(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] - dtstart = datetime.datetime(2012,7,15, 10,0,0, tzinfo=itip_event['start'].tzinfo) + dtstart = datetime.datetime(2012, 7, 15, 10, 0, 0, tzinfo=itip_event['start'].tzinfo) second = Event(from_string=str(itip_event['xml'])) second.set_start(dtstart + datetime.timedelta(hours=1)) second.set_end(dtstart + datetime.timedelta(hours=2)) second.set_recurrence_id(dtstart) second.set_transparency(True) itip_event['xml'].add_exception(second) self.assertEqual(len(itip_event['xml'].get_exceptions()), 1) event = Event() - event.set_start(datetime.datetime(2012,7,11, 9,30,0, tzinfo=itip_event['start'].tzinfo)) - event.set_end(datetime.datetime(2012,7,11, 10,30,0, tzinfo=itip_event['start'].tzinfo)) + event.set_start(datetime.datetime(2012, 7, 11, 9, 30, 0, tzinfo=itip_event['start'].tzinfo)) + event.set_end(datetime.datetime(2012, 7, 11, 10, 30, 0, tzinfo=itip_event['start'].tzinfo)) self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (no)") event = Event() - event.set_start(datetime.datetime(2012,7,15, 11,0,0, tzinfo=itip_event['start'].tzinfo)) - event.set_end(datetime.datetime(2012,7,15, 11,30,0, tzinfo=itip_event['start'].tzinfo)) + event.set_start(datetime.datetime(2012, 7, 15, 11, 0, 0, tzinfo=itip_event['start'].tzinfo)) + event.set_end(datetime.datetime(2012, 7, 15, 11, 30, 0, tzinfo=itip_event['start'].tzinfo)) self.assertFalse(itip.check_event_conflict(event, itip_event), "Conflicting dates (exception)") - def test_003_send_reply(self): itip_events = itip.events_from_message(message_from_string(itip_non_multipart)) itip.send_reply("resource-collection-car@example.org", itip_events, "SUMMARY=%(summary)s; STATUS=%(status)s; NAME=%(name)s;") self.assertEqual(len(self.smtplog), 1) self.assertEqual(self.smtplog[0][0], 'resource-collection-car@example.org', "From attendee") self.assertEqual(self.smtplog[0][1], 'john.doe@example.org', "To organizer") _accepted = participant_status_label('ACCEPTED') message = message_from_string(self.smtplog[0][2]) - self.assertEqual(message.get('Subject'), _("Invitation for %(summary)s was %(status)s") % { 'summary':'test', 'status':_accepted }) + self.assertEqual(message.get('Subject'), _("Invitation for %(summary)s was %(status)s") % {'summary': 'test', 'status': _accepted}) - text = str(message.get_payload(0)); + text = str(message.get_payload(0)) self.assertIn('SUMMARY=3Dtest', text) self.assertIn('STATUS=3D' + _accepted, text) def test_004_send_reply_unicode(self): itip_events = itip.events_from_message(message_from_string(itip_non_multipart.replace('SUMMARY:test', "SUMMARY:With äöü"))) itip.send_reply("resource-collection-car@example.org", itip_events, "SUMMARY=%(summary)s; STATUS=%(status)s; NAME=%(name)s;") self.assertEqual(len(self.smtplog), 1) self.assertIn("Subject: =?utf-8?q?Invitation_for_With_=C3=A4=C3=B6=C3=BC_was_Accepted?=", self.smtplog[0][2]) self.assertIn('SUMMARY=3DWith =C3=A4=C3=B6=C3=BC', self.smtplog[0][2]) diff --git a/tests/unit/test-011-wallace_resources.py b/tests/unit/test-011-wallace_resources.py index dd7834b..01ef0d1 100644 --- a/tests/unit/test-011-wallace_resources.py +++ b/tests/unit/test-011-wallace_resources.py @@ -1,239 +1,234 @@ import pykolab import logging import datetime from pykolab import itip from icalendar import Calendar from email import message from email import message_from_string from wallace import module_resources from twisted.trial import unittest # define some iTip MIME messages itip_multipart = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Fri, 13 Jul 2012 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: resource-collection-car@example.org Subject: "test" has been updated --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable *test* --=_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:doe@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= o:resource-collection-car@example.org ATTENDEE;ROLE=3DOPT-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailto:anoth= er-resource@example.org TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """ itip_non_multipart = """Return-Path: Sender: doe@example.org Content-Type: text/calendar; method=REQUEST; charset=UTF-8 Content-Transfer-Encoding: quoted-printable To: resource-collection-car@example.org From: doe@example.org Date: Mon, 24 Feb 2014 11:27:28 +0100 Message-ID: <1a3aa8995e83dd24cf9247e538ac913a@example.org> Subject: test 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:doe@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DACCEPTED;RSVP=3DTRUE:mailt= o:resource-collection-car@example.org TRANSP:OPAQUE END:VEVENT END:VCALENDAR """ conf = pykolab.getConf() if not hasattr(conf, 'defaults'): conf.finalize_conf() + class TestWallaceResources(unittest.TestCase): def setUp(self): # monkey-patch the pykolab.auth module to check API calls # without actually connecting to LDAP self.patch(pykolab.auth.Auth, "connect", self._mock_nop) self.patch(pykolab.auth.Auth, "disconnect", self._mock_nop) self.patch(pykolab.auth.Auth, "find_resource", self._mock_find_resource) self.patch(pykolab.auth.Auth, "get_entry_attributes", self._mock_get_entry_attributes) self.patch(pykolab.auth.Auth, "search_entry_by_attribute", self._mock_search_entry_by_attribute) # intercept calls to smtplib.SMTP.sendmail() import smtplib self.patch(smtplib.SMTP, "__init__", self._mock_smtp_init) self.patch(smtplib.SMTP, "quit", self._mock_nop) self.patch(smtplib.SMTP, "sendmail", self._mock_smtp_sendmail) - self.smtplog = []; + self.smtplog = [] def _mock_nop(self, domain=None): pass def _mock_find_resource(self, address): - if not 'resource' in address: - return []; + if 'resource' not in address: + return [] (prefix, domain) = address.split('@') entry_dn = "cn=" + prefix + ",ou=Resources,dc=" + ",dc=".join(domain.split('.')) - return [ entry_dn ]; + return [entry_dn] def _mock_get_entry_attributes(self, domain, entry, attributes): (_, uid) = entry.split(',')[0].split('=') - return { 'cn': uid, 'mail': uid + "@example.org", '_attrib': attributes } + return {'cn': uid, 'mail': uid + "@example.org", '_attrib': attributes} def _mock_search_entry_by_attribute(self, attr, value, **kw): results = [] if value == "cn=Room 101,ou=Resources,dc=example,dc=org": - results.append(('cn=Rooms,ou=Resources,dc=example,dc=org', { attr: value, 'owner': 'uid=doe,ou=People,dc=example,dc=org' })) + results.append(('cn=Rooms,ou=Resources,dc=example,dc=org', {attr: value, 'owner': 'uid=doe,ou=People,dc=example,dc=org'})) return results def _mock_smtp_init(self, host=None, port=None, local_hostname=None, timeout=0): pass def _mock_smtp_sendmail(self, from_addr, to_addr, message, mail_options=None, rcpt_options=None): self.smtplog.append((from_addr, to_addr, message)) def _get_ics_part(self, message): ics_part = None for part in message.walk(): if part.get_content_type() == 'text/calendar': ics_part = part return ics_part def _get_ical(self, ics): if hasattr(Calendar, 'from_ical'): cal = Calendar.from_ical(ics) elif hasattr(Calendar, 'from_string'): cal = Calendar.from_string(ics) for e in cal.walk(): if e.name == "VEVENT": return e return None - def test_002_resource_record_from_email_address(self): res = module_resources.resource_record_from_email_address("doe@example.org") - self.assertEqual(len(res), 0); + self.assertEqual(len(res), 0) def test_003_resource_records_from_itip_events(self): message = message_from_string(itip_multipart) itips = itip.events_from_message(message) res = module_resources.resource_records_from_itip_events(itips) - self.assertEqual(len(res), 2, "Return resources: %r" % (res)); + self.assertEqual(len(res), 2, "Return resources: %r" % (res)) res = module_resources.resource_records_from_itip_events(itips, message['To']) - self.assertEqual(len(res), 1, "Return target resource: %r" % (res)); - self.assertEqual("cn=resource-collection-car,ou=Resources,dc=example,dc=org", res[0]); - + self.assertEqual(len(res), 1, "Return target resource: %r" % (res)) + self.assertEqual("cn=resource-collection-car,ou=Resources,dc=example,dc=org", res[0]) def test_004_get_resource_owner(self): - owner1 = module_resources.get_resource_owner({ 'owner': "uid=foo,ou=People,cd=example,dc=org" }) + owner1 = module_resources.get_resource_owner({'owner': "uid=foo,ou=People,cd=example,dc=org"}) self.assertIsInstance(owner1, dict) self.assertEqual("foo@example.org", owner1['mail']) self.assertIn("telephoneNumber", owner1['_attrib']) - owner2 = module_resources.get_resource_owner({ 'owner': ["uid=john,ou=People,cd=example,dc=org", "uid=jane,ou=People,cd=example,dc=org"] }) + owner2 = module_resources.get_resource_owner({'owner': ["uid=john,ou=People,cd=example,dc=org", "uid=jane,ou=People,cd=example,dc=org"]}) self.assertIsInstance(owner2, dict) self.assertEqual("john@example.org", owner2['mail']) - owner3 = module_resources.get_resource_owner({ 'dn': "cn=cars,ou=Resources,cd=example,dc=org" }) + owner3 = module_resources.get_resource_owner({'dn': "cn=cars,ou=Resources,cd=example,dc=org"}) self.assertEqual(owner3, None) - owner4 = module_resources.get_resource_owner({ 'dn': "cn=Room 101,ou=Resources,dc=example,dc=org" }) + owner4 = module_resources.get_resource_owner({'dn': "cn=Room 101,ou=Resources,dc=example,dc=org"}) self.assertEqual("doe@example.org", owner4['mail']) - def test_005_send_response_accept(self): itip_event = itip.events_from_message(message_from_string(itip_non_multipart)) module_resources.send_response("resource-collection-car@example.org", itip_event) - self.assertEqual(len(self.smtplog), 1); + self.assertEqual(len(self.smtplog), 1) self.assertEqual("resource-collection-car@example.org", self.smtplog[0][0]) self.assertEqual("doe@example.org", self.smtplog[0][1]) response = message_from_string(self.smtplog[0][2]) self.assertIn("ACCEPTED".lower(), response['subject'].lower(), "Participant status in message subject: %r" % (response['subject'])) self.assertTrue(response.is_multipart()) # find ics part of the response ics_part = self._get_ics_part(response) self.assertIsInstance(ics_part, message.Message) self.assertEqual(ics_part.get_param('method'), "REPLY") - def test_006_send_response_delegate(self): # delegate resource-collection-car@example.org => resource-car-audi-a4@example.org itip_event = itip.events_from_message(message_from_string(itip_non_multipart))[0] itip_event['xml'].delegate('resource-collection-car@example.org', 'resource-car-audi-a4@example.org') itip_event['xml'].set_attendee_participant_status(itip_event['xml'].get_attendee('resource-car-audi-a4@example.org'), "ACCEPTED") module_resources.send_response("resource-collection-car@example.org", itip_event) - self.assertEqual(len(self.smtplog), 2); + self.assertEqual(len(self.smtplog), 2) self.assertEqual("resource-collection-car@example.org", self.smtplog[0][0]) self.assertEqual("resource-car-audi-a4@example.org", self.smtplog[1][0]) # delegated resource responds ACCEPTED response1 = message_from_string(self.smtplog[0][2]) ical1 = self._get_ical(self._get_ics_part(response1).get_payload(decode=True)) self.assertIn("DELEGATED".lower(), response1['subject'].lower(), "Participant status in message subject: %r" % (response1['subject'])) self.assertEqual(ical1['attendee'][1].__str__(), "MAILTO:resource-car-audi-a4@example.org") # resource collection responds DELEGATED response2 = message_from_string(self.smtplog[1][2]) ical2 = self._get_ical(self._get_ics_part(response2).get_payload(decode=True)) self.assertIn("ACCEPTED".lower(), response2['subject'].lower(), "Delegation message subject: %r" % (response2['subject'])) self.assertEqual(ical2['attendee'].__str__(), "MAILTO:resource-car-audi-a4@example.org") self.assertEqual(ical2['attendee'].params['PARTSTAT'], u"ACCEPTED") - - diff --git a/tests/unit/test-012-wallace_invitationpolicy.py b/tests/unit/test-012-wallace_invitationpolicy.py index 8250b89..2ed8709 100644 --- a/tests/unit/test-012-wallace_invitationpolicy.py +++ b/tests/unit/test-012-wallace_invitationpolicy.py @@ -1,178 +1,178 @@ # -*- coding: utf-8 -*- import os import pykolab import logging import time from icalendar import Calendar from email import message from email import message_from_string from wallace import module_invitationpolicy as MIP from twisted.trial import unittest from pykolab.auth.ldap import LDAP from pykolab.constants import * # define some iTip MIME messages itip_multipart = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_c8894dbdb8baeedacae836230e3436fd" From: "Doe, John" Date: Fri, 13 Jul 2012 13:54:14 +0100 Message-ID: <240fe7ae7e139129e9eb95213c1016d7@example.org> User-Agent: Roundcube Webmail/0.9-0.3.el6.kolab_3.0 To: jane.doe@example.org Subject: "test" has been updated --=_c8894dbdb8baeedacae836230e3436fd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable *test* --=_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 1.0.1//NONSGML Calendar//EN CALSCALE:GREGORIAN METHOD:REQUEST BEGIN:VEVENT UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271 DTSTAMP:20120713T125414Z DTSTART;TZID=3DEurope/London:20120713T100000 DTEND;TZID=3DEurope/London:20120713T110000 SUMMARY:test DESCRIPTION:test ORGANIZER;CN=3D"Doe, John":mailto:john.doe@example.org ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= o:jane.doe@example.org ATTENDEE;ROLE=3DOPT-PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailt= user.external@example.com SEQUENCE:1 TRANSP:OPAQUE END:VEVENT END:VCALENDAR --=_c8894dbdb8baeedacae836230e3436fd-- """ conf = pykolab.getConf() if not hasattr(conf, 'defaults'): conf.finalize_conf() + class TestWallaceInvitationpolicy(unittest.TestCase): def setUp(self): # monkey-patch the pykolab.auth module to check API calls # without actually connecting to LDAP self.patch(pykolab.auth.Auth, "connect", self._mock_nop) self.patch(pykolab.auth.Auth, "disconnect", self._mock_nop) self.patch(pykolab.auth.Auth, "find_user_dn", self._mock_find_user_dn) self.patch(pykolab.auth.Auth, "get_entry_attributes", self._mock_get_entry_attributes) self.patch(pykolab.auth.Auth, "list_domains", self._mock_list_domains) # intercept calls to smtplib.SMTP.sendmail() import smtplib self.patch(smtplib.SMTP, "__init__", self._mock_smtp_init) self.patch(smtplib.SMTP, "quit", self._mock_nop) self.patch(smtplib.SMTP, "sendmail", self._mock_smtp_sendmail) - self.smtplog = []; + self.smtplog = [] def _mock_find_user_dn(self, value, kolabuser=False): (prefix, domain) = value.split('@') return "uid=" + prefix + ",ou=People,dc=" + ",dc=".join(domain.split('.')) def _mock_get_entry_attributes(self, domain, entry, attributes): (_, uid) = entry.split(',')[0].split('=') - return { 'cn': uid, 'mail': uid + "@example.org", '_attrib': attributes } + return {'cn': uid, 'mail': uid + "@example.org", '_attrib': attributes} def _mock_list_domains(self): return {'example.org': 'example.org'} def _mock_nop(self, domain=None): pass def _mock_smtp_init(self, host=None, port=None, local_hostname=None, timeout=0): pass def _mock_smtp_sendmail(self, from_addr, to_addr, message, mail_options=None, rcpt_options=None): self.smtplog.append((from_addr, to_addr, message)) return True def test_001_itip_events_from_message(self): itips = pykolab.itip.events_from_message(message_from_string(itip_multipart)) self.assertEqual(len(itips), 1, "Multipart iTip message with text/calendar") self.assertEqual(itips[0]['method'], "REQUEST", "iTip request method property") self.assertEqual(len(itips[0]['attendees']), 2, "List attendees from iTip") self.assertEqual(itips[0]['attendees'][0], "mailto:jane.doe@example.org", "First attendee from iTip") def test_002_user_dn_from_email_address(self): res = MIP.user_dn_from_email_address("doe@example.org") self.assertEqual("uid=doe,ou=People,dc=example,dc=org", res) def test_003_get_matching_invitation_policy(self): - user = { 'kolabinvitationpolicy': [ + user = {'kolabinvitationpolicy': [ 'TASK_REJECT:*', 'EVENT_ACCEPT:example.org', 'EVENT_REJECT:gmail.com', 'ALL_UPDATE:outlook:com', 'ALL_MANUAL:*' - ] } + ]} self.assertEqual(MIP.get_matching_invitation_policies(user, 'a@fastmail.net', MIP.COND_TYPE_EVENT), [MIP.ACT_MANUAL]) self.assertEqual(MIP.get_matching_invitation_policies(user, 'b@example.org', MIP.COND_TYPE_EVENT), [MIP.ACT_ACCEPT, MIP.ACT_MANUAL]) self.assertEqual(MIP.get_matching_invitation_policies(user, 'c@gmail.com', MIP.COND_TYPE_EVENT), [MIP.ACT_REJECT, MIP.ACT_MANUAL]) self.assertEqual(MIP.get_matching_invitation_policies(user, 'd@somedomain.net', MIP.COND_TYPE_TASK), [MIP.ACT_REJECT, MIP.ACT_MANUAL]) - user = { 'kolabinvitationpolicy': ['ALL_SAVE_TO_FOLDER:maya.foo@example.org', 'ACT_REJECT:others'] } + user = {'kolabinvitationpolicy': ['ALL_SAVE_TO_FOLDER:maya.foo@example.org', 'ACT_REJECT:others']} self.assertEqual(MIP.get_matching_invitation_policies(user, 'maya.foo@example.org', MIP.COND_TYPE_ALL), [MIP.ACT_SAVE_TO_FOLDER]) self.assertEqual(MIP.get_matching_invitation_policies(user, 'd@somedomain.net', MIP.COND_TYPE_ALL), [MIP.ACT_MANUAL]) # def test_004_write_locks(self): # user = { 'cn': 'John Doe', 'mail': "doe@example.org" } # # lock_key = MIP.get_lock_key(user, '1234567890-abcdef') # lock_file = os.path.join(MIP.mybasepath, 'locks', lock_key + '.lock') # MIP.set_write_lock(lock_key) # # time.sleep(1) # self.assertTrue(os.path.isfile(lock_file)) # self.assertFalse(MIP.set_write_lock(lock_key, False)) # # MIP.remove_write_lock(lock_key) # self.assertFalse(os.path.isfile(lock_file)) def test_005_is_auto_reply(self): - all_manual = [ 'ACT_MANUAL' ] - accept_none = [ 'ACT_REJECT' ] - accept_all = [ 'ACT_ACCEPT', 'ACT_UPDATE' ] - accept_cond = [ 'ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_REJECT_IF_CONFLICT' ] - accept_some = [ 'ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_SAVE_TO_CALENDAR:example.org', 'ACT_REJECT_IF_CONFLICT' ] - accept_avail = [ 'ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_REJECT_IF_CONFLICT:example.org' ] - - self.assertFalse( MIP.is_auto_reply({ 'kolabinvitationpolicy':all_manual }, 'user@domain.org', 'event')) - self.assertTrue( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_none }, 'user@domain.org', 'event')) - self.assertTrue( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_all }, 'user@domain.com', 'event')) - self.assertTrue( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_cond }, 'user@domain.com', 'event')) - self.assertTrue( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_some }, 'user@domain.com', 'event')) - self.assertFalse( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_some }, 'sam@example.org', 'event')) - self.assertFalse( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_avail }, 'user@domain.com', 'event')) - self.assertTrue( MIP.is_auto_reply({ 'kolabinvitationpolicy':accept_avail }, 'john@example.org', 'event')) + all_manual = ['ACT_MANUAL'] + accept_none = ['ACT_REJECT'] + accept_all = ['ACT_ACCEPT', 'ACT_UPDATE'] + accept_cond = ['ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_REJECT_IF_CONFLICT'] + accept_some = ['ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_SAVE_TO_CALENDAR:example.org', 'ACT_REJECT_IF_CONFLICT'] + accept_avail = ['ACT_ACCEPT_IF_NO_CONFLICT', 'ACT_REJECT_IF_CONFLICT:example.org'] + + self.assertFalse( MIP.is_auto_reply({'kolabinvitationpolicy': all_manual}, 'user@domain.org', 'event')) + self.assertTrue( MIP.is_auto_reply({'kolabinvitationpolicy': accept_none}, 'user@domain.org', 'event')) + self.assertTrue( MIP.is_auto_reply({'kolabinvitationpolicy': accept_all}, 'user@domain.com', 'event')) + self.assertTrue( MIP.is_auto_reply({'kolabinvitationpolicy': accept_cond}, 'user@domain.com', 'event')) + self.assertTrue( MIP.is_auto_reply({'kolabinvitationpolicy': accept_some}, 'user@domain.com', 'event')) + self.assertFalse( MIP.is_auto_reply({'kolabinvitationpolicy': accept_some}, 'sam@example.org', 'event')) + self.assertFalse( MIP.is_auto_reply({'kolabinvitationpolicy': accept_avail}, 'user@domain.com', 'event')) + self.assertTrue( MIP.is_auto_reply({'kolabinvitationpolicy': accept_avail}, 'john@example.org', 'event')) def test_006_send_update_notification(self): itips = pykolab.itip.events_from_message(message_from_string(itip_multipart.replace('SUMMARY:test', 'SUMMARY:with äöü'))) - MIP.send_update_notification(itips[0]['xml'], { 'mail': 'sender@example.org' }, old=None, reply=True) + MIP.send_update_notification(itips[0]['xml'], {'mail': 'sender@example.org'}, old=None, reply=True) self.assertEqual(len(self.smtplog), 1) self.assertIn("Subject: =?utf-8?", self.smtplog[0][2]) self.assertIn("The event 'with =C3=A4=C3=B6=C3=BC' at", self.smtplog[0][2]) - diff --git a/tests/unit/test-014-conf-and-raw.py b/tests/unit/test-014-conf-and-raw.py index 677cb2f..e7f0ba6 100644 --- a/tests/unit/test-014-conf-and-raw.py +++ b/tests/unit/test-014-conf-and-raw.py @@ -1,38 +1,39 @@ # -*- coding: utf-8 -*- import os import pykolab import tempfile import unittest conf = pykolab.getConf() conf.finalize_conf(fatal=False) + class TestConfRaw(unittest.TestCase): config_file = None @classmethod def setup_class(self, *args, **kw): (fp, self.config_file) = tempfile.mkstemp() os.write(fp, '[kolab]\n') os.close(fp) conf.read_config(self.config_file) @classmethod def teardown_class(self, *args, **kw): os.remove(self.config_file) def test_001_set(self): password = '$%something' conf.command_set('kolab', 'test_password', password) - + def test_002_get(self): password = conf.get('kolab', 'test_password') self.assertEqual('$%something', password) def test_003_get_raw(self): password = conf.get_raw('kolab', 'test_password') self.assertNotEqual('$%something', password) if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test-015-translate.py b/tests/unit/test-015-translate.py index e9d3bdd..513bcfc 100644 --- a/tests/unit/test-015-translate.py +++ b/tests/unit/test-015-translate.py @@ -1,21 +1,22 @@ # -*- coding: utf-8 -*- import unittest import gettext from pykolab import translate + class TestTranslate(unittest.TestCase): def test_001_default_langs(self): self.assertTrue(len(translate.getDefaultLangs()) > 0) def test_002_translate(self): from pykolab.translate import _ self.assertEqual(_("Folder name"), "Folder name") def test_003_set_lang(self): from pykolab.translate import _ self.assertEqual(_("Folder name"), "Folder name") if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test-016-todo.py b/tests/unit/test-016-todo.py index b94d12d..4760399 100644 --- a/tests/unit/test-016-todo.py +++ b/tests/unit/test-016-todo.py @@ -1,304 +1,302 @@ import datetime import pytz import sys import unittest import kolabformat import icalendar from pykolab.xml import Attendee from pykolab.xml import Todo from pykolab.xml import TodoIntegrityError from pykolab.xml import InvalidEventStatusError from pykolab.xml import todo_from_ical from pykolab.xml import todo_from_string from pykolab.xml import todo_from_message ical_todo = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN BEGIN:VTODO UID:18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0 DTSTAMP;VALUE=DATE-TIME:20140820T101333Z CREATED;VALUE=DATE-TIME:20140731T100704Z LAST-MODIFIED;VALUE=DATE-TIME:20140820T101333Z DTSTART;VALUE=DATE-TIME;TZID=Europe/London:20140818T180000 DUE;VALUE=DATE-TIME;TZID=Europe/London:20140822T133000 RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=2MO,-1WE;UNTIL=20150220T180000Z SUMMARY:Sample Task assignment DESCRIPTION:Summary: Sample Task assignment\\nDue Date: 08/11/14\\nDue Time: \\n13:30 AM SEQUENCE:3 CATEGORIES:iTip PRIORITY:1 STATUS:IN-PROCESS PERCENT-COMPLETE:20 ATTENDEE;CN="Doe, John";PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;CUTYPE= INDIVIDUAL;RSVP=TRUE:mailto:john.doe@example.org ORGANIZER;CN=Thomas:mailto:thomas.bruederli@example.org END:VTODO END:VCALENDAR """ ical_todo_attachment = """ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN BEGIN:VTODO UID:18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0 DTSTAMP;VALUE=DATE-TIME:20140820T101333Z CREATED;VALUE=DATE-TIME:20140731T100704Z LAST-MODIFIED;VALUE=DATE-TIME:20140820T101333Z DUE;VALUE=DATE-TIME;TZID=Europe/London:20150228T133000 SUMMARY:Task with attachment SEQUENCE:3 PRIORITY:1 STATUS:IN-PROCESS ORGANIZER;CN=Thomas:mailto:thomas.bruederli@example.org ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=image/png;X-LABEL=silhouette.pn g:iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAIAAADY27xgAAAAGXRFWHRTb2Z0d2FyZQBBZG9i ZSBJbWFnZVJlYWR5ccllPAAAAsRJREFUeNrsmeluKjEMhTswrAWB4P3fECGx79CjsTDmOKRkpF xxpfoHSmchX7ybFrfb7eszpPH1MfKH8ofyH6KUtd/c7/en0wmfWBdF0Wq1Op1Ou91uNGoer6iX V1ar1Xa7xUJeB4qsr9frdyVlWWZH2VZyPp+xPXHIAoK70+m02+1m9JXj8bhcLi+Xi3J4xUCazS bUltdtd7ud7ldUIhC3u+iTwF0sFhlR4Kds4LtRZK1w4te5UM6V6JaqhqC3CQ28OAsKggJfbZ3U eozCqZ4koHIZCGmD9ivuos9YONFirmxrI0UNZG1kbZeUXdJQNJNa91RlqMn0ekYUMZDup6dXVV m+1OSZhqLx6bVCELJGSsyFQtFrF15JGYMZgoxubWGDSDVhvTipDKWhoBOIpFobxtlbJ0Gh0/tg lgXal4woUHi/36fQoBQncDAlupa8DeVwOPRe4lUyGAwQ+dl7W+xBXkJBhEUqR32UoJfYIKrR4d ZBgcdIRqfEqn+mekl9FNRbSTA249la3ev1/kXHD47ZbEYR5L9kMplkd9vNZqMFyIYxxfN8Pk8q QGlagT5QDtfrNYUMlWW9LiGNPPSmC/+OgpK2r4RO6dOatZd+4gAAemdIi6Fg9EKLD4vASWkzv3 ew06NSCiA40CumAIoaIrhrcAwjF7aDo58gUchgNV+0n1BAcDgcoAZrXV9mI4qkhtK6FJFhi9Fo ZKPsgQI1ACJieH/Kd570t+xFoIzHYzl5Q40CFGrSqGuks3qmYIKJfIl0nPKLxAMFw7Dv1+2QYf vFSOBQubbOFDSc7ZcfWvHv6DzhOzT6IeOVPuz8Roex0f6EgsE/2IL4qdg7hIXz7/pBie7q1uWr tp66xrif0l1KwUE4P7Y9Gci/ZgtNRFX+Rw06Q2RigsjuDc3urwKHxuNITaaxyD9mT2WvSDAXn/ Pvhh8BBgBjyfPSGbSYcwAAAABJRU5ErkJggg== END:VTODO END:VCALENDAR """ xml_todo = """ Roundcube-libkolab-1.1 Libkolabxml-1.1 2.0 3.1.0 18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0 2014-07-31T10:07:04Z 2014-08-20T10:13:33Z 3 PUBLIC iTip 9F3E68BED4A94DA2A51EE589F7FDC6C8-A4BF5BBB9FEAA271 /kolab.org/Europe/Berlin 2014-08-18T18:00:00 /kolab.org/Europe/Berlin 2014-08-22T13:30:00 Sample Task assignment Summary: Sample Task assignment Due Date: 08/11/14 Due Time: 13:30 AM 1 IN-PROCESS 20 Thomas mailto:%3Cthomas%40example.org%3E Doe, John NEEDS-ACTION REQ-PARTICIPANT true mailto:%3Cjohn%40example.org%3E DISPLAY alarm 1 START -PT2H """ + class TestTodoXML(unittest.TestCase): todo = Todo() 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) + raise AssertionError("%s != %s" % (type(_value), _type)) def test_001_minimal(self): self.todo.set_summary("test") self.assertEqual("test", self.todo.get_summary()) self.assertIsInstance(self.todo.__str__(), str) def test_002_full(self): self.todo.set_summary("test full") status = self.todo.get_status() self.assertEqual(status, kolabformat.StatusUndefined) self.assertRaises(InvalidEventStatusError, self.todo.set_status, (-1)) self.todo.set_status(status) # TODO: add more setters and getter tests here def test_010_load_from_xml(self): todo = todo_from_string(xml_todo) self.assertEqual(todo.uid, '18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0') self.assertEqual(todo.get_sequence(), 3) self.assertIsInstance(todo.get_due(), datetime.datetime) self.assertEqual(str(todo.get_due()), "2014-08-22 13:30:00+02:00") self.assertEqual(str(todo.get_start()), "2014-08-18 18:00:00+02:00") self.assertEqual(todo.get_categories(), ['iTip']) self.assertEqual(todo.get_attendee_by_email("john@example.org").get_participant_status(), kolabformat.PartNeedsAction) self.assertIsInstance(todo.get_organizer(), kolabformat.ContactReference) self.assertEqual(todo.get_organizer().name(), "Thomas") self.assertEqual(todo.get_status(True), "IN-PROCESS") self.assertEqual(todo.get_related_to(), "9F3E68BED4A94DA2A51EE589F7FDC6C8-A4BF5BBB9FEAA271") - def test_020_load_from_ical(self): ical_str = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Roundcube//Roundcube libcalendaring 1.1.0//Sabre//Sabre VObject 2.1.3//EN CALSCALE:GREGORIAN METHOD:REQUEST """ + ical_todo + "END:VCALENDAR" ical = icalendar.Calendar.from_ical(ical_str) vtodo = ical.walk('VTODO')[0] - #print vtodo + # print vtodo todo = todo_from_ical(ical.walk('VTODO')[0].to_ical()) self.assertEqual(todo.get_summary(), "Sample Task assignment") self.assertIsInstance(todo.get_start(), datetime.datetime) self.assertEqual(todo.get_percentcomplete(), 20) - #print str(todo) + # print str(todo) data = todo.to_dict() self.assertIsInstance(data['rrule'], dict) self.assertEqual(data['rrule']['freq'], 'MONTHLY') self.assertEqual(data['rrule']['interval'], 2) self.assertEqual(data['rrule']['byday'], '2MO,-1WE') self.assertIsInstance(data['rrule']['until'], datetime.datetime) def test_021_as_string_itip(self): self.todo.set_summary("test") self.todo.set_start(datetime.datetime(2014, 9, 20, 11, 00, 00, tzinfo=pytz.timezone("Europe/London"))) self.todo.set_due(datetime.datetime(2014, 9, 23, 12, 30, 00, tzinfo=pytz.timezone("Europe/London"))) self.todo.set_sequence(3) self.todo.add_custom_property('X-Custom', 'check') # render iCal and parse again using the icalendar lib ical = icalendar.Calendar.from_ical(self.todo.as_string_itip()) vtodo = ical.walk('VTODO')[0] self.assertEqual(vtodo['uid'], self.todo.get_uid()) self.assertEqual(vtodo['summary'], "test") self.assertEqual(vtodo['sequence'], 3) self.assertEqual(vtodo['X-CUSTOM'], "check") self.assertIsInstance(vtodo['due'].dt, datetime.datetime) self.assertIsInstance(vtodo['dtstamp'].dt, datetime.datetime) - def test_022_ical_with_attachment(self): todo = todo_from_ical(ical_todo_attachment) vattachment = todo.get_attachments() self.assertEqual(len(vattachment), 1) attachment = vattachment[0] self.assertEqual(attachment.mimetype(), 'image/png') self.assertEqual(attachment.label(), 'silhouette.png') def test_030_to_dict(self): data = todo_from_string(xml_todo).to_dict() self.assertIsInstance(data, dict) self.assertIsInstance(data['start'], datetime.datetime) self.assertIsInstance(data['due'], datetime.datetime) self.assertEqual(data['uid'], '18C2EBBD8B31D99F7AA578EDFDFB1AC0-FCBB6C4091F28CA0') self.assertEqual(data['summary'], 'Sample Task assignment') self.assertEqual(data['description'], "Summary: Sample Task assignment\nDue Date: 08/11/14\nDue Time: 13:30 AM") self.assertEqual(data['priority'], 1) self.assertEqual(data['sequence'], 3) self.assertEqual(data['status'], 'IN-PROCESS') self.assertIsInstance(data['alarm'], list) self.assertEqual(len(data['alarm']), 1) self.assertEqual(data['alarm'][0]['action'], 'DISPLAY') - if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/unit/test-017-diff.py b/tests/unit/test-017-diff.py index 4b0c72c..46e0a77 100644 --- a/tests/unit/test-017-diff.py +++ b/tests/unit/test-017-diff.py @@ -1,207 +1,207 @@ import unittest from pykolab.xml import Todo from pykolab.xml.utils import compute_diff from pykolab.xml.utils import order_proplists xml_todo_01 = """ Roundcube-libkolab-1.1 Libkolabxml-1.1 2.0 3.1.0 E49000485B9EE3299A8870AD6A3E75E5-A4BF5BBB9FEAA271 2015-03-25T10:32:18Z 2015-03-25T16:09:11Z 0 Old attachments image/png silhouette.png cid:silhouette.1427297477.7514.png text/plain notes.txt cid:notes.1427298885.9012.txt image/gif landspeeder-lego-icon.gif cid:landspeeder-lego-icon.1427299751.8542.gif """ xml_todo_02 = """ Roundcube-libkolab-1.1 Libkolabxml-1.1 2.0 3.1.0 E49000485B9EE3299A8870AD6A3E75E5-A4BF5BBB9FEAA271 2015-03-25T10:32:18Z 2015-03-25T16:09:53Z 1 New attachments Removed attachment text/plain notes.txt cid:notes.1427298885.9012.txt image/gif landspeeder-lego-icon.gif cid:landspeeder-lego-icon.1427299751.8542.gif """ + class TestComputeDiff(unittest.TestCase): - + def test_000_order_proplists(self): one = { "uri": "cid:one", "label": "one.txt" } two = { "uri": "cid:two", "label": "two.png" } three = { "foo": "three" } four = { "foo": "four" } (aa, bb) = order_proplists([one, two, four], [three, two, one, four]) self.assertEqual(len(aa), 3) self.assertEqual(len(bb), 4) self.assertEqual(aa[0], bb[0]) self.assertEqual(aa[1], bb[1]) (aa, bb) = order_proplists([four, three, one, two], [two, one]) self.assertEqual(len(aa), 4) self.assertEqual(len(bb), 2) self.assertEqual(aa[0], bb[0]) self.assertEqual(aa[1], bb[1]) def test_000_order_proplists2(self): a1 = {'code': '4567', 'locality': 'Worktown', 'country': 'Switzerland', 'region': '', 'label': '', 'street': 'Workstreet 22', 'type': 'work'} a2 = {'code': '55550', 'locality': 'San Francisco', 'country': 'USA', 'region': 'CA', 'label': '', 'street': 'O-steet', 'type': 'office'} a3 = {'code': '6666', 'locality': 'Workcity', 'country': 'Switzerland', 'region': 'ZH', 'label': '', 'street': 'Workstreet 22', 'type': 'work'} a4 = dict(a2) (aa, bb) = order_proplists([a1, a2], [a3, a4]) self.assertEqual(aa[1], bb[1]) def test_001_attachments(self): old = Todo(from_string=xml_todo_01) new = Todo(from_string=xml_todo_02) diff = compute_diff(old.to_dict(), new.to_dict()) self.assertEqual(len(diff), 5) self.assertEqual(diff[0]['property'], 'sequence') self.assertEqual(diff[0]['old'], 0) self.assertEqual(diff[0]['new'], 1) self.assertEqual(diff[1]['property'], 'description') self.assertEqual(diff[1]['old'], '') self.assertEqual(diff[2]['property'], 'summary') self.assertEqual(diff[2]['old'], 'Old attachments') self.assertEqual(diff[2]['new'], 'New attachments') self.assertEqual(diff[3]['property'], 'attach') self.assertEqual(diff[3]['new'], None) self.assertEqual(diff[3]['old']['uri'], "cid:silhouette.1427297477.7514.png") self.assertEqual(diff[4]['property'], 'lastmodified-date') - if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/unit/test-018-note.py b/tests/unit/test-018-note.py index dca077f..7a7bddf 100644 --- a/tests/unit/test-018-note.py +++ b/tests/unit/test-018-note.py @@ -1,96 +1,97 @@ import datetime import pytz import unittest import kolabformat from pykolab.xml import Note from pykolab.xml import NoteIntegrityError from pykolab.xml import note_from_string xml_note = """ d407f007-cb52-42cb-8e06-67f6132d718f Roundcube-libkolab-1.1 Libkolabxml-1.1 2015-03-26T08:12:37Z 2015-03-26T08:12:37Z One Two PUBLIC Kolab Note <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><meta http-equiv="Content-Type" /></head><body> <p>This is a HTML note</p> </body></html> """ + class TestNoteXML(unittest.TestCase): + 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) + raise AssertionError("%s != %s" % (type(_value), _type)) def test_001_minimal(self): note = Note() note.set_summary("test") self.assertEqual(note.summary(), "test") self.assertIsInstance(note.__str__(), str) def test_002_full(self): note = Note() note.set_summary("test") note.set_description("Description") note.set_classification("CONFIDENTIAL") note.add_category("Foo") note.add_category("Bar") # print str(note) self.assertEqual(len(note.get_uid()), 36) self.assertEqual(note.summary(), "test") self.assertEqual(note.description(), "Description") self.assertEqual(note.get_classification(), "CONFIDENTIAL") self.assertEqual(note.get_classification(False), kolabformat.ClassConfidential) self.assertEqual(len(note.categories()), 2) def test_010_load_from_xml(self): note = note_from_string(xml_note) self.assertEqual(note.get_uid(), "d407f007-cb52-42cb-8e06-67f6132d718f") self.assertEqual(note.summary(), "Kolab Note") self.assertIsInstance(note.get_created(), datetime.datetime) self.assertEqual(note.get_created().tzinfo, pytz.utc) self.assertIsInstance(note.get_lastmodified(), datetime.datetime) self.assertEqual(note.get_lastmodified().tzinfo, pytz.utc) def test_011_to_xml(self): note = Note() with self.assertRaises(ValueError): note.set_classification(-1) def test_012_to_xml(self): # minimal note = Note() xml = str(note) self.assertTrue('' in xml) self.assertTrue('' in xml) def test_020_to_dict(self): data = note_from_string(xml_note).to_dict() self.assertIsInstance(data, dict) - self.assertTrue(data.has_key('uid')) + self.assertTrue('uid' in data) self.assertIsInstance(data.get('created', None), datetime.datetime) self.assertIsInstance(data.get('lastmodified-date', None), datetime.datetime) self.assertEqual(data.get('summary', None), "Kolab Note") self.assertEqual(data.get('classification', None), 'PUBLIC') self.assertIsInstance(data.get('categories', None), list) self.assertEqual(len(data.get('categories', None)), 2) self.assertTrue('

This is a HTML note

' in data.get('description', None)) - if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test-019-contact.py b/tests/unit/test-019-contact.py index d5366f6..6446ad9 100644 --- a/tests/unit/test-019-contact.py +++ b/tests/unit/test-019-contact.py @@ -1,351 +1,351 @@ import datetime import pytz import unittest import kolabformat from pykolab.xml import Contact from pykolab.xml import ContactIntegrityError from pykolab.xml import contact_from_string from pykolab.xml import contact_from_message from email import message_from_string xml_contact = """ urn:uuid:437656b2-d55e-11e4-a43b-080027b7afc5 3.1.0 Roundcube-libkolab-1.1 Libkolabxml-1.2 20150328T152236Z individual Sample Dude Dude Sample M. Dr. Jr. This is a sample contact for testing <text>Head of everything</text> Kolab Inc. R&D Department x-manager Jane Manager x-assistant Mrs. Moneypenny O-steet San Francisco CA 55550 USA www.kolab.org home Homestreet 11 Hometown 12345 Germany work Workstreet 22 Worktown 4567 Switzerland the dude spouse Leia child Jay child Bob 20010401 20100705 data:image/gif;base64,R0lGODlhAQABAPAAAOjq6gAAACH/C1hNUCBEYXRhWE1QAT8AIfkEBQAAAAAsAAAAAAEAAQAAAgJEAQA7 M home +49-555-11223344 work +49-555-44556677 cell +41-777-55588899 jabber:dude@kolab.org home home@kolab.org work work@kolab.org data:application/pgp-keys;base64,LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tDQpWZXJzaW9uOiBHbnVQRy9NYWNHUEcyIHYyLjAuMjINCg0KbVFHaUJFSVNOcUVSQkFDUnovb3J5L0JEY3pBWUFUR3JnTSt5WDgzV2pkaUVrNmZKNFFUekk2ZFZ1TkxTNy4uLg0KLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ== """ contact_mime_message = """MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_4ff5155d75dc1328b7f5fe10ddce8d24" From: john.doe@example.org To: john.doe@example.org Date: Mon, 13 Apr 2015 15:26:44 +0200 X-Kolab-Type: application/x-vnd.kolab.contact X-Kolab-Mime-Version: 3.0 Subject: 05cfc56d-2bb3-46d1-ada4-5f5310337fb2 User-Agent: Roundcube Webmail/1.2-git --=_4ff5155d75dc1328b7f5fe10ddce8d24 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=ISO-8859-1 This is a Kolab Groupware object. To view this object you will need an emai= l client that understands the Kolab Groupware format. For a list of such em= ail clients please visit http://www.kolab.org/ --=_4ff5155d75dc1328b7f5fe10ddce8d24 Content-Transfer-Encoding: 8bit Content-Type: application/vcard+xml; charset=UTF-8; name=kolab.xml Content-Disposition: attachment; filename=kolab.xml; size=1636 urn:uuid:05cfc56d-2bb3-46d1-ada4-5f5310337fb2 3.1.0 Roundcube-libkolab-1.1 Libkolabxml-1.1 20150413T132644Z individual User One User One DAV This is a Kolab contact home +1555224488 home dav.user01@example.org home user.one@example.org --=_4ff5155d75dc1328b7f5fe10ddce8d24-- """ + class TestContactXML(unittest.TestCase): contact = Contact() 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) + raise AssertionError("%s != %s" % (type(_value), _type)) def test_001_minimal(self): self.contact.set_name("test") self.assertEqual("test", self.contact.name()) self.assertIsInstance(self.contact.__str__(), str) def test_002_full(self): self.contact.set_name("test") # TODO: add more setters and getter tests here def test_010_load_from_xml(self): contact = contact_from_string(xml_contact) self.assertEqual(contact.get_uid(), '437656b2-d55e-11e4-a43b-080027b7afc5') self.assertEqual(contact.name(), 'Sample Dude') def test_011_load_from_message(self): contact = contact_from_message(message_from_string(contact_mime_message)) self.assertEqual(contact.get_uid(), '05cfc56d-2bb3-46d1-ada4-5f5310337fb2') self.assertEqual(contact.name(), 'User One') def test_020_to_dict(self): data = contact_from_string(xml_contact).to_dict() self.assertIsInstance(data, dict) self.assertIsInstance(data['lastmodified-date'], datetime.datetime) self.assertEqual(data['uid'], '437656b2-d55e-11e4-a43b-080027b7afc5') self.assertEqual(data['fn'], 'Sample Dude') self.assertEqual(data['given'], 'Sample') self.assertEqual(data['surname'], 'Dude') self.assertEqual(data['prefix'], 'Dr.') self.assertEqual(data['suffix'], 'Jr.') self.assertIsInstance(data['birthday'], datetime.date) self.assertIsInstance(data['anniversary'], datetime.date) self.assertEqual(data['organization'], 'Kolab Inc.') self.assertEqual(data['department'], 'R&D Department') self.assertEqual(data['manager'], ['Jane Manager']) self.assertEqual(data['note'], 'This is a sample contact for testing') self.assertEqual(len(data['address']), 3) self.assertEqual(data['address'][0]['type'], 'home') self.assertEqual(data['address'][1]['type'], 'work') self.assertEqual(data['address'][2]['type'], 'office') self.assertEqual(len(data['tel']), 3) self.assertEqual(data['tel'][0]['type'], 'home') self.assertEqual(data['tel'][0]['number'], '+49-555-11223344') self.assertEqual(data['tel'][1]['type'], 'work') self.assertEqual(data['tel'][2]['type'], 'mobile') self.assertEqual(len(data['email']), 2) self.assertEqual(data['email'][0]['type'], 'home') self.assertEqual(data['email'][0]['address'], 'home@kolab.org') self.assertEqual(len(data['url']), 1) self.assertEqual(len(data['key']), 1) self.assertEqual(data['key'][0]['type'], 'pgp') self.assertIsInstance(data['photo'], dict) self.assertEqual(data['photo']['mimetype'], 'image/gif') - if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/unit/test-020-auth_cache.py b/tests/unit/test-020-auth_cache.py index c4740dc..d30d403 100644 --- a/tests/unit/test-020-auth_cache.py +++ b/tests/unit/test-020-auth_cache.py @@ -1,115 +1,113 @@ # coding: utf8 import unittest import datetime import os from pykolab.auth.ldap import auth_cache import pykolab conf = pykolab.getConf() conf.finalize_conf() import sqlalchemy from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import Integer from sqlalchemy import MetaData from sqlalchemy import String from sqlalchemy import Table from sqlalchemy import Text from sqlalchemy import desc from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import mapper try: from sqlalchemy.orm import relationship except: from sqlalchemy.orm import relation as relationship try: from sqlalchemy.orm import sessionmaker except: from sqlalchemy.orm import create_session metadata = MetaData() -## -## Classes -## - DeclarativeBase = declarative_base() + class Entry(DeclarativeBase): __tablename__ = 'entries' id = Column(Integer, primary_key=True) domain = Column(String(256), index=True, nullable=True) key = Column(Text, index=True, nullable=False) value = Column(Text, nullable=False) last_change = Column(DateTime, nullable=False, default=datetime.datetime.now()) def __init__(self, key, value): self.key = key if not isinstance(value, unicode): self.value = unicode(value, 'utf-8') else: self.value = value engine = create_engine('sqlite:////tmp/%s.db' % (os.getpid()), echo=False, echo_pool=False) DeclarativeBase.metadata.create_all(engine) Session = sessionmaker(bind=engine) db = Session() auth_cache.db = db + class TestAuthCache(unittest.TestCase): def test_001_plain_insert(self): auth_cache.set_entry( 'somekey', 'ou=People,dc=example,dc=org' ) result = auth_cache.get_entry('somekey') self.assertEqual(result, 'ou=People,dc=example,dc=org') def test_002_plain_encoding_insert(self): auth_cache.set_entry( 'somekey2', 'ou=Geschäftsbereich,ou=People,dc=example,dc=org' ) result = auth_cache.get_entry('somekey2') self.assertEqual(result, 'ou=Gesch\xc3\xa4ftsbereich,ou=People,dc=example,dc=org') def test_003_unicode_insert(self): auth_cache.set_entry( 'somekey3', u'ou=Geschäftsbereich,ou=People,dc=example,dc=org' ) result = auth_cache.get_entry('somekey3') self.assertEqual(result, 'ou=Gesch\xc3\xa4ftsbereich,ou=People,dc=example,dc=org') @unittest.skip("Double encoding or decoding") def test_004_unicode_escape(self): auth_cache.set_entry( 'somekey4', u'ou=Gesch\xc3\xa4ftsbereich,ou=People,dc=example,dc=org' ) result = auth_cache.get_entry('somekey4') self.assertEqual(result, u'ou=Gesch\xc3\xa4ftsbereich,ou=People,dc=example,dc=org') def test_005_longkey(self): auth_cache.set_entry( 'v' + 'e'*512 + 'rylongkey', 'v' + 'e'*512 + 'rylongvalue' ) result = auth_cache.get_entry('v' + 'e'*512 + 'rylongkey') self.assertEqual(result, 'v' + 'e'*512 + 'rylongvalue') diff --git a/ucs/kolab_sieve.py b/ucs/kolab_sieve.py index 1f78bf5..d7125a7 100755 --- a/ucs/kolab_sieve.py +++ b/ucs/kolab_sieve.py @@ -1,159 +1,155 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # # workaround for PEP 366 __package__ = '' import listener import logging import os import sys import univention_baseconfig import univention.debug as ulog sys.path = [ os.path.abspath( os.path.join( os.path.dirname( os.path.realpath(os.path.abspath(__file__)) ), '..' ) - ) ] + sys.path + )] + sys.path sys.stderr = open('/dev/null', 'a') name = 'kolab_sieve' description = "Sieve Script Management for Kolab Groupware on UCS" # The filter has to be composed to make sure only Kolab Groupware # related objects are passed along to this listener module. filter = '(objectClass=kolabInetOrgPerson)' #attributes = [ '*' ] import pykolab from pykolab import constants from pykolab import utils log = pykolab.getLogger('pykolab.listener') log.remove_stdout_handler() log.setLevel(logging.DEBUG) log.debuglevel = 9 conf = pykolab.getConf() conf.finalize_conf(fatal=False) conf.debuglevel = 9 from pykolab.auth import Auth + def handler(*args, **kw): auth = Auth() auth.connect() if len(args) == 4: # moddn, not relevant for Sieve Script Management pass elif len(args) == 3: dn = args[0] new = utils.normalize(args[1]) old = utils.normalize(args[2]) if isinstance(old, dict) and len(old.keys()) > 0: # Either the entry changed or was deleted if isinstance(new, dict) and len(new.keys()) > 0: # The entry was modified. result_attr = conf.get('cyrus-sasl', 'result_attribute') - if not new.has_key(result_attr): + if result_attr not in new: log.error( "Entry %r does not have attribute %r" % ( dn, result_attr ) ) return # See if the mailserver_attribute exists mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() - if mailserver_attribute == None: + if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return - if new.has_key(mailserver_attribute): + if mailserver_attribute in new: if not new[mailserver_attribute] == constants.fqdn: log.info( "The mail server for user %r is set, and it is not me (%r)" % ( dn, new[mailserver_attribute] ) ) return else: log.error("Entry %r does not have a mail server set" % (dn)) return conf.plugins.exec_hook( 'sieve_mgmt_refresh', - kw = { - 'user': new[result_attr] - } + kw={'user': new[result_attr]} ) else: # The entry was deleted. This is irrelevant for # Sieve Script Management return elif isinstance(new, dict) and len(new.keys()) > 0: # Old is not a dict (or empty), so the entry is just created # See if the mailserver_attribute exists mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() result_attr = conf.get('cyrus-sasl', 'result_attribute').lower() - if mailserver_attribute == None: + if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return - if new.has_key(mailserver_attribute): + if mailserver_attribute in new: if not new[mailserver_attribute] == constants.fqdn: log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, new[mailserver_attribute])) return conf.plugins.exec_hook( 'sieve_mgmt_refresh', - kw = { - 'user': new[result_attr] - } + kw={'user': new[result_attr]} ) else: log.info("entry %r changed, but no new or old attributes" % (dn)) - diff --git a/ucs/listener.py b/ucs/listener.py index be7b9e9..67c3dca 100755 --- a/ucs/listener.py +++ b/ucs/listener.py @@ -1,198 +1,200 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # # workaround for PEP 366 __package__ = '' import listener import logging import os import sys from univention.config_registry import ConfigRegistry import univention.debug as ulog sys.path = [ os.path.abspath( os.path.join( os.path.dirname( os.path.realpath(os.path.abspath(__file__)) ), '..' ) - ) ] + sys.path + )] + sys.path #sys.stderr = open('/dev/null', 'a') name = 'kolab' description = "Kolab Groupware Listener for UCS" # The filter has to be composed to make sure only Kolab Groupware # related objects are passed along to this listener module. filter = '(|(objectClass=kolabInetOrgPerson)(objectClass=univentionMailSharedFolder))' -#attributes = [ '*' ] +# attributes = ['*'] import pykolab from pykolab import constants from pykolab import utils log = pykolab.getLogger('pykolab.listener') -#log.remove_stdout_handler() +# log.remove_stdout_handler() log.setLevel(logging.DEBUG) log.debuglevel = 9 conf = pykolab.getConf() conf.finalize_conf(fatal=False) conf.debuglevel = 9 from pykolab.auth import Auth + def handler(*args, **kw): log.info("kolab.handler(args(%d): %r, kw: %r)" % (len(args), args, kw)) auth = Auth() auth.connect() if len(args) == 4: # moddn dn = args[0] new = utils.normalize(args[1]) old = utils.normalize(args[2]) command = args[4] pass elif len(args) == 3: dn = args[0] new = utils.normalize(args[1]) old = utils.normalize(args[2]) if isinstance(old, dict) and len(old.keys()) > 0: # Two options: # - entry changed # - entry deleted log.info("user %r, old is dict" % (dn)) if isinstance(new, dict) and len(new.keys()) > 0: log.info("Modify entry %r" % (dn)) mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() - if mailserver_attribute == None: + if mailserver_attribute is None: log.error("Mail server attribute is not set") return - if old.has_key(mailserver_attribute): + if mailserver_attribute in old: log.info("Modified entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, new[mailserver_attribute])) if not old[mailserver_attribute] == constants.fqdn: # Even though the new mailserver can be us, it is the # *current* mail server that needs to push for the XFER. log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, old[mailserver_attribute])) return else: # If old has no mailserver attribute, but new does, we need to create # the user locally. - if new.has_key(mailserver_attribute): + if mailserver_attribute in new: if not new[mailserver_attribute] == constants.fqdn: log.info("The mail server for user %r is set (in new, not old), but it is not me (%r)" % (dn, new[mailserver_attribute])) return else: log.info("Entry %r does not have a mail server attribute." % (dn)) return auth._auth._synchronize_callback( - change_type = 'modify', - previous_dn = None, - change_number = None, - dn = dn, - entry = new + change_type='modify', + previous_dn=None, + change_number=None, + dn=dn, + entry=new ) else: log.info("Delete entry %r" % (dn)) # See if the mailserver_attribute exists mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() - if mailserver_attribute == None: + if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return - if old.has_key(mailserver_attribute): + if mailserver_attribute in old: log.info("Deleted entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, old[mailserver_attribute])) if not old[mailserver_attribute] == constants.fqdn: log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, old[mailserver_attribute])) return else: log.info("Entry deletion notification for %r does not have a mail server attribute specified." % (dn)) cfg = ConfigRegistry() cfg.load() if cfg.is_true('mail/cyrus/mailbox/delete', True): auth._auth._synchronize_callback( - change_type = 'delete', - previous_dn = None, - change_number = None, - dn = dn, - entry = old + change_type='delete', + previous_dn=None, + change_number=None, + dn=dn, + entry=old ) elif isinstance(new, dict) and len(new.keys()) > 0: # Old is not a dict (or empty), so the entry is just created log.info("Add entry %r" % (dn)) # See if the mailserver_attribute exists mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() - if mailserver_attribute == None: + if mailserver_attribute is None: log.error("Mail server attribute is not set") # TODO: Perhaps, query for IMAP servers. If there is only one, # we know what to do. return - if new.has_key(mailserver_attribute): + if mailserver_attribute in new: log.info("Added entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, new[mailserver_attribute])) if not new[mailserver_attribute] == constants.fqdn: log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, new[mailserver_attribute])) return else: log.info("Added entry %r does not have a mail server attribute set." % (dn)) return auth._auth._synchronize_callback( - change_type = 'add', - previous_dn = None, - change_number = None, - dn = dn, - entry = new + change_type='add', + previous_dn=None, + change_number=None, + dn=dn, + entry=new ) else: log.info("entry %r changed, but no new or old attributes" % (dn)) + def initialize(): log.info("kolab.initialize()") diff --git a/wallace.py b/wallace.py index 9371823..a45f34a 100755 --- a/wallace.py +++ b/wallace.py @@ -1,40 +1,39 @@ #!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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 . # import sys # For development purposes sys.path.extend(['.', '..']) from pykolab.translate import _ try: from pykolab.constants import * except ImportError, e: print >> sys.stderr, _("Cannot load pykolab/constants.py:") print >> sys.stderr, "%s" % e sys.exit(1) import wallace if __name__ == "__main__": wallace = wallace.WallaceDaemon() wallace.run() -