~maria-captains/bzr-email/serg

« back to all changes in this revision

Viewing changes to tests/test_smtp_connection.py

  • Committer: Jelmer Vernooij
  • Date: 2012-03-23 14:49:09 UTC
  • Revision ID: jelmer@samba.org-20120323144909-9qkt66v7y5fn42jl
Use smtp_connection from bzrlib.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2010 by Canonical Ltd
2
 
#   Authors: Robert Collins <robert.collins@canonical.com>
3
 
#
4
 
# This program is free software; you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation; either version 2 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
 
18
 
from cStringIO import StringIO
19
 
 
20
 
from bzrlib import (
21
 
    config,
22
 
    __version__ as _bzrlib_version,
23
 
    )
24
 
from bzrlib.tests import TestCase
25
 
from bzrlib.plugins.email.smtp_connection import SMTPConnection
26
 
 
27
 
 
28
 
class InstrumentedSMTPConnection(SMTPConnection):
29
 
    """Instrument SMTPConnection.
30
 
 
31
 
    We don't want to actually connect or send messages, so this just
32
 
    fakes it.
33
 
    """
34
 
 
35
 
    class FakeSMTP(object):
36
 
        """Fakes an SMTP connection."""
37
 
 
38
 
        def __init__(self, actions):
39
 
            self.actions = actions
40
 
 
41
 
        def sendmail(self, from_addr, to_addrs, msg):
42
 
            self.actions.append(('sendmail', from_addr, to_addrs, msg))
43
 
 
44
 
        def login(self, username, password):
45
 
            self.actions.append(('login', username, password))
46
 
 
47
 
    def __init__(self, config):
48
 
        super(InstrumentedSMTPConnection, self).__init__(config)
49
 
        self.actions = []
50
 
 
51
 
    def _create_connection(self):
52
 
        self.actions.append(('create_connection',))
53
 
        self._connection = InstrumentedSMTPConnection.FakeSMTP(self.actions)
54
 
 
55
 
    def _basic_message(self, *args, **kwargs):
56
 
        """Override to force the boundary for easier testing."""
57
 
        msg, from_email, to_emails = super(InstrumentedSMTPConnection,
58
 
        self)._basic_message(*args, **kwargs)
59
 
        msg.set_boundary('=====123456==')
60
 
        return msg, from_email, to_emails
61
 
 
62
 
 
63
 
class TestSMTPConnection(TestCase):
64
 
 
65
 
    def get_connection(self, text):
66
 
        my_config = config.GlobalConfig.from_string(text)
67
 
        return InstrumentedSMTPConnection(my_config)
68
 
 
69
 
    def test_defaults(self):
70
 
        conn = self.get_connection('')
71
 
        self.assertEqual('localhost', conn._smtp_server)
72
 
        self.assertEqual(None, conn._smtp_username)
73
 
        self.assertEqual(None, conn._smtp_password)
74
 
 
75
 
    def test_smtp_server(self):
76
 
        conn = self.get_connection('[DEFAULT]\nsmtp_server=host:10\n')
77
 
        self.assertEqual('host:10', conn._smtp_server)
78
 
 
79
 
    def test_smtp_username(self):
80
 
        conn = self.get_connection('')
81
 
        self.assertIs(None, conn._smtp_username)
82
 
 
83
 
        conn = self.get_connection('[DEFAULT]\nsmtp_username=joebody\n')
84
 
        self.assertEqual(u'joebody', conn._smtp_username)
85
 
 
86
 
    def test_smtp_password(self):
87
 
        conn = self.get_connection('')
88
 
        self.assertIs(None, conn._smtp_password)
89
 
 
90
 
        conn = self.get_connection('[DEFAULT]\nsmtp_password=mypass\n')
91
 
        self.assertEqual(u'mypass', conn._smtp_password)
92
 
 
93
 
    def assertSplitEquals(self, username, email, address):
94
 
        actual = SMTPConnection._split_address(address)
95
 
        self.assertEqual((username, email), actual)
96
 
 
97
 
    def test__split_address(self):
98
 
        self.assertSplitEquals(u'Joe Foo', 'joe@foo.com',
99
 
                               u'Joe Foo <joe@foo.com>')
100
 
        self.assertSplitEquals(u'Joe F\xb5', 'joe@foo.com',
101
 
                               u'Joe F\xb5 <joe@foo.com>')
102
 
        self.assertSplitEquals('', 'joe', 'joe')
103
 
 
104
 
    def test_simple_send(self):
105
 
        """Test that we build up a reasonable looking email.
106
 
 
107
 
        This also tests that we extract the right email addresses, etc, and it
108
 
        gets passed to sendmail() with the right parameters.
109
 
        """
110
 
        conn = self.get_connection('')
111
 
        from_addr = u'Jerry F\xb5z <jerry@fooz.com>'
112
 
        to_addr = u'Biz N\xe5 <biz@na.com>'
113
 
        subject = u'Hello Biz N\xe5'
114
 
        message=(u'Hello Biz N\xe5\n'
115
 
                 u'I haven\'t heard\n'
116
 
                 u'from you in a while\n')
