source: main/waeup.sirp/trunk/src/waeup/sirp/tests/test_smtp.py @ 7705

Last change on this file since 7705 was 7506, checked in by Henrik Bettermann, 13 years ago

Let test pass if EXTERNAL_MAIL_TESTS == True.

  • Property svn:keywords set to Id
File size: 13.2 KB
Line 
1# -*- coding: utf-8 -*-
2## $Id: test_smtp.py 7506 2012-01-25 11:05:12Z henrik $
3##
4## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
5## This program is free software; you can redistribute it and/or modify
6## it under the terms of the GNU General Public License as published by
7## the Free Software Foundation; either version 2 of the License, or
8## (at your option) any later version.
9##
10## This program is distributed in the hope that it will be useful,
11## but WITHOUT ANY WARRANTY; without even the implied warranty of
12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13## GNU General Public License for more details.
14##
15## You should have received a copy of the GNU General Public License
16## along with this program; if not, write to the Free Software
17## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18##
19# Tests for email-related components
20import base64
21import logging
22import tempfile
23import shutil
24import unittest
25from StringIO import StringIO
26from zope.component import getUtility
27from zope.component.hooks import setSite
28from zope.sendmail.interfaces import IMailDelivery
29from waeup.sirp.app import University
30from waeup.sirp.interfaces import IMailService
31from waeup.sirp.smtp import (
32    encode_header_item, encode_address, encode_body, FakeSMTPDelivery,
33    send_mail)
34from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
35
36#
37# SMTP test config
38#
39
40# Also run tests that send mail to external servers?
41#   If you enable this, please make sure the external smtp hosts and receivers
42#   do exist really and are not bothered by being spammed by a test programme.
43EXTERNAL_MAIL_TESTS = False
44
45# Maybe existing receiver of externally sent mail. If this mail account
46# exists really, it might receive mail from the tests!
47EXTERNAL_MAIL_RECEIVER = 'no-user@waeup.org'
48
49# Names of mail deliveries to use when external mail tests are enabled.
50#   See local mail.zcml for names of available deliveries.
51EXTERNAL_DIRECT_DELIVERY = 'Direct SMTP on localhost'
52EXTERNAL_QUEUED_DELIVERY = 'Queued SMTP on localhost'
53
54#
55# end of SMTP test config
56#
57
58def external_mail_test(func):
59    if not EXTERNAL_MAIL_TESTS:
60        myself = __file__
61        if myself.endswith('.pyc'):
62            myself = myself[:-2]
63        print "WARNING: external mail tests are skipped!"
64        print "WARNING: edit %s to enable them." % myself
65        return
66    return func
67
68class HelperTests(unittest.TestCase):
69
70    def test_encode_header_item(self):
71        # encoding header items works with strings and unicodes, as
72        # long as we pass in utf-8 encoded stuff.
73        result1 = encode_header_item(u'Plain Name'.encode('utf-8'))
74        result2 = encode_header_item(u'Name with umläut'.encode('utf-8'))
75        result3 = encode_header_item(u'Plain Name')
76        result4 = encode_header_item(u'Name with umläut')
77        self.assertEqual(result1, u'Plain Name')
78        self.assertEqual(result2, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
79        self.assertEqual(result3, u'Plain Name')
80        self.assertEqual(result4, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
81        return
82
83    def test_encode_address(self):
84        # we can correctly encode address parts
85        result1 = encode_address('foo@bar.baz')
86        result2 = encode_address(u'foo@bar.baz', 'The Foo' )
87        result3 = encode_address('foo@bar.baz', u'The Foo')
88        result4 = encode_address('foo@bar.baz', u'With Umläut')
89        self.assertEqual(result1, 'foo@bar.baz')
90        self.assertEqual(result2, 'The Foo <foo@bar.baz>')
91        self.assertEqual(result3, 'The Foo <foo@bar.baz>')
92        self.assertEqual(result4,
93                         '=?iso-8859-1?q?With_Uml=E4ut?= <foo@bar.baz>')
94        return
95
96    def test_encode_body(self):
97        result1 = encode_body(u'Simple Text')
98        self.assertEqual(
99            str(result1).split('\n')[1:],
100            ['MIME-Version: 1.0',
101             'Content-Type: text/plain; charset="us-ascii"',
102             'Content-Transfer-Encoding: 7bit',
103             '',
104             'Simple Text'])
105        return
106
107    def test_encode_body_latin1_string(self):
108        # utf-8 encoded byte streams are transferred correctly when
109        # they contain chars unrepresentable in ascii but available in latin1
110        text = u'Simple text with Ümläut'.encode('utf-8')
111        result1 = encode_body(text)
112        result1 = str(result1).split('\n')[1:]
113        self.assertEqual(
114            result1,
115            ['MIME-Version: 1.0',
116             'Content-Type: text/plain; charset="iso-8859-1"',
117             'Content-Transfer-Encoding: quoted-printable',
118             '',
119             'Simple text with =DCml=E4ut'])
120        return
121
122    def test_encode_body_latin1_unicode(self):
123        # unicode strings are transferred correctly when they contain
124        # chars unrepresentable in ascii but available in latin1
125        text = u'Simple text with Ümläut'
126        result1 = encode_body(text)
127        result1 = str(result1).split('\n')[1:]
128        self.assertEqual(
129            result1,
130            ['MIME-Version: 1.0',
131             'Content-Type: text/plain; charset="iso-8859-1"',
132             'Content-Transfer-Encoding: quoted-printable',
133             '',
134             'Simple text with =DCml=E4ut'])
135        return
136
137    def test_encode_body_utf8_string(self):
138        # utf-8 encoded byte streams are transferred correctly when
139        # they contain chars unrepresentable in ascii but available in latin1
140        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
141        text = text.encode('utf-8') # turn unicode into byte stream
142        result1 = encode_body(text)
143        result1 = str(result1).split('\n')[1:]
144        self.assertEqual(
145            result1,
146            ['MIME-Version: 1.0',
147             'Content-Type: text/plain; charset="utf-8"',
148             'Content-Transfer-Encoding: base64',
149             '',
150             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
151        self.assertEqual(
152            base64.b64decode(result1[-2]).decode('utf-8'),
153            u"Simple text with ü and capital pi: " + unichr(0x3a0))
154        return
155
156    def test_encode_body_utf8_unicode(self):
157        # utf-8 encoded byte streams are transferred correctly when
158        # they contain chars unrepresentable in latin1
159        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
160        result1 = encode_body(text)
161        result1 = str(result1).split('\n')[1:]
162        self.assertEqual(
163            result1,
164            ['MIME-Version: 1.0',
165             'Content-Type: text/plain; charset="utf-8"',
166             'Content-Transfer-Encoding: base64',
167             '',
168             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
169        self.assertEqual(
170            base64.b64decode(result1[-2]).decode('utf-8'),
171            u"Simple text with ü and capital pi: " + unichr(0x3a0))
172        return
173
174class FunctionalMailerTests(FunctionalTestCase):
175
176    layer = FunctionalLayer
177
178    def setUp(self):
179        super(FunctionalMailerTests, self).setUp()
180        self.setup_logging()
181        return
182
183    def tearDown(self):
184        super(FunctionalMailerTests, self).tearDown()
185        self.teardown_logging()
186        return
187
188    def setup_logging(self):
189        # setup a log-handler that catches all fake mailer output
190        self.stream = StringIO()
191        handler = logging.StreamHandler(self.stream)
192        logger = logging.getLogger('test.smtp')
193        logger.addHandler(handler)
194        logger.setLevel(logging.INFO)
195        return
196
197    def get_fake_smtp_output(self):
198        # get output generated by fake mailer
199        self.stream.flush()
200        self.stream.seek(0)
201        return self.stream.read()
202
203    def teardown_logging(self):
204        # remove the log handler for fake mailer output
205        logger = logging.getLogger('test.smtp')
206        handlers = [x for x in logger.handlers]
207        for handler in handlers:
208            logger.removeHandler(handler)
209        return
210
211    def test_get_fake_mailer(self):
212        # we can get the fake mailer if we want
213        mailer = getUtility(IMailDelivery, name='No email service')
214        self.assertTrue(isinstance(mailer, FakeSMTPDelivery))
215        return
216
217    def test_get_default_mailer(self):
218        # we can get a default mailer if we want
219        mailer = getUtility(IMailService)
220        self.assertTrue(isinstance(mailer(), FakeSMTPDelivery))
221        return
222
223    def test_send_mail(self):
224        # we can really send mail.
225        mail_id = send_mail(
226            u'A sender', u'sender@example.com',
227            u'A recipient', u'recpt@example.com',
228            u'A subject',
229            u'This is a test mail.')
230        self.assertEqual(mail_id, 'fake-message-id@example.com')
231        self.assertEqual(
232            self.get_fake_smtp_output().split('\n'),
233            [u'Sending email from sender@example.com to recpt@example.com:',
234             u'Message:',
235             u'msg: MIME-Version: 1.0',
236             u'msg: Content-Type: text/plain; charset="us-ascii"',
237             u'msg: Content-Transfer-Encoding: 7bit',
238             u'msg: From: A sender <sender@example.com>',
239             u'msg: To: A recipient <recpt@example.com>',
240             u'msg: Subject: A subject',
241             u'msg: ',
242             u'msg: This is a test mail.',
243             u'']
244            )
245        return
246
247    def test_send_mail_utf8_strings(self):
248        # we can send mail with utf-8 encoded strings as input
249        mail_id = send_mail(
250            u'A sender', u'sender@example.com',
251            u'A recipient', u'recpt@example.com',
252            u'A subject',
253            u'This is a test mail with ümläut.'.encode('utf-8'))
254        self.assertEqual(mail_id, 'fake-message-id@example.com')
255        self.assertEqual(
256            self.get_fake_smtp_output().split('\n'),
257            [u'Sending email from sender@example.com to recpt@example.com:',
258             u'Message:', u'msg: MIME-Version: 1.0',
259             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
260             u'msg: Content-Transfer-Encoding: quoted-printable',
261             u'msg: From: A sender <sender@example.com>',
262             u'msg: To: A recipient <recpt@example.com>',
263             u'msg: Subject: A subject',
264             u'msg: ',
265             u'msg: This is a test mail with =FCml=E4ut.',
266             u'']
267            )
268        return
269
270    def test_send_mail_utf8_unicode(self):
271        # we can send mail with utf-8 encoded unicode as input
272        mail_id = send_mail(
273            u'A sender', u'sender@example.com',
274            u'A recipient', u'recpt@example.com',
275            u'A subject',
276            u'This is a test mail with ümläut.')
277        self.assertEqual(mail_id, 'fake-message-id@example.com')
278        self.assertEqual(
279            self.get_fake_smtp_output().split('\n'),
280            [u'Sending email from sender@example.com to recpt@example.com:',
281             u'Message:', u'msg: MIME-Version: 1.0',
282             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
283             u'msg: Content-Transfer-Encoding: quoted-printable',
284             u'msg: From: A sender <sender@example.com>',
285             u'msg: To: A recipient <recpt@example.com>',
286             u'msg: Subject: A subject',
287             u'msg: ',
288             u'msg: This is a test mail with =FCml=E4ut.',
289             u'']
290            )
291        return
292
293
294class ExternalMailerTests(FunctionalTestCase):
295
296    layer = FunctionalLayer
297
298    def setUp(self):
299        super(ExternalMailerTests, self).setUp()
300        # Setup a sample site for each test
301        app = University()
302        self.dc_root = tempfile.mkdtemp()
303        app['datacenter'].setStoragePath(self.dc_root)
304
305        # Prepopulate the ZODB...
306        self.getRootFolder()['app'] = app
307        self.app = self.getRootFolder()['app']
308        return
309
310    def tearDown(self):
311        super(ExternalMailerTests, self).tearDown()
312        shutil.rmtree(self.dc_root)
313        return
314
315    def test_config_default_mailer(self):
316        # The default mailer set in config is 'no mailer'.
317        self.assertEqual(
318            getattr(self.app.get('configuration', None), 'smtp_mailer'),
319            u'No email service')
320        return
321
322    @external_mail_test
323    def test_send_direct_mail(self):
324        # send mail using direct mail delivery
325        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
326        setSite(self.app)
327        result = send_mail(
328            'test program', 'no-reply@waeup.org',
329            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
330            'Test Mail from WAeUP SIRP',
331            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
332            )
333        import transaction
334        transaction.commit() # The mail is really sent when transactions is
335                             # committed
336        self.assertTrue('@' in result)
337        return
338
339    @external_mail_test
340    def test_send_queued_mail(self):
341        # send mail using queued mail delivery
342        self.app['configuration'].smtp_mailer = EXTERNAL_QUEUED_DELIVERY
343        setSite(self.app)
344        result = send_mail(
345            'test program', 'no-reply@waeup.org',
346            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
347            'Test Mail from WAeUP SIRP',
348            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
349            )
350        import transaction
351        transaction.commit() # The mail is really sent when transactions is
352                             # committed
353        self.assertTrue('@' in result)
354        return
Note: See TracBrowser for help on using the repository browser.