imip-agent

Annotated tests/test_handle.py

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