~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

__metaclass__ = type

from doctest import DocTestSuite
import re

from zope.testing.renormalizing import RENormalizing

from canonical.testing.layers import LaunchpadFunctionalLayer


def test_simple_sendmail():
    r"""
    Send an email (faked by TestMailer - no actual email is sent)

    >>> import email
    >>> from email.MIMEText import MIMEText
    >>> import transaction
    >>> from lp.services.mail import stub
    >>> from lp.services.mail.sendmail import simple_sendmail

    >>> body = 'The email body'
    >>> subject = 'The email subject'
    >>> message_id1 = simple_sendmail(
    ...     'nobody1@example.com', ['nobody2@example.com'], subject, body
    ...     )

    We should have a message id, a string

    >>> bool(message_id1)
    True
    >>> isinstance(message_id1,str)
    True

    We can also send arbitrary headers through. Note how Python's
    email package handles Message-Id headers

    >>> message_id2 = simple_sendmail(
    ...     'nobody@example.com', ['nobody2@example.com'], subject, body,
    ...     {'Message-Id': '<myMessageId>', 'X-Fnord': 'True'}
    ...     )
    >>> message_id2
    'myMessageId'

    The TestMailer stores sent emails in memory (which we cleared in the
    setUp() method). But the actual email has yet to be sent, as that
    happens when the transaction is committed.

    >>> len(stub.test_emails)
    0
    >>> transaction.commit()
    >>> len(stub.test_emails)
    2
    >>> stub.test_emails[0] == stub.test_emails[1]
    False

    We have two emails, but we have no idea what order they are in!

    Let's sort them, and verify that the first one is the one we want
    because only the first one contains the string 'nobody@example.com'
    in its raw message.

    >>> sorted_test_emails = sorted(list(stub.test_emails))
    >>> for from_addr, to_addrs, raw_message in sorted_test_emails:
    ...     print from_addr, to_addrs, 'nobody@example.com' in raw_message
    bounces@canonical.com ['nobody2@example.com'] True
    bounces@canonical.com ['nobody2@example.com'] False

    >>> from_addr, to_addrs, raw_message = sorted_test_emails[0]
    >>> from_addr
    'bounces@canonical.com'
    >>> to_addrs
    ['nobody2@example.com']

    The message should be a sane RFC2822 document

    >>> message = email.message_from_string(raw_message)
    >>> message['From']
    'nobody@example.com'
    >>> message['To']
    'nobody2@example.com'
    >>> message['Subject'] == subject
    True
    >>> message['Message-Id']
    '<myMessageId>'
    >>> message.get_payload() == body
    True

    Character set should be utf-8 as per Bug #39758. utf8 isn't good enough.

    >>> message['Content-Type']
    'text/plain; charset="utf-8"'

    And we want quoted printable, as it generally makes things readable
    and for languages it doesn't help, the only downside to base64 is bloat.

    >>> message['Content-Transfer-Encoding']
    'quoted-printable'

    The message has a number of additional headers added by default.
    'X-Generated-By' not only indicates that the source is Launchpad, but
    shows the bzr revision and instance name.

    >>> message['X-Generated-By']
    'Launchpad (canonical.com); Revision="1999";\n\tInstance="launchpad-lazr.conf"'

    """

def test_suite():
    suite = DocTestSuite(checker=RENormalizing([
        (re.compile(r'Revision="\d+"'), 'Revision="1999"')]))
    suite.layer = LaunchpadFunctionalLayer
    return suite