Code

- Factor MailGW message parsing into a separate class, thanks to John
[roundup.git] / test / test_mailgw.py
1 # -*- encoding: utf-8 -*-
2 #
3 # Copyright (c) 2001 Richard Jones, richard@bofh.asn.au.
4 # This module is free software, and you may redistribute it and/or modify
5 # under the same terms as Python, so long as this copyright message and
6 # disclaimer are retained in their original form.
7 #
8 # This module is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 #
12 # $Id: test_mailgw.py,v 1.96 2008-08-19 01:40:59 richard Exp $
14 # TODO: test bcc
16 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time
18 from cStringIO import StringIO
20 if not os.environ.has_key('SENDMAILDEBUG'):
21     os.environ['SENDMAILDEBUG'] = 'mail-test.log'
22 SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
24 from roundup import mailgw, i18n, roundupdb
25 from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, \
26     parseContent, IgnoreLoop, IgnoreBulk, MailUsageError, MailUsageHelp
27 from roundup import init, instance, password, rfc2822, __version__
28 from roundup.anypy.sets_ import set
30 #import db_test_base
31 import memorydb
33 class Message(rfc822.Message):
34     """String-based Message class with equivalence test."""
35     def __init__(self, s):
36         rfc822.Message.__init__(self, StringIO(s.strip()))
38     def __eq__(self, other):
39         return (self.dict == other.dict and
40                 self.fp.read() == other.fp.read())
42 class Tracker(object):
43     def open(self, journaltag):
44         return self.db
46 class DiffHelper:
47     def compareMessages(self, new, old):
48         """Compare messages for semantic equivalence."""
49         new, old = Message(new), Message(old)
51         # all Roundup-generated messages have "Precedence: bulk"
52         old['Precedence'] = 'bulk'
54         # don't try to compare the date
55         del new['date'], old['date']
57         if not new == old:
58             res = []
60             replace = {}
61             for key in new.keys():
62                 if key.startswith('from '):
63                     # skip the unix from line
64                     continue
65                 if key.lower() == 'x-roundup-version':
66                     # version changes constantly, so handle it specially
67                     if new[key] != __version__:
68                         res.append('  %s: %r != %r' % (key, __version__,
69                             new[key]))
70                 elif key.lower() == 'content-type' and 'boundary=' in new[key]:
71                     # handle mime messages
72                     newmime = new[key].split('=',1)[-1].strip('"')
73                     oldmime = old.get(key, '').split('=',1)[-1].strip('"')
74                     replace ['--' + newmime] = '--' + oldmime
75                     replace ['--' + newmime + '--'] = '--' + oldmime + '--'
76                 elif new.get(key, '') != old.get(key, ''):
77                     res.append('  %s: %r != %r' % (key, old.get(key, ''),
78                         new.get(key, '')))
80             body_diff = self.compareStrings(new.fp.read(), old.fp.read(),
81                 replace=replace)
82             if body_diff:
83                 res.append('')
84                 res.extend(body_diff)
86             if res:
87                 res.insert(0, 'Generated message not correct (diff follows, expected vs. actual):')
88                 raise AssertionError, '\n'.join(res)
90     def compareStrings(self, s2, s1, replace={}):
91         '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
92            the first to be the "original" but in the calls in this file,
93            the second arg is the original. Ho hum.
94            Do replacements over the replace dict -- used for mime boundary
95         '''
96         l1 = s1.strip().split('\n')
97         l2 = [replace.get(i,i) for i in s2.strip().split('\n')]
98         if l1 == l2:
99             return
100         s = difflib.SequenceMatcher(None, l1, l2)
101         res = []
102         for value, s1s, s1e, s2s, s2e in s.get_opcodes():
103             if value == 'equal':
104                 for i in range(s1s, s1e):
105                     res.append('  %s'%l1[i])
106             elif value == 'delete':
107                 for i in range(s1s, s1e):
108                     res.append('- %s'%l1[i])
109             elif value == 'insert':
110                 for i in range(s2s, s2e):
111                     res.append('+ %s'%l2[i])
112             elif value == 'replace':
113                 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
114                     res.append('- %s'%l1[i])
115                     res.append('+ %s'%l2[j])
117         return res
119 class MailgwTestCase(unittest.TestCase, DiffHelper):
120     count = 0
121     schema = 'classic'
122     def setUp(self):
123         self.old_translate_ = mailgw._
124         roundupdb._ = mailgw._ = i18n.get_translation(language='C').gettext
125         MailgwTestCase.count = MailgwTestCase.count + 1
127         # and open the database / "instance"
128         self.db = memorydb.create('admin')
129         self.instance = Tracker()
130         self.instance.db = self.db
131         self.instance.config = self.db.config
132         self.instance.MailGW = MailGW
134         self.chef_id = self.db.user.create(username='Chef',
135             address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
136         self.richard_id = self.db.user.create(username='richard',
137             address='richard@test.test', roles='User')
138         self.mary_id = self.db.user.create(username='mary',
139             address='mary@test.test', roles='User', realname='Contrary, Mary')
140         self.john_id = self.db.user.create(username='john',
141             address='john@test.test', roles='User', realname='John Doe',
142             alternate_addresses='jondoe@test.test\njohn.doe@test.test')
143         self.rgg_id = self.db.user.create(username='rgg',
144             address='rgg@test.test', roles='User')
146     def tearDown(self):
147         roundupdb._ = mailgw._ = self.old_translate_
148         if os.path.exists(SENDMAILDEBUG):
149             os.remove(SENDMAILDEBUG)
150         self.db.close()
152     def _create_mailgw(self, message, args=()):
153         class MailGW(self.instance.MailGW):
154             def handle_message(self, message):
155                 return self._handle_message(message)
156         handler = MailGW(self.instance, args)
157         handler.db = self.db
158         return handler
160     def _handle_mail(self, message, args=()):
161         handler = self._create_mailgw(message, args)
162         handler.trapExceptions = 0
163         return handler.main(StringIO(message))
165     def _get_mail(self):
166         f = open(SENDMAILDEBUG)
167         try:
168             return f.read()
169         finally:
170             f.close()
172     def testEmptyMessage(self):
173         nodeid = self._handle_mail('''Content-Type: text/plain;
174   charset="iso-8859-1"
175 From: Chef <chef@bork.bork.bork>
176 To: issue_tracker@your.tracker.email.domain.example
177 Cc: richard@test.test
178 Reply-To: chef@bork.bork.bork
179 Message-Id: <dummy_test_message_id>
180 Subject: [issue] Testing...
182 ''')
183         assert not os.path.exists(SENDMAILDEBUG)
184         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
186     def testMessageWithFromInIt(self):
187         nodeid = self._handle_mail('''Content-Type: text/plain;
188   charset="iso-8859-1"
189 From: Chef <chef@bork.bork.bork>
190 To: issue_tracker@your.tracker.email.domain.example
191 Cc: richard@test.test
192 Reply-To: chef@bork.bork.bork
193 Message-Id: <dummy_test_message_id>
194 Subject: [issue] Testing...
196 From here to there!
197 ''')
198         assert not os.path.exists(SENDMAILDEBUG)
199         msgid = self.db.issue.get(nodeid, 'messages')[0]
200         self.assertEqual(self.db.msg.get(msgid, 'content'), 'From here to there!')
202     def testNoMessageId(self):
203         self.instance.config['MAIL_DOMAIN'] = 'example.com'
204         nodeid = self._handle_mail('''Content-Type: text/plain;
205   charset="iso-8859-1"
206 From: Chef <chef@bork.bork.bork>
207 To: issue_tracker@your.tracker.email.domain.example
208 Cc: richard@test.test
209 Reply-To: chef@bork.bork.bork
210 Subject: [issue] Testing...
212 Hi there!
213 ''')
214         assert not os.path.exists(SENDMAILDEBUG)
215         msgid = self.db.issue.get(nodeid, 'messages')[0]
216         messageid = self.db.msg.get(msgid, 'messageid')
217         x1, x2 = messageid.split('@')
218         self.assertEqual(x2, 'example.com>')
219         x = x1.split('.')[-1]
220         self.assertEqual(x, 'issueNone')
221         nodeid = self._handle_mail('''Content-Type: text/plain;
222   charset="iso-8859-1"
223 From: Chef <chef@bork.bork.bork>
224 To: issue_tracker@your.tracker.email.domain.example
225 Subject: [issue%(nodeid)s] Testing...
227 Just a test reply
228 '''%locals())
229         msgid = self.db.issue.get(nodeid, 'messages')[-1]
230         messageid = self.db.msg.get(msgid, 'messageid')
231         x1, x2 = messageid.split('@')
232         self.assertEqual(x2, 'example.com>')
233         x = x1.split('.')[-1]
234         self.assertEqual(x, "issue%s"%nodeid)
236     def testOptions(self):
237         nodeid = self._handle_mail('''Content-Type: text/plain;
238   charset="iso-8859-1"
239 From: Chef <chef@bork.bork.bork>
240 To: issue_tracker@your.tracker.email.domain.example
241 Message-Id: <dummy_test_message_id>
242 Reply-To: chef@bork.bork.bork
243 Subject: [issue] Testing...
245 Hi there!
246 ''', (('-C', 'issue'), ('-S', 'status=chatting;priority=critical')))
247         self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
248         self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
250     def testOptionsMulti(self):
251         nodeid = self._handle_mail('''Content-Type: text/plain;
252   charset="iso-8859-1"
253 From: Chef <chef@bork.bork.bork>
254 To: issue_tracker@your.tracker.email.domain.example
255 Message-Id: <dummy_test_message_id>
256 Reply-To: chef@bork.bork.bork
257 Subject: [issue] Testing...
259 Hi there!
260 ''', (('-C', 'issue'), ('-S', 'status=chatting'), ('-S', 'priority=critical')))
261         self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
262         self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
264     def doNewIssue(self):
265         nodeid = self._handle_mail('''Content-Type: text/plain;
266   charset="iso-8859-1"
267 From: Chef <chef@bork.bork.bork>
268 To: issue_tracker@your.tracker.email.domain.example
269 Cc: richard@test.test
270 Message-Id: <dummy_test_message_id>
271 Subject: [issue] Testing...
273 This is a test submission of a new issue.
274 ''')
275         assert not os.path.exists(SENDMAILDEBUG)
276         l = self.db.issue.get(nodeid, 'nosy')
277         l.sort()
278         self.assertEqual(l, [self.chef_id, self.richard_id])
279         return nodeid
281     def testNewIssue(self):
282         self.doNewIssue()
284     def testNewIssueNosy(self):
285         self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes'
286         nodeid = self._handle_mail('''Content-Type: text/plain;
287   charset="iso-8859-1"
288 From: Chef <chef@bork.bork.bork>
289 To: issue_tracker@your.tracker.email.domain.example
290 Cc: richard@test.test
291 Message-Id: <dummy_test_message_id>
292 Subject: [issue] Testing...
294 This is a test submission of a new issue.
295 ''')
296         assert not os.path.exists(SENDMAILDEBUG)
297         l = self.db.issue.get(nodeid, 'nosy')
298         l.sort()
299         self.assertEqual(l, [self.chef_id, self.richard_id])
301     def testAlternateAddress(self):
302         self._handle_mail('''Content-Type: text/plain;
303   charset="iso-8859-1"
304 From: John Doe <john.doe@test.test>
305 To: issue_tracker@your.tracker.email.domain.example
306 Message-Id: <dummy_test_message_id>
307 Subject: [issue] Testing...
309 This is a test submission of a new issue.
310 ''')
311         userlist = self.db.user.list()
312         assert not os.path.exists(SENDMAILDEBUG)
313         self.assertEqual(userlist, self.db.user.list(),
314             "user created when it shouldn't have been")
316     def testNewIssueNoClass(self):
317         self._handle_mail('''Content-Type: text/plain;
318   charset="iso-8859-1"
319 From: Chef <chef@bork.bork.bork>
320 To: issue_tracker@your.tracker.email.domain.example
321 Cc: richard@test.test
322 Message-Id: <dummy_test_message_id>
323 Subject: Testing...
325 This is a test submission of a new issue.
326 ''')
327         assert not os.path.exists(SENDMAILDEBUG)
329     def testNewIssueAuthMsg(self):
330         # TODO: fix the damn config - this is apalling
331         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
332         self._handle_mail('''Content-Type: text/plain;
333   charset="iso-8859-1"
334 From: Chef <chef@bork.bork.bork>
335 To: issue_tracker@your.tracker.email.domain.example
336 Message-Id: <dummy_test_message_id>
337 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
339 This is a test submission of a new issue.
340 ''')
341         self.compareMessages(self._get_mail(),
342 '''FROM: roundup-admin@your.tracker.email.domain.example
343 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
344 Content-Type: text/plain; charset="utf-8"
345 Subject: [issue1] Testing...
346 To: chef@bork.bork.bork, mary@test.test, richard@test.test
347 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
348 Reply-To: Roundup issue tracker
349  <issue_tracker@your.tracker.email.domain.example>
350 MIME-Version: 1.0
351 Message-Id: <dummy_test_message_id>
352 X-Roundup-Name: Roundup issue tracker
353 X-Roundup-Loop: hello
354 X-Roundup-Issue-Status: unread
355 Content-Transfer-Encoding: quoted-printable
358 New submission from Bork, Chef <chef@bork.bork.bork>:
360 This is a test submission of a new issue.
362 ----------
363 assignedto: richard
364 messages: 1
365 nosy: Chef, mary, richard
366 status: unread
367 title: Testing...
369 _______________________________________________________________________
370 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
371 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
372 _______________________________________________________________________
373 ''')
375     def testNewIssueNoAuthorInfo(self):
376         self.db.config.MAIL_ADD_AUTHORINFO = 'no'
377         self._handle_mail('''Content-Type: text/plain;
378   charset="iso-8859-1"
379 From: Chef <chef@bork.bork.bork>
380 To: issue_tracker@your.tracker.email.domain.example
381 Message-Id: <dummy_test_message_id>
382 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
384 This is a test submission of a new issue.
385 ''')
386         self.compareMessages(self._get_mail(),
387 '''FROM: roundup-admin@your.tracker.email.domain.example
388 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
389 Content-Type: text/plain; charset="utf-8"
390 Subject: [issue1] Testing...
391 To: mary@test.test, richard@test.test
392 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
393 Reply-To: Roundup issue tracker
394  <issue_tracker@your.tracker.email.domain.example>
395 MIME-Version: 1.0
396 Message-Id: <dummy_test_message_id>
397 X-Roundup-Name: Roundup issue tracker
398 X-Roundup-Loop: hello
399 X-Roundup-Issue-Status: unread
400 Content-Transfer-Encoding: quoted-printable
402 This is a test submission of a new issue.
404 ----------
405 assignedto: richard
406 messages: 1
407 nosy: Chef, mary, richard
408 status: unread
409 title: Testing...
411 _______________________________________________________________________
412 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
413 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
414 _______________________________________________________________________
415 ''')
417     def testNewIssueNoAuthorEmail(self):
418         self.db.config.MAIL_ADD_AUTHOREMAIL = 'no'
419         self._handle_mail('''Content-Type: text/plain;
420   charset="iso-8859-1"
421 From: Chef <chef@bork.bork.bork>
422 To: issue_tracker@your.tracker.email.domain.example
423 Message-Id: <dummy_test_message_id>
424 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
426 This is a test submission of a new issue.
427 ''')
428         self.compareMessages(self._get_mail(),
429 '''FROM: roundup-admin@your.tracker.email.domain.example
430 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
431 Content-Type: text/plain; charset="utf-8"
432 Subject: [issue1] Testing...
433 To: mary@test.test, richard@test.test
434 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
435 Reply-To: Roundup issue tracker
436  <issue_tracker@your.tracker.email.domain.example>
437 MIME-Version: 1.0
438 Message-Id: <dummy_test_message_id>
439 X-Roundup-Name: Roundup issue tracker
440 X-Roundup-Loop: hello
441 X-Roundup-Issue-Status: unread
442 Content-Transfer-Encoding: quoted-printable
444 New submission from Bork, Chef:
446 This is a test submission of a new issue.
448 ----------
449 assignedto: richard
450 messages: 1
451 nosy: Chef, mary, richard
452 status: unread
453 title: Testing...
455 _______________________________________________________________________
456 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
457 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
458 _______________________________________________________________________
459 ''')
461     multipart_msg = '''From: mary <mary@test.test>
462 To: issue_tracker@your.tracker.email.domain.example
463 Message-Id: <followup_dummy_id>
464 In-Reply-To: <dummy_test_message_id>
465 Subject: [issue1] Testing...
466 Content-Type: multipart/mixed; boundary="bxyzzy"
467 Content-Disposition: inline
470 --bxyzzy
471 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE"
472 Content-Disposition: inline
474 --bCsyhTFzCvuiizWE
475 Content-Type: text/plain; charset=us-ascii
476 Content-Disposition: inline
478 test attachment first text/plain
480 --bCsyhTFzCvuiizWE
481 Content-Type: application/octet-stream
482 Content-Disposition: attachment; filename="first.dvi"
483 Content-Transfer-Encoding: base64
485 SnVzdCBhIHRlc3QgAQo=
487 --bCsyhTFzCvuiizWE
488 Content-Type: text/plain; charset=us-ascii
489 Content-Disposition: inline
491 test attachment second text/plain
493 --bCsyhTFzCvuiizWE
494 Content-Type: text/html
495 Content-Disposition: inline
497 <html>
498 to be ignored.
499 </html>
501 --bCsyhTFzCvuiizWE--
503 --bxyzzy
504 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF"
505 Content-Disposition: inline
507 --bCsyhTFzCvuiizWF
508 Content-Type: text/plain; charset=us-ascii
509 Content-Disposition: inline
511 test attachment third text/plain
513 --bCsyhTFzCvuiizWF
514 Content-Type: application/octet-stream
515 Content-Disposition: attachment; filename="second.dvi"
516 Content-Transfer-Encoding: base64
518 SnVzdCBhIHRlc3QK
520 --bCsyhTFzCvuiizWF--
522 --bxyzzy--
523 '''
525     multipart_msg_latin1 = '''From: mary <mary@test.test>
526 To: issue_tracker@your.tracker.email.domain.example
527 Message-Id: <followup_dummy_id>
528 In-Reply-To: <dummy_test_message_id>
529 Subject: [issue1] Testing...
530 Content-Type: multipart/alternative; boundary=001485f339f8f361fb049188dbba
533 --001485f339f8f361fb049188dbba
534 Content-Type: text/plain; charset=ISO-8859-1
535 Content-Transfer-Encoding: quoted-printable
537 umlaut =E4=F6=FC=C4=D6=DC=DF
539 --001485f339f8f361fb049188dbba
540 Content-Type: text/html; charset=ISO-8859-1
541 Content-Transfer-Encoding: quoted-printable
543 <html>umlaut =E4=F6=FC=C4=D6=DC=DF</html>
545 --001485f339f8f361fb049188dbba--
546 '''
548     multipart_msg_rfc822 = '''From: mary <mary@test.test>
549 To: issue_tracker@your.tracker.email.domain.example
550 Message-Id: <followup_dummy_id>
551 In-Reply-To: <dummy_test_message_id>
552 Subject: [issue1] Testing...
553 Content-Type: multipart/mixed; boundary=001485f339f8f361fb049188dbba
555 This is a multi-part message in MIME format.
556 --001485f339f8f361fb049188dbba
557 Content-Type: text/plain; charset=ISO-8859-15
558 Content-Transfer-Encoding: 7bit
560 First part: Text
562 --001485f339f8f361fb049188dbba
563 Content-Type: message/rfc822; name="Fwd: Original email subject.eml"
564 Content-Transfer-Encoding: 7bit
565 Content-Disposition: attachment; filename="Fwd: Original email subject.eml"
567 Message-Id: <followup_dummy_id_2>
568 In-Reply-To: <dummy_test_message_id_2>
569 MIME-Version: 1.0
570 Subject: Fwd: Original email subject
571 Date: Mon, 23 Aug 2010 08:23:33 +0200
572 Content-Type: multipart/alternative; boundary="090500050101020406060002"
574 This is a multi-part message in MIME format.
575 --090500050101020406060002
576 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
577 Content-Transfer-Encoding: 7bit
579 some text in inner email
580 ========================
582 --090500050101020406060002
583 Content-Type: text/html; charset=ISO-8859-15
584 Content-Transfer-Encoding: 7bit
586 <html>
587 some text in inner email
588 ========================
589 </html>
591 --090500050101020406060002--
593 --001485f339f8f361fb049188dbba--
594 '''
596     def testMultipartKeepAlternatives(self):
597         self.doNewIssue()
598         self._handle_mail(self.multipart_msg)
599         messages = self.db.issue.get('1', 'messages')
600         messages.sort()
601         msg = self.db.msg.getnode (messages[-1])
602         assert(len(msg.files) == 5)
603         names = {0 : 'first.dvi', 4 : 'second.dvi'}
604         content = {3 : 'test attachment third text/plain\n',
605                    4 : 'Just a test\n'}
606         for n, id in enumerate (msg.files):
607             f = self.db.file.getnode (id)
608             self.assertEqual(f.name, names.get (n, 'unnamed'))
609             if n in content :
610                 self.assertEqual(f.content, content [n])
611         self.assertEqual(msg.content, 'test attachment second text/plain')
613     def testMultipartDropAlternatives(self):
614         self.doNewIssue()
615         self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
616         self._handle_mail(self.multipart_msg)
617         messages = self.db.issue.get('1', 'messages')
618         messages.sort()
619         msg = self.db.msg.getnode (messages[-1])
620         assert(len(msg.files) == 2)
621         names = {1 : 'second.dvi'}
622         content = {0 : 'test attachment third text/plain\n',
623                    1 : 'Just a test\n'}
624         for n, id in enumerate (msg.files):
625             f = self.db.file.getnode (id)
626             self.assertEqual(f.name, names.get (n, 'unnamed'))
627             if n in content :
628                 self.assertEqual(f.content, content [n])
629         self.assertEqual(msg.content, 'test attachment second text/plain')
631     def testMultipartCharsetUTF8NoAttach(self):
632         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
633         self.doNewIssue()
634         self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
635         self._handle_mail(self.multipart_msg_latin1)
636         messages = self.db.issue.get('1', 'messages')
637         messages.sort()
638         msg = self.db.msg.getnode (messages[-1])
639         assert(len(msg.files) == 1)
640         name = 'unnamed'
641         content = '<html>' + c + '</html>\n'
642         for n, id in enumerate (msg.files):
643             f = self.db.file.getnode (id)
644             self.assertEqual(f.name, name)
645             self.assertEqual(f.content, content)
646         self.assertEqual(msg.content, c)
647         self.compareMessages(self._get_mail(),
648 '''FROM: roundup-admin@your.tracker.email.domain.example
649 TO: chef@bork.bork.bork, richard@test.test
650 Content-Type: text/plain; charset="utf-8"
651 Subject: [issue1] Testing...
652 To: chef@bork.bork.bork, richard@test.test
653 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
654 Reply-To: Roundup issue tracker
655  <issue_tracker@your.tracker.email.domain.example>
656 MIME-Version: 1.0
657 Message-Id: <followup_dummy_id>
658 In-Reply-To: <dummy_test_message_id>
659 X-Roundup-Name: Roundup issue tracker
660 X-Roundup-Loop: hello
661 X-Roundup-Issue-Status: chatting
662 X-Roundup-Issue-Files: unnamed
663 Content-Transfer-Encoding: quoted-printable
666 Contrary, Mary <mary@test.test> added the comment:
668 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
669 File 'unnamed' not attached - you can download it from http://tracker.examp=
670 le/cgi-bin/roundup.cgi/bugs/file1.
672 ----------
673 status: unread -> chatting
675 _______________________________________________________________________
676 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
677 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
678 _______________________________________________________________________
679 ''')
681     def testMultipartCharsetLatin1NoAttach(self):
682         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
683         self.doNewIssue()
684         self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
685         self.db.config.MAIL_CHARSET = 'iso-8859-1'
686         self._handle_mail(self.multipart_msg_latin1)
687         messages = self.db.issue.get('1', 'messages')
688         messages.sort()
689         msg = self.db.msg.getnode (messages[-1])
690         assert(len(msg.files) == 1)
691         name = 'unnamed'
692         content = '<html>' + c + '</html>\n'
693         for n, id in enumerate (msg.files):
694             f = self.db.file.getnode (id)
695             self.assertEqual(f.name, name)
696             self.assertEqual(f.content, content)
697         self.assertEqual(msg.content, c)
698         self.compareMessages(self._get_mail(),
699 '''FROM: roundup-admin@your.tracker.email.domain.example
700 TO: chef@bork.bork.bork, richard@test.test
701 Content-Type: text/plain; charset="iso-8859-1"
702 Subject: [issue1] Testing...
703 To: chef@bork.bork.bork, richard@test.test
704 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
705 Reply-To: Roundup issue tracker
706  <issue_tracker@your.tracker.email.domain.example>
707 MIME-Version: 1.0
708 Message-Id: <followup_dummy_id>
709 In-Reply-To: <dummy_test_message_id>
710 X-Roundup-Name: Roundup issue tracker
711 X-Roundup-Loop: hello
712 X-Roundup-Issue-Status: chatting
713 X-Roundup-Issue-Files: unnamed
714 Content-Transfer-Encoding: quoted-printable
717 Contrary, Mary <mary@test.test> added the comment:
719 umlaut =E4=F6=FC=C4=D6=DC=DF
720 File 'unnamed' not attached - you can download it from http://tracker.examp=
721 le/cgi-bin/roundup.cgi/bugs/file1.
723 ----------
724 status: unread -> chatting
726 _______________________________________________________________________
727 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
728 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
729 _______________________________________________________________________
730 ''')
732     def testMultipartCharsetUTF8AttachFile(self):
733         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
734         self.doNewIssue()
735         self._handle_mail(self.multipart_msg_latin1)
736         messages = self.db.issue.get('1', 'messages')
737         messages.sort()
738         msg = self.db.msg.getnode (messages[-1])
739         assert(len(msg.files) == 1)
740         name = 'unnamed'
741         content = '<html>' + c + '</html>\n'
742         for n, id in enumerate (msg.files):
743             f = self.db.file.getnode (id)
744             self.assertEqual(f.name, name)
745             self.assertEqual(f.content, content)
746         self.assertEqual(msg.content, c)
747         self.compareMessages(self._get_mail(),
748 '''FROM: roundup-admin@your.tracker.email.domain.example
749 TO: chef@bork.bork.bork, richard@test.test
750 Content-Type: multipart/mixed; boundary="utf-8"
751 Subject: [issue1] Testing...
752 To: chef@bork.bork.bork, richard@test.test
753 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
754 Reply-To: Roundup issue tracker
755  <issue_tracker@your.tracker.email.domain.example>
756 MIME-Version: 1.0
757 Message-Id: <followup_dummy_id>
758 In-Reply-To: <dummy_test_message_id>
759 X-Roundup-Name: Roundup issue tracker
760 X-Roundup-Loop: hello
761 X-Roundup-Issue-Status: chatting
762 X-Roundup-Issue-Files: unnamed
763 Content-Transfer-Encoding: quoted-printable
766 --utf-8
767 MIME-Version: 1.0
768 Content-Type: text/plain; charset="utf-8"
769 Content-Transfer-Encoding: quoted-printable
772 Contrary, Mary <mary@test.test> added the comment:
774 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
776 ----------
777 status: unread -> chatting
779 _______________________________________________________________________
780 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
781 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
782 _______________________________________________________________________
783 --utf-8
784 Content-Type: text/html
785 MIME-Version: 1.0
786 Content-Transfer-Encoding: base64
787 Content-Disposition: attachment;
788  filename="unnamed"
790 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
792 --utf-8--
793 ''')
795     def testMultipartCharsetLatin1AttachFile(self):
796         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
797         self.doNewIssue()
798         self.db.config.MAIL_CHARSET = 'iso-8859-1'
799         self._handle_mail(self.multipart_msg_latin1)
800         messages = self.db.issue.get('1', 'messages')
801         messages.sort()
802         msg = self.db.msg.getnode (messages[-1])
803         assert(len(msg.files) == 1)
804         name = 'unnamed'
805         content = '<html>' + c + '</html>\n'
806         for n, id in enumerate (msg.files):
807             f = self.db.file.getnode (id)
808             self.assertEqual(f.name, name)
809             self.assertEqual(f.content, content)
810         self.assertEqual(msg.content, c)
811         self.compareMessages(self._get_mail(),
812 '''FROM: roundup-admin@your.tracker.email.domain.example
813 TO: chef@bork.bork.bork, richard@test.test
814 Content-Type: multipart/mixed; boundary="utf-8"
815 Subject: [issue1] Testing...
816 To: chef@bork.bork.bork, richard@test.test
817 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
818 Reply-To: Roundup issue tracker
819  <issue_tracker@your.tracker.email.domain.example>
820 MIME-Version: 1.0
821 Message-Id: <followup_dummy_id>
822 In-Reply-To: <dummy_test_message_id>
823 X-Roundup-Name: Roundup issue tracker
824 X-Roundup-Loop: hello
825 X-Roundup-Issue-Status: chatting
826 X-Roundup-Issue-Files: unnamed
827 Content-Transfer-Encoding: quoted-printable
830 --utf-8
831 MIME-Version: 1.0
832 Content-Type: text/plain; charset="iso-8859-1"
833 Content-Transfer-Encoding: quoted-printable
836 Contrary, Mary <mary@test.test> added the comment:
838 umlaut =E4=F6=FC=C4=D6=DC=DF
840 ----------
841 status: unread -> chatting
843 _______________________________________________________________________
844 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
845 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
846 _______________________________________________________________________
847 --utf-8
848 Content-Type: text/html
849 MIME-Version: 1.0
850 Content-Transfer-Encoding: base64
851 Content-Disposition: attachment;
852  filename="unnamed"
854 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
856 --utf-8--
857 ''')
859     def testMultipartRFC822(self):
860         self.doNewIssue()
861         self._handle_mail(self.multipart_msg_rfc822)
862         messages = self.db.issue.get('1', 'messages')
863         messages.sort()
864         msg = self.db.msg.getnode (messages[-1])
865         assert(len(msg.files) == 1)
866         name = "Fwd: Original email subject.eml"
867         for n, id in enumerate (msg.files):
868             f = self.db.file.getnode (id)
869             self.assertEqual(f.name, name)
870         self.assertEqual(msg.content, 'First part: Text')
871         self.compareMessages(self._get_mail(),
872 '''TO: chef@bork.bork.bork, richard@test.test
873 Content-Type: text/plain; charset="utf-8"
874 Subject: [issue1] Testing...
875 To: chef@bork.bork.bork, richard@test.test
876 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
877 Reply-To: Roundup issue tracker
878  <issue_tracker@your.tracker.email.domain.example>
879 MIME-Version: 1.0
880 Message-Id: <followup_dummy_id>
881 In-Reply-To: <dummy_test_message_id>
882 X-Roundup-Name: Roundup issue tracker
883 X-Roundup-Loop: hello
884 X-Roundup-Issue-Status: chatting
885 X-Roundup-Issue-Files: Fwd: Original email subject.eml
886 Content-Transfer-Encoding: quoted-printable
889 --utf-8
890 MIME-Version: 1.0
891 Content-Type: text/plain; charset="utf-8"
892 Content-Transfer-Encoding: quoted-printable
895 Contrary, Mary <mary@test.test> added the comment:
897 First part: Text
899 ----------
900 status: unread -> chatting
902 _______________________________________________________________________
903 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
904 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
905 _______________________________________________________________________
906 --utf-8
907 Content-Type: message/rfc822
908 MIME-Version: 1.0
909 Content-Disposition: attachment;
910  filename="Fwd: Original email subject.eml"
912 Message-Id: <followup_dummy_id_2>
913 In-Reply-To: <dummy_test_message_id_2>
914 MIME-Version: 1.0
915 Subject: Fwd: Original email subject
916 Date: Mon, 23 Aug 2010 08:23:33 +0200
917 Content-Type: multipart/alternative; boundary="090500050101020406060002"
919 This is a multi-part message in MIME format.
920 --090500050101020406060002
921 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
922 Content-Transfer-Encoding: 7bit
924 some text in inner email
925 ========================
927 --090500050101020406060002
928 Content-Type: text/html; charset=ISO-8859-15
929 Content-Transfer-Encoding: 7bit
931 <html>
932 some text in inner email
933 ========================
934 </html>
936 --090500050101020406060002--
938 --utf-8--
939 ''')
941     def testMultipartRFC822Unpack(self):
942         self.doNewIssue()
943         self.db.config.MAILGW_UNPACK_RFC822 = True
944         self._handle_mail(self.multipart_msg_rfc822)
945         messages = self.db.issue.get('1', 'messages')
946         messages.sort()
947         msg = self.db.msg.getnode (messages[-1])
948         self.assertEqual(len(msg.files), 2)
949         t = 'some text in inner email\n========================\n'
950         content = {0 : t, 1 : '<html>\n' + t + '</html>\n'}
951         for n, id in enumerate (msg.files):
952             f = self.db.file.getnode (id)
953             self.assertEqual(f.name, 'unnamed')
954             if n in content :
955                 self.assertEqual(f.content, content [n])
956         self.assertEqual(msg.content, 'First part: Text')
958     def testSimpleFollowup(self):
959         self.doNewIssue()
960         self._handle_mail('''Content-Type: text/plain;
961   charset="iso-8859-1"
962 From: mary <mary@test.test>
963 To: issue_tracker@your.tracker.email.domain.example
964 Message-Id: <followup_dummy_id>
965 In-Reply-To: <dummy_test_message_id>
966 Subject: [issue1] Testing...
968 This is a second followup
969 ''')
970         self.compareMessages(self._get_mail(),
971 '''FROM: roundup-admin@your.tracker.email.domain.example
972 TO: chef@bork.bork.bork, richard@test.test
973 Content-Type: text/plain; charset="utf-8"
974 Subject: [issue1] Testing...
975 To: chef@bork.bork.bork, richard@test.test
976 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
977 Reply-To: Roundup issue tracker
978  <issue_tracker@your.tracker.email.domain.example>
979 MIME-Version: 1.0
980 Message-Id: <followup_dummy_id>
981 In-Reply-To: <dummy_test_message_id>
982 X-Roundup-Name: Roundup issue tracker
983 X-Roundup-Loop: hello
984 X-Roundup-Issue-Status: chatting
985 Content-Transfer-Encoding: quoted-printable
988 Contrary, Mary <mary@test.test> added the comment:
990 This is a second followup
992 ----------
993 status: unread -> chatting
995 _______________________________________________________________________
996 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
997 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
998 _______________________________________________________________________
999 ''')
1001     def testFollowup(self):
1002         self.doNewIssue()
1004         self._handle_mail('''Content-Type: text/plain;
1005   charset="iso-8859-1"
1006 From: richard <richard@test.test>
1007 To: issue_tracker@your.tracker.email.domain.example
1008 Message-Id: <followup_dummy_id>
1009 In-Reply-To: <dummy_test_message_id>
1010 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1012 This is a followup
1013 ''')
1014         l = self.db.issue.get('1', 'nosy')
1015         l.sort()
1016         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1017             self.john_id])
1019         self.compareMessages(self._get_mail(),
1020 '''FROM: roundup-admin@your.tracker.email.domain.example
1021 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1022 Content-Type: text/plain; charset="utf-8"
1023 Subject: [issue1] Testing...
1024 To: chef@bork.bork.bork, john@test.test, mary@test.test
1025 From: richard <issue_tracker@your.tracker.email.domain.example>
1026 Reply-To: Roundup issue tracker
1027  <issue_tracker@your.tracker.email.domain.example>
1028 MIME-Version: 1.0
1029 Message-Id: <followup_dummy_id>
1030 In-Reply-To: <dummy_test_message_id>
1031 X-Roundup-Name: Roundup issue tracker
1032 X-Roundup-Loop: hello
1033 X-Roundup-Issue-Status: chatting
1034 Content-Transfer-Encoding: quoted-printable
1037 richard <richard@test.test> added the comment:
1039 This is a followup
1041 ----------
1042 assignedto:  -> mary
1043 nosy: +john, mary
1044 status: unread -> chatting
1046 _______________________________________________________________________
1047 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1048 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1049 _______________________________________________________________________
1050 ''')
1052     def testFollowupNoSubjectChange(self):
1053         self.db.config.MAILGW_SUBJECT_UPDATES_TITLE = 'no'
1054         self.doNewIssue()
1056         self._handle_mail('''Content-Type: text/plain;
1057   charset="iso-8859-1"
1058 From: richard <richard@test.test>
1059 To: issue_tracker@your.tracker.email.domain.example
1060 Message-Id: <followup_dummy_id>
1061 In-Reply-To: <dummy_test_message_id>
1062 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john]
1064 This is a followup
1065 ''')
1066         l = self.db.issue.get('1', 'nosy')
1067         l.sort()
1068         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1069             self.john_id])
1071         self.compareMessages(self._get_mail(),
1072 '''FROM: roundup-admin@your.tracker.email.domain.example
1073 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1074 Content-Type: text/plain; charset="utf-8"
1075 Subject: [issue1] Testing...
1076 To: chef@bork.bork.bork, john@test.test, mary@test.test
1077 From: richard <issue_tracker@your.tracker.email.domain.example>
1078 Reply-To: Roundup issue tracker
1079  <issue_tracker@your.tracker.email.domain.example>
1080 MIME-Version: 1.0
1081 Message-Id: <followup_dummy_id>
1082 In-Reply-To: <dummy_test_message_id>
1083 X-Roundup-Name: Roundup issue tracker
1084 X-Roundup-Loop: hello
1085 X-Roundup-Issue-Status: chatting
1086 Content-Transfer-Encoding: quoted-printable
1089 richard <richard@test.test> added the comment:
1091 This is a followup
1093 ----------
1094 assignedto:  -> mary
1095 nosy: +john, mary
1096 status: unread -> chatting
1098 _______________________________________________________________________
1099 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1100 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1101 _______________________________________________________________________
1102 ''')
1103         self.assertEqual(self.db.issue.get('1','title'), 'Testing...')
1105     def testFollowupExplicitSubjectChange(self):
1106         self.doNewIssue()
1108         self._handle_mail('''Content-Type: text/plain;
1109   charset="iso-8859-1"
1110 From: richard <richard@test.test>
1111 To: issue_tracker@your.tracker.email.domain.example
1112 Message-Id: <followup_dummy_id>
1113 In-Reply-To: <dummy_test_message_id>
1114 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john; title=new title]
1116 This is a followup
1117 ''')
1118         l = self.db.issue.get('1', 'nosy')
1119         l.sort()
1120         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1121             self.john_id])
1123         self.compareMessages(self._get_mail(),
1124 '''FROM: roundup-admin@your.tracker.email.domain.example
1125 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1126 Content-Type: text/plain; charset="utf-8"
1127 Subject: [issue1] new title
1128 To: chef@bork.bork.bork, john@test.test, mary@test.test
1129 From: richard <issue_tracker@your.tracker.email.domain.example>
1130 Reply-To: Roundup issue tracker
1131  <issue_tracker@your.tracker.email.domain.example>
1132 MIME-Version: 1.0
1133 Message-Id: <followup_dummy_id>
1134 In-Reply-To: <dummy_test_message_id>
1135 X-Roundup-Name: Roundup issue tracker
1136 X-Roundup-Loop: hello
1137 X-Roundup-Issue-Status: chatting
1138 Content-Transfer-Encoding: quoted-printable
1141 richard <richard@test.test> added the comment:
1143 This is a followup
1145 ----------
1146 assignedto:  -> mary
1147 nosy: +john, mary
1148 status: unread -> chatting
1149 title: Testing... -> new title
1151 _______________________________________________________________________
1152 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1153 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1154 _______________________________________________________________________
1155 ''')
1157     def testNosyGeneration(self):
1158         self.db.issue.create(title='test')
1160         # create a nosy message
1161         msg = self.db.msg.create(content='This is a test',
1162             author=self.richard_id, messageid='<dummy_test_message_id>')
1163         self.db.journaltag = 'richard'
1164         l = self.db.issue.create(title='test', messages=[msg],
1165             nosy=[self.chef_id, self.mary_id, self.john_id])
1167         self.compareMessages(self._get_mail(),
1168 '''FROM: roundup-admin@your.tracker.email.domain.example
1169 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1170 Content-Type: text/plain; charset="utf-8"
1171 Subject: [issue2] test
1172 To: chef@bork.bork.bork, john@test.test, mary@test.test
1173 From: richard <issue_tracker@your.tracker.email.domain.example>
1174 Reply-To: Roundup issue tracker
1175  <issue_tracker@your.tracker.email.domain.example>
1176 MIME-Version: 1.0
1177 Message-Id: <dummy_test_message_id>
1178 X-Roundup-Name: Roundup issue tracker
1179 X-Roundup-Loop: hello
1180 X-Roundup-Issue-Status: unread
1181 Content-Transfer-Encoding: quoted-printable
1184 New submission from richard <richard@test.test>:
1186 This is a test
1188 ----------
1189 messages: 1
1190 nosy: Chef, john, mary, richard
1191 status: unread
1192 title: test
1194 _______________________________________________________________________
1195 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1196 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue2>
1197 _______________________________________________________________________
1198 ''')
1200     def testPropertyChangeOnly(self):
1201         self.doNewIssue()
1202         oldvalues = self.db.getnode('issue', '1').copy()
1203         oldvalues['assignedto'] = None
1204         # reconstruct old behaviour: This would reuse the
1205         # database-handle from the doNewIssue above which has committed
1206         # as user "Chef". So we close and reopen the db as that user.
1207         #self.db.close() actually don't close 'cos this empties memorydb
1208         self.db = self.instance.open('Chef')
1209         self.db.issue.set('1', assignedto=self.chef_id)
1210         self.db.commit()
1211         self.db.issue.nosymessage('1', None, oldvalues)
1213         new_mail = ""
1214         for line in self._get_mail().split("\n"):
1215             if "Message-Id: " in line:
1216                 continue
1217             if "Date: " in line:
1218                 continue
1219             new_mail += line+"\n"
1221         self.compareMessages(new_mail, """
1222 FROM: roundup-admin@your.tracker.email.domain.example
1223 TO: chef@bork.bork.bork, richard@test.test
1224 Content-Type: text/plain; charset="utf-8"
1225 Subject: [issue1] Testing...
1226 To: chef@bork.bork.bork, richard@test.test
1227 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
1228 X-Roundup-Name: Roundup issue tracker
1229 X-Roundup-Loop: hello
1230 X-Roundup-Issue-Status: unread
1231 X-Roundup-Version: 1.3.3
1232 In-Reply-To: <dummy_test_message_id>
1233 MIME-Version: 1.0
1234 Reply-To: Roundup issue tracker
1235  <issue_tracker@your.tracker.email.domain.example>
1236 Content-Transfer-Encoding: quoted-printable
1239 Change by Bork, Chef <chef@bork.bork.bork>:
1242 ----------
1243 assignedto:  -> Chef
1245 _______________________________________________________________________
1246 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1247 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1248 _______________________________________________________________________
1249 """)
1252     #
1253     # FOLLOWUP TITLE MATCH
1254     #
1255     def testFollowupTitleMatch(self):
1256         self.doNewIssue()
1257         self._handle_mail('''Content-Type: text/plain;
1258   charset="iso-8859-1"
1259 From: richard <richard@test.test>
1260 To: issue_tracker@your.tracker.email.domain.example
1261 Message-Id: <followup_dummy_id>
1262 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1264 This is a followup
1265 ''')
1266         self.compareMessages(self._get_mail(),
1267 '''FROM: roundup-admin@your.tracker.email.domain.example
1268 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1269 Content-Type: text/plain; charset="utf-8"
1270 Subject: [issue1] Testing...
1271 To: chef@bork.bork.bork, john@test.test, mary@test.test
1272 From: richard <issue_tracker@your.tracker.email.domain.example>
1273 Reply-To: Roundup issue tracker
1274  <issue_tracker@your.tracker.email.domain.example>
1275 MIME-Version: 1.0
1276 Message-Id: <followup_dummy_id>
1277 In-Reply-To: <dummy_test_message_id>
1278 X-Roundup-Name: Roundup issue tracker
1279 X-Roundup-Loop: hello
1280 X-Roundup-Issue-Status: chatting
1281 Content-Transfer-Encoding: quoted-printable
1284 richard <richard@test.test> added the comment:
1286 This is a followup
1288 ----------
1289 assignedto:  -> mary
1290 nosy: +john, mary
1291 status: unread -> chatting
1293 _______________________________________________________________________
1294 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1295 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1296 _______________________________________________________________________
1297 ''')
1299     def testFollowupTitleMatchMultiRe(self):
1300         nodeid1 = self.doNewIssue()
1301         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1302   charset="iso-8859-1"
1303 From: richard <richard@test.test>
1304 To: issue_tracker@your.tracker.email.domain.example
1305 Message-Id: <followup_dummy_id>
1306 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1308 This is a followup
1309 ''')
1311         nodeid3 = self._handle_mail('''Content-Type: text/plain;
1312   charset="iso-8859-1"
1313 From: richard <richard@test.test>
1314 To: issue_tracker@your.tracker.email.domain.example
1315 Message-Id: <followup2_dummy_id>
1316 Subject: Ang: Re: Testing...
1318 This is a followup
1319 ''')
1320         self.assertEqual(nodeid1, nodeid2)
1321         self.assertEqual(nodeid1, nodeid3)
1323     def testFollowupTitleMatchNever(self):
1324         nodeid = self.doNewIssue()
1325         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
1326         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1327   charset="iso-8859-1"
1328 From: richard <richard@test.test>
1329 To: issue_tracker@your.tracker.email.domain.example
1330 Message-Id: <followup_dummy_id>
1331 Subject: Re: Testing...
1333 This is a followup
1334 '''), nodeid)
1336     def testFollowupTitleMatchNeverInterval(self):
1337         nodeid = self.doNewIssue()
1338         # force failure of the interval
1339         time.sleep(2)
1340         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
1341         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1342   charset="iso-8859-1"
1343 From: richard <richard@test.test>
1344 To: issue_tracker@your.tracker.email.domain.example
1345 Message-Id: <followup_dummy_id>
1346 Subject: Re: Testing...
1348 This is a followup
1349 '''), nodeid)
1352     def testFollowupTitleMatchInterval(self):
1353         nodeid = self.doNewIssue()
1354         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
1355         self.assertEqual(self._handle_mail('''Content-Type: text/plain;
1356   charset="iso-8859-1"
1357 From: richard <richard@test.test>
1358 To: issue_tracker@your.tracker.email.domain.example
1359 Message-Id: <followup_dummy_id>
1360 Subject: Re: Testing...
1362 This is a followup
1363 '''), nodeid)
1366     def testFollowupNosyAuthor(self):
1367         self.doNewIssue()
1368         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1369         self._handle_mail('''Content-Type: text/plain;
1370   charset="iso-8859-1"
1371 From: john@test.test
1372 To: issue_tracker@your.tracker.email.domain.example
1373 Message-Id: <followup_dummy_id>
1374 In-Reply-To: <dummy_test_message_id>
1375 Subject: [issue1] Testing...
1377 This is a followup
1378 ''')
1380         self.compareMessages(self._get_mail(),
1381 '''FROM: roundup-admin@your.tracker.email.domain.example
1382 TO: chef@bork.bork.bork, richard@test.test
1383 Content-Type: text/plain; charset="utf-8"
1384 Subject: [issue1] Testing...
1385 To: chef@bork.bork.bork, richard@test.test
1386 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1387 Reply-To: Roundup issue tracker
1388  <issue_tracker@your.tracker.email.domain.example>
1389 MIME-Version: 1.0
1390 Message-Id: <followup_dummy_id>
1391 In-Reply-To: <dummy_test_message_id>
1392 X-Roundup-Name: Roundup issue tracker
1393 X-Roundup-Loop: hello
1394 X-Roundup-Issue-Status: chatting
1395 Content-Transfer-Encoding: quoted-printable
1398 John Doe <john@test.test> added the comment:
1400 This is a followup
1402 ----------
1403 nosy: +john
1404 status: unread -> chatting
1406 _______________________________________________________________________
1407 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1408 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1409 _______________________________________________________________________
1411 ''')
1413     def testFollowupNosyRecipients(self):
1414         self.doNewIssue()
1415         self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
1416         self._handle_mail('''Content-Type: text/plain;
1417   charset="iso-8859-1"
1418 From: richard@test.test
1419 To: issue_tracker@your.tracker.email.domain.example
1420 Cc: john@test.test
1421 Message-Id: <followup_dummy_id>
1422 In-Reply-To: <dummy_test_message_id>
1423 Subject: [issue1] Testing...
1425 This is a followup
1426 ''')
1427         self.compareMessages(self._get_mail(),
1428 '''FROM: roundup-admin@your.tracker.email.domain.example
1429 TO: chef@bork.bork.bork
1430 Content-Type: text/plain; charset="utf-8"
1431 Subject: [issue1] Testing...
1432 To: chef@bork.bork.bork
1433 From: richard <issue_tracker@your.tracker.email.domain.example>
1434 Reply-To: Roundup issue tracker
1435  <issue_tracker@your.tracker.email.domain.example>
1436 MIME-Version: 1.0
1437 Message-Id: <followup_dummy_id>
1438 In-Reply-To: <dummy_test_message_id>
1439 X-Roundup-Name: Roundup issue tracker
1440 X-Roundup-Loop: hello
1441 X-Roundup-Issue-Status: chatting
1442 Content-Transfer-Encoding: quoted-printable
1445 richard <richard@test.test> added the comment:
1447 This is a followup
1449 ----------
1450 nosy: +john
1451 status: unread -> chatting
1453 _______________________________________________________________________
1454 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1455 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1456 _______________________________________________________________________
1458 ''')
1460     def testFollowupNosyAuthorAndCopy(self):
1461         self.doNewIssue()
1462         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1463         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
1464         self._handle_mail('''Content-Type: text/plain;
1465   charset="iso-8859-1"
1466 From: john@test.test
1467 To: issue_tracker@your.tracker.email.domain.example
1468 Message-Id: <followup_dummy_id>
1469 In-Reply-To: <dummy_test_message_id>
1470 Subject: [issue1] Testing...
1472 This is a followup
1473 ''')
1474         self.compareMessages(self._get_mail(),
1475 '''FROM: roundup-admin@your.tracker.email.domain.example
1476 TO: chef@bork.bork.bork, john@test.test, richard@test.test
1477 Content-Type: text/plain; charset="utf-8"
1478 Subject: [issue1] Testing...
1479 To: chef@bork.bork.bork, john@test.test, richard@test.test
1480 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1481 Reply-To: Roundup issue tracker
1482  <issue_tracker@your.tracker.email.domain.example>
1483 MIME-Version: 1.0
1484 Message-Id: <followup_dummy_id>
1485 In-Reply-To: <dummy_test_message_id>
1486 X-Roundup-Name: Roundup issue tracker
1487 X-Roundup-Loop: hello
1488 X-Roundup-Issue-Status: chatting
1489 Content-Transfer-Encoding: quoted-printable
1492 John Doe <john@test.test> added the comment:
1494 This is a followup
1496 ----------
1497 nosy: +john
1498 status: unread -> chatting
1500 _______________________________________________________________________
1501 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1502 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1503 _______________________________________________________________________
1505 ''')
1507     def testFollowupNoNosyAuthor(self):
1508         self.doNewIssue()
1509         self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
1510         self._handle_mail('''Content-Type: text/plain;
1511   charset="iso-8859-1"
1512 From: john@test.test
1513 To: issue_tracker@your.tracker.email.domain.example
1514 Message-Id: <followup_dummy_id>
1515 In-Reply-To: <dummy_test_message_id>
1516 Subject: [issue1] Testing...
1518 This is a followup
1519 ''')
1520         self.compareMessages(self._get_mail(),
1521 '''FROM: roundup-admin@your.tracker.email.domain.example
1522 TO: chef@bork.bork.bork, richard@test.test
1523 Content-Type: text/plain; charset="utf-8"
1524 Subject: [issue1] Testing...
1525 To: chef@bork.bork.bork, richard@test.test
1526 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1527 Reply-To: Roundup issue tracker
1528  <issue_tracker@your.tracker.email.domain.example>
1529 MIME-Version: 1.0
1530 Message-Id: <followup_dummy_id>
1531 In-Reply-To: <dummy_test_message_id>
1532 X-Roundup-Name: Roundup issue tracker
1533 X-Roundup-Loop: hello
1534 X-Roundup-Issue-Status: chatting
1535 Content-Transfer-Encoding: quoted-printable
1538 John Doe <john@test.test> added the comment:
1540 This is a followup
1542 ----------
1543 status: unread -> chatting
1545 _______________________________________________________________________
1546 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1547 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1548 _______________________________________________________________________
1550 ''')
1552     def testFollowupNoNosyRecipients(self):
1553         self.doNewIssue()
1554         self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
1555         self._handle_mail('''Content-Type: text/plain;
1556   charset="iso-8859-1"
1557 From: richard@test.test
1558 To: issue_tracker@your.tracker.email.domain.example
1559 Cc: john@test.test
1560 Message-Id: <followup_dummy_id>
1561 In-Reply-To: <dummy_test_message_id>
1562 Subject: [issue1] Testing...
1564 This is a followup
1565 ''')
1566         self.compareMessages(self._get_mail(),
1567 '''FROM: roundup-admin@your.tracker.email.domain.example
1568 TO: chef@bork.bork.bork
1569 Content-Type: text/plain; charset="utf-8"
1570 Subject: [issue1] Testing...
1571 To: chef@bork.bork.bork
1572 From: richard <issue_tracker@your.tracker.email.domain.example>
1573 Reply-To: Roundup issue tracker
1574  <issue_tracker@your.tracker.email.domain.example>
1575 MIME-Version: 1.0
1576 Message-Id: <followup_dummy_id>
1577 In-Reply-To: <dummy_test_message_id>
1578 X-Roundup-Name: Roundup issue tracker
1579 X-Roundup-Loop: hello
1580 X-Roundup-Issue-Status: chatting
1581 Content-Transfer-Encoding: quoted-printable
1584 richard <richard@test.test> added the comment:
1586 This is a followup
1588 ----------
1589 status: unread -> chatting
1591 _______________________________________________________________________
1592 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1593 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1594 _______________________________________________________________________
1596 ''')
1598     def testFollowupEmptyMessage(self):
1599         self.doNewIssue()
1601         self._handle_mail('''Content-Type: text/plain;
1602   charset="iso-8859-1"
1603 From: richard <richard@test.test>
1604 To: issue_tracker@your.tracker.email.domain.example
1605 Message-Id: <followup_dummy_id>
1606 In-Reply-To: <dummy_test_message_id>
1607 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1609 ''')
1610         l = self.db.issue.get('1', 'nosy')
1611         l.sort()
1612         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1613             self.john_id])
1615         # should be no file created (ie. no message)
1616         assert not os.path.exists(SENDMAILDEBUG)
1618     def testFollowupEmptyMessageNoSubject(self):
1619         self.doNewIssue()
1621         self._handle_mail('''Content-Type: text/plain;
1622   charset="iso-8859-1"
1623 From: richard <richard@test.test>
1624 To: issue_tracker@your.tracker.email.domain.example
1625 Message-Id: <followup_dummy_id>
1626 In-Reply-To: <dummy_test_message_id>
1627 Subject: [issue1] [assignedto=mary; nosy=+john]
1629 ''')
1630         l = self.db.issue.get('1', 'nosy')
1631         l.sort()
1632         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1633             self.john_id])
1635         # should be no file created (ie. no message)
1636         assert not os.path.exists(SENDMAILDEBUG)
1638     def testNosyRemove(self):
1639         self.doNewIssue()
1641         self._handle_mail('''Content-Type: text/plain;
1642   charset="iso-8859-1"
1643 From: richard <richard@test.test>
1644 To: issue_tracker@your.tracker.email.domain.example
1645 Message-Id: <followup_dummy_id>
1646 In-Reply-To: <dummy_test_message_id>
1647 Subject: [issue1] Testing... [nosy=-richard]
1649 ''')
1650         l = self.db.issue.get('1', 'nosy')
1651         l.sort()
1652         self.assertEqual(l, [self.chef_id])
1654         # NO NOSY MESSAGE SHOULD BE SENT!
1655         assert not os.path.exists(SENDMAILDEBUG)
1657     def testNewUserAuthor(self):
1658         self.db.commit()
1659         l = self.db.user.list()
1660         l.sort()
1661         message = '''Content-Type: text/plain;
1662   charset="iso-8859-1"
1663 From: fubar <fubar@bork.bork.bork>
1664 To: issue_tracker@your.tracker.email.domain.example
1665 Message-Id: <dummy_test_message_id>
1666 Subject: [issue] Testing...
1668 This is a test submission of a new issue.
1669 '''
1670         self.db.security.role['anonymous'].permissions=[]
1671         anonid = self.db.user.lookup('anonymous')
1672         self.db.user.set(anonid, roles='Anonymous')
1673         try:
1674             self._handle_mail(message)
1675         except Unauthorized, value:
1676             body_diff = self.compareMessages(str(value), """
1677 You are not a registered user.
1679 Unknown address: fubar@bork.bork.bork
1680 """)
1681             assert not body_diff, body_diff
1682         else:
1683             raise AssertionError, "Unathorized not raised when handling mail"
1685         # Add Web Access role to anonymous, and try again to make sure
1686         # we get a "please register at:" message this time.
1687         p = [
1688             self.db.security.getPermission('Register', 'user'),
1689             self.db.security.getPermission('Web Access', None),
1690         ]
1691         self.db.security.role['anonymous'].permissions=p
1692         try:
1693             self._handle_mail(message)
1694         except Unauthorized, value:
1695             body_diff = self.compareMessages(str(value), """
1696 You are not a registered user. Please register at:
1698 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1700 ...before sending mail to the tracker.
1702 Unknown address: fubar@bork.bork.bork
1703 """)
1704             assert not body_diff, body_diff
1705         else:
1706             raise AssertionError, "Unathorized not raised when handling mail"
1708         # Make sure list of users is the same as before.
1709         m = self.db.user.list()
1710         m.sort()
1711         self.assertEqual(l, m)
1713         # now with the permission
1714         p = [
1715             self.db.security.getPermission('Register', 'user'),
1716             self.db.security.getPermission('Email Access', None),
1717         ]
1718         self.db.security.role['anonymous'].permissions=p
1719         self._handle_mail(message)
1720         m = self.db.user.list()
1721         m.sort()
1722         self.assertNotEqual(l, m)
1724     def testNewUserAuthorEncodedName(self):
1725         l = set(self.db.user.list())
1726         # From: name has Euro symbol in it
1727         message = '''Content-Type: text/plain;
1728   charset="iso-8859-1"
1729 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1730 To: issue_tracker@your.tracker.email.domain.example
1731 Message-Id: <dummy_test_message_id>
1732 Subject: [issue] Testing...
1734 This is a test submission of a new issue.
1735 '''
1736         p = [
1737             self.db.security.getPermission('Register', 'user'),
1738             self.db.security.getPermission('Email Access', None),
1739             self.db.security.getPermission('Create', 'issue'),
1740             self.db.security.getPermission('Create', 'msg'),
1741         ]
1742         self.db.security.role['anonymous'].permissions = p
1743         self._handle_mail(message)
1744         m = set(self.db.user.list())
1745         new = list(m - l)[0]
1746         name = self.db.user.get(new, 'realname')
1747         self.assertEquals(name, 'H€llo')
1749     def testUnknownUser(self):
1750         l = set(self.db.user.list())
1751         message = '''Content-Type: text/plain;
1752   charset="iso-8859-1"
1753 From: Nonexisting User <nonexisting@bork.bork.bork>
1754 To: issue_tracker@your.tracker.email.domain.example
1755 Message-Id: <dummy_test_message_id>
1756 Subject: [issue] Testing nonexisting user...
1758 This is a test submission of a new issue.
1759 '''
1760         handler = self._create_mailgw(message)
1761         # we want a bounce message:
1762         handler.trapExceptions = 1
1763         ret = handler.main(StringIO(message))
1764         self.compareMessages(self._get_mail(),
1765 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1766 TO: nonexisting@bork.bork.bork
1767 From nobody Tue Jul 14 12:04:11 2009
1768 Content-Type: multipart/mixed; boundary="===============0639262320=="
1769 MIME-Version: 1.0
1770 Subject: Failed issue tracker submission
1771 To: nonexisting@bork.bork.bork
1772 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1773 Date: Tue, 14 Jul 2009 12:04:11 +0000
1774 Precedence: bulk
1775 X-Roundup-Name: Roundup issue tracker
1776 X-Roundup-Loop: hello
1777 X-Roundup-Version: 1.4.8
1778 MIME-Version: 1.0
1780 --===============0639262320==
1781 Content-Type: text/plain; charset="us-ascii"
1782 MIME-Version: 1.0
1783 Content-Transfer-Encoding: 7bit
1787 You are not a registered user. Please register at:
1789 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1791 ...before sending mail to the tracker.
1793 Unknown address: nonexisting@bork.bork.bork
1795 --===============0639262320==
1796 Content-Type: text/plain; charset="us-ascii"
1797 MIME-Version: 1.0
1798 Content-Transfer-Encoding: 7bit
1800 Content-Type: text/plain;
1801   charset="iso-8859-1"
1802 From: Nonexisting User <nonexisting@bork.bork.bork>
1803 To: issue_tracker@your.tracker.email.domain.example
1804 Message-Id: <dummy_test_message_id>
1805 Subject: [issue] Testing nonexisting user...
1807 This is a test submission of a new issue.
1809 --===============0639262320==--
1810 ''')
1812     def testEnc01(self):
1813         self.db.user.set(self.mary_id,
1814             realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode
1815             ('latin-1').encode('utf-8'))
1816         self.doNewIssue()
1817         self._handle_mail('''Content-Type: text/plain;
1818   charset="iso-8859-1"
1819 From: mary <mary@test.test>
1820 To: issue_tracker@your.tracker.email.domain.example
1821 Message-Id: <followup_dummy_id>
1822 In-Reply-To: <dummy_test_message_id>
1823 Subject: [issue1] Testing...
1824 Content-Type: text/plain;
1825         charset="iso-8859-1"
1826 Content-Transfer-Encoding: quoted-printable
1828 A message with encoding (encoded oe =F6)
1830 ''')
1831         self.compareMessages(self._get_mail(),
1832 '''FROM: roundup-admin@your.tracker.email.domain.example
1833 TO: chef@bork.bork.bork, richard@test.test
1834 Content-Type: text/plain; charset="utf-8"
1835 Subject: [issue1] Testing...
1836 To: chef@bork.bork.bork, richard@test.test
1837 From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?=
1838  <issue_tracker@your.tracker.email.domain.example>
1839 Reply-To: Roundup issue tracker
1840  <issue_tracker@your.tracker.email.domain.example>
1841 MIME-Version: 1.0
1842 Message-Id: <followup_dummy_id>
1843 In-Reply-To: <dummy_test_message_id>
1844 X-Roundup-Name: Roundup issue tracker
1845 X-Roundup-Loop: hello
1846 X-Roundup-Issue-Status: chatting
1847 Content-Transfer-Encoding: quoted-printable
1850 =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary <mary@test.test> added the=
1851  comment:
1853 A message with encoding (encoded oe =C3=B6)
1855 ----------
1856 status: unread -> chatting
1858 _______________________________________________________________________
1859 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1860 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1861 _______________________________________________________________________
1862 ''')
1864     def testEncNonUTF8(self):
1865         self.doNewIssue()
1866         self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1867         self._handle_mail('''Content-Type: text/plain;
1868   charset="iso-8859-1"
1869 From: mary <mary@test.test>
1870 To: issue_tracker@your.tracker.email.domain.example
1871 Message-Id: <followup_dummy_id>
1872 In-Reply-To: <dummy_test_message_id>
1873 Subject: [issue1] Testing...
1874 Content-Type: text/plain;
1875         charset="iso-8859-1"
1876 Content-Transfer-Encoding: quoted-printable
1878 A message with encoding (encoded oe =F6)
1880 ''')
1881         self.compareMessages(self._get_mail(),
1882 '''FROM: roundup-admin@your.tracker.email.domain.example
1883 TO: chef@bork.bork.bork, richard@test.test
1884 Content-Type: text/plain; charset="iso-8859-1"
1885 Subject: [issue1] Testing...
1886 To: chef@bork.bork.bork, richard@test.test
1887 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1888 Reply-To: Roundup issue tracker
1889  <issue_tracker@your.tracker.email.domain.example>
1890 MIME-Version: 1.0
1891 Message-Id: <followup_dummy_id>
1892 In-Reply-To: <dummy_test_message_id>
1893 X-Roundup-Name: Roundup issue tracker
1894 X-Roundup-Loop: hello
1895 X-Roundup-Issue-Status: chatting
1896 Content-Transfer-Encoding: quoted-printable
1899 Contrary, Mary <mary@test.test> added the comment:
1901 A message with encoding (encoded oe =F6)
1903 ----------
1904 status: unread -> chatting
1906 _______________________________________________________________________
1907 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1908 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1909 _______________________________________________________________________
1910 ''')
1913     def testMultipartEnc01(self):
1914         self.doNewIssue()
1915         self._handle_mail('''Content-Type: text/plain;
1916   charset="iso-8859-1"
1917 From: mary <mary@test.test>
1918 To: issue_tracker@your.tracker.email.domain.example
1919 Message-Id: <followup_dummy_id>
1920 In-Reply-To: <dummy_test_message_id>
1921 Subject: [issue1] Testing...
1922 Content-Type: multipart/mixed;
1923         boundary="----_=_NextPart_000_01"
1925 This message is in MIME format. Since your mail reader does not understand
1926 this format, some or all of this message may not be legible.
1928 ------_=_NextPart_000_01
1929 Content-Type: text/plain;
1930         charset="iso-8859-1"
1931 Content-Transfer-Encoding: quoted-printable
1933 A message with first part encoded (encoded oe =F6)
1935 ''')
1936         self.compareMessages(self._get_mail(),
1937 '''FROM: roundup-admin@your.tracker.email.domain.example
1938 TO: chef@bork.bork.bork, richard@test.test
1939 Content-Type: text/plain; charset="utf-8"
1940 Subject: [issue1] Testing...
1941 To: chef@bork.bork.bork, richard@test.test
1942 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1943 Reply-To: Roundup issue tracker
1944  <issue_tracker@your.tracker.email.domain.example>
1945 MIME-Version: 1.0
1946 Message-Id: <followup_dummy_id>
1947 In-Reply-To: <dummy_test_message_id>
1948 X-Roundup-Name: Roundup issue tracker
1949 X-Roundup-Loop: hello
1950 X-Roundup-Issue-Status: chatting
1951 Content-Transfer-Encoding: quoted-printable
1954 Contrary, Mary <mary@test.test> added the comment:
1956 A message with first part encoded (encoded oe =C3=B6)
1958 ----------
1959 status: unread -> chatting
1961 _______________________________________________________________________
1962 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1963 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1964 _______________________________________________________________________
1965 ''')
1967     def testContentDisposition(self):
1968         self.doNewIssue()
1969         self._handle_mail('''Content-Type: text/plain;
1970   charset="iso-8859-1"
1971 From: mary <mary@test.test>
1972 To: issue_tracker@your.tracker.email.domain.example
1973 Message-Id: <followup_dummy_id>
1974 In-Reply-To: <dummy_test_message_id>
1975 Subject: [issue1] Testing...
1976 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
1977 Content-Disposition: inline
1980 --bCsyhTFzCvuiizWE
1981 Content-Type: text/plain; charset=us-ascii
1982 Content-Disposition: inline
1984 test attachment binary
1986 --bCsyhTFzCvuiizWE
1987 Content-Type: application/octet-stream
1988 Content-Disposition: attachment; filename="main.dvi"
1989 Content-Transfer-Encoding: base64
1991 SnVzdCBhIHRlc3QgAQo=
1993 --bCsyhTFzCvuiizWE--
1994 ''')
1995         messages = self.db.issue.get('1', 'messages')
1996         messages.sort()
1997         file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
1998         self.assertEqual(file.name, 'main.dvi')
1999         self.assertEqual(file.content, 'Just a test \001\n')
2001     def testFollowupStupidQuoting(self):
2002         self.doNewIssue()
2004         self._handle_mail('''Content-Type: text/plain;
2005   charset="iso-8859-1"
2006 From: richard <richard@test.test>
2007 To: issue_tracker@your.tracker.email.domain.example
2008 Message-Id: <followup_dummy_id>
2009 In-Reply-To: <dummy_test_message_id>
2010 Subject: Re: "[issue1] Testing... "
2012 This is a followup
2013 ''')
2014         self.compareMessages(self._get_mail(),
2015 '''FROM: roundup-admin@your.tracker.email.domain.example
2016 TO: chef@bork.bork.bork
2017 Content-Type: text/plain; charset="utf-8"
2018 Subject: [issue1] Testing...
2019 To: chef@bork.bork.bork
2020 From: richard <issue_tracker@your.tracker.email.domain.example>
2021 Reply-To: Roundup issue tracker
2022  <issue_tracker@your.tracker.email.domain.example>
2023 MIME-Version: 1.0
2024 Message-Id: <followup_dummy_id>
2025 In-Reply-To: <dummy_test_message_id>
2026 X-Roundup-Name: Roundup issue tracker
2027 X-Roundup-Loop: hello
2028 X-Roundup-Issue-Status: chatting
2029 Content-Transfer-Encoding: quoted-printable
2032 richard <richard@test.test> added the comment:
2034 This is a followup
2036 ----------
2037 status: unread -> chatting
2039 _______________________________________________________________________
2040 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2041 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2042 _______________________________________________________________________
2043 ''')
2045     def testEmailQuoting(self):
2046         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
2047         self.innerTestQuoting('''This is a followup
2048 ''')
2050     def testEmailQuotingRemove(self):
2051         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
2052         self.innerTestQuoting('''Blah blah wrote:
2053 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2054 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2057 This is a followup
2058 ''')
2060     def innerTestQuoting(self, expect):
2061         nodeid = self.doNewIssue()
2063         messages = self.db.issue.get(nodeid, 'messages')
2065         self._handle_mail('''Content-Type: text/plain;
2066   charset="iso-8859-1"
2067 From: richard <richard@test.test>
2068 To: issue_tracker@your.tracker.email.domain.example
2069 Message-Id: <followup_dummy_id>
2070 In-Reply-To: <dummy_test_message_id>
2071 Subject: Re: [issue1] Testing...
2073 Blah blah wrote:
2074 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2075 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2078 This is a followup
2079 ''')
2080         # figure the new message id
2081         newmessages = self.db.issue.get(nodeid, 'messages')
2082         for msg in messages:
2083             newmessages.remove(msg)
2084         messageid = newmessages[0]
2086         self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
2088     def testUserLookup(self):
2089         i = self.db.user.create(username='user1', address='user1@foo.com')
2090         self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
2091         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
2092         i = self.db.user.create(username='user2', address='USER2@foo.com')
2093         self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
2094         self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
2096     def testUserAlternateLookup(self):
2097         i = self.db.user.create(username='user1', address='user1@foo.com',
2098                                 alternate_addresses='user1@bar.com')
2099         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
2100         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
2102     def testUserCreate(self):
2103         i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
2104         self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
2106     def testRFC2822(self):
2107         ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
2108         unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
2109         unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
2110         self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
2111         self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
2113     def testRegistrationConfirmation(self):
2114         otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
2115         self.db.getOTKManager().set(otk, username='johannes')
2116         self._handle_mail('''Content-Type: text/plain;
2117   charset="iso-8859-1"
2118 From: Chef <chef@bork.bork.bork>
2119 To: issue_tracker@your.tracker.email.domain.example
2120 Cc: richard@test.test
2121 Message-Id: <dummy_test_message_id>
2122 Subject: Re: Complete your registration to Roundup issue tracker
2123  -- key %s
2125 This is a test confirmation of registration.
2126 ''' % otk)
2127         self.db.user.lookup('johannes')
2129     def testFollowupOnNonIssue(self):
2130         self.db.keyword.create(name='Foo')
2131         self._handle_mail('''Content-Type: text/plain;
2132   charset="iso-8859-1"
2133 From: richard <richard@test.test>
2134 To: issue_tracker@your.tracker.email.domain.example
2135 Message-Id: <followup_dummy_id>
2136 In-Reply-To: <dummy_test_message_id>
2137 Subject: [keyword1] Testing... [name=Bar]
2139 ''')
2140         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2142     def testResentFrom(self):
2143         nodeid = self._handle_mail('''Content-Type: text/plain;
2144   charset="iso-8859-1"
2145 From: Chef <chef@bork.bork.bork>
2146 Resent-From: mary <mary@test.test>
2147 To: issue_tracker@your.tracker.email.domain.example
2148 Cc: richard@test.test
2149 Message-Id: <dummy_test_message_id>
2150 Subject: [issue] Testing...
2152 This is a test submission of a new issue.
2153 ''')
2154         assert not os.path.exists(SENDMAILDEBUG)
2155         l = self.db.issue.get(nodeid, 'nosy')
2156         l.sort()
2157         self.assertEqual(l, [self.richard_id, self.mary_id])
2158         return nodeid
2160     def testDejaVu(self):
2161         self.assertRaises(IgnoreLoop, self._handle_mail,
2162             '''Content-Type: text/plain;
2163   charset="iso-8859-1"
2164 From: Chef <chef@bork.bork.bork>
2165 X-Roundup-Loop: hello
2166 To: issue_tracker@your.tracker.email.domain.example
2167 Cc: richard@test.test
2168 Message-Id: <dummy_test_message_id>
2169 Subject: Re: [issue] Testing...
2171 Hi, I've been mis-configured to loop messages back to myself.
2172 ''')
2174     def testItsBulkStupid(self):
2175         self.assertRaises(IgnoreBulk, self._handle_mail,
2176             '''Content-Type: text/plain;
2177   charset="iso-8859-1"
2178 From: Chef <chef@bork.bork.bork>
2179 Precedence: bulk
2180 To: issue_tracker@your.tracker.email.domain.example
2181 Cc: richard@test.test
2182 Message-Id: <dummy_test_message_id>
2183 Subject: Re: [issue] Testing...
2185 Hi, I'm on holidays, and this is a dumb auto-responder.
2186 ''')
2188     def testAutoReplyEmailsAreIgnored(self):
2189         self.assertRaises(IgnoreBulk, self._handle_mail,
2190             '''Content-Type: text/plain;
2191   charset="iso-8859-1"
2192 From: Chef <chef@bork.bork.bork>
2193 To: issue_tracker@your.tracker.email.domain.example
2194 Cc: richard@test.test
2195 Message-Id: <dummy_test_message_id>
2196 Subject: Re: [issue] Out of office AutoReply: Back next week
2198 Hi, I am back in the office next week
2199 ''')
2201     def testNoSubject(self):
2202         self.assertRaises(MailUsageError, self._handle_mail,
2203             '''Content-Type: text/plain;
2204   charset="iso-8859-1"
2205 From: Chef <chef@bork.bork.bork>
2206 To: issue_tracker@your.tracker.email.domain.example
2207 Cc: richard@test.test
2208 Reply-To: chef@bork.bork.bork
2209 Message-Id: <dummy_test_message_id>
2211 ''')
2213     #
2214     # TEST FOR INVALID DESIGNATOR HANDLING
2215     #
2216     def testInvalidDesignator(self):
2217         self.assertRaises(MailUsageError, self._handle_mail,
2218             '''Content-Type: text/plain;
2219   charset="iso-8859-1"
2220 From: Chef <chef@bork.bork.bork>
2221 To: issue_tracker@your.tracker.email.domain.example
2222 Subject: [frobulated] testing
2223 Cc: richard@test.test
2224 Reply-To: chef@bork.bork.bork
2225 Message-Id: <dummy_test_message_id>
2227 ''')
2228         self.assertRaises(MailUsageError, self._handle_mail,
2229             '''Content-Type: text/plain;
2230   charset="iso-8859-1"
2231 From: Chef <chef@bork.bork.bork>
2232 To: issue_tracker@your.tracker.email.domain.example
2233 Subject: [issue12345] testing
2234 Cc: richard@test.test
2235 Reply-To: chef@bork.bork.bork
2236 Message-Id: <dummy_test_message_id>
2238 ''')
2240     def testInvalidClassLoose(self):
2241         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2242         nodeid = self._handle_mail('''Content-Type: text/plain;
2243   charset="iso-8859-1"
2244 From: Chef <chef@bork.bork.bork>
2245 To: issue_tracker@your.tracker.email.domain.example
2246 Subject: [frobulated] testing
2247 Cc: richard@test.test
2248 Reply-To: chef@bork.bork.bork
2249 Message-Id: <dummy_test_message_id>
2251 ''')
2252         assert not os.path.exists(SENDMAILDEBUG)
2253         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2254             '[frobulated] testing')
2256     def testInvalidClassLooseReply(self):
2257         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2258         nodeid = self._handle_mail('''Content-Type: text/plain;
2259   charset="iso-8859-1"
2260 From: Chef <chef@bork.bork.bork>
2261 To: issue_tracker@your.tracker.email.domain.example
2262 Subject: Re: [frobulated] testing
2263 Cc: richard@test.test
2264 Reply-To: chef@bork.bork.bork
2265 Message-Id: <dummy_test_message_id>
2267 ''')
2268         assert not os.path.exists(SENDMAILDEBUG)
2269         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2270             '[frobulated] testing')
2272     def testInvalidClassLoose(self):
2273         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2274         nodeid = self._handle_mail('''Content-Type: text/plain;
2275   charset="iso-8859-1"
2276 From: Chef <chef@bork.bork.bork>
2277 To: issue_tracker@your.tracker.email.domain.example
2278 Subject: [issue1234] testing
2279 Cc: richard@test.test
2280 Reply-To: chef@bork.bork.bork
2281 Message-Id: <dummy_test_message_id>
2283 ''')
2284         assert not os.path.exists(SENDMAILDEBUG)
2285         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2286             '[issue1234] testing')
2288     def testClassLooseOK(self):
2289         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2290         self.db.keyword.create(name='Foo')
2291         nodeid = self._handle_mail('''Content-Type: text/plain;
2292   charset="iso-8859-1"
2293 From: Chef <chef@bork.bork.bork>
2294 To: issue_tracker@your.tracker.email.domain.example
2295 Subject: [keyword1] Testing... [name=Bar]
2296 Cc: richard@test.test
2297 Reply-To: chef@bork.bork.bork
2298 Message-Id: <dummy_test_message_id>
2300 ''')
2301         assert not os.path.exists(SENDMAILDEBUG)
2302         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2304     def testClassStrictInvalid(self):
2305         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2306         self.instance.config.MAILGW_DEFAULT_CLASS = ''
2308         message = '''Content-Type: text/plain;
2309   charset="iso-8859-1"
2310 From: Chef <chef@bork.bork.bork>
2311 To: issue_tracker@your.tracker.email.domain.example
2312 Subject: Testing...
2313 Cc: richard@test.test
2314 Reply-To: chef@bork.bork.bork
2315 Message-Id: <dummy_test_message_id>
2317 '''
2318         self.assertRaises(MailUsageError, self._handle_mail, message)
2320     def testClassStrictValid(self):
2321         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2322         self.instance.config.MAILGW_DEFAULT_CLASS = ''
2324         nodeid = self._handle_mail('''Content-Type: text/plain;
2325   charset="iso-8859-1"
2326 From: Chef <chef@bork.bork.bork>
2327 To: issue_tracker@your.tracker.email.domain.example
2328 Subject: [issue] Testing...
2329 Cc: richard@test.test
2330 Reply-To: chef@bork.bork.bork
2331 Message-Id: <dummy_test_message_id>
2333 ''')
2335         assert not os.path.exists(SENDMAILDEBUG)
2336         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
2338     #
2339     # TEST FOR INVALID COMMANDS HANDLING
2340     #
2341     def testInvalidCommands(self):
2342         self.assertRaises(MailUsageError, self._handle_mail,
2343             '''Content-Type: text/plain;
2344   charset="iso-8859-1"
2345 From: Chef <chef@bork.bork.bork>
2346 To: issue_tracker@your.tracker.email.domain.example
2347 Subject: testing [frobulated]
2348 Cc: richard@test.test
2349 Reply-To: chef@bork.bork.bork
2350 Message-Id: <dummy_test_message_id>
2352 ''')
2354     def testInvalidCommandPassthrough(self):
2355         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
2356         nodeid = self._handle_mail('''Content-Type: text/plain;
2357   charset="iso-8859-1"
2358 From: Chef <chef@bork.bork.bork>
2359 To: issue_tracker@your.tracker.email.domain.example
2360 Subject: testing [frobulated]
2361 Cc: richard@test.test
2362 Reply-To: chef@bork.bork.bork
2363 Message-Id: <dummy_test_message_id>
2365 ''')
2366         assert not os.path.exists(SENDMAILDEBUG)
2367         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2368             'testing [frobulated]')
2370     def testInvalidCommandPassthroughLoose(self):
2371         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2372         nodeid = self._handle_mail('''Content-Type: text/plain;
2373   charset="iso-8859-1"
2374 From: Chef <chef@bork.bork.bork>
2375 To: issue_tracker@your.tracker.email.domain.example
2376 Subject: testing [frobulated]
2377 Cc: richard@test.test
2378 Reply-To: chef@bork.bork.bork
2379 Message-Id: <dummy_test_message_id>
2381 ''')
2382         assert not os.path.exists(SENDMAILDEBUG)
2383         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2384             'testing [frobulated]')
2386     def testInvalidCommandPassthroughLooseOK(self):
2387         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2388         nodeid = self._handle_mail('''Content-Type: text/plain;
2389   charset="iso-8859-1"
2390 From: Chef <chef@bork.bork.bork>
2391 To: issue_tracker@your.tracker.email.domain.example
2392 Subject: testing [assignedto=mary]
2393 Cc: richard@test.test
2394 Reply-To: chef@bork.bork.bork
2395 Message-Id: <dummy_test_message_id>
2397 ''')
2398         assert not os.path.exists(SENDMAILDEBUG)
2399         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2400         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2402     def testCommandDelimiters(self):
2403         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2404         nodeid = self._handle_mail('''Content-Type: text/plain;
2405   charset="iso-8859-1"
2406 From: Chef <chef@bork.bork.bork>
2407 To: issue_tracker@your.tracker.email.domain.example
2408 Subject: testing {assignedto=mary}
2409 Cc: richard@test.test
2410 Reply-To: chef@bork.bork.bork
2411 Message-Id: <dummy_test_message_id>
2413 ''')
2414         assert not os.path.exists(SENDMAILDEBUG)
2415         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2416         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2418     def testPrefixDelimiters(self):
2419         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2420         self.db.keyword.create(name='Foo')
2421         self._handle_mail('''Content-Type: text/plain;
2422   charset="iso-8859-1"
2423 From: richard <richard@test.test>
2424 To: issue_tracker@your.tracker.email.domain.example
2425 Message-Id: <followup_dummy_id>
2426 In-Reply-To: <dummy_test_message_id>
2427 Subject: {keyword1} Testing... {name=Bar}
2429 ''')
2430         assert not os.path.exists(SENDMAILDEBUG)
2431         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2433     def testCommandDelimitersIgnore(self):
2434         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2435         nodeid = self._handle_mail('''Content-Type: text/plain;
2436   charset="iso-8859-1"
2437 From: Chef <chef@bork.bork.bork>
2438 To: issue_tracker@your.tracker.email.domain.example
2439 Subject: testing [assignedto=mary]
2440 Cc: richard@test.test
2441 Reply-To: chef@bork.bork.bork
2442 Message-Id: <dummy_test_message_id>
2444 ''')
2445         assert not os.path.exists(SENDMAILDEBUG)
2446         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2447             'testing [assignedto=mary]')
2448         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
2450     def testReplytoMatch(self):
2451         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2452         nodeid = self.doNewIssue()
2453         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2454   charset="iso-8859-1"
2455 From: Chef <chef@bork.bork.bork>
2456 To: issue_tracker@your.tracker.email.domain.example
2457 Message-Id: <dummy_test_message_id2>
2458 In-Reply-To: <dummy_test_message_id>
2459 Subject: Testing...
2461 Followup message.
2462 ''')
2464         nodeid3 = self._handle_mail('''Content-Type: text/plain;
2465   charset="iso-8859-1"
2466 From: Chef <chef@bork.bork.bork>
2467 To: issue_tracker@your.tracker.email.domain.example
2468 Message-Id: <dummy_test_message_id3>
2469 In-Reply-To: <dummy_test_message_id2>
2470 Subject: Testing...
2472 Yet another message in the same thread/issue.
2473 ''')
2475         self.assertEqual(nodeid, nodeid2)
2476         self.assertEqual(nodeid, nodeid3)
2478     def testHelpSubject(self):
2479         message = '''Content-Type: text/plain;
2480   charset="iso-8859-1"
2481 From: Chef <chef@bork.bork.bork>
2482 To: issue_tracker@your.tracker.email.domain.example
2483 Message-Id: <dummy_test_message_id2>
2484 In-Reply-To: <dummy_test_message_id>
2485 Subject: hElp
2488 '''
2489         self.assertRaises(MailUsageHelp, self._handle_mail, message)
2491     def testMaillistSubject(self):
2492         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
2493         self.db.keyword.create(name='Foo')
2494         self._handle_mail('''Content-Type: text/plain;
2495   charset="iso-8859-1"
2496 From: Chef <chef@bork.bork.bork>
2497 To: issue_tracker@your.tracker.email.domain.example
2498 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
2499 Cc: richard@test.test
2500 Reply-To: chef@bork.bork.bork
2501 Message-Id: <dummy_test_message_id>
2503 ''')
2505         assert not os.path.exists(SENDMAILDEBUG)
2506         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2508     def testUnknownPrefixSubject(self):
2509         self.db.keyword.create(name='Foo')
2510         self._handle_mail('''Content-Type: text/plain;
2511   charset="iso-8859-1"
2512 From: Chef <chef@bork.bork.bork>
2513 To: issue_tracker@your.tracker.email.domain.example
2514 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
2515 Cc: richard@test.test
2516 Reply-To: chef@bork.bork.bork
2517 Message-Id: <dummy_test_message_id>
2519 ''')
2521         assert not os.path.exists(SENDMAILDEBUG)
2522         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2524     def testOneCharSubject(self):
2525         message = '''Content-Type: text/plain;
2526   charset="iso-8859-1"
2527 From: Chef <chef@bork.bork.bork>
2528 To: issue_tracker@your.tracker.email.domain.example
2529 Subject: b
2530 Cc: richard@test.test
2531 Reply-To: chef@bork.bork.bork
2532 Message-Id: <dummy_test_message_id>
2534 '''
2535         try:
2536             self._handle_mail(message)
2537         except MailUsageError:
2538             self.fail('MailUsageError raised')
2540     def testIssueidLast(self):
2541         nodeid1 = self.doNewIssue()
2542         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2543   charset="iso-8859-1"
2544 From: mary <mary@test.test>
2545 To: issue_tracker@your.tracker.email.domain.example
2546 Message-Id: <followup_dummy_id>
2547 In-Reply-To: <dummy_test_message_id>
2548 Subject: New title [issue1]
2550 This is a second followup
2551 ''')
2553         assert nodeid1 == nodeid2
2554         self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
2556     def testSecurityMessagePermissionContent(self):
2557         id = self.doNewIssue()
2558         issue = self.db.issue.getnode (id)
2559         self.db.security.addRole(name='Nomsg')
2560         self.db.security.addPermissionToRole('Nomsg', 'Email Access')
2561         for cl in 'issue', 'file', 'keyword':
2562             for p in 'View', 'Edit', 'Create':
2563                 self.db.security.addPermissionToRole('Nomsg', p, cl)
2564         self.db.user.set(self.mary_id, roles='Nomsg')
2565         nodeid = self._handle_mail('''Content-Type: text/plain;
2566   charset="iso-8859-1"
2567 From: Chef <chef@bork.bork.bork>
2568 To: issue_tracker@your.tracker.email.domain.example
2569 Message-Id: <dummy_test_message_id_2>
2570 Subject: [issue%(id)s] Testing... [nosy=+mary]
2572 Just a test reply
2573 '''%locals())
2574         assert os.path.exists(SENDMAILDEBUG)
2575         self.compareMessages(self._get_mail(),
2576 '''FROM: roundup-admin@your.tracker.email.domain.example
2577 TO: chef@bork.bork.bork, richard@test.test
2578 Content-Type: text/plain; charset="utf-8"
2579 Subject: [issue1] Testing...
2580 To: richard@test.test
2581 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
2582 Reply-To: Roundup issue tracker
2583  <issue_tracker@your.tracker.email.domain.example>
2584 MIME-Version: 1.0
2585 Message-Id: <dummy_test_message_id_2>
2586 In-Reply-To: <dummy_test_message_id>
2587 X-Roundup-Name: Roundup issue tracker
2588 X-Roundup-Loop: hello
2589 X-Roundup-Issue-Status: chatting
2590 Content-Transfer-Encoding: quoted-printable
2593 Bork, Chef <chef@bork.bork.bork> added the comment:
2595 Just a test reply
2597 ----------
2598 nosy: +mary
2599 status: unread -> chatting
2601 _______________________________________________________________________
2602 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2603 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2604 _______________________________________________________________________
2605 ''')
2607     def testOutlookAttachment(self):
2608         message = '''X-MimeOLE: Produced By Microsoft Exchange V6.5
2609 Content-class: urn:content-classes:message
2610 MIME-Version: 1.0
2611 Content-Type: multipart/mixed;
2612         boundary="----_=_NextPart_001_01CACA65.40A51CBC"
2613 Subject: Example of a failed outlook attachment e-mail
2614 Date: Tue, 23 Mar 2010 01:43:44 -0700
2615 Message-ID: <CA37F17219784343816CA6613D2E339205E7D0F9@nrcwstexb1.nrc.ca>
2616 X-MS-Has-Attach: yes
2617 X-MS-TNEF-Correlator: 
2618 Thread-Topic: Example of a failed outlook attachment e-mail
2619 Thread-Index: AcrKJo/t3pUBBwTpSwWNE3LE67UBDQ==
2620 From: "Hugh" <richard@test.test>
2621 To: <richard@test.test>
2622 X-OriginalArrivalTime: 23 Mar 2010 08:45:57.0350 (UTC) FILETIME=[41893860:01CACA65]
2624 This is a multi-part message in MIME format.
2626 ------_=_NextPart_001_01CACA65.40A51CBC
2627 Content-Type: multipart/alternative;
2628         boundary="----_=_NextPart_002_01CACA65.40A51CBC"
2631 ------_=_NextPart_002_01CACA65.40A51CBC
2632 Content-Type: text/plain;
2633         charset="us-ascii"
2634 Content-Transfer-Encoding: quoted-printable
2637 Hi Richard,
2639 I suppose this isn't the exact message that was sent but is a resend of
2640 one of my trial messages that failed.  For your benefit I changed the
2641 subject line and am adding these words to the message body.  Should
2642 still be as problematic, but if you like I can resend an exact copy of a
2643 failed message changing nothing except putting your address instead of
2644 our tracker.
2646 Thanks very much for taking time to look into this.  Much appreciated.
2648  <<battery backup>>=20
2650 ------_=_NextPart_002_01CACA65.40A51CBC
2651 Content-Type: text/html;
2652         charset="us-ascii"
2653 Content-Transfer-Encoding: quoted-printable
2655 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2656 <HTML>
2657 <HEAD>
2658 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2659 charset=3Dus-ascii">
2660 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2661 6.5.7654.12">
2662 <TITLE>Example of a failed outlook attachment e-mail</TITLE>
2663 </HEAD>
2664 <BODY>
2665 <!-- Converted from text/rtf format -->
2666 <BR>
2668 <P><FONT SIZE=3D2 FACE=3D"Arial">Hi Richard,</FONT>
2669 </P>
2671 <P><FONT SIZE=3D2 FACE=3D"Arial">I suppose this isn't the exact message =
2672 that was sent but is a resend of one of my trial messages that =
2673 failed.&nbsp; For your benefit I changed the subject line and am adding =
2674 these words to the message body.&nbsp; Should still be as problematic, =
2675 but if you like I can resend an exact copy of a failed message changing =
2676 nothing except putting your address instead of our tracker.</FONT></P>
2678 <P><FONT SIZE=3D2 FACE=3D"Arial">Thanks very much for taking time to =
2679 look into this.&nbsp; Much appreciated.</FONT>
2680 </P>
2681 <BR>
2683 <P><FONT FACE=3D"Arial" SIZE=3D2 COLOR=3D"#000000"> &lt;&lt;battery =
2684 backup&gt;&gt; </FONT>
2685 </P>
2687 </BODY>
2688 </HTML>
2689 ------_=_NextPart_002_01CACA65.40A51CBC--
2691 ------_=_NextPart_001_01CACA65.40A51CBC
2692 Content-Type: message/rfc822
2693 Content-Transfer-Encoding: 7bit
2695 X-MimeOLE: Produced By Microsoft Exchange V6.5
2696 MIME-Version: 1.0
2697 Content-Type: multipart/alternative;
2698         boundary="----_=_NextPart_003_01CAC15A.29717800"
2699 X-OriginalArrivalTime: 11 Mar 2010 20:33:51.0249 (UTC) FILETIME=[28FEE010:01CAC15A]
2700 Content-class: urn:content-classes:message
2701 Subject: battery backup
2702 Date: Thu, 11 Mar 2010 13:33:43 -0700
2703 Message-ID: <p06240809c7bf02f9624c@[128.114.22.203]>
2704 X-MS-Has-Attach: 
2705 X-MS-TNEF-Correlator: 
2706 Thread-Topic: battery backup
2707 Thread-Index: AcrBWimtulTrSvBdQ2CcfZ8lyQdxmQ==
2708 From: "Jerry" <jerry@test.test>
2709 To: "Hugh" <hugh@test.test>
2711 This is a multi-part message in MIME format.
2713 ------_=_NextPart_003_01CAC15A.29717800
2714 Content-Type: text/plain;
2715         charset="iso-8859-1"
2716 Content-Transfer-Encoding: quoted-printable
2718 Dear Hugh,
2719         A car batter has an energy capacity of ~ 500Wh.  A UPS=20
2720 battery is worse than this.
2722 if we need to provied 100kW for 30 minutes that will take 100 car=20
2723 batteries.  This seems like an awful lot of batteries.
2725 Of course I like your idea of making the time 1 minute, so we get to=20
2726 a more modest number of batteries
2728 Jerry
2731 ------_=_NextPart_003_01CAC15A.29717800
2732 Content-Type: text/html;
2733         charset="iso-8859-1"
2734 Content-Transfer-Encoding: quoted-printable
2736 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2737 <HTML>
2738 <HEAD>
2739 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2740 charset=3Diso-8859-1">
2741 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2742 6.5.7654.12">
2743 <TITLE>battery backup</TITLE>
2744 </HEAD>
2745 <BODY>
2746 <!-- Converted from text/plain format -->
2748 <P><FONT SIZE=3D2>Dear Hugh,</FONT>
2750 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT SIZE=3D2>A car =
2751 batter has an energy capacity of ~ 500Wh.&nbsp; A UPS </FONT>
2753 <BR><FONT SIZE=3D2>battery is worse than this.</FONT>
2754 </P>
2756 <P><FONT SIZE=3D2>if we need to provied 100kW for 30 minutes that will =
2757 take 100 car </FONT>
2759 <BR><FONT SIZE=3D2>batteries.&nbsp; This seems like an awful lot of =
2760 batteries.</FONT>
2761 </P>
2763 <P><FONT SIZE=3D2>Of course I like your idea of making the time 1 =
2764 minute, so we get to </FONT>
2766 <BR><FONT SIZE=3D2>a more modest number of batteries</FONT>
2767 </P>
2769 <P><FONT SIZE=3D2>Jerry</FONT>
2770 </P>
2772 </BODY>
2773 </HTML>
2774 ------_=_NextPart_003_01CAC15A.29717800--
2776 ------_=_NextPart_001_01CACA65.40A51CBC--
2777 '''
2778         nodeid = self._handle_mail(message)
2779         assert not os.path.exists(SENDMAILDEBUG)
2780         msgid = self.db.issue.get(nodeid, 'messages')[0]
2781         self.assert_(self.db.msg.get(msgid, 'content').startswith('Hi Richard'))
2782         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1', '2'])
2783         fileid = self.db.msg.get(msgid, 'files')[0]
2784         self.assertEqual(self.db.file.get(fileid, 'type'), 'text/html')
2785         fileid = self.db.msg.get(msgid, 'files')[1]
2786         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2788     def testForwardedMessageAttachment(self):
2789         message = '''Return-Path: <rgg@test.test>
2790 Received: from localhost(127.0.0.1), claiming to be "[115.130.26.69]"
2791 via SMTP by localhost, id smtpdAAApLaWrq; Tue Apr 13 23:10:05 2010
2792 Message-ID: <4BC4F9C7.50409@test.test>
2793 Date: Wed, 14 Apr 2010 09:09:59 +1000
2794 From: Rupert Goldie <rgg@test.test>
2795 User-Agent: Thunderbird 2.0.0.24 (Windows/20100228)
2796 MIME-Version: 1.0
2797 To: ekit issues <issues@test.test>
2798 Subject: [Fwd: PHP ERROR (fb)] post limit reached
2799 Content-Type: multipart/mixed; boundary="------------000807090608060304010403"
2801 This is a multi-part message in MIME format.
2802 --------------000807090608060304010403
2803 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
2804 Content-Transfer-Encoding: 7bit
2806 Catch this exception and log it without emailing.
2808 --------------000807090608060304010403
2809 Content-Type: message/rfc822; name="PHP ERROR (fb).eml"
2810 Content-Transfer-Encoding: 7bit
2811 Content-Disposition: inline; filename="PHP ERROR (fb).eml"
2813 Return-Path: <ektravj@test.test>
2814 X-Sieve: CMU Sieve 2.2
2815 via SMTP by crown.off.ekorp.com, id smtpdAAA1JaW1o; Tue Apr 13 23:01:04 2010
2816 X-Virus-Scanned: by amavisd-new at ekit.com
2817 To: facebook-errors@test.test
2818 From: ektravj@test.test
2819 Subject: PHP ERROR (fb)
2820 Message-Id: <20100413230100.D601D27E84@mail2.elax3.ekorp.com>
2821 Date: Tue, 13 Apr 2010 23:01:00 +0000 (UTC)
2823 [13-Apr-2010 22:49:02] PHP Fatal error:  Uncaught exception 'Exception' with message 'Facebook Error Message: Feed action request limit reached' in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php:280
2824 Stack trace:
2825 #0 /app/01/www/virtual/fb.ekit.com/htdocs/gateway/ekit/feed/index.php(178): fb_exceptions(Object(FacebookRestClientException))
2826 #1 {main}
2827  thrown in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php on line 280
2830 --------------000807090608060304010403--
2831 '''
2832         nodeid = self._handle_mail(message)
2833         assert not os.path.exists(SENDMAILDEBUG)
2834         msgid = self.db.issue.get(nodeid, 'messages')[0]
2835         self.assertEqual(self.db.msg.get(msgid, 'content'),
2836             'Catch this exception and log it without emailing.')
2837         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1'])
2838         fileid = self.db.msg.get(msgid, 'files')[0]
2839         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2841 def test_suite():
2842     suite = unittest.TestSuite()
2843     suite.addTest(unittest.makeSuite(MailgwTestCase))
2844     return suite
2846 if __name__ == '__main__':
2847     runner = unittest.TextTestRunner()
2848     unittest.main(testRunner=runner)
2850 # vim: set filetype=python sts=4 sw=4 et si :