imip-agent

tests/test_handle.py

1253:333740ca50b6
2017-09-12 Paul Boddie Consider period replacement status when comparing form periods.
     1 #!/usr/bin/env python     2      3 """     4 A handler to help with testing.     5      6 Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from imiptools.client import ClientForObject    23 from imiptools.data import Object, get_address, parse_object    24 from imiptools.dates import get_datetime, to_timezone    25 from imiptools.mail import Messenger    26 from imiptools.period import RecurringPeriod    27 from imiptools.stores import get_store, get_journal    28 from os.path import split    29 import sys    30     31 class TestClient(ClientForObject):    32     33     """    34     A content handler for use in testing, as opposed to operating within the    35     mail processing pipeline.    36     """    37     38     # Action methods.    39     40     def handle_request(self, action, start=None, end=None, recurrenceid=None):    41     42         """    43         Process the current request for the current user. Return whether the    44         given 'action' was taken.    45     46         If 'start' and 'end' are specified, they will be used in any    47         counter-proposal.    48     49         Where 'recurrenceid' is specified and refers to a new recurrence, the    50         action will apply only to this new recurrence.    51         """    52     53         have_new_recurrence = self.obj.get_recurrenceid() != recurrenceid    54     55         if have_new_recurrence:    56             self.obj["RECURRENCE-ID"] = [(recurrenceid, {})]    57             self.obj.remove_all(["RDATE", "RRULE"])    58     59         # Reply only on behalf of this user.    60     61         if action in ("accept", "decline"):    62             attendee_attr = self.update_participation(action == "accept" and "ACCEPTED" or "DECLINED")    63             method = "REPLY"    64     65         elif action == "counter":    66             attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user)    67             method = "COUNTER"    68     69         # Nothing else is supported.    70     71         else:    72             return None    73     74         # For counter-proposals or new recurrences, set a new main period for    75         # the event.    76     77         if action == "counter" or have_new_recurrence:    78             period = self.obj.get_main_period(self.get_tzid())    79     80             # Use the existing or configured time zone for the specified    81             # datetimes.    82     83             start = to_timezone(get_datetime(start), period.tzid)    84             end = to_timezone(get_datetime(end), period.tzid)    85             period = RecurringPeriod(start, end, period.tzid, period.origin, period.get_start_attr(), period.get_end_attr())    86             self.obj.set_period(period)    87     88         # Where no attendees remain, no message is generated.    89     90         if not attendee_attr:    91             return None    92     93         # NOTE: This is a simpler form of the code in imipweb.client.    94     95         organiser = get_address(self.obj.get_value("ORGANIZER"))    96     97         self.update_sender(attendee_attr)    98         self.obj["ATTENDEE"] = [(self.user, attendee_attr)]    99         self.update_dtstamp()   100         self.update_sequence(False)   101    102         message = self.messenger.make_outgoing_message(   103             [self.obj.to_part(method)],   104             [organiser],   105             outgoing_bcc=get_address(self.user)   106             )   107    108         return message.as_string()   109    110 # A simple main program that attempts to handle a stored request, writing the   111 # response message to standard output.   112    113 if __name__ == "__main__":   114     progname = split(sys.argv[0])[-1]   115    116     try:   117         action, store_type, store_dir, journal_dir, preferences_dir, user = sys.argv[1:7]   118         if len(sys.argv) >= 10:   119             start, end = sys.argv[7:9]   120             i = 9   121         else:   122             start, end = None, None   123             i = 7   124         uid, recurrenceid = (sys.argv[i:i+2] + [None] * 2)[:2]   125     except ValueError:   126         print >>sys.stderr, """\   127 Usage: %s <action> <store type> <store directory> <journal directory>   128        <preferences directory> <user URI> [ <start> <end> ]   129        <uid> <recurrence-id>   130    131 Need 'accept', 'counter' or 'decline', a store type, a store directory, a   132 journal directory, a preferences directory, user URI, any counter-proposal or   133 new recurrence datetimes (see below), plus the appropriate event UID and   134 RECURRENCE-ID (if a recurrence is involved).   135    136 The RECURRENCE-ID must be in exactly the form employed by the store, not a   137 different but equivalent representation, if the identifier is to refer to an   138 existing recurrence.   139    140 Alternatively, omit the UID and RECURRENCE-ID and provide event-only details on   141 standard input to force the script to handle an event not already present in the   142 store.   143    144 If 'counter' has been indicated, alternative start and end datetimes are also   145 required. If a specific recurrence is being separated from an event, such   146 datetimes are also required in order to set the main period of the recurrence.   147 """   148         sys.exit(1)   149    150     store = get_store(store_type, store_dir)   151     journal = get_journal(store_type, journal_dir)   152    153     if uid is not None:   154         obj = store.get_event(user, uid, recurrenceid)   155    156         # Permit new recurrences by getting the parent object.   157    158         if not obj:   159             obj = store.get_event(user, uid)   160    161         if not obj:   162             print >>sys.stderr, "No such event:", uid, recurrenceid   163             sys.exit(1)   164     else:   165         obj = Object(parse_object(sys.stdin, "utf-8"))   166    167     handler = TestClient(obj, user, Messenger(), store, None, journal, preferences_dir)   168     response = handler.handle_request(action, start, end, recurrenceid)   169    170     if response:   171         if uid is not None:   172             store.dequeue_request(user, uid, recurrenceid)   173         print response   174     else:   175         sys.exit(1)   176    177 # vim: tabstop=4 expandtab shiftwidth=4