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

Last change on this file since 16995 was 16428, checked in by Henrik Bettermann, 4 years ago

Fix test.

  • Property svn:keywords set to Id
File size: 16.0 KB
RevLine 
[7470]1# -*- coding: utf-8 -*-
[7476]2## $Id: test_smtp.py 16428 2021-03-23 13:15:55Z 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
[11585]58
[7470]59def external_mail_test(func):
60    if not EXTERNAL_MAIL_TESTS:
61        myself = __file__
62        if myself.endswith('.pyc'):
[14016]63            myself = myself[:-1]
[7470]64        print "WARNING: external mail tests are skipped!"
65        print "WARNING: edit %s to enable them." % myself
66        return
67    return func
68
[11585]69
[7470]70class HelperTests(unittest.TestCase):
71
72    def test_encode_header_item(self):
73        # encoding header items works with strings and unicodes, as
74        # long as we pass in utf-8 encoded stuff.
75        result1 = encode_header_item(u'Plain Name'.encode('utf-8'))
76        result2 = encode_header_item(u'Name with umläut'.encode('utf-8'))
77        result3 = encode_header_item(u'Plain Name')
78        result4 = encode_header_item(u'Name with umläut')
[8382]79        result5 = encode_header_item(None)
[7470]80        self.assertEqual(result1, u'Plain Name')
[7472]81        self.assertEqual(result2, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
[7470]82        self.assertEqual(result3, u'Plain Name')
[7472]83        self.assertEqual(result4, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
[8382]84        self.assertTrue(result5 is None)
[7470]85        return
86
87    def test_encode_address(self):
88        # we can correctly encode address parts
89        result1 = encode_address('foo@bar.baz')
[11585]90        result2 = encode_address(u'foo@bar.baz', 'The Foo')
[7470]91        result3 = encode_address('foo@bar.baz', u'The Foo')
92        result4 = encode_address('foo@bar.baz', u'With Umläut')
93        self.assertEqual(result1, 'foo@bar.baz')
94        self.assertEqual(result2, 'The Foo <foo@bar.baz>')
95        self.assertEqual(result3, 'The Foo <foo@bar.baz>')
[7472]96        self.assertEqual(result4,
97                         '=?iso-8859-1?q?With_Uml=E4ut?= <foo@bar.baz>')
[7470]98        return
99
100    def test_encode_body(self):
101        result1 = encode_body(u'Simple Text')
102        self.assertEqual(
103            str(result1).split('\n')[1:],
104            ['MIME-Version: 1.0',
105             'Content-Type: text/plain; charset="us-ascii"',
106             'Content-Transfer-Encoding: 7bit',
107             '',
108             'Simple Text'])
109        return
110
111    def test_encode_body_latin1_string(self):
112        # utf-8 encoded byte streams are transferred correctly when
113        # they contain chars unrepresentable in ascii but available in latin1
114        text = u'Simple text with Ümläut'.encode('utf-8')
115        result1 = encode_body(text)
116        result1 = str(result1).split('\n')[1:]
117        self.assertEqual(
118            result1,
119            ['MIME-Version: 1.0',
120             'Content-Type: text/plain; charset="iso-8859-1"',
121             'Content-Transfer-Encoding: quoted-printable',
122             '',
123             'Simple text with =DCml=E4ut'])
124        return
125
126    def test_encode_body_latin1_unicode(self):
127        # unicode strings are transferred correctly when they contain
128        # chars unrepresentable in ascii but available in latin1
129        text = u'Simple text with Ümläut'
130        result1 = encode_body(text)
131        result1 = str(result1).split('\n')[1:]
132        self.assertEqual(
133            result1,
134            ['MIME-Version: 1.0',
135             'Content-Type: text/plain; charset="iso-8859-1"',
136             'Content-Transfer-Encoding: quoted-printable',
137             '',
138             'Simple text with =DCml=E4ut'])
139        return
140
141    def test_encode_body_utf8_string(self):
142        # utf-8 encoded byte streams are transferred correctly when
143        # they contain chars unrepresentable in ascii but available in latin1
144        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
[11585]145        text = text.encode('utf-8')  # turn unicode into byte stream
[7470]146        result1 = encode_body(text)
147        result1 = str(result1).split('\n')[1:]
148        self.assertEqual(
149            result1,
150            ['MIME-Version: 1.0',
151             'Content-Type: text/plain; charset="utf-8"',
152             'Content-Transfer-Encoding: base64',
153             '',
154             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
155        self.assertEqual(
156            base64.b64decode(result1[-2]).decode('utf-8'),
157            u"Simple text with ü and capital pi: " + unichr(0x3a0))
158        return
159
160    def test_encode_body_utf8_unicode(self):
161        # utf-8 encoded byte streams are transferred correctly when
162        # they contain chars unrepresentable in latin1
163        text = u"Simple text with ü and capital pi: " + unichr(0x3a0)
164        result1 = encode_body(text)
165        result1 = str(result1).split('\n')[1:]
166        self.assertEqual(
167            result1,
168            ['MIME-Version: 1.0',
169             'Content-Type: text/plain; charset="utf-8"',
170             'Content-Transfer-Encoding: base64',
171             '',
172             'U2ltcGxlIHRleHQgd2l0aCDDvCBhbmQgY2FwaXRhbCBwaTogzqA=', ''])
173        self.assertEqual(
174            base64.b64decode(result1[-2]).decode('utf-8'),
175            u"Simple text with ü and capital pi: " + unichr(0x3a0))
176        return
177
[11585]178
[7470]179class FunctionalMailerTests(FunctionalTestCase):
180
181    layer = FunctionalLayer
182
183    def setUp(self):
184        super(FunctionalMailerTests, self).setUp()
185        self.setup_logging()
186        return
187
188    def tearDown(self):
189        super(FunctionalMailerTests, self).tearDown()
190        self.teardown_logging()
191        return
192
193    def setup_logging(self):
194        # setup a log-handler that catches all fake mailer output
195        self.stream = StringIO()
196        handler = logging.StreamHandler(self.stream)
197        logger = logging.getLogger('test.smtp')
198        logger.addHandler(handler)
199        logger.setLevel(logging.INFO)
200        return
201
202    def get_fake_smtp_output(self):
203        # get output generated by fake mailer
204        self.stream.flush()
205        self.stream.seek(0)
206        return self.stream.read()
207
208    def teardown_logging(self):
209        # remove the log handler for fake mailer output
210        logger = logging.getLogger('test.smtp')
211        handlers = [x for x in logger.handlers]
212        for handler in handlers:
213            logger.removeHandler(handler)
214        return
215
216    def test_get_fake_mailer(self):
217        # we can get the fake mailer if we want
218        mailer = getUtility(IMailDelivery, name='No email service')
219        self.assertTrue(isinstance(mailer, FakeSMTPDelivery))
220        return
221
222    def test_get_default_mailer(self):
223        # we can get a default mailer if we want
224        mailer = getUtility(IMailService)
225        self.assertTrue(isinstance(mailer(), FakeSMTPDelivery))
226        return
227
228    def test_send_mail(self):
229        # we can really send mail.
230        mail_id = send_mail(
231            u'A sender', u'sender@example.com',
232            u'A recipient', u'recpt@example.com',
233            u'A subject',
234            u'This is a test mail.')
235        self.assertEqual(mail_id, 'fake-message-id@example.com')
236        self.assertEqual(
237            self.get_fake_smtp_output().split('\n'),
[11586]238            [u'Sending email from no-reply@waeup.org to '
[11585]239             u'recpt@example.com, sender@example.com:',
[7470]240             u'Message:',
241             u'msg: MIME-Version: 1.0',
242             u'msg: Content-Type: text/plain; charset="us-ascii"',
243             u'msg: Content-Transfer-Encoding: 7bit',
[11585]244             u'msg: From: A sender <no-reply@waeup.org>',
245             u'msg: To: A recipient <recpt@example.com>',
[11778]246             # u'msg: Cc: A sender <sender@example.com>',
247             u'msg: Reply-To: A sender <sender@example.com>',
248             u'msg: Subject: A subject',
249             u'msg: ',
250             u'msg: This is a test mail.',
251             u'']
252            )
253        return
254
255    def test_send_mail_with_cc(self):
256        # with cc=True the message will be CCed to the from address
257        mail_id = send_mail(
258            u'A sender', u'sender@example.com',
259            u'A recipient', u'recpt@example.com',
260            u'A subject',
[16299]261            u'This is a test mail.',None,
[16428]262            (('recpt1@example.com', 'A cc recipient'),),
263            (('recpt2@example.com', 'A bcc recipient'),
264             ('recpt3@example.com', 'A 2nd bcc recipient')),
[16299]265            )
[11778]266        self.assertEqual(mail_id, 'fake-message-id@example.com')
267        self.assertEqual(
268            self.get_fake_smtp_output().split('\n'),
269            [u'Sending email from no-reply@waeup.org to '
[16428]270             u'recpt@example.com, recpt1@example.com, recpt2@example.com, '
271             u'recpt3@example.com, sender@example.com:',
[11778]272             u'Message:',
273             u'msg: MIME-Version: 1.0',
274             u'msg: Content-Type: text/plain; charset="us-ascii"',
275             u'msg: Content-Transfer-Encoding: 7bit',
276             u'msg: From: A sender <no-reply@waeup.org>',
277             u'msg: To: A recipient <recpt@example.com>',
[16428]278             u'msg: Cc: A cc recipient <recpt1@example.com>',
279             u'msg: Bcc: A bcc recipient <recpt2@example.com>',
280             u'msg: Bcc: A 2nd bcc recipient <recpt3@example.com>',
[11585]281             u'msg: Reply-To: A sender <sender@example.com>',
[7470]282             u'msg: Subject: A subject',
283             u'msg: ',
284             u'msg: This is a test mail.',
285             u'']
[16428]286
287
[7470]288            )
289        return
290
291    def test_send_mail_utf8_strings(self):
292        # we can send mail with utf-8 encoded strings as input
293        mail_id = send_mail(
294            u'A sender', u'sender@example.com',
295            u'A recipient', u'recpt@example.com',
296            u'A subject',
297            u'This is a test mail with ümläut.'.encode('utf-8'))
298        self.assertEqual(mail_id, 'fake-message-id@example.com')
299        self.assertEqual(
300            self.get_fake_smtp_output().split('\n'),
[11586]301            [u'Sending email from no-reply@waeup.org '
[11585]302             u'to recpt@example.com, sender@example.com:',
[7470]303             u'Message:', u'msg: MIME-Version: 1.0',
304             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
305             u'msg: Content-Transfer-Encoding: quoted-printable',
[11585]306             u'msg: From: A sender <no-reply@waeup.org>',
307             u'msg: To: A recipient <recpt@example.com>',
[11778]308             # u'msg: Cc: A sender <sender@example.com>',
[11585]309             u'msg: Reply-To: A sender <sender@example.com>',
[7470]310             u'msg: Subject: A subject',
311             u'msg: ',
312             u'msg: This is a test mail with =FCml=E4ut.',
313             u'']
314            )
315        return
316
317    def test_send_mail_utf8_unicode(self):
318        # we can send mail with utf-8 encoded unicode as input
319        mail_id = send_mail(
320            u'A sender', u'sender@example.com',
321            u'A recipient', u'recpt@example.com',
322            u'A subject',
323            u'This is a test mail with ümläut.')
324        self.assertEqual(mail_id, 'fake-message-id@example.com')
325        self.assertEqual(
326            self.get_fake_smtp_output().split('\n'),
[11586]327            [u'Sending email from no-reply@waeup.org '
[11585]328             u'to recpt@example.com, sender@example.com:',
[7470]329             u'Message:', u'msg: MIME-Version: 1.0',
330             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
331             u'msg: Content-Transfer-Encoding: quoted-printable',
[11585]332             u'msg: From: A sender <no-reply@waeup.org>',
333             u'msg: To: A recipient <recpt@example.com>',
[11778]334             # u'msg: Cc: A sender <sender@example.com>',
[11585]335             u'msg: Reply-To: A sender <sender@example.com>',
[7470]336             u'msg: Subject: A subject',
337             u'msg: ',
338             u'msg: This is a test mail with =FCml=E4ut.',
339             u'']
340            )
341        return
342
343
344class ExternalMailerTests(FunctionalTestCase):
345
346    layer = FunctionalLayer
347
348    def setUp(self):
349        super(ExternalMailerTests, self).setUp()
350        # Setup a sample site for each test
351        app = University()
352        self.dc_root = tempfile.mkdtemp()
353        app['datacenter'].setStoragePath(self.dc_root)
354
355        # Prepopulate the ZODB...
356        self.getRootFolder()['app'] = app
357        self.app = self.getRootFolder()['app']
358        return
359
360    def tearDown(self):
361        super(ExternalMailerTests, self).tearDown()
362        shutil.rmtree(self.dc_root)
363        return
364
365    def test_config_default_mailer(self):
366        # The default mailer set in config is 'no mailer'.
367        self.assertEqual(
368            getattr(self.app.get('configuration', None), 'smtp_mailer'),
369            u'No email service')
370        return
371
372    @external_mail_test
373    def test_send_direct_mail(self):
374        # send mail using direct mail delivery
375        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
376        setSite(self.app)
377        result = send_mail(
378            'test program', 'no-reply@waeup.org',
379            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
[7819]380            'Test Mail from WAeUP Kofa',
[7470]381            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
382            )
383        import transaction
[11585]384        transaction.commit()  # The mail is really sent when transactions is
385                              # committed
[7506]386        self.assertTrue('@' in result)
[7470]387        return
388
389    @external_mail_test
[9306]390    def test_send_direct_mails(self):
391        # send several mails using direct mail delivery
392        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
393        setSite(self.app)
394        result = send_mail(
395            'test program', 'no-reply@waeup.org',
396            'test mail receiver',
397            '%s, %s' % (EXTERNAL_MAIL_RECEIVER, EXTERNAL_MAIL_RECEIVER),
398            'Test Mail from WAeUP Kofa',
399            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
400            )
401        import transaction
[11585]402        transaction.commit()  # The mail is really sent when transactions is
403                              # committed
[9306]404        self.assertTrue('@' in result)
405        return
406
407    @external_mail_test
[7470]408    def test_send_queued_mail(self):
409        # send mail using queued mail delivery
410        self.app['configuration'].smtp_mailer = EXTERNAL_QUEUED_DELIVERY
411        setSite(self.app)
412        result = send_mail(
413            'test program', 'no-reply@waeup.org',
414            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
[7819]415            'Test Mail from WAeUP Kofa',
[7470]416            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
417            )
418        import transaction
[11585]419        transaction.commit()  # The mail is really sent when transactions is
420                              # committed
[7506]421        self.assertTrue('@' in result)
[7470]422        return
Note: See TracBrowser for help on using the repository browser.