comparison test/test_mailgw_roundupmessage.py @ 5493:725266c03eab

updated mailgw to no longer use mimetools based on jerrykan's patch
author Christof Meerwald <cmeerw@cmeerw.org>
date Sun, 12 Aug 2018 16:15:10 +0100
parents
children 081be318661b
comparison
equal deleted inserted replaced
5492:6b0c542642be 5493:725266c03eab
1 # -*- coding: utf-8 -*-
2 import email
3 import textwrap
4 from unittest import TestCase
5
6 from roundup.mailgw import RoundupMessage
7
8 PART_TYPES = {
9 'multipart/signed': ' boundary="boundary-{indent}";\n',
10 'multipart/mixed': ' boundary="boundary-{indent}";\n',
11 'multipart/alternative': ' boundary="boundary-{indent}";\n',
12 'text/plain': ' name="foo.txt"\n\nfoo\n',
13 'text/plain_2': ' name="foo2.txt"\n\nfoo2\n',
14 'text/plain_3': ' name="foo3.txt"\n\nfoo3\n',
15 'text/html': ' name="foo.html"\n\n<html>foo</html>\n',
16 'application/pgp-signature': ' name="foo.gpg"\nfoo\n',
17 'application/pdf': ' name="foo.pdf"\nfoo\n',
18 'application/pdf_2': ' name="foo2.pdf"\nfoo2\n',
19 'message/rfc822': '\nSubject: foo\n\nfoo\n',
20 }
21
22
23 def message_from_string(msg):
24 return email.message_from_string(
25 textwrap.dedent(msg).lstrip(),
26 RoundupMessage)
27
28
29 def construct_message(spec, depth=0):
30 parts = []
31 for content_type in spec:
32 if isinstance(content_type, list):
33 parts.extend(construct_message(content_type, depth=(depth + 1)))
34 parts.append('\n--boundary-{0}--\n'.format(depth + 1))
35 else:
36 if depth > 0:
37 parts.append('\n--boundary-{0}\n'.format(depth))
38
39 parts.append(
40 'Content-Type: {0};\n'.format(content_type.split('_')[0]))
41 parts.append(PART_TYPES[content_type].format(indent=(depth + 1)))
42
43 if depth == 0:
44 return email.message_from_string(''.join(parts), RoundupMessage)
45 else:
46 return parts
47
48
49 class FlattenRoundupMessageTests(TestCase):
50 def test_flatten_with_from(self):
51 msg_string = textwrap.dedent("""
52 From: Some User <some.user@example.com>
53 To: issue_tracker@example.com
54 Message-Id: <dummy_test_message_id>
55 Subject: Test line start with from
56
57 From here to there!
58 """).lstrip()
59
60 msg = email.message_from_string(msg_string, RoundupMessage)
61 self.assertEqual(msg.flatten(), msg_string)
62
63
64 class HeaderRoundupMessageTests(TestCase):
65 msg = message_from_string("""
66 Content-Type: text/plain;
67 charset="iso-8859-1"
68 From: =?utf8?b?SOKCrGxsbw==?= <hello@example.com>
69 To: Issue Tracker <issue_tracker@example.com>
70 Cc: =?utf8?b?SOKCrGxsbw==?= <hello@example.com>,
71 Some User <some.user@example.com>
72 Message-Id: <dummy_test_message_id>
73 Subject: [issue] Testing...
74
75 This is a test submission of a new issue.
76 """)
77
78 def test_get_plain_header(self):
79 self.assertEqual(
80 self.msg.get_header('to'),
81 'Issue Tracker <issue_tracker@example.com>')
82
83 def test_get_encoded_header(self):
84 self.assertEqual(
85 self.msg.get_header('from'),
86 'H€llo <hello@example.com>')
87
88 def test_get_address_list(self):
89 self.assertEqual(self.msg.get_address_list('cc'), [
90 ('H€llo', 'hello@example.com'),
91 ('Some User', 'some.user@example.com'),
92 ])
93
94
95 class BodyRoundupMessageTests(TestCase):
96 def test_get_body_iso_8859_1(self):
97 msg = message_from_string("""
98 Content-Type: text/plain; charset="iso-8859-1"
99 Content-Transfer-Encoding: quoted-printable
100
101 A message with encoding (encoded oe =F6)
102 """)
103
104 self.assertEqual(
105 msg.get_body(),
106 'A message with encoding (encoded oe ö)\n')
107
108 def test_get_body_utf_8(self):
109 msg = message_from_string("""
110 Content-Type: text/plain; charset="utf-8"
111 Content-Transfer-Encoding: quoted-printable
112
113 A message with encoding (encoded oe =C3=B6)
114 """)
115
116 self.assertEqual(
117 msg.get_body(),
118 'A message with encoding (encoded oe ö)\n')
119
120 def test_get_body_base64(self):
121 msg = message_from_string("""
122 Content-Type: application/octet-stream
123 Content-Disposition: attachment; filename="message.dat"
124 Content-Transfer-Encoding: base64
125
126 dGVzdCBlbmNvZGVkIG1lc3NhZ2U=
127 """)
128
129 self.assertEqual(msg.get_body(), 'test encoded message')
130
131
132 class AsAttachmentRoundupMessageTests(TestCase):
133 def test_text_plain(self):
134 msg = message_from_string("""
135 Content-Type: text/plain; charset="iso-8859-1
136
137 Plain text message
138 """)
139
140 self.assertEqual(
141 msg.as_attachment(),
142 (None, 'text/plain', 'Plain text message\n'))
143
144 def test_octet_stream(self):
145 msg = message_from_string("""
146 Content-Type: application/octet-stream
147 Content-Disposition: attachment; filename="message.dat"
148 Content-Transfer-Encoding: base64
149
150 dGVzdCBlbmNvZGVkIG1lc3NhZ2U=
151 """)
152
153 self.assertEqual(
154 msg.as_attachment(),
155 ('message.dat', 'application/octet-stream',
156 'test encoded message'))
157
158 def test_rfc822(self):
159 msg = message_from_string("""
160 Content-Type: message/rfc822
161
162 Subject: foo
163
164 foo
165 """)
166
167 self.assertEqual(
168 msg.as_attachment(),
169 ('foo.eml', 'message/rfc822', 'Subject: foo\n\nfoo\n'))
170
171 def test_rfc822_no_subject(self):
172 msg = message_from_string("""
173 Content-Type: message/rfc822
174
175 X-No-Headers: nope
176
177 foo
178 """)
179
180 self.assertEqual(
181 msg.as_attachment(),
182 (None, 'message/rfc822', 'X-No-Headers: nope\n\nfoo\n'))
183
184 def test_rfc822_no_payload(self):
185 msg = message_from_string("""\
186 Content-Type: message/rfc822
187 """)
188
189 self.assertEqual(
190 msg.as_attachment(),
191 (None, 'message/rfc822', '\n'))
192
193
194 class ExtractContentRoundupMessageTests(TestCase):
195 def test_text_plain(self):
196 msg = construct_message(['text/plain'])
197
198 self.assertEqual(msg.extract_content(), ('foo\n', [], False))
199
200 def test_attached_text_plain(self):
201 msg = construct_message([
202 'multipart/mixed', [
203 'text/plain',
204 'text/plain',
205 ],
206 ])
207
208 self.assertEqual(msg.extract_content(), (
209 'foo\n',
210 [('foo.txt', 'text/plain', 'foo\n')],
211 False
212 ))
213
214 def test_multipart_mixed(self):
215 msg = construct_message([
216 'multipart/mixed', [
217 'text/plain',
218 'application/pdf',
219 ],
220 ])
221
222 self.assertEqual(msg.extract_content(), (
223 'foo\n',
224 [('foo.pdf', 'application/pdf', 'foo\n')],
225 False
226 ))
227
228 def test_multipart_alternative(self):
229 msg = construct_message([
230 'multipart/alternative', [
231 'text/plain',
232 'text/html',
233 ],
234 ])
235
236 self.assertEqual(msg.extract_content(), (
237 'foo\n',
238 [('foo.html', 'text/html', '<html>foo</html>\n')],
239 False
240 ))
241
242 def test_deep_multipart_alternative(self):
243 msg = construct_message([
244 'multipart/mixed', [
245 'multipart/alternative', [
246 'text/plain',
247 'application/pdf',
248 'text/plain_2',
249 'text/html',
250 ],
251 'multipart/alternative', [
252 'text/plain_3',
253 'application/pdf_2',
254 ],
255 ],
256 ])
257
258 self.assertEqual(msg.extract_content(), (
259 'foo2\n', [
260 ('foo.pdf', 'application/pdf', 'foo\n'),
261 ('foo.txt', 'text/plain', 'foo\n'),
262 ('foo.html', 'text/html', '<html>foo</html>\n'),
263 ('foo3.txt', 'text/plain', 'foo3\n'),
264 ('foo2.pdf', 'application/pdf', 'foo2\n'),
265 ],
266 False
267 ))
268
269 def test_deep_multipart_alternative_ignore(self):
270 msg = construct_message([
271 'multipart/mixed', [
272 'multipart/alternative', [
273 'text/plain',
274 'application/pdf',
275 'text/plain_2',
276 'text/html',
277 ],
278 'multipart/alternative', [
279 'text/plain_3',
280 'application/pdf_2',
281 ],
282 ],
283 ])
284
285 msg.extract_content(ignore_alternatives=True)
286 self.assertEqual(msg.extract_content(ignore_alternatives=True), (
287 'foo2\n', [
288 ('foo3.txt', 'text/plain', 'foo3\n'),
289 ('foo2.pdf', 'application/pdf', 'foo2\n'),
290 ],
291 False
292 ))
293
294 def test_signed_text(self):
295 msg = construct_message([
296 'multipart/signed', [
297 'text/plain',
298 'application/pgp-signature',
299 ],
300 ])
301
302 self.assertEqual(msg.extract_content(), ('foo\n', [], False))
303
304 def test_signed_attachemts(self):
305 msg = construct_message([
306 'multipart/signed', [
307 'multipart/mixed', [
308 'text/plain',
309 'application/pdf',
310 ],
311 'application/pgp-signature',
312 ],
313 ])
314
315 self.assertEqual(msg.extract_content(), (
316 'foo\n',
317 [('foo.pdf', 'application/pdf', 'foo\n')],
318 False
319 ))
320
321 def test_attached_signature(self):
322 msg = construct_message([
323 'multipart/mixed', [
324 'text/plain',
325 'application/pgp-signature',
326 ],
327 ])
328
329 self.assertEqual(msg.extract_content(), (
330 'foo\n',
331 [('foo.gpg', 'application/pgp-signature', 'foo\n')],
332 False
333 ))
334
335 def test_rfc822_message(self):
336 msg = construct_message([
337 'multipart/mixed', [
338 'message/rfc822',
339 ],
340 ])
341
342 self.assertEqual(msg.extract_content(), (
343 None,
344 [('foo.eml', 'message/rfc822', 'Subject: foo\n\nfoo\n')],
345 False
346 ))
347
348 def test_rfc822_message_unpack(self):
349 msg = construct_message([
350 'multipart/mixed', [
351 'text/plain',
352 'message/rfc822',
353 ],
354 ])
355
356 self.assertEqual(msg.extract_content(unpack_rfc822=True), (
357 'foo\n',
358 [(None, 'text/plain', 'foo\n')],
359 False
360 ))
361
362
363 class PgpDetectRoundupMessageTests(TestCase):
364 def test_pgp_message_signed(self):
365 msg = message_from_string("""
366 Content-Type: multipart/signed; micalg=pgp-sha1;
367 protocol="application/pgp-signature"
368
369 Fake Body
370 """)
371
372 self.assertTrue(msg.pgp_signed())
373
374 def test_pgp_message_not_signed(self):
375 msg = message_from_string("""
376 Content-Type: text/plain
377
378 Fake Body
379 """)
380
381 self.assertFalse(msg.pgp_signed())
382
383 def test_pgp_message_signed_protocol_missing(self):
384 msg = message_from_string("""
385 Content-Type: multipart/signed; micalg=pgp-sha1
386
387 Fake Body
388 """)
389
390 self.assertFalse(msg.pgp_signed())
391
392 def test_pgp_message_signed_protocol_invalid(self):
393 msg = message_from_string("""
394 Content-Type: multipart/signed;
395 protocol="application/not-pgp-signature"
396
397 Fake Body
398 """)
399
400 self.assertFalse(msg.pgp_signed())
401
402 def test_pgp_message_encrypted(self):
403 msg = message_from_string("""
404 Content-Type: multipart/encrypted;
405 protocol="application/pgp-encrypted"
406
407 Fake Body
408 """)
409
410 self.assertTrue(msg.pgp_encrypted())
411
412 def test_pgp_message_not_encrypted(self):
413 msg = message_from_string("""
414 Content-Type: text/plain
415
416 Fake Body
417 """)
418
419 self.assertFalse(msg.pgp_encrypted())
420
421 def test_pgp_message_encrypted_protocol_missing(self):
422 msg = message_from_string("""
423 Content-Type: multipart/encrypted
424
425 Fake Body
426 """)
427
428 self.assertFalse(msg.pgp_encrypted())
429
430 def test_pgp_message_encrypted_protocol_invalid(self):
431 msg = message_from_string("""
432 Content-Type: multipart/encrypted;
433 protocol="application/not-pgp-encrypted"
434
435 Fake Body
436 """)
437
438 self.assertFalse(msg.pgp_encrypted())
439
440 # TODO: testing of the verify_signature() and decrypt() RoundupMessage methods.
441 # The whole PGP testing stuff seems a bit messy, so we will rely on the tests
442 # in test_mailgw for the time being

Roundup Issue Tracker: http://roundup-tracker.org/