~launchpad-pqm/launchpad/devel

8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
3
4
"""Helper functions dealing with emails in tests.
5
"""
6
__metaclass__ = type
7
8
import email
4204.2.20 by Julian Edwards
Address remaining review issues and fix some affected tests.
9
import operator
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
10
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
11
import transaction
7675.845.8 by Edwin Grubbs
Added test helper function. Tests now work.
12
from zope.component import getUtility
13
14
from lp.registry.interfaces.persontransferjob import (
7675.845.9 by Edwin Grubbs
Refactored and renamed AddMemberNotificationJob to MembershipNotificationJob.
15
    IMembershipNotificationJobSource,
7675.845.8 by Edwin Grubbs
Added test helper function. Tests now work.
16
    )
17
from lp.services.job.runner import JobRunner
14550.1.1 by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad
18
from lp.services.log.logger import DevNullLogger
8377.6.17 by Tim Penhey
Move the doctest and fix stub imports.
19
from lp.services.mail import stub
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
20
21
5294.1.4 by Jeroen Vermeulen
Allow caller to suppress commit.
22
def pop_notifications(sort_key=None, commit=True):
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
23
    """Return generated emails as email messages.
24
5294.1.4 by Jeroen Vermeulen
Allow caller to suppress commit.
25
    A helper function which optionally commits the transaction, so
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
26
    that the notifications are queued in stub.test_emails and pops these
27
    notifications from the queue.
4204.2.18 by Julian Edwards
Merge RF and address review comments. Forgot to commit after the RF merge since
28
5294.1.4 by Jeroen Vermeulen
Allow caller to suppress commit.
29
    :param sort_key: define sorting function.  sort_key specifies a
4785.3.7 by Jeroen Vermeulen
Removed whitespace at ends of lines
30
    function of one argument that is used to extract a comparison key from
4204.2.20 by Julian Edwards
Address remaining review issues and fix some affected tests.
31
    each list element.  (See the sorted() Python built-in.)
9080.5.6 by Danilo Šegan
Address barry's review comments and rework tests to use mail_helpers instead.
32
    :param commit: whether to commit before reading email (defaults to True).
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
33
    """
5294.1.4 by Jeroen Vermeulen
Allow caller to suppress commit.
34
    if commit:
35
        transaction.commit()
4204.2.20 by Julian Edwards
Address remaining review issues and fix some affected tests.
36
    if sort_key is None:
6334.3.22 by Aaron Bentley
Fix lint errors
37
        sort_key = operator.itemgetter('To')
4204.2.20 by Julian Edwards
Address remaining review issues and fix some affected tests.
38
8771.4.13 by Aaron Bentley
Fix test broken by making to headers different from envelope to.
39
    notifications = []
40
    for fromaddr, toaddrs, raw_message in stub.test_emails:
41
        notification = email.message_from_string(raw_message)
42
        notification['X-Envelope-To'] = ', '.join(toaddrs)
43
        notification['X-Envelope-From'] = fromaddr
44
        notifications.append(notification)
3691.431.35 by Tim Penhey
post 2nd review updates - should be good to go now
45
    stub.test_emails = []
4204.2.20 by Julian Edwards
Address remaining review issues and fix some affected tests.
46
47
    return sorted(notifications, key=sort_key)
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
48
49
14527.4.1 by Colin Watson
Make a couple of tests more reliable, with the aid of a new sort_addresses helper.
50
def sort_addresses(header):
51
    """Sort an address-list in an e-mail header field body."""
52
    addresses = set(address.strip() for address in header.split(','))
53
    return ", ".join(sorted(addresses))
