/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
1
# Copyright (C) 2005 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
    """
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
34
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
35
    class FakeSMTP(object):
36
        """Fakes an SMTP connection."""
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
37
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
38
        def __init__(self, actions):
39
            self.actions = actions
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
40
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
41
        def sendmail(self, from_addr, to_addrs, msg):
42
            self.actions.append(('sendmail', from_addr, to_addrs, msg))
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
43
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
44
        def login(self, username, password):
45
            self.actions.append(('login', username, password))
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
46
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
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
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
62
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
63
class TestSMTPConnection(TestCase):
64
65
    def get_connection(self, text):
66
        my_config = config.GlobalConfig()
67
        config_file = StringIO(text)
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
68
        my_config._get_parser(config_file)
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
69
        return InstrumentedSMTPConnection(my_config)
70
71
    def test_defaults(self):
72
        conn = self.get_connection('')
73
        self.assertEqual('localhost', conn._smtp_server)
74
        self.assertEqual(None, conn._smtp_username)
75
        self.assertEqual(None, conn._smtp_password)
76
0.175.8 by John Arbash Meinel
Figure out and test how to read back one of these Unicode aware emails.
77
    def test_smtp_server(self):
78
        conn = self.get_connection('[DEFAULT]\nsmtp_server=host:10\n')
79
        self.assertEqual('host:10', conn._smtp_server)
80
81
    def test_smtp_username(self):
82
        conn = self.get_connection('')
83
        self.assertIs(None, conn._smtp_username)
84
85
        conn = self.get_connection('[DEFAULT]\nsmtp_username=joebody\n')
86
        self.assertEqual(u'joebody', conn._smtp_username)
87
88
    def test_smtp_password(self):
89
        conn = self.get_connection('')
90
        self.assertIs(None, conn._smtp_password)
91
92
        conn = self.get_connection('[DEFAULT]\nsmtp_password=mypass\n')
93
        self.assertEqual(u'mypass', conn._smtp_password)
94
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
95
    def assertSplitEquals(self, username, email, address):
96
        actual = SMTPConnection._split_address(address)
97
        self.assertEqual((username, email), actual)
98
99
    def test__split_address(self):
100
        self.assertSplitEquals(u'Joe Foo', 'joe@foo.com',
101
                               u'Joe Foo <joe@foo.com>')
102
        self.assertSplitEquals(u'Joe F\xb5', 'joe@foo.com',
0.175.8 by John Arbash Meinel
Figure out and test how to read back one of these Unicode aware emails.
103
                               u'Joe F\xb5 <joe@foo.com>')
104
        self.assertSplitEquals('', 'joe', 'joe')
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
105
106
    def test_simple_send(self):
107
        """Test that we build up a reasonable looking email.
0.175.12 by John Arbash Meinel
Remove trailing whitespace.
108
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
109
        This also tests that we extract the right email addresses, etc, and it
110
        gets passed to sendmail() with the right parameters.
