source: main/waeup.kofa/trunk/src/waeup/kofa/tests/test_smtp.py @ 11070

Last change on this file since 11070 was 10855, checked in by Henrik Bettermann, 11 years ago

Add additional recipient.

  • Property svn:keywords set to Id
File size: 14.2 KB
RevLine 
[7470]1# -*- coding: utf-8 -*-
[7476]2## $Id: test_smtp.py 10855 2013-12-19 12:23:23Z henrik $
[7470]3##
[7476]4## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
[7470]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.
[7476]9##
[7470]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.
[7476]14##
[7470]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
[7474]27from zope.component.hooks import setSite
[7470]28from zope.sendmail.interfaces import IMailDelivery
[7811]29from waeup.kofa.app import University
30from waeup.kofa.interfaces import IMailService
31from waeup.kofa.smtp import (
[7470]32    encode_header_item, encode_address, encode_body, FakeSMTPDelivery,
[7473]33    send_mail)
[7811]34from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
[7470]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!
[9306]47EXTERNAL_MAIL_RECEIVER = 'no-reply@waeup.org'
[7470]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')
[8382]77        result5 = encode_header_item(None)
[7470]78        self.assertEqual(result1, u'Plain Name')
[7472]79        self.assertEqual(result2, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
[7470]80        self.assertEqual(result3, u'Plain Name')
[7472]81        self.assertEqual(result4, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
[8382]82        self.assertTrue(result5 is None)
[7470]83        return
84
85    def test_encode_address(self):
86        # we can correctly encode address parts
87        result1 = encode_address('foo@bar.baz')
88        result2 = encode_address(u'foo@bar.baz', 'The Foo' )
89        result3 = encode_address('foo@bar.baz', u'The Foo')
90        result4 = encode_address('foo@bar.baz', u'With Umläut')
91        self.assertEqual(result1, 'foo@bar.baz')
92        self.assertEqual(result2, 'The Foo <foo@bar.baz>')
93        self.assertEqual(result3, 'The Foo <foo@bar.baz>')
[7472]94        self.assertEqual(result4,
95                         '=?iso-8859-1?q?With_Uml=E4ut?= <foo@bar.baz>')
[7470]96        return
97
98    def test_encode_body(self):
99        result1 = encode_body(u'Simple Text')
100        self.assertEqual(
101            str(result1).split('\n')[1:],
102            ['MIME-Version: 1.0',
103             'Content-Type: text/plain; charset="us-ascii"',
104             'Content-Transfer-Encoding: 7bit',
105             '',
106             'Simple Text'])
107        return
108
109    def test_encode_body_latin1_string(self):
110        # utf-8 encoded byte streams are transferred correctly when
111        # they contain chars unrepresentable in ascii but available in latin1
112        text = u'Simple text with Ümläut'.encode('utf-8')
113        result1 = encode_body(text)
114        result1 = str(result1).split('\n')[1:]
115        self.assertEqual(
116            result1,
117            ['MIME-Version: 1.0',
118             'Content-Type: text/plain; charset="iso-8859-1"',
119             'Content-Transfer-Encoding: quoted-printable',
120             '',
121             'Simple text with =DCml=E4ut'])
122        return
123
124    def test_encode_body_latin1_unicode(self):
125        # unicode strings are transferred correctly when they contain
126        # chars unrepresentable in ascii but available in latin1
127        text = u'Simple text with Ümläut'
128        result1 = encode_body(text)
129        result1 = str(result1).split('\n')[1:]
130        self.assertEqual(
131            result1,
132            ['MIME-Version: 1.0',
133             'Content-Type: text/plain; charset="iso-8859-1"',
134             'Content-Transfer-Encoding: quoted-printable',
135             '',
136             'Simple text with =DCml=E4ut'])
137        return
138
139    def test_encode_body_utf8_string(self):
140        # utf-8 encoded byte streams are transferred correctly when
141        # they contain chars unrepresentable in ascii but available in latin1
142        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
143        text = text.encode('utf-8') # turn unicode into byte stream
144        result1 = encode_body(text)
145        result1 = str(result1).split('\n')[1:]
146        self.assertEqual(
147            result1,
148            ['MIME-Version: 1.0',
149             'Content-Type: text/plain; charset="utf-8"',
150             'Content-Transfer-Encoding: base64',
151             '',
152             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
153        self.assertEqual(
154            base64.b64decode(result1[-2]).decode('utf-8'),
155            u"Simple text with ü and capital pi: " + unichr(0x3a0))
156        return
157
158    def test_encode_body_utf8_unicode(self):
159        # utf-8 encoded byte streams are transferred correctly when
160        # they contain chars unrepresentable in latin1
161        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
162        result1 = encode_body(text)
163        result1 = str(result1).split('\n')[1:]
164        self.assertEqual(
165            result1,
166            ['MIME-Version: 1.0',
167             'Content-Type: text/plain; charset="utf-8"',
168             'Content-Transfer-Encoding: base64',
169             '',
170             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
171        self.assertEqual(
172            base64.b64decode(result1[-2]).decode('utf-8'),
173            u"Simple text with ü and capital pi: " + unichr(0x3a0))
174        return
175
176class FunctionalMailerTests(FunctionalTestCase):
177
178    layer = FunctionalLayer
179
180    def setUp(self):
181        super(FunctionalMailerTests, self).setUp()
182        self.setup_logging()
183        return
184
185    def tearDown(self):
186        super(FunctionalMailerTests, self).tearDown()
187        self.teardown_logging()
188        return
189
190    def setup_logging(self):
191        # setup a log-handler that catches all fake mailer output
192        self.stream = StringIO()
193        handler = logging.StreamHandler(self.stream)
194        logger = logging.getLogger('test.smtp')
195        logger.addHandler(handler)
196        logger.setLevel(logging.INFO)
197        return
198
199    def get_fake_smtp_output(self):
200        # get output generated by fake mailer
201        self.stream.flush()
202        self.stream.seek(0)
203        return self.stream.read()
204
205    def teardown_logging(self):
206        # remove the log handler for fake mailer output
207        logger = logging.getLogger('test.smtp')
208        handlers = [x for x in logger.handlers]
209        for handler in handlers:
210            logger.removeHandler(handler)
211        return
212
213    def test_get_fake_mailer(self):
214        # we can get the fake mailer if we want
215        mailer = getUtility(IMailDelivery, name='No email service')
216        self.assertTrue(isinstance(mailer, FakeSMTPDelivery))
217        return
218
219    def test_get_default_mailer(self):
220        # we can get a default mailer if we want
221        mailer = getUtility(IMailService)
222        self.assertTrue(isinstance(mailer(), FakeSMTPDelivery))
223        return
224
225    def test_send_mail(self):
226        # we can really send mail.
227        mail_id = send_mail(
228            u'A sender', u'sender@example.com',
229            u'A recipient', u'recpt@example.com',
230            u'A subject',
231            u'This is a test mail.')
232        self.assertEqual(mail_id, 'fake-message-id@example.com')
233        self.assertEqual(
234            self.get_fake_smtp_output().split('\n'),
[10855]235            [u'Sending email from sender@example.com to recpt@example.com, sender@example.com:',
[7470]236             u'Message:',
237             u'msg: MIME-Version: 1.0',
238             u'msg: Content-Type: text/plain; charset="us-ascii"',
239             u'msg: Content-Transfer-Encoding: 7bit',
240             u'msg: From: A sender <sender@example.com>',
[10854]241             u'msg: Cc: A sender <sender@example.com>',
[7470]242             u'msg: To: A recipient <recpt@example.com>',
243             u'msg: Subject: A subject',
244             u'msg: ',
245             u'msg: This is a test mail.',
246             u'']
247            )
248        return
249
250    def test_send_mail_utf8_strings(self):
251        # we can send mail with utf-8 encoded strings as input
252        mail_id = send_mail(
253            u'A sender', u'sender@example.com',
254            u'A recipient', u'recpt@example.com',
255            u'A subject',
256            u'This is a test mail with ümläut.'.encode('utf-8'))
257        self.assertEqual(mail_id, 'fake-message-id@example.com')
258        self.assertEqual(
259            self.get_fake_smtp_output().split('\n'),
[10855]260            [u'Sending email from sender@example.com to recpt@example.com, sender@example.com:',
[7470]261             u'Message:', u'msg: MIME-Version: 1.0',
262             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
263             u'msg: Content-Transfer-Encoding: quoted-printable',
264             u'msg: From: A sender <sender@example.com>',
[10854]265             u'msg: Cc: A sender <sender@example.com>',
[7470]266             u'msg: To: A recipient <recpt@example.com>',
267             u'msg: Subject: A subject',
268             u'msg: ',
269             u'msg: This is a test mail with =FCml=E4ut.',
270             u'']
271            )
272        return
273
274    def test_send_mail_utf8_unicode(self):
275        # we can send mail with utf-8 encoded unicode as input
276        mail_id = send_mail(
277            u'A sender', u'sender@example.com',
278            u'A recipient', u'recpt@example.com',
279            u'A subject',
280            u'This is a test mail with ümläut.')
281        self.assertEqual(mail_id, 'fake-message-id@example.com')
282        self.assertEqual(
283            self.get_fake_smtp_output().split('\n'),
[10855]284            [u'Sending email from sender@example.com to recpt@example.com, sender@example.com:',
[7470]285             u'Message:', u'msg: MIME-Version: 1.0',
286             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
287             u'msg: Content-Transfer-Encoding: quoted-printable',
288             u'msg: From: A sender <sender@example.com>',
[10854]289             u'msg: Cc: A sender <sender@example.com>',
[7470]290             u'msg: To: A recipient <recpt@example.com>',
291             u'msg: Subject: A subject',
292             u'msg: ',
293             u'msg: This is a test mail with =FCml=E4ut.',
294             u'']
295            )
296        return
297
298
299class ExternalMailerTests(FunctionalTestCase):
300
301    layer = FunctionalLayer
302
303    def setUp(self):
304        super(ExternalMailerTests, self).setUp()
305        # Setup a sample site for each test
306        app = University()
307        self.dc_root = tempfile.mkdtemp()
308        app['datacenter'].setStoragePath(self.dc_root)
309
310        # Prepopulate the ZODB...
311        self.getRootFolder()['app'] = app
312        self.app = self.getRootFolder()['app']
313        return
314
315    def tearDown(self):
316        super(ExternalMailerTests, self).tearDown()
317        shutil.rmtree(self.dc_root)
318        return
319
320    def test_config_default_mailer(self):
321        # The default mailer set in config is 'no mailer'.
322        self.assertEqual(
323            getattr(self.app.get('configuration', None), 'smtp_mailer'),
324            u'No email service')
325        return
326
327    @external_mail_test
328    def test_send_direct_mail(self):
329        # send mail using direct mail delivery
330        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
331        setSite(self.app)
332        result = send_mail(
333            'test program', 'no-reply@waeup.org',
334            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
[7819]335            'Test Mail from WAeUP Kofa',
[7470]336            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
337            )
338        import transaction
339        transaction.commit() # The mail is really sent when transactions is
340                             # committed
[7506]341        self.assertTrue('@' in result)
[7470]342        return
343
344    @external_mail_test
[9306]345    def test_send_direct_mails(self):
346        # send several mails using direct mail delivery
347        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
348        setSite(self.app)
349        result = send_mail(
350            'test program', 'no-reply@waeup.org',
351            'test mail receiver',
352            '%s, %s' % (EXTERNAL_MAIL_RECEIVER, EXTERNAL_MAIL_RECEIVER),
353            'Test Mail from WAeUP Kofa',
354            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
355            )
356        import transaction
357        transaction.commit() # The mail is really sent when transactions is
358                             # committed
359        self.assertTrue('@' in result)
360        return
361
362    @external_mail_test
[7470]363    def test_send_queued_mail(self):
364        # send mail using queued mail delivery
365        self.app['configuration'].smtp_mailer = EXTERNAL_QUEUED_DELIVERY
366        setSite(self.app)
367        result = send_mail(
368            'test program', 'no-reply@waeup.org',
369            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
[7819]370            'Test Mail from WAeUP Kofa',
[7470]371            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
372            )
373        import transaction
374        transaction.commit() # The mail is really sent when transactions is
375                             # committed
[7506]376        self.assertTrue('@' in result)
[7470]377        return
Note: See TracBrowser for help on using the repository browser.