54
55
6334.3.13 by Aaron Bentley
Be more specific about the notification message
56
def print_emails(include_reply_to=False, group_similar=False,
9209.3.9 by Curtis Hovey
Only print rationale headers when requested.
57
                 include_rationale=False, notifications=None):
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
58
    """Pop all messages from stub.test_emails and print them with
59
     their recipients.
60
61
    Since the same message may be sent more than once (for different
62
    recipients), setting 'group_similar' will print each distinct
63
    message only once and group all recipients of that message
64
    together in the 'To:' field.  It will also strip the first line of
65
    the email body.  (The line with "Hello Foo," which is likely
66
    distinct for each recipient.)
6334.3.13 by Aaron Bentley
Be more specific about the notification message
67
9209.3.2 by Curtis Hovey
Added partial rational to team membership emails. The test fails because there are
68
    :param include_reply_to: Include the reply-to header if True.
69
    :param group_similar: Group messages sent to multiple recipients if True.
70
    :param include_rationale: Include the X-Launchpad-Message-Rationale
71
        header.
72
    :param notifications: Use the provided list of notifications instead of
73
        the stack.
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
74
    """
75
    distinct_bodies = {}
6334.3.13 by Aaron Bentley
Be more specific about the notification message
76
    if notifications is None:
77
        notifications = pop_notifications()
78
    for message in notifications:
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
79
        recipients = set(
80
            recipient.strip()
81
            for recipient in message['To'].split(','))
82
        body = message.get_payload()
83
        if group_similar:
84
            # Strip the first line as it's different for each recipient.
14527.4.2 by Colin Watson
Lint.
85
            body = body[body.find('\n') + 1:]
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
86
        if body in distinct_bodies and group_similar:
87
            message, existing_recipients = distinct_bodies[body]
88
            distinct_bodies[body] = (
89
                message, existing_recipients.union(recipients))
90
        else:
91
            distinct_bodies[body] = (message, recipients)
92
    for body in sorted(distinct_bodies):
93
        message, recipients = distinct_bodies[body]
94
        print 'From:', message['From']
95
        print 'To:', ", ".join(sorted(recipients))
96
        if include_reply_to:
97
            print 'Reply-To:', message['Reply-To']
9209.3.6 by Curtis Hovey
Only test the function that is available.
98
        rationale_header = 'X-Launchpad-Message-Rationale'
99
        if include_rationale and rationale_header in message:
9209.3.2 by Curtis Hovey
Added partial rational to team membership emails. The test fails because there are
100
            print (
9209.3.6 by Curtis Hovey
Only test the function that is available.
101
                '%s: %s' % (rationale_header, message[rationale_header]))
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
102
        print 'Subject:', message['Subject']
103
        print body
14527.4.2 by Colin Watson
Lint.
104
        print "-" * 40
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
105
7675.845.8 by Edwin Grubbs
Added test helper function. Tests now work.
106
9209.3.2 by Curtis Hovey
Added partial rational to team membership emails. The test fails because there are
107
def print_distinct_emails(include_reply_to=False, include_rationale=True):
5711.1.24 by Maris Fogels
Extracted the print_emails() and print_distinct_emails() helper functions from some doctests.
108
    """A convenient shortcut for `print_emails`(group_similar=True)."""
109
    return print_emails(group_similar=True,
9209.3.9 by Curtis Hovey
Only print rationale headers when requested.
110
                        include_reply_to=include_reply_to,
111
                        include_rationale=include_rationale)
7675.845.8 by Edwin Grubbs
Added test helper function. Tests now work.
112
113
114
def run_mail_jobs():
115
    """Process job queues that send out emails.
116
7675.845.11 by Edwin Grubbs
Addressed review comments.
117
    If a new job type is added that sends emails, this function can be
118
    extended to run those jobs, so that testing emails doesn't require a
119
    bunch of different function calls to process different queues.
7675.845.8 by Edwin Grubbs
Added test helper function. Tests now work.
120
    """
121
    # Commit the transaction to make sure that the JobRunner can find
122
    # the queued jobs.
123
    transaction.commit()
7675.845.9 by Edwin Grubbs
Refactored and renamed AddMemberNotificationJob to MembershipNotificationJob.
124
    job_source = getUtility(IMembershipNotificationJobSource)
12070.1.25 by Tim Penhey
Replace MockLogger with DevNullLogger.
125
    logger = DevNullLogger()
7675.845.8 by Edwin Grubbs
Added test helper function. Tests now work.
126
    runner = JobRunner.fromReady(job_source, logger)
127
    runner.runAll()