117
 
        conn.send_text_email(from_addr, [to_addr], subject, message)
118
 
        self.assertEqual(('create_connection',), conn.actions[0])
119
 
        self.assertEqual(('sendmail', 'jerry@fooz.com', ['biz@na.com']),
120
 
                         conn.actions[1][:3])
121
 
        self.assertEqualDiff((
122
 
   'Content-Type: multipart/mixed; boundary="=====123456=="\n'
123
 
   'MIME-Version: 1.0\n'
124
 
   'From: =?utf-8?q?Jerry_F=C2=B5z?= <jerry@fooz.com>\n'
125
 
   'User-Agent: bzr/%s\n'
126
 
   'To: =?utf-8?q?Biz_N=C3=A5?= <biz@na.com>\n'
127
 
   'Subject: =?utf-8?q?Hello_Biz_N=C3=A5?=\n'
128
 
   '\n'
129
 
   '--=====123456==\n'
130
 
   'Content-Type: text/plain; charset="utf-8"\n'
131
 
   'MIME-Version: 1.0\n'
132
 
   'Content-Transfer-Encoding: base64\n'
133
 
   '\n'
134
 
   'SGVsbG8gQml6IE7DpQpJIGhhdmVuJ3QgaGVhcmQKZnJvbSB5b3UgaW4gYSB3aGlsZQo=\n'
135
 
   '\n'
136
 
   '--=====123456==--'
137
 
   ) % _bzrlib_version, conn.actions[1][3])
138
 
 
139
 
    def test_send_text_and_attachment_email(self):
140
 
        conn = self.get_connection('')
141
 
        from_addr = u'Jerry F\xb5z <jerry@fooz.com>'
142
 
        to_addr = u'Biz N\xe5 <biz@na.com>'
143
 
        subject = u'Hello Biz N\xe5'
144
 
        message=(u'Hello Biz N\xe5\n'
145
 
                 u'See my attached patch\n')
146
 
        diff_txt = ('=== diff contents\n'
147
 
                    '--- old\n'
148
 
                    '+++ new\n'
149
 
                    ' unchanged\n'
150
 
                    '-old binary\xb5\n'
151
 
                    '-new binary\xe5\n'
152
 
                    ' unchanged\n')
153
 
        conn.send_text_and_attachment_email(from_addr, [to_addr], subject,
154
 
                                            message, diff_txt, 'test.diff')
155
 
        self.assertEqual(('create_connection',), conn.actions[0])
156
 
        self.assertEqual(('sendmail', 'jerry@fooz.com', ['biz@na.com']),
157
 
                         conn.actions[1][:3])
158
 
        self.assertEqualDiff((
159
 
   'Content-Type: multipart/mixed; boundary="=====123456=="\n'
160
 
   'MIME-Version: 1.0\n'
161
 
   'From: =?utf-8?q?Jerry_F=C2=B5z?= <jerry@fooz.com>\n'
162
 
   'User-Agent: bzr/%s\n'
163
 
   'To: =?utf-8?q?Biz_N=C3=A5?= <biz@na.com>\n'
164
 
   'Subject: =?utf-8?q?Hello_Biz_N=C3=A5?=\n'
165
 
   '\n'
166
 
   '--=====123456==\n'
167
 
   'Content-Type: text/plain; charset="utf-8"\n'
168
 
   'MIME-Version: 1.0\n'
169
 
   'Content-Transfer-Encoding: base64\n'
170
 
   '\n'
171
 
   'SGVsbG8gQml6IE7DpQpTZWUgbXkgYXR0YWNoZWQgcGF0Y2gK\n'
172
 
   '\n'
173
 
   '--=====123456==\n'
174
 
   'Content-Type: text/plain; charset="8-bit"; name="test.diff"\n'
175
 
   'MIME-Version: 1.0\n'
176
 
   'Content-Transfer-Encoding: base64\n'
177
 
   'Content-Disposition: inline; filename="test.diff"\n'
178
 
   '\n'
179
 
   'PT09IGRpZmYgY29udGVudHMKLS0tIG9sZAorKysgbmV3CiB1bmNoYW5nZWQKLW9sZCBiaW5hcnm1\n'
180
 
   'Ci1uZXcgYmluYXJ55QogdW5jaGFuZ2VkCg==\n'
181
 
   '\n'
182
 
   '--=====123456==--'
183
 
   ) % _bzrlib_version, conn.actions[1][3])
184
 
 
185
 
    def test_create_and_send(self):
186
 
        """Test that you can create a custom email, and send it."""
187
 
        conn = self.get_connection('')
188
 
        email_msg, from_email, to_emails = conn.create_email(
189
 
            'Joe Foo <joe@foo.com>',
190
 
            ['Jane Foo <jane@foo.com>', 'Barry Foo <barry@foo.com>'],
191
 
            'Hi Jane and Barry',
192
 
            'Check out the attachment\n')
193
 
        self.assertEqual('joe@foo.com', from_email)
194
 
        self.assertEqual(['jane@foo.com', 'barry@foo.com'], to_emails)
195
 
 
196
 
        try:
197
 
            # python 2.5
198
 
            from email.mime.nonmultipart import MIMENonMultipart
