| 1 | |
|---|
| 2 | |
|---|
| 3 | """Test the TurboMail Message class.""" |
|---|
| 4 | |
|---|
| 5 | from datetime import datetime, timedelta |
|---|
| 6 | import email |
|---|
| 7 | from email.Header import decode_header, Header |
|---|
| 8 | try: |
|---|
| 9 | from email.mime.text import MIMEText |
|---|
| 10 | except ImportError: |
|---|
| 11 | |
|---|
| 12 | from email.MIMEText import MIMEText |
|---|
| 13 | from email.Utils import formatdate, parsedate |
|---|
| 14 | |
|---|
| 15 | import logging |
|---|
| 16 | import re |
|---|
| 17 | import time |
|---|
| 18 | import unittest |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | from turbomail.message import Message |
|---|
| 22 | |
|---|
| 23 | logging.disable(logging.WARNING) |
|---|
| 24 | |
|---|
| 25 | |
|---|
| 26 | class TestBasicMessage(unittest.TestCase): |
|---|
| 27 | """Test the basic output of the Message class.""" |
|---|
| 28 | |
|---|
| 29 | def setUp(self): |
|---|
| 30 | self.message = Message( |
|---|
| 31 | author=("Author", "author@example.com"), |
|---|
| 32 | to=("Recipient", "recipient@example.com"), |
|---|
| 33 | subject="Test message subject.", |
|---|
| 34 | plain="This is a test message plain text body." |
|---|
| 35 | ) |
|---|
| 36 | |
|---|
| 37 | def test_message_properties(self): |
|---|
| 38 | self.assertEqual(self.message.author, [("Author", "author@example.com")]) |
|---|
| 39 | self.assertEqual(str(self.message.author), "Author <author@example.com>") |
|---|
| 40 | self.failUnless(isinstance(self.message.mime, MIMEText)) |
|---|
| 41 | |
|---|
| 42 | def test_message_string_with_basic(self): |
|---|
| 43 | message_string = str(self.message) |
|---|
| 44 | msg = email.message_from_string(message_string) |
|---|
| 45 | self.assertEqual('Author <author@example.com>', msg['From']) |
|---|
| 46 | self.assertEqual('Recipient <recipient@example.com>', msg['To']) |
|---|
| 47 | self.assertEqual('Test message subject.', msg['Subject']) |
|---|
| 48 | self.assertEqual('This is a test message plain text body.', msg.get_payload()) |
|---|
| 49 | |
|---|
| 50 | def test_message_recipients_and_addresses(self): |
|---|
| 51 | self.message.cc = 'cc@example.com' |
|---|
| 52 | self.message.bcc = 'bcc@example.com' |
|---|
| 53 | self.message.sender = 'sender@example.com' |
|---|
| 54 | self.message.reply_to = 'replyto@example.com' |
|---|
| 55 | self.message.disposition = 'disposition@example.com' |
|---|
| 56 | |
|---|
| 57 | message_string = str(self.message) |
|---|
| 58 | msg = email.message_from_string(message_string) |
|---|
| 59 | |
|---|
| 60 | self.assertEqual('cc@example.com', msg['cc']) |
|---|
| 61 | |
|---|
| 62 | |
|---|
| 63 | self.assertEqual(None, msg['bcc']) |
|---|
| 64 | self.assertEqual('sender@example.com', msg['sender']) |
|---|
| 65 | self.assertEqual('replyto@example.com', msg['reply-to']) |
|---|
| 66 | self.assertEqual('disposition@example.com', msg['disposition-notification-to']) |
|---|
| 67 | |
|---|
| 68 | def test_mime_generation(self): |
|---|
| 69 | mime = self.message.mime |
|---|
| 70 | self.failUnless(self.message.mime is mime) |
|---|
| 71 | self.message.subject = "Test message subject." |
|---|
| 72 | self.failIf(self.message.mime is mime) |
|---|
| 73 | |
|---|
| 74 | def test_recipients_collection(self): |
|---|
| 75 | self.message.cc.append("copied@example.com") |
|---|
| 76 | self.assertEqual(["recipient@example.com", "copied@example.com"], |
|---|
| 77 | self.message.recipients.addresses) |
|---|
| 78 | |
|---|
| 79 | def test_smtp_from_as_envelope(self): |
|---|
| 80 | self.message.smtp_from = 'devnull@example.com' |
|---|
| 81 | self.assertEqual('devnull@example.com', str(self.message.envelope_sender)) |
|---|
| 82 | |
|---|
| 83 | def test_subject_with_umlaut(self): |
|---|
| 84 | subject_string = u"Test with ÀöÌ" |
|---|
| 85 | self.message.subject = subject_string |
|---|
| 86 | self.message.encoding = "UTF-8" |
|---|
| 87 | |
|---|
| 88 | msg = email.message_from_string(str(self.message)) |
|---|
| 89 | encoded_subject = str(Header(subject_string, "UTF-8")) |
|---|
| 90 | self.assertEqual(encoded_subject, msg['Subject']) |
|---|
| 91 | |
|---|
| 92 | def test_from_with_umlaut(self): |
|---|
| 93 | from_name = u"Karl MÃŒller" |
|---|
| 94 | from_email = u"karl.mueller@example.com" |
|---|
| 95 | |
|---|
| 96 | self.message.author = [(from_name, from_email)] |
|---|
| 97 | self.message.encoding = "ISO-8859-1" |
|---|
| 98 | |
|---|
| 99 | msg = email.message_from_string(str(self.message)) |
|---|
| 100 | encoded_name = "%s <%s>" % (str(Header(from_name, "ISO-8859-1")), from_email) |
|---|
| 101 | self.assertEqual(encoded_name, msg['From']) |
|---|
| 102 | |
|---|
| 103 | def test_multiple_authors(self): |
|---|
| 104 | """For better readability, provide a property 'authors' (alias for |
|---|
| 105 | author).""" |
|---|
| 106 | self.message.authors = 'foo@example.com' |
|---|
| 107 | self.assertEqual(self.message.authors, self.message.author) |
|---|
| 108 | |
|---|
| 109 | self.message.authors = ['bar@example.com', 'baz@example.com'] |
|---|
| 110 | self.message.sender = 'foo@example.com' |
|---|
| 111 | msg = email.message_from_string(str(self.message)) |
|---|
| 112 | from_addresses = re.split(r",\n?\s+", msg['From']) |
|---|
| 113 | self.assertEqual(['bar@example.com', 'baz@example.com'], from_addresses) |
|---|
| 114 | |
|---|
| 115 | def test_multiple_authors_require_sender(self): |
|---|
| 116 | self.message.authors = ['bar@example.com', 'baz@example.com'] |
|---|
| 117 | self.assertRaises(ValueError, str, self.message) |
|---|
| 118 | |
|---|
| 119 | self.message.sender = 'bar@example.com' |
|---|
| 120 | str(self.message) |
|---|
| 121 | |
|---|
| 122 | def test_permit_one_sender_at_most(self): |
|---|
| 123 | self.message.sender = ['bar@example.com', 'baz@example.com'] |
|---|
| 124 | self.assertRaises(ValueError, str, self.message) |
|---|
| 125 | |
|---|
| 126 | self.message.sender = 'bar@example.com' |
|---|
| 127 | str(self.message) |
|---|
| 128 | |
|---|
| 129 | def test_raise_error_for_unknown_kwargs_at_class_instantiation(self): |
|---|
| 130 | self.assertRaises(TypeError, Message, invalid_argument=True) |
|---|
| 131 | |
|---|
| 132 | def test_add_custom_headers_dict(self): |
|---|
| 133 | "Test that custom headers (dict type) can be attached." |
|---|
| 134 | self.message.headers = {'Precedence': 'bulk', 'X-User': 'Alice'} |
|---|
| 135 | msg = email.message_from_string(str(self.message)) |
|---|
| 136 | self.assertEqual('bulk', msg['Precedence']) |
|---|
| 137 | self.assertEqual('Alice', msg['X-User']) |
|---|
| 138 | |
|---|
| 139 | def test_add_custom_headers_tuple(self): |
|---|
| 140 | "Test that a custom header (tuple type) can be attached." |
|---|
| 141 | self.message.headers = (('Precedence', 'bulk'), ('X-User', 'Alice')) |
|---|
| 142 | |
|---|
| 143 | msg = email.message_from_string(str(self.message)) |
|---|
| 144 | self.assertEqual('bulk', msg['Precedence']) |
|---|
| 145 | self.assertEqual('Alice', msg['X-User']) |
|---|
| 146 | |
|---|
| 147 | def test_add_custom_headers_list(self): |
|---|
| 148 | "Test that a custom header (list type) can be attached." |
|---|
| 149 | self.message.headers = [('Precedence', 'bulk'), ('X-User', 'Alice')] |
|---|
| 150 | |
|---|
| 151 | msg = email.message_from_string(str(self.message)) |
|---|
| 152 | self.assertEqual('bulk', msg['Precedence']) |
|---|
| 153 | self.assertEqual('Alice', msg['X-User']) |
|---|
| 154 | |
|---|
| 155 | def test_no_sender_header_if_no_sender_required(self): |
|---|
| 156 | msg = email.message_from_string(str(self.message)) |
|---|
| 157 | self.assertEqual(None, msg['Sender']) |
|---|
| 158 | |
|---|
| 159 | def _date_header_to_datetime(self, date_string): |
|---|
| 160 | """Converts a date_string from the Date header into a naive datetime |
|---|
| 161 | object.""" |
|---|
| 162 | assert date_string != None |
|---|
| 163 | epoch_seconds = time.mktime(parsedate(date_string)) |
|---|
| 164 | datetime_obj = datetime.fromtimestamp(epoch_seconds) |
|---|
| 165 | return datetime_obj |
|---|
| 166 | |
|---|
| 167 | def _almost_now(self, date_string): |
|---|
| 168 | """Returns True if the date_string represents a time which is 'almost |
|---|
| 169 | now'.""" |
|---|
| 170 | delta = abs(datetime.now() - self._date_header_to_datetime(date_string)) |
|---|
| 171 | return (delta < timedelta(seconds=1)) |
|---|
| 172 | |
|---|
| 173 | def test_date_header_added_even_if_date_not_set_explicitely(self): |
|---|
| 174 | msg = email.message_from_string(str(self.message)) |
|---|
| 175 | self.assertTrue(self._almost_now(msg['Date'])) |
|---|
| 176 | |
|---|
| 177 | def test_date_can_be_set_as_string(self): |
|---|
| 178 | date_string = 'Fri, 26 Dec 2008 11:19:42 +0530' |
|---|
| 179 | self.message.date = date_string |
|---|
| 180 | msg = email.message_from_string(str(self.message)) |
|---|
| 181 | self.assertEqual(date_string, msg['Date']) |
|---|
| 182 | |
|---|
| 183 | def test_date_can_be_set_as_float(self): |
|---|
| 184 | expected_date = datetime(2008, 12, 26, 12, 55) |
|---|
| 185 | expected_time = time.mktime(expected_date.timetuple()) |
|---|
| 186 | self.message.date = expected_time |
|---|
| 187 | msg = email.message_from_string(str(self.message)) |
|---|
| 188 | header_string = msg['Date'] |
|---|
| 189 | header_date = self._date_header_to_datetime(header_string) |
|---|
| 190 | self.assertEqual(expected_date, header_date) |
|---|
| 191 | expected_datestring = formatdate(expected_time, localtime=True) |
|---|
| 192 | self.assertEqual(expected_datestring, header_string) |
|---|
| 193 | |
|---|
| 194 | def test_date_can_be_set_as_datetime(self): |
|---|
| 195 | expected_date = datetime(2008, 12, 26, 12, 55) |
|---|
| 196 | self.message.date = expected_date |
|---|
| 197 | msg = email.message_from_string(str(self.message)) |
|---|
| 198 | header_date = self._date_header_to_datetime(msg['Date']) |
|---|
| 199 | self.assertEqual(expected_date, header_date) |
|---|
| 200 | |
|---|
| 201 | def test_date_header_is_set_even_if_reset_to_none(self): |
|---|
| 202 | self.message.date = None |
|---|
| 203 | msg = email.message_from_string(str(self.message)) |
|---|
| 204 | self.assertTrue(self._almost_now(msg['Date'])) |
|---|
| 205 | |
|---|