/tests/regressiontests/mail/tests.py
Python | 636 lines | 526 code | 53 blank | 57 comment | 12 complexity | b7b9f39bda23742efbd09c1c0187628f MD5 | raw file
Possible License(s): BSD-3-Clause
1# coding: utf-8 2import asyncore 3import email 4import os 5import shutil 6import smtpd 7import sys 8from StringIO import StringIO 9import tempfile 10import threading 11 12from django.conf import settings 13from django.core import mail 14from django.core.mail import (EmailMessage, mail_admins, mail_managers, 15 EmailMultiAlternatives, send_mail, send_mass_mail) 16from django.core.mail.backends import console, dummy, locmem, filebased, smtp 17from django.core.mail.message import BadHeaderError 18from django.test import TestCase 19from django.utils.translation import ugettext_lazy 20from django.utils.functional import wraps 21 22 23def alter_django_settings(**kwargs): 24 oldvalues = {} 25 nonexistant = [] 26 for setting, newvalue in kwargs.iteritems(): 27 try: 28 oldvalues[setting] = getattr(settings, setting) 29 except AttributeError: 30 nonexistant.append(setting) 31 setattr(settings, setting, newvalue) 32 return oldvalues, nonexistant 33 34 35def restore_django_settings(state): 36 oldvalues, nonexistant = state 37 for setting, oldvalue in oldvalues.iteritems(): 38 setattr(settings, setting, oldvalue) 39 for setting in nonexistant: 40 delattr(settings, setting) 41 42 43def with_django_settings(**kwargs): 44 def decorator(test): 45 @wraps(test) 46 def decorated_test(self): 47 state = alter_django_settings(**kwargs) 48 try: 49 return test(self) 50 finally: 51 restore_django_settings(state) 52 return decorated_test 53 return decorator 54 55 56class MailTests(TestCase): 57 """ 58 Non-backend specific tests. 59 """ 60 61 def test_ascii(self): 62 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com']) 63 message = email.message() 64 self.assertEqual(message['Subject'].encode(), 'Subject') 65 self.assertEqual(message.get_payload(), 'Content') 66 self.assertEqual(message['From'], 'from@example.com') 67 self.assertEqual(message['To'], 'to@example.com') 68 69 def test_multiple_recipients(self): 70 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com']) 71 message = email.message() 72 self.assertEqual(message['Subject'].encode(), 'Subject') 73 self.assertEqual(message.get_payload(), 'Content') 74 self.assertEqual(message['From'], 'from@example.com') 75 self.assertEqual(message['To'], 'to@example.com, other@example.com') 76 77 def test_cc(self): 78 """Regression test for #7722""" 79 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com']) 80 message = email.message() 81 self.assertEqual(message['Cc'], 'cc@example.com') 82 self.assertEqual(email.recipients(), ['to@example.com', 'cc@example.com']) 83 84 # Test multiple CC with multiple To 85 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com'], cc=['cc@example.com', 'cc.other@example.com']) 86 message = email.message() 87 self.assertEqual(message['Cc'], 'cc@example.com, cc.other@example.com') 88 self.assertEqual(email.recipients(), ['to@example.com', 'other@example.com', 'cc@example.com', 'cc.other@example.com']) 89 90 # Testing with Bcc 91 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com'], cc=['cc@example.com', 'cc.other@example.com'], bcc=['bcc@example.com']) 92 message = email.message() 93 self.assertEqual(message['Cc'], 'cc@example.com, cc.other@example.com') 94 self.assertEqual(email.recipients(), ['to@example.com', 'other@example.com', 'cc@example.com', 'cc.other@example.com', 'bcc@example.com']) 95 96 def test_header_injection(self): 97 email = EmailMessage('Subject\nInjection Test', 'Content', 'from@example.com', ['to@example.com']) 98 self.assertRaises(BadHeaderError, email.message) 99 email = EmailMessage(ugettext_lazy('Subject\nInjection Test'), 'Content', 'from@example.com', ['to@example.com']) 100 self.assertRaises(BadHeaderError, email.message) 101 102 def test_space_continuation(self): 103 """ 104 Test for space continuation character in long (ascii) subject headers (#7747) 105 """ 106 email = EmailMessage('Long subject lines that get wrapped should use a space continuation character to get expected behaviour in Outlook and Thunderbird', 'Content', 'from@example.com', ['to@example.com']) 107 message = email.message() 108 self.assertEqual(message['Subject'], 'Long subject lines that get wrapped should use a space continuation\n character to get expected behaviour in Outlook and Thunderbird') 109 110 def test_message_header_overrides(self): 111 """ 112 Specifying dates or message-ids in the extra headers overrides the 113 default values (#9233) 114 """ 115 headers = {"date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"} 116 email = EmailMessage('subject', 'content', 'from@example.com', ['to@example.com'], headers=headers) 117 self.assertEqual(email.message().as_string(), 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: subject\nFrom: from@example.com\nTo: to@example.com\ndate: Fri, 09 Nov 2001 01:08:47 -0000\nMessage-ID: foo\n\ncontent') 118 119 def test_from_header(self): 120 """ 121 Make sure we can manually set the From header (#9214) 122 """ 123 email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 124 message = email.message() 125 self.assertEqual(message['From'], 'from@example.com') 126 127 def test_multiple_message_call(self): 128 """ 129 Regression for #13259 - Make sure that headers are not changed when 130 calling EmailMessage.message() 131 """ 132 email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 133 message = email.message() 134 self.assertEqual(message['From'], 'from@example.com') 135 message = email.message() 136 self.assertEqual(message['From'], 'from@example.com') 137 138 def test_unicode_address_header(self): 139 """ 140 Regression for #11144 - When a to/from/cc header contains unicode, 141 make sure the email addresses are parsed correctly (especially with 142 regards to commas) 143 """ 144 email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Firstname Sürname" <to@example.com>', 'other@example.com']) 145 self.assertEqual(email.message()['To'], '=?utf-8?q?Firstname_S=C3=BCrname?= <to@example.com>, other@example.com') 146 email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>', 'other@example.com']) 147 self.assertEqual(email.message()['To'], '=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com') 148 149 def test_unicode_headers(self): 150 email = EmailMessage(u"G?eg?ó?ka", "Content", "from@example.com", ["to@example.com"], 151 headers={"Sender": '"Firstname Sürname" <sender@example.com>', 152 "Comments": 'My Sürname is non-ASCII'}) 153 message = email.message() 154 self.assertEqual(message['Subject'], '=?utf-8?b?R8W8ZWfFvMOzxYJrYQ==?=') 155 self.assertEqual(message['Sender'], '=?utf-8?q?Firstname_S=C3=BCrname?= <sender@example.com>') 156 self.assertEqual(message['Comments'], '=?utf-8?q?My_S=C3=BCrname_is_non-ASCII?=') 157 158 def test_safe_mime_multipart(self): 159 """ 160 Make sure headers can be set with a different encoding than utf-8 in 161 SafeMIMEMultipart as well 162 """ 163 headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"} 164 subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>' 165 text_content = 'This is an important message.' 166 html_content = '<p>This is an <strong>important</strong> message.</p>' 167 msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers) 168 msg.attach_alternative(html_content, "text/html") 169 msg.encoding = 'iso-8859-1' 170 self.assertEqual(msg.message()['To'], '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>') 171 self.assertEqual(msg.message()['Subject'].encode(), u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=') 172 173 def test_encoding(self): 174 """ 175 Regression for #12791 - Encode body correctly with other encodings 176 than utf-8 177 """ 178 email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com']) 179 email.encoding = 'iso-8859-1' 180 message = email.message() 181 self.assertTrue(message.as_string().startswith('Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: other@example.com')) 182 self.assertEqual(message.get_payload(), 'Firstname S=FCrname is a great guy.') 183 184 # Make sure MIME attachments also works correctly with other encodings than utf-8 185 text_content = 'Firstname Sürname is a great guy.' 186 html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>' 187 msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com']) 188 msg.encoding = 'iso-8859-1' 189 msg.attach_alternative(html_content, "text/html") 190 self.assertEqual(msg.message().get_payload(0).as_string(), 'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nFirstname S=FCrname is a great guy.') 191 self.assertEqual(msg.message().get_payload(1).as_string(), 'Content-Type: text/html; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\n<p>Firstname S=FCrname is a <strong>great</strong> guy.</p>') 192 193 def test_attachments(self): 194 """Regression test for #9367""" 195 headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"} 196 subject, from_email, to = 'hello', 'from@example.com', 'to@example.com' 197 text_content = 'This is an important message.' 198 html_content = '<p>This is an <strong>important</strong> message.</p>' 199 msg = EmailMultiAlternatives(subject, text_content, from_email, [to], headers=headers) 200 msg.attach_alternative(html_content, "text/html") 201 msg.attach("an attachment.pdf", "%PDF-1.4.%...", mimetype="application/pdf") 202 msg_str = msg.message().as_string() 203 message = email.message_from_string(msg_str) 204 self.assertTrue(message.is_multipart()) 205 self.assertEqual(message.get_content_type(), 'multipart/mixed') 206 self.assertEqual(message.get_default_type(), 'text/plain') 207 payload = message.get_payload() 208 self.assertEqual(payload[0].get_content_type(), 'multipart/alternative') 209 self.assertEqual(payload[1].get_content_type(), 'application/pdf') 210 211 def test_dummy_backend(self): 212 """ 213 Make sure that dummy backends returns correct number of sent messages 214 """ 215 connection = dummy.EmailBackend() 216 email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 217 self.assertEqual(connection.send_messages([email, email, email]), 3) 218 219 def test_arbitrary_keyword(self): 220 """ 221 Make sure that get_connection() accepts arbitrary keyword that might be 222 used with custom backends. 223 """ 224 c = mail.get_connection(fail_silently=True, foo='bar') 225 self.assertTrue(c.fail_silently) 226 227 def test_custom_backend(self): 228 """Test custom backend defined in this suite.""" 229 conn = mail.get_connection('regressiontests.mail.custombackend.EmailBackend') 230 self.assertTrue(hasattr(conn, 'test_outbox')) 231 email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 232 conn.send_messages([email]) 233 self.assertEqual(len(conn.test_outbox), 1) 234 235 def test_backend_arg(self): 236 """Test backend argument of mail.get_connection()""" 237 self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.smtp.EmailBackend'), smtp.EmailBackend)) 238 self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.locmem.EmailBackend'), locmem.EmailBackend)) 239 self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.dummy.EmailBackend'), dummy.EmailBackend)) 240 self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.console.EmailBackend'), console.EmailBackend)) 241 tmp_dir = tempfile.mkdtemp() 242 try: 243 self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.filebased.EmailBackend', file_path=tmp_dir), filebased.EmailBackend)) 244 finally: 245 shutil.rmtree(tmp_dir) 246 self.assertTrue(isinstance(mail.get_connection(), locmem.EmailBackend)) 247 248 @with_django_settings( 249 EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend', 250 ADMINS=[('nobody', 'nobody@example.com')], 251 MANAGERS=[('nobody', 'nobody@example.com')]) 252 def test_connection_arg(self): 253 """Test connection argument to send_mail(), et. al.""" 254 mail.outbox = [] 255 256 # Send using non-default connection 257 connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend') 258 send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection) 259 self.assertEqual(mail.outbox, []) 260 self.assertEqual(len(connection.test_outbox), 1) 261 self.assertEqual(connection.test_outbox[0].subject, 'Subject') 262 263 connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend') 264 send_mass_mail([ 265 ('Subject1', 'Content1', 'from1@example.com', ['to1@example.com']), 266 ('Subject2', 'Content2', 'from2@example.com', ['to2@example.com']), 267 ], connection=connection) 268 self.assertEqual(mail.outbox, []) 269 self.assertEqual(len(connection.test_outbox), 2) 270 self.assertEqual(connection.test_outbox[0].subject, 'Subject1') 271 self.assertEqual(connection.test_outbox[1].subject, 'Subject2') 272 273 connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend') 274 mail_admins('Admin message', 'Content', connection=connection) 275 self.assertEqual(mail.outbox, []) 276 self.assertEqual(len(connection.test_outbox), 1) 277 self.assertEqual(connection.test_outbox[0].subject, '[Django] Admin message') 278 279 connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend') 280 mail_managers('Manager message', 'Content', connection=connection) 281 self.assertEqual(mail.outbox, []) 282 self.assertEqual(len(connection.test_outbox), 1) 283 self.assertEqual(connection.test_outbox[0].subject, '[Django] Manager message') 284 285 def test_dont_mangle_from_in_body(self): 286 # Regression for #13433 - Make sure that EmailMessage doesn't mangle 287 # 'From ' in message body. 288 email = EmailMessage('Subject', 'From the future', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 289 self.assertFalse('>From the future' in email.message().as_string()) 290 291 292class BaseEmailBackendTests(object): 293 email_backend = None 294 295 def setUp(self): 296 self.__settings_state = alter_django_settings(EMAIL_BACKEND=self.email_backend) 297 298 def tearDown(self): 299 restore_django_settings(self.__settings_state) 300 301 def assertStartsWith(self, first, second): 302 if not first.startswith(second): 303 self.longMessage = True 304 self.assertEqual(first[:len(second)], second, "First string doesn't start with the second.") 305 306 def get_mailbox_content(self): 307 raise NotImplementedError 308 309 def flush_mailbox(self): 310 raise NotImplementedError 311 312 def get_the_message(self): 313 mailbox = self.get_mailbox_content() 314 self.assertEqual(len(mailbox), 1, 315 "Expected exactly one message, got %d.\n%r" % (len(mailbox), [ 316 m.as_string() for m in mailbox])) 317 return mailbox[0] 318 319 def test_send(self): 320 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com']) 321 num_sent = mail.get_connection().send_messages([email]) 322 self.assertEqual(num_sent, 1) 323 message = self.get_the_message() 324 self.assertEqual(message["subject"], "Subject") 325 self.assertEqual(message.get_payload(), "Content") 326 self.assertEqual(message["from"], "from@example.com") 327 self.assertEqual(message.get_all("to"), ["to@example.com"]) 328 329 def test_send_many(self): 330 email1 = EmailMessage('Subject', 'Content1', 'from@example.com', ['to@example.com']) 331 email2 = EmailMessage('Subject', 'Content2', 'from@example.com', ['to@example.com']) 332 num_sent = mail.get_connection().send_messages([email1, email2]) 333 self.assertEqual(num_sent, 2) 334 messages = self.get_mailbox_content() 335 self.assertEqual(len(messages), 2) 336 self.assertEqual(messages[0].get_payload(), "Content1") 337 self.assertEqual(messages[1].get_payload(), "Content2") 338 339 def test_send_verbose_name(self): 340 email = EmailMessage("Subject", "Content", '"Firstname Sürname" <from@example.com>', 341 ["to@example.com"]) 342 email.send() 343 message = self.get_the_message() 344 self.assertEqual(message["subject"], "Subject") 345 self.assertEqual(message.get_payload(), "Content") 346 self.assertEqual(message["from"], "=?utf-8?q?Firstname_S=C3=BCrname?= <from@example.com>") 347 348 @with_django_settings(MANAGERS=[('nobody', 'nobody@example.com')]) 349 def test_html_mail_managers(self): 350 """Test html_message argument to mail_managers""" 351 mail_managers('Subject', 'Content', html_message='HTML Content') 352 message = self.get_the_message() 353 354 self.assertEqual(message.get('subject'), '[Django] Subject') 355 self.assertEqual(message.get_all('to'), ['nobody@example.com']) 356 self.assertTrue(message.is_multipart()) 357 self.assertEqual(len(message.get_payload()), 2) 358 self.assertEqual(message.get_payload(0).get_payload(), 'Content') 359 self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain') 360 self.assertEqual(message.get_payload(1).get_payload(), 'HTML Content') 361 self.assertEqual(message.get_payload(1).get_content_type(), 'text/html') 362 363 @with_django_settings(ADMINS=[('nobody', 'nobody@example.com')]) 364 def test_html_mail_admins(self): 365 """Test html_message argument to mail_admins """ 366 mail_admins('Subject', 'Content', html_message='HTML Content') 367 message = self.get_the_message() 368 369 self.assertEqual(message.get('subject'), '[Django] Subject') 370 self.assertEqual(message.get_all('to'), ['nobody@example.com']) 371 self.assertTrue(message.is_multipart()) 372 self.assertEqual(len(message.get_payload()), 2) 373 self.assertEqual(message.get_payload(0).get_payload(), 'Content') 374 self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain') 375 self.assertEqual(message.get_payload(1).get_payload(), 'HTML Content') 376 self.assertEqual(message.get_payload(1).get_content_type(), 'text/html') 377 378 @with_django_settings(ADMINS=[('nobody', 'nobody+admin@example.com')], 379 MANAGERS=[('nobody', 'nobody+manager@example.com')]) 380 def test_manager_and_admin_mail_prefix(self): 381 """ 382 String prefix + lazy translated subject = bad output 383 Regression for #13494 384 """ 385 mail_managers(ugettext_lazy('Subject'), 'Content') 386 message = self.get_the_message() 387 self.assertEqual(message.get('subject'), '[Django] Subject') 388 389 self.flush_mailbox() 390 mail_admins(ugettext_lazy('Subject'), 'Content') 391 message = self.get_the_message() 392 self.assertEqual(message.get('subject'), '[Django] Subject') 393 394 @with_django_settings(ADMINS=(), MANAGERS=()) 395 def test_empty_admins(self): 396 """ 397 Test that mail_admins/mail_managers doesn't connect to the mail server 398 if there are no recipients (#9383) 399 """ 400 mail_admins('hi', 'there') 401 self.assertEqual(self.get_mailbox_content(), []) 402 mail_managers('hi', 'there') 403 self.assertEqual(self.get_mailbox_content(), []) 404 405 def test_message_cc_header(self): 406 """ 407 Regression test for #7722 408 """ 409 email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com']) 410 mail.get_connection().send_messages([email]) 411 message = self.get_the_message() 412 self.assertStartsWith(message.as_string(), 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nCc: cc@example.com\nDate: ') 413 414 def test_idn_send(self): 415 """ 416 Regression test for #14301 417 """ 418 self.assertTrue(send_mail('Subject', 'Content', 'from@öäü.com', [u'to@öäü.com'])) 419 message = self.get_the_message() 420 self.assertEqual(message.get('subject'), 'Subject') 421 self.assertEqual(message.get('from'), 'from@xn--4ca9at.com') 422 self.assertEqual(message.get('to'), 'to@xn--4ca9at.com') 423 424 self.flush_mailbox() 425 m = EmailMessage('Subject', 'Content', 'from@öäü.com', 426 [u'to@öäü.com'], cc=[u'cc@öäü.com']) 427 m.send() 428 message = self.get_the_message() 429 self.assertEqual(message.get('subject'), 'Subject') 430 self.assertEqual(message.get('from'), 'from@xn--4ca9at.com') 431 self.assertEqual(message.get('to'), 'to@xn--4ca9at.com') 432 self.assertEqual(message.get('cc'), 'cc@xn--4ca9at.com') 433 434 def test_recipient_without_domain(self): 435 """ 436 Regression test for #15042 437 """ 438 self.assertTrue(send_mail("Subject", "Content", "tester", ["django"])) 439 message = self.get_the_message() 440 self.assertEqual(message.get('subject'), 'Subject') 441 self.assertEqual(message.get('from'), "tester") 442 self.assertEqual(message.get('to'), "django") 443 444 445class LocmemBackendTests(BaseEmailBackendTests, TestCase): 446 email_backend = 'django.core.mail.backends.locmem.EmailBackend' 447 448 def get_mailbox_content(self): 449 return [m.message() for m in mail.outbox] 450 451 def flush_mailbox(self): 452 mail.outbox = [] 453 454 def tearDown(self): 455 super(LocmemBackendTests, self).tearDown() 456 mail.outbox = [] 457 458 def test_locmem_shared_messages(self): 459 """ 460 Make sure that the locmen backend populates the outbox. 461 """ 462 connection = locmem.EmailBackend() 463 connection2 = locmem.EmailBackend() 464 email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 465 connection.send_messages([email]) 466 connection2.send_messages([email]) 467 self.assertEqual(len(mail.outbox), 2) 468 469 470class FileBackendTests(BaseEmailBackendTests, TestCase): 471 email_backend = 'django.core.mail.backends.filebased.EmailBackend' 472 473 def setUp(self): 474 super(FileBackendTests, self).setUp() 475 self.tmp_dir = tempfile.mkdtemp() 476 self.__settings_state = alter_django_settings(EMAIL_FILE_PATH=self.tmp_dir) 477 478 def tearDown(self): 479 restore_django_settings(self.__settings_state) 480 shutil.rmtree(self.tmp_dir) 481 super(FileBackendTests, self).tearDown() 482 483 def flush_mailbox(self): 484 for filename in os.listdir(self.tmp_dir): 485 os.unlink(os.path.join(self.tmp_dir, filename)) 486 487 def get_mailbox_content(self): 488 messages = [] 489 for filename in os.listdir(self.tmp_dir): 490 session = open(os.path.join(self.tmp_dir, filename)).read().split('\n' + ('-' * 79) + '\n') 491 messages.extend(email.message_from_string(m) for m in session if m) 492 return messages 493 494 def test_file_sessions(self): 495 """Make sure opening a connection creates a new file""" 496 msg = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) 497 connection = mail.get_connection() 498 connection.send_messages([msg]) 499 500 self.assertEqual(len(os.listdir(self.tmp_dir)), 1) 501 message = email.message_from_file(open(os.path.join(self.tmp_dir, os.listdir(self.tmp_dir)[0]))) 502 self.assertEqual(message.get_content_type(), 'text/plain') 503 self.assertEqual(message.get('subject'), 'Subject') 504 self.assertEqual(message.get('from'), 'from@example.com') 505 self.assertEqual(message.get('to'), 'to@example.com') 506 507 connection2 = mail.get_connection() 508 connection2.send_messages([msg]) 509 self.assertEqual(len(os.listdir(self.tmp_dir)), 2) 510 511 connection.send_messages([msg]) 512 self.assertEqual(len(os.listdir(self.tmp_dir)), 2) 513 514 msg.connection = mail.get_connection() 515 self.assertTrue(connection.open()) 516 msg.send() 517 self.assertEqual(len(os.listdir(self.tmp_dir)), 3) 518 msg.send() 519 self.assertEqual(len(os.listdir(self.tmp_dir)), 3) 520 521 522class ConsoleBackendTests(BaseEmailBackendTests, TestCase): 523 email_backend = 'django.core.mail.backends.console.EmailBackend' 524 525 def setUp(self): 526 super(ConsoleBackendTests, self).setUp() 527 self.__stdout = sys.stdout 528 self.stream = sys.stdout = StringIO() 529 530 def tearDown(self): 531 del self.stream 532 sys.stdout = self.__stdout 533 del self.__stdout 534 super(ConsoleBackendTests, self).tearDown() 535 536 def flush_mailbox(self): 537 self.stream = sys.stdout = StringIO() 538 539 def get_mailbox_content(self): 540 messages = self.stream.getvalue().split('\n' + ('-' * 79) + '\n') 541 return [email.message_from_string(m) for m in messages if m] 542 543 def test_console_stream_kwarg(self): 544 """ 545 Test that the console backend can be pointed at an arbitrary stream. 546 """ 547 s = StringIO() 548 connection = mail.get_connection('django.core.mail.backends.console.EmailBackend', stream=s) 549 send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection) 550 self.assertTrue(s.getvalue().startswith('Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nDate: ')) 551 552 553class FakeSMTPServer(smtpd.SMTPServer, threading.Thread): 554 """ 555 Asyncore SMTP server wrapped into a thread. Based on DummyFTPServer from: 556 http://svn.python.org/view/python/branches/py3k/Lib/test/test_ftplib.py?revision=86061&view=markup 557 """ 558 559 def __init__(self, *args, **kwargs): 560 threading.Thread.__init__(self) 561 smtpd.SMTPServer.__init__(self, *args, **kwargs) 562 self._sink = [] 563 self.active = False 564 self.active_lock = threading.Lock() 565 self.sink_lock = threading.Lock() 566 567 def process_message(self, peer, mailfrom, rcpttos, data): 568 m = email.message_from_string(data) 569 maddr = email.Utils.parseaddr(m.get('from'))[1] 570 if mailfrom != maddr: 571 return "553 '%s' != '%s'" % (mailfrom, maddr) 572 self.sink_lock.acquire() 573 self._sink.append(m) 574 self.sink_lock.release() 575 576 def get_sink(self): 577 self.sink_lock.acquire() 578 try: 579 return self._sink[:] 580 finally: 581 self.sink_lock.release() 582 583 def flush_sink(self): 584 self.sink_lock.acquire() 585 self._sink[:] = [] 586 self.sink_lock.release() 587 588 def start(self): 589 assert not self.active 590 self.__flag = threading.Event() 591 threading.Thread.start(self) 592 self.__flag.wait() 593 594 def run(self): 595 self.active = True 596 self.__flag.set() 597 while self.active and asyncore.socket_map: 598 self.active_lock.acquire() 599 asyncore.loop(timeout=0.1, count=1) 600 self.active_lock.release() 601 asyncore.close_all() 602 603 def stop(self): 604 assert self.active 605 self.active = False 606 self.join() 607 608 609class SMTPBackendTests(BaseEmailBackendTests, TestCase): 610 email_backend = 'django.core.mail.backends.smtp.EmailBackend' 611 612 @classmethod 613 def setUpClass(cls): 614 cls.server = FakeSMTPServer(('127.0.0.1', 0), None) 615 cls.settings = alter_django_settings( 616 EMAIL_HOST="127.0.0.1", 617 EMAIL_PORT=cls.server.socket.getsockname()[1]) 618 cls.server.start() 619 620 @classmethod 621 def tearDownClass(cls): 622 cls.server.stop() 623 624 def setUp(self): 625 super(SMTPBackendTests, self).setUp() 626 self.server.flush_sink() 627 628 def tearDown(self): 629 self.server.flush_sink() 630 super(SMTPBackendTests, self).tearDown() 631 632 def flush_mailbox(self): 633 self.server.flush_sink() 634 635 def get_mailbox_content(self): 636 return self.server.get_sink()