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

Last change on this file since 7473 was 7473, checked in by uli, 13 years ago

Move IMailService to interfaces module.

File size: 13.3 KB
Line 
1# -*- coding: utf-8 -*-
2##
3## test_smtp.py
4## Login : <uli@pu.smp.net>
5## Started on  Wed Dec 21 15:08:50 2011 Uli Fouquet
6## $Id$
7##
8## Copyright (C) 2011 Uli Fouquet
9## This program is free software; you can redistribute it and/or modify
10## it under the terms of the GNU General Public License as published by
11## the Free Software Foundation; either version 2 of the License, or
12## (at your option) any later version.
13##
14## This program is distributed in the hope that it will be useful,
15## but WITHOUT ANY WARRANTY; without even the implied warranty of
16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17## GNU General Public License for more details.
18##
19## You should have received a copy of the GNU General Public License
20## along with this program; if not, write to the Free Software
21## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22##
23# Tests for email-related components
24import base64
25import logging
26import tempfile
27import shutil
28import unittest
29from StringIO import StringIO
30from zope.component import getUtility
31from zope.component.hooks import setSite, clearSite
32from zope.sendmail.interfaces import IMailDelivery
33from waeup.sirp.app import University
34from waeup.sirp.interfaces import IMailService
35from waeup.sirp.smtp import (
36    encode_header_item, encode_address, encode_body, FakeSMTPDelivery,
37    send_mail)
38from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
39
40#
41# SMTP test config
42#
43
44# Also run tests that send mail to external servers?
45#   If you enable this, please make sure the external smtp hosts and receivers
46#   do exist really and are not bothered by being spammed by a test programme.
47EXTERNAL_MAIL_TESTS = False
48
49# Maybe existing receiver of externally sent mail. If this mail account
50# exists really, it might receive mail from the tests!
51EXTERNAL_MAIL_RECEIVER = 'no-user@waeup.org'
52
53# Names of mail deliveries to use when external mail tests are enabled.
54#   See local mail.zcml for names of available deliveries.
55EXTERNAL_DIRECT_DELIVERY = 'Direct SMTP on localhost'
56EXTERNAL_QUEUED_DELIVERY = 'Queued SMTP on localhost'
57
58#
59# end of SMTP test config
60#
61
62def external_mail_test(func):
63    if not EXTERNAL_MAIL_TESTS:
64        myself = __file__
65        if myself.endswith('.pyc'):
66            myself = myself[:-2]
67        print "WARNING: external mail tests are skipped!"
68        print "WARNING: edit %s to enable them." % myself
69        return
70    return func
71
72class HelperTests(unittest.TestCase):
73
74    def test_encode_header_item(self):
75        # encoding header items works with strings and unicodes, as
76        # long as we pass in utf-8 encoded stuff.
77        result1 = encode_header_item(u'Plain Name'.encode('utf-8'))
78        result2 = encode_header_item(u'Name with umläut'.encode('utf-8'))
79        result3 = encode_header_item(u'Plain Name')
80        result4 = encode_header_item(u'Name with umläut')
81        self.assertEqual(result1, u'Plain Name')
82        self.assertEqual(result2, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
83        self.assertEqual(result3, u'Plain Name')
84        self.assertEqual(result4, u'=?iso-8859-1?q?Name_with_uml=E4ut?=')
85        return
86
87    def test_encode_address(self):
88        # we can correctly encode address parts
89        result1 = encode_address('foo@bar.baz')
90        result2 = encode_address(u'foo@bar.baz', 'The Foo' )
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>')
96        self.assertEqual(result4,
97                         '=?iso-8859-1?q?With_Uml=E4ut?= <foo@bar.baz>')
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)
145        text = text.encode('utf-8') # turn unicode into byte stream
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
178class FunctionalMailerTests(FunctionalTestCase):
179
180    layer = FunctionalLayer
181
182    def setUp(self):
183        super(FunctionalMailerTests, self).setUp()
184        self.setup_logging()
185        return
186
187    def tearDown(self):
188        super(FunctionalMailerTests, self).tearDown()
189        self.teardown_logging()
190        return
191
192    def setup_logging(self):
193        # setup a log-handler that catches all fake mailer output
194        self.stream = StringIO()
195        handler = logging.StreamHandler(self.stream)
196        logger = logging.getLogger('test.smtp')
197        logger.addHandler(handler)
198        logger.setLevel(logging.INFO)
199        return
200
201    def get_fake_smtp_output(self):
202        # get output generated by fake mailer
203        self.stream.flush()
204        self.stream.seek(0)
205        return self.stream.read()
206
207    def teardown_logging(self):
208        # remove the log handler for fake mailer output
209        logger = logging.getLogger('test.smtp')
210        handlers = [x for x in logger.handlers]
211        for handler in handlers:
212            logger.removeHandler(handler)
213        return
214
215    def test_get_fake_mailer(self):
216        # we can get the fake mailer if we want
217        mailer = getUtility(IMailDelivery, name='No email service')
218        self.assertTrue(isinstance(mailer, FakeSMTPDelivery))
219        return
220
221    def test_get_default_mailer(self):
222        # we can get a default mailer if we want
223        mailer = getUtility(IMailService)
224        self.assertTrue(isinstance(mailer(), FakeSMTPDelivery))
225        return
226
227    def test_send_mail(self):
228        # we can really send mail.
229        mail_id = send_mail(
230            u'A sender', u'sender@example.com',
231            u'A recipient', u'recpt@example.com',
232            u'A subject',
233            u'This is a test mail.')
234        self.assertEqual(mail_id, 'fake-message-id@example.com')
235        self.assertEqual(
236            self.get_fake_smtp_output().split('\n'),
237            [u'Sending email from sender@example.com to recpt@example.com:',
238             u'Message:',
239             u'msg: MIME-Version: 1.0',
240             u'msg: Content-Type: text/plain; charset="us-ascii"',
241             u'msg: Content-Transfer-Encoding: 7bit',
242             u'msg: From: A sender <sender@example.com>',
243             u'msg: To: A recipient <recpt@example.com>',
244             u'msg: Subject: A subject',
245             u'msg: ',
246             u'msg: This is a test mail.',
247             u'']
248            )
249        return
250
251    def test_send_mail_utf8_strings(self):
252        # we can send mail with utf-8 encoded strings as input
253        mail_id = send_mail(
254            u'A sender', u'sender@example.com',
255            u'A recipient', u'recpt@example.com',
256            u'A subject',
257            u'This is a test mail with ümläut.'.encode('utf-8'))
258        self.assertEqual(mail_id, 'fake-message-id@example.com')
259        self.assertEqual(
260            self.get_fake_smtp_output().split('\n'),
261            [u'Sending email from sender@example.com to recpt@example.com:',
262             u'Message:', u'msg: MIME-Version: 1.0',
263             u'msg: Content-Type: text/plain; charset="iso-8859-1"',
264             u'msg: Content-Transfer-Encoding: quoted-printable',
265             u'msg: From: A sender <sender@example.com>',
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'),
284            [u'Sending email from sender@example.com to recpt@example.com:',
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>',
289             u'msg: To: A recipient <recpt@example.com>',
290             u'msg: Subject: A subject',
291             u'msg: ',
292             u'msg: This is a test mail with =FCml=E4ut.',
293             u'']
294            )
295        return
296
297
298class ExternalMailerTests(FunctionalTestCase):
299
300    layer = FunctionalLayer
301
302    def setUp(self):
303        super(ExternalMailerTests, self).setUp()
304        # Setup a sample site for each test
305        app = University()
306        self.dc_root = tempfile.mkdtemp()
307        app['datacenter'].setStoragePath(self.dc_root)
308
309        # Prepopulate the ZODB...
310        self.getRootFolder()['app'] = app
311        self.app = self.getRootFolder()['app']
312        return
313
314    def tearDown(self):
315        super(ExternalMailerTests, self).tearDown()
316        shutil.rmtree(self.dc_root)
317        return
318
319    def test_config_default_mailer(self):
320        # The default mailer set in config is 'no mailer'.
321        self.assertEqual(
322            getattr(self.app.get('configuration', None), 'smtp_mailer'),
323            u'No email service')
324        return
325
326    @external_mail_test
327    def test_send_direct_mail(self):
328        # send mail using direct mail delivery
329        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
330        setSite(self.app)
331        result = send_mail(
332            'test program', 'no-reply@waeup.org',
333            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
334            'Test Mail from WAeUP SIRP',
335            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
336            )
337        import transaction
338        transaction.commit() # The mail is really sent when transactions is
339                             # committed
340        self.assertEqual(result, 'asd')
341        return
342
343    @external_mail_test
344    def test_send_queued_mail(self):
345        # send mail using queued mail delivery
346        self.app['configuration'].smtp_mailer = EXTERNAL_QUEUED_DELIVERY
347        setSite(self.app)
348        result = send_mail(
349            'test program', 'no-reply@waeup.org',
350            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
351            'Test Mail from WAeUP SIRP',
352            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
353            )
354        import transaction
355        transaction.commit() # The mail is really sent when transactions is
356                             # committed
357        self.assertEqual(result, 'asd')
358        return
Note: See TracBrowser for help on using the repository browser.