199
 
            from email.encoders import encode_base64
200
 
        except ImportError:
201
 
            # python 2.4
202
 
            from email.MIMENonMultipart import MIMENonMultipart
203
 
            from email.Encoders import encode_base64
204
 
 
205
 
        attachment_txt = '\x00foo\xff\xff\xff\xff'
206
 
        attachment = MIMENonMultipart('application', 'octet-stream')
207
 
        attachment.set_payload(attachment_txt)
208
 
        encode_base64(attachment)
209
 
 
210
 
        email_msg.attach(attachment)
211
 
 
212
 
        # This will add someone to send to, but not include it in the To list.
213
 
        to_emails.append('b@cc.com')
214
 
        conn.send_email(email_msg, from_email, to_emails)
215
 
 
216
 
        self.assertEqual(('create_connection',), conn.actions[0])
217
 
        self.assertEqual(('sendmail', 'joe@foo.com',
218
 
                          ['jane@foo.com', 'barry@foo.com', 'b@cc.com']),
219
 
                         conn.actions[1][:3])
220
 
        self.assertEqualDiff((
221
 
   'Content-Type: multipart/mixed; boundary="=====123456=="\n'
222
 
   'MIME-Version: 1.0\n'
223
 
   'From: Joe Foo <joe@foo.com>\n'
224
 
   'User-Agent: bzr/%s\n'
225
 
   'To: Jane Foo <jane@foo.com>, Barry Foo <barry@foo.com>\n'
226
 
   'Subject: Hi Jane and Barry\n'
227
 
   '\n'
228
 
   '--=====123456==\n'
229
 
   'Content-Type: text/plain; charset="utf-8"\n'
230
 
   'MIME-Version: 1.0\n'
231
 
   'Content-Transfer-Encoding: base64\n'
232
 
   '\n'
233
 
   'Q2hlY2sgb3V0IHRoZSBhdHRhY2htZW50Cg==\n'
234
 
   '\n'
235
 
   '--=====123456==\n'
236
 
   'Content-Type: application/octet-stream\n'
237
 
   'MIME-Version: 1.0\n'
238
 
   'Content-Transfer-Encoding: base64\n'
239
 
   '\n'
240
 
   'AGZvb/////8=\n'
241
 
   '--=====123456==--'
242
 
   ) % _bzrlib_version, conn.actions[1][3])
243
 
 
244
 
    def test_email_parse(self):
245
 
        """Check that python's email can parse our emails."""
246
 
        conn = self.get_connection('')
247
 
        from_addr = u'Jerry F\xb5z <jerry@fooz.com>'
248
 
        to_addr = u'Biz N\xe5 <biz@na.com>'
249
 
        subject = u'Hello Biz N\xe5'
250
 
        message=(u'Hello Biz N\xe5\n'
251
 
                 u'See my attached patch\n')
252
 
        diff_txt = ('=== diff contents\n'
253
 
                    '--- old\n'
254
 
                    '+++ new\n'
255
 
                    ' unchanged\n'
256
 
                    '-old binary\xb5\n'
257
 
                    '-new binary\xe5\n'
258
 
                    ' unchanged\n')
259
 
        conn.send_text_and_attachment_email(from_addr, [to_addr], subject,
260
 
                                            message, diff_txt, 'test.diff')
261
 
        self.assertEqual(('create_connection',), conn.actions[0])
262
 
        self.assertEqual(('sendmail', 'jerry@fooz.com', ['biz@na.com']),
263
 
                         conn.actions[1][:3])
264
 
        email_message_text = conn.actions[1][3]
265
 
 
266
 
        try:
267
 
            # python 2.5
268
 
            from email.parser import Parser
269
 
            from email.header import decode_header
270
 
        except ImportError:
271
 
            # python 2.4
272
 
            from email.Parser import Parser
273
 
            from email.Header import decode_header
274
 
 
275
 
        def decode(s):
276
 
            """Convert a header string to a unicode string.
277
 
 
278
 
            This handles '=?utf-8?q?foo=C2=B5?=' => u'Foo\\xb5'
279
 
            """
280
 
            return ' '.join([chunk.decode(encoding or 'ascii')
281
 
                             for chunk, encoding in decode_header(s)])
282
 
 
283
 
        p = Parser()
284
 
        email_message = p.parsestr(email_message_text)
285
 
 
286
 
        self.assertEqual(from_addr, decode(email_message['From']))
287
 
        self.assertEqual(to_addr, decode(email_message['To']))
288
 
        self.assertEqual(subject, decode(email_message['Subject']))
289
 
        text_payload = email_message.get_payload(0)
290
 
        diff_payload = email_message.get_payload(1)
291
 
        # I haven't found a way to have python's email read the charset=""
292
 
        # portion of the Content-Type header. So I'm doing it manually
293
 
        # The 'decode=True' here means to decode from base64 => 8-bit text.
294
 
        # text_payload.get_charset() returns None
295
 
        text = text_payload.get_payload(decode=True).decode('utf-8')
296
 
        self.assertEqual(message, text)
297
 
        self.assertEqual(diff_txt, diff_payload.get_payload(decode=True))