111
        """
112
        conn = self.get_connection('')
113
        from_addr = u'Jerry F\xb5z <jerry@fooz.com>'
114
        to_addr = u'Biz N\xe5 <biz@na.com>'
115
        subject = u'Hello Biz N\xe5'
116
        message=(u'Hello Biz N\xe5\n'
117
                 u'I haven\'t heard\n'
118
                 u'from you in a while\n')
119
        conn.send_text_email(from_addr, [to_addr], subject, message)
120
        self.assertEqual(('create_connection',), conn.actions[0])
121
        self.assertEqual(('sendmail', 'jerry@fooz.com', ['biz@na.com']),
122
                         conn.actions[1][:3])
123
        self.assertEqualDiff((
124
   'Content-Type: multipart/mixed; boundary="=====123456=="\n'
125
   'MIME-Version: 1.0\n'
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
126
   'From: =?utf-8?q?Jerry_F=C2=B5z?= <jerry@fooz.com>\n'
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
127
   'User-Agent: bzr/%s\n'
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
128
   'To: =?utf-8?q?Biz_N=C3=A5?= <biz@na.com>\n'
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
129
   'Subject: =?utf-8?q?Hello_Biz_N=C3=A5?=\n'
130
   '\n'
131
   '--=====123456==\n'
132
   'Content-Type: text/plain; charset="utf-8"\n'
133
   'MIME-Version: 1.0\n'
134
   'Content-Transfer-Encoding: base64\n'
135
   '\n'
136
   'SGVsbG8gQml6IE7DpQpJIGhhdmVuJ3QgaGVhcmQKZnJvbSB5b3UgaW4gYSB3aGlsZQo=\n'
137
   '\n'
138
   '--=====123456==--'
139
   ) % _bzrlib_version, conn.actions[1][3])
140
141
    def test_send_text_and_attachment_email(self):
142
        conn = self.get_connection('')
143
        from_addr = u'Jerry F\xb5z <jerry@fooz.com>'
144
        to_addr = u'Biz N\xe5 <biz@na.com>'
145
        subject = u'Hello Biz N\xe5'
146
        message=(u'Hello Biz N\xe5\n'
147
                 u'See my attached patch\n')
148
        diff_txt = ('=== diff contents\n'
149
                    '--- old\n'
150
                    '+++ new\n'
151
                    ' unchanged\n'
152
                    '-old binary\xb5\n'
153
                    '-new binary\xe5\n'
154
                    ' unchanged\n')
155
        conn.send_text_and_attachment_email(from_addr, [to_addr], subject,
156
                                            message, diff_txt, 'test.diff')
157
        self.assertEqual(('create_connection',), conn.actions[0])
158
        self.assertEqual(('sendmail', 'jerry@fooz.com', ['biz@na.com']),
159
                         conn.actions[1][:3])
160
        self.assertEqualDiff((
161
   'Content-Type: multipart/mixed; boundary="=====123456=="\n'
162
   'MIME-Version: 1.0\n'
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
163
   'From: =?utf-8?q?Jerry_F=C2=B5z?= <jerry@fooz.com>\n'
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
164
   'User-Agent: bzr/%s\n'
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
165
   'To: =?utf-8?q?Biz_N=C3=A5?= <biz@na.com>\n'
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
166
   'Subject: =?utf-8?q?Hello_Biz_N=C3=A5?=\n'
167
   '\n'
168
   '--=====123456==\n'
169
   'Content-Type: text/plain; charset="utf-8"\n'
170
   'MIME-Version: 1.0\n'
171
   'Content-Transfer-Encoding: base64\n'
172
   '\n'
173
   'SGVsbG8gQml6IE7DpQpTZWUgbXkgYXR0YWNoZWQgcGF0Y2gK\n'
174
   '\n'
175
   '--=====123456==\n'
176
   'Content-Type: text/plain; charset="8-bit"; name="test.diff"\n'
177
   'MIME-Version: 1.0\n'
178
   'Content-Transfer-Encoding: base64\n'
179
   'Content-Disposition: inline; filename="test.diff"\n'
180
   '\n'
181
   'PT09IGRpZmYgY29udGVudHMKLS0tIG9sZAorKysgbmV3CiB1bmNoYW5nZWQKLW9sZCBiaW5hcnm1\n'
182
   'Ci1uZXcgYmluYXJ55QogdW5jaGFuZ2VkCg==\n'
183
   '\n'
184
   '--=====123456==--'
185
   ) % _bzrlib_version, conn.actions[1][3])
186
187
    def test_create_and_send(self):
188
        """Test that you can create a custom email, and send it."""
189
        conn = self.get_connection('')
190
        email_msg, from_email, to_emails = conn.create_email(
191
            'Joe Foo <joe@foo.com>',
192
            ['Jane Foo <jane@foo.com>', 'Barry Foo <barry@foo.com>'],
193
            'Hi Jane and Barry',
194
            'Check out the attachment\n')
195
        self.assertEqual('joe@foo.com', from_email)
196
        self.assertEqual(['jane@foo.com', 'barry@foo.com'], to_emails)
197
198
        try:
199
            # python 2.5
200
            from email.mime.nonmultipart import MIMENonMultipart
201
            from email.encoders import encode_base64
202
        except ImportError:
203
            # python 2.4
204
            from email.MIMENonMultipart import MIMENonMultipart
205
            from email.Encoders import encode_base64
206
207
        attachment_txt = '\x00foo\xff\xff\xff\xff'
208
        attachment = MIMENonMultipart('application', 'octet-stream')
209
        attachment.set_payload(attachment_txt)
210
        encode_base64(attachment)
211
212
        email_msg.attach(attachment)
213
214
        # This will add someone to send to, but not include it in the To list.
215
        to_emails.append('b@cc.com')
216
        conn.send_email(email_msg, from_email, to_emails)
217
218
        self.assertEqual(('create_connection',), conn.actions[0])
219
        self.assertEqual(('sendmail', 'joe@foo.com',
220
                          ['jane@foo.com', 'barry@foo.com', 'b@cc.com']),
221
                         conn.actions[1][:3])
222
        self.assertEqualDiff((
223
   'Content-Type: multipart/mixed; boundary="=====123456=="\n'
224
   'MIME-Version: 1.0\n'
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
225
   'From: Joe Foo <joe@foo.com>\n'
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
226
   'User-Agent: bzr/%s\n'
0.175.11 by John Arbash Meinel
Cleanup from review comments by Marius Gedminas
227
   'To: Jane Foo <jane@foo.com>, Barry Foo <barry@foo.com>\n'
0.175.7 by John Arbash Meinel
split out SMTPConnection to its own file.
228
   'Subject: Hi Jane and Barry\n'
229
   '\n'
230
   '--=====123456==\n'
231
   'Content-Type: text/plain; charset="utf-8"\n'
232
   'MIME-Version: 1.0\n'
233
   'Content-Transfer-Encoding: base64\n'
234
   '\n'
235
   'Q2hlY2sgb3V0IHRoZSBhdHRhY2htZW50Cg==\n'
236
   '\n'
237
   '--=====123456==\n'
238
   'Content-Type: application/octet-stream\n'
239
   'MIME-Version: 1.0\n'
240
   'Content-Transfer-Encoding: base64\n'
241
   '\n'
242
   'AGZvb/////8=\n'
243
   '--=====123456==--'
244
   ) % _bzrlib_version, conn.actions[1][3])
0.175.8 by John Arbash Meinel
Figure out and test how to read back one of these Unicode aware emails.
245
246
    def test_email_parse(self):
247
        """Check that python's email can parse our emails."""
248
        conn = self.get_connection('')
249
        from_addr = u'Jerry F\xb5z <jerry@fooz.com>'
250
        to_addr = u'Biz N\xe5 <biz@na.com>'
251
        subject = u'Hello Biz N\xe5'
252
        message=(u'Hello Biz N\xe5\n'
253
                 u'See my attached patch\n')
254
        diff_txt = ('=== diff contents\n'
255
                    '--- old\n'
256
                    '+++ new\n'
257
                    ' unchanged\n'
258
                    '-old binary\xb5\n'
259
                    '-new binary\xe5\n'
260
                    ' unchanged\n')
261
        conn.send_text_and_attachment_email(from_addr, [to_addr], subject,
262
                                            message, diff_txt, 'test.diff')
263
        self.assertEqual(('create_connection',), conn.actions[0])
264
        self.assertEqual(('sendmail', 'jerry@fooz.com', ['biz@na.com']),
265
                         conn.actions[1][:3])
266
        email_message_text = conn.actions[1][3]
267
268
        try:
269
            # python 2.5
270
            from email.parser import Parser
271
            from email.header import decode_header
272
        except ImportError:
273
            # python 2.4
274
            from email.Parser import Parser
275
            from email.Header import decode_header
276
277
        def decode(s):
278
            """Convert a header string to a unicode string.
279
280
            This handles '=?utf-8?q?foo=C2=B5?=' => u'Foo\\xb5'
281
            """
282
            return ' '.join([chunk.decode(encoding or 'ascii')
283
                             for chunk, encoding in decode_header(s)])
284
285
        p = Parser()
286
        email_message = p.parsestr(email_message_text)
287
288
        self.assertEqual(from_addr, decode(email_message['From']))
289
        self.assertEqual(to_addr, decode(email_message['To']))
290
        self.assertEqual(subject, decode(email_message['Subject']))
291
        text_payload = email_message.get_payload(0)
292
        diff_payload = email_message.get_payload(1)
293
        # I haven't found a way to have python's email read the charset=""
294
        # portion of the Content-Type header. So I'm doing it manually
295
        # The 'decode=True' here means to decode from base64 => 8-bit text.
296
        # text_payload.get_charset() returns None
297
        text = text_payload.get_payload(decode=True).decode('utf-8')
298
        self.assertEqual(message, text)
299
        self.assertEqual(diff_txt, diff_payload.get_payload(decode=True))