Code

749a93c0472f493014d60cbaadcd8eab137cdebd
[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.mailgw import MailGW, Unauthorized, uidFromAddress, \
25     parseContent, IgnoreLoop, IgnoreBulk, MailUsageError, MailUsageHelp
26 from roundup import init, instance, password, rfc2822, __version__
27 from roundup.anypy.sets_ import set
29 import db_test_base
31 class Message(rfc822.Message):
32     """String-based Message class with equivalence test."""
33     def __init__(self, s):
34         rfc822.Message.__init__(self, StringIO(s.strip()))
36     def __eq__(self, other):
37         return (self.dict == other.dict and
38                 self.fp.read() == other.fp.read())
40 class DiffHelper:
41     def compareMessages(self, new, old):
42         """Compare messages for semantic equivalence."""
43         new, old = Message(new), Message(old)
45         # all Roundup-generated messages have "Precedence: bulk"
46         old['Precedence'] = 'bulk'
48         # don't try to compare the date
49         del new['date'], old['date']
51         if not new == old:
52             res = []
54             replace = {}
55             for key in new.keys():
56                 if key.startswith('from '):
57                     # skip the unix from line
58                     continue
59                 if key.lower() == 'x-roundup-version':
60                     # version changes constantly, so handle it specially
61                     if new[key] != __version__:
62                         res.append('  %s: %r != %r' % (key, __version__,
63                             new[key]))
64                 elif key.lower() == 'content-type' and 'boundary=' in new[key]:
65                     # handle mime messages
66                     newmime = new[key].split('=',1)[-1].strip('"')
67                     oldmime = old.get(key, '').split('=',1)[-1].strip('"')
68                     replace ['--' + newmime] = '--' + oldmime
69                     replace ['--' + newmime + '--'] = '--' + oldmime + '--'
70                 elif new.get(key, '') != old.get(key, ''):
71                     res.append('  %s: %r != %r' % (key, old.get(key, ''),
72                         new.get(key, '')))
74             body_diff = self.compareStrings(new.fp.read(), old.fp.read(),
75                 replace=replace)
76             if body_diff:
77                 res.append('')
78                 res.extend(body_diff)
80             if res:
81                 res.insert(0, 'Generated message not correct (diff follows):')
82                 raise AssertionError, '\n'.join(res)
84     def compareStrings(self, s2, s1, replace={}):
85         '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
86            the first to be the "original" but in the calls in this file,
87            the second arg is the original. Ho hum.
88            Do replacements over the replace dict -- used for mime boundary
89         '''
90         l1 = s1.strip().split('\n')
91         l2 = [replace.get(i,i) for i in s2.strip().split('\n')]
92         if l1 == l2:
93             return
94         s = difflib.SequenceMatcher(None, l1, l2)
95         res = []
96         for value, s1s, s1e, s2s, s2e in s.get_opcodes():
97             if value == 'equal':
98                 for i in range(s1s, s1e):
99                     res.append('  %s'%l1[i])
100             elif value == 'delete':
101                 for i in range(s1s, s1e):
102                     res.append('- %s'%l1[i])
103             elif value == 'insert':
104                 for i in range(s2s, s2e):
105                     res.append('+ %s'%l2[i])
106             elif value == 'replace':
107                 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
108                     res.append('- %s'%l1[i])
109                     res.append('+ %s'%l2[j])
111         return res
113 class MailgwTestCase(unittest.TestCase, DiffHelper):
114     count = 0
115     schema = 'classic'
116     def setUp(self):
117         MailgwTestCase.count = MailgwTestCase.count + 1
118         self.dirname = '_test_mailgw_%s'%self.count
119         # set up and open a tracker
120         self.instance = db_test_base.setupTracker(self.dirname)
122         # and open the database
123         self.db = self.instance.open('admin')
124         self.chef_id = self.db.user.create(username='Chef',
125             address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
126         self.richard_id = self.db.user.create(username='richard',
127             address='richard@test.test', roles='User')
128         self.mary_id = self.db.user.create(username='mary',
129             address='mary@test.test', roles='User', realname='Contrary, Mary')
130         self.john_id = self.db.user.create(username='john',
131             address='john@test.test', roles='User', realname='John Doe',
132             alternate_addresses='jondoe@test.test\njohn.doe@test.test')
134     def tearDown(self):
135         if os.path.exists(SENDMAILDEBUG):
136             os.remove(SENDMAILDEBUG)
137         self.db.close()
138         try:
139             shutil.rmtree(self.dirname)
140         except OSError, error:
141             if error.errno not in (errno.ENOENT, errno.ESRCH): raise
143     def _handle_mail(self, message):
144         # handler will open a new db handle. On single-threaded
145         # databases we'll have to close our current connection
146         self.db.commit()
147         self.db.close()
148         handler = self.instance.MailGW(self.instance)
149         handler.trapExceptions = 0
150         ret = handler.main(StringIO(message))
151         # handler had its own database, open new connection
152         self.db = self.instance.open('admin')
153         return ret
155     def _get_mail(self):
156         f = open(SENDMAILDEBUG)
157         try:
158             return f.read()
159         finally:
160             f.close()
162     def testEmptyMessage(self):
163         nodeid = self._handle_mail('''Content-Type: text/plain;
164   charset="iso-8859-1"
165 From: Chef <chef@bork.bork.bork>
166 To: issue_tracker@your.tracker.email.domain.example
167 Cc: richard@test.test
168 Reply-To: chef@bork.bork.bork
169 Message-Id: <dummy_test_message_id>
170 Subject: [issue] Testing...
172 ''')
173         assert not os.path.exists(SENDMAILDEBUG)
174         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
176     def doNewIssue(self):
177         nodeid = self._handle_mail('''Content-Type: text/plain;
178   charset="iso-8859-1"
179 From: Chef <chef@bork.bork.bork>
180 To: issue_tracker@your.tracker.email.domain.example
181 Cc: richard@test.test
182 Message-Id: <dummy_test_message_id>
183 Subject: [issue] Testing...
185 This is a test submission of a new issue.
186 ''')
187         assert not os.path.exists(SENDMAILDEBUG)
188         l = self.db.issue.get(nodeid, 'nosy')
189         l.sort()
190         self.assertEqual(l, [self.chef_id, self.richard_id])
191         return nodeid
193     def testNewIssue(self):
194         self.doNewIssue()
196     def testNewIssueNosy(self):
197         self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes'
198         nodeid = self._handle_mail('''Content-Type: text/plain;
199   charset="iso-8859-1"
200 From: Chef <chef@bork.bork.bork>
201 To: issue_tracker@your.tracker.email.domain.example
202 Cc: richard@test.test
203 Message-Id: <dummy_test_message_id>
204 Subject: [issue] Testing...
206 This is a test submission of a new issue.
207 ''')
208         assert not os.path.exists(SENDMAILDEBUG)
209         l = self.db.issue.get(nodeid, 'nosy')
210         l.sort()
211         self.assertEqual(l, [self.chef_id, self.richard_id])
213     def testAlternateAddress(self):
214         self._handle_mail('''Content-Type: text/plain;
215   charset="iso-8859-1"
216 From: John Doe <john.doe@test.test>
217 To: issue_tracker@your.tracker.email.domain.example
218 Message-Id: <dummy_test_message_id>
219 Subject: [issue] Testing...
221 This is a test submission of a new issue.
222 ''')
223         userlist = self.db.user.list()
224         assert not os.path.exists(SENDMAILDEBUG)
225         self.assertEqual(userlist, self.db.user.list(),
226             "user created when it shouldn't have been")
228     def testNewIssueNoClass(self):
229         self._handle_mail('''Content-Type: text/plain;
230   charset="iso-8859-1"
231 From: Chef <chef@bork.bork.bork>
232 To: issue_tracker@your.tracker.email.domain.example
233 Cc: richard@test.test
234 Message-Id: <dummy_test_message_id>
235 Subject: Testing...
237 This is a test submission of a new issue.
238 ''')
239         assert not os.path.exists(SENDMAILDEBUG)
241     def testNewIssueAuthMsg(self):
242         # TODO: fix the damn config - this is apalling
243         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
244         self._handle_mail('''Content-Type: text/plain;
245   charset="iso-8859-1"
246 From: Chef <chef@bork.bork.bork>
247 To: issue_tracker@your.tracker.email.domain.example
248 Message-Id: <dummy_test_message_id>
249 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
251 This is a test submission of a new issue.
252 ''')
253         self.compareMessages(self._get_mail(),
254 '''FROM: roundup-admin@your.tracker.email.domain.example
255 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
256 Content-Type: text/plain; charset="utf-8"
257 Subject: [issue1] Testing...
258 To: chef@bork.bork.bork, mary@test.test, richard@test.test
259 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
260 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
261 MIME-Version: 1.0
262 Message-Id: <dummy_test_message_id>
263 X-Roundup-Name: Roundup issue tracker
264 X-Roundup-Loop: hello
265 X-Roundup-Issue-Status: unread
266 Content-Transfer-Encoding: quoted-printable
269 New submission from Bork, Chef <chef@bork.bork.bork>:
271 This is a test submission of a new issue.
273 ----------
274 assignedto: richard
275 messages: 1
276 nosy: Chef, mary, richard
277 status: unread
278 title: Testing...
280 _______________________________________________________________________
281 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
282 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
283 _______________________________________________________________________
284 ''')
286     def testNewIssueNoAuthorInfo(self):
287         self.db.config.MAIL_ADD_AUTHORINFO = 'no'
288         self._handle_mail('''Content-Type: text/plain;
289   charset="iso-8859-1"
290 From: Chef <chef@bork.bork.bork>
291 To: issue_tracker@your.tracker.email.domain.example
292 Message-Id: <dummy_test_message_id>
293 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
295 This is a test submission of a new issue.
296 ''')
297         self.compareMessages(self._get_mail(),
298 '''FROM: roundup-admin@your.tracker.email.domain.example
299 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
300 Content-Type: text/plain; charset="utf-8"
301 Subject: [issue1] Testing...
302 To: mary@test.test, richard@test.test
303 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
304 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
305 MIME-Version: 1.0
306 Message-Id: <dummy_test_message_id>
307 X-Roundup-Name: Roundup issue tracker
308 X-Roundup-Loop: hello
309 X-Roundup-Issue-Status: unread
310 Content-Transfer-Encoding: quoted-printable
312 This is a test submission of a new issue.
314 ----------
315 assignedto: richard
316 messages: 1
317 nosy: Chef, mary, richard
318 status: unread
319 title: Testing...
321 _______________________________________________________________________
322 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
323 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
324 _______________________________________________________________________
325 ''')
327     def testNewIssueNoAuthorEmail(self):
328         self.db.config.MAIL_ADD_AUTHOREMAIL = 'no'
329         self._handle_mail('''Content-Type: text/plain;
330   charset="iso-8859-1"
331 From: Chef <chef@bork.bork.bork>
332 To: issue_tracker@your.tracker.email.domain.example
333 Message-Id: <dummy_test_message_id>
334 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
336 This is a test submission of a new issue.
337 ''')
338         self.compareMessages(self._get_mail(),
339 '''FROM: roundup-admin@your.tracker.email.domain.example
340 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
341 Content-Type: text/plain; charset="utf-8"
342 Subject: [issue1] Testing...
343 To: mary@test.test, richard@test.test
344 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
345 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
346 MIME-Version: 1.0
347 Message-Id: <dummy_test_message_id>
348 X-Roundup-Name: Roundup issue tracker
349 X-Roundup-Loop: hello
350 X-Roundup-Issue-Status: unread
351 Content-Transfer-Encoding: quoted-printable
353 New submission from Bork, Chef:
355 This is a test submission of a new issue.
357 ----------
358 assignedto: richard
359 messages: 1
360 nosy: Chef, mary, richard
361 status: unread
362 title: Testing...
364 _______________________________________________________________________
365 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
366 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
367 _______________________________________________________________________
368 ''')
370     multipart_msg = '''From: mary <mary@test.test>
371 To: issue_tracker@your.tracker.email.domain.example
372 Message-Id: <followup_dummy_id>
373 In-Reply-To: <dummy_test_message_id>
374 Subject: [issue1] Testing...
375 Content-Type: multipart/mixed; boundary="bxyzzy"
376 Content-Disposition: inline
379 --bxyzzy
380 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE"
381 Content-Disposition: inline
383 --bCsyhTFzCvuiizWE
384 Content-Type: text/plain; charset=us-ascii
385 Content-Disposition: inline
387 test attachment first text/plain
389 --bCsyhTFzCvuiizWE
390 Content-Type: application/octet-stream
391 Content-Disposition: attachment; filename="first.dvi"
392 Content-Transfer-Encoding: base64
394 SnVzdCBhIHRlc3QgAQo=
396 --bCsyhTFzCvuiizWE
397 Content-Type: text/plain; charset=us-ascii
398 Content-Disposition: inline
400 test attachment second text/plain
402 --bCsyhTFzCvuiizWE
403 Content-Type: text/html
404 Content-Disposition: inline
406 <html>
407 to be ignored.
408 </html>
410 --bCsyhTFzCvuiizWE--
412 --bxyzzy
413 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF"
414 Content-Disposition: inline
416 --bCsyhTFzCvuiizWF
417 Content-Type: text/plain; charset=us-ascii
418 Content-Disposition: inline
420 test attachment third text/plain
422 --bCsyhTFzCvuiizWF
423 Content-Type: application/octet-stream
424 Content-Disposition: attachment; filename="second.dvi"
425 Content-Transfer-Encoding: base64
427 SnVzdCBhIHRlc3QK
429 --bCsyhTFzCvuiizWF--
431 --bxyzzy--
432 '''
434     def testMultipartKeepAlternatives(self):
435         self.doNewIssue()
436         self._handle_mail(self.multipart_msg)
437         messages = self.db.issue.get('1', 'messages')
438         messages.sort()
439         msg = self.db.msg.getnode (messages[-1])
440         assert(len(msg.files) == 5)
441         names = {0 : 'first.dvi', 4 : 'second.dvi'}
442         content = {3 : 'test attachment third text/plain\n',
443                    4 : 'Just a test\n'}
444         for n, id in enumerate (msg.files):
445             f = self.db.file.getnode (id)
446             self.assertEqual(f.name, names.get (n, 'unnamed'))
447             if n in content :
448                 self.assertEqual(f.content, content [n])
449         self.assertEqual(msg.content, 'test attachment second text/plain')
451     def testMultipartDropAlternatives(self):
452         self.doNewIssue()
453         self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
454         self._handle_mail(self.multipart_msg)
455         messages = self.db.issue.get('1', 'messages')
456         messages.sort()
457         msg = self.db.msg.getnode (messages[-1])
458         assert(len(msg.files) == 2)
459         names = {1 : 'second.dvi'}
460         content = {0 : 'test attachment third text/plain\n',
461                    1 : 'Just a test\n'}
462         for n, id in enumerate (msg.files):
463             f = self.db.file.getnode (id)
464             self.assertEqual(f.name, names.get (n, 'unnamed'))
465             if n in content :
466                 self.assertEqual(f.content, content [n])
467         self.assertEqual(msg.content, 'test attachment second text/plain')
469     def testSimpleFollowup(self):
470         self.doNewIssue()
471         self._handle_mail('''Content-Type: text/plain;
472   charset="iso-8859-1"
473 From: mary <mary@test.test>
474 To: issue_tracker@your.tracker.email.domain.example
475 Message-Id: <followup_dummy_id>
476 In-Reply-To: <dummy_test_message_id>
477 Subject: [issue1] Testing...
479 This is a second followup
480 ''')
481         self.compareMessages(self._get_mail(),
482 '''FROM: roundup-admin@your.tracker.email.domain.example
483 TO: chef@bork.bork.bork, richard@test.test
484 Content-Type: text/plain; charset="utf-8"
485 Subject: [issue1] Testing...
486 To: chef@bork.bork.bork, richard@test.test
487 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
488 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
489 MIME-Version: 1.0
490 Message-Id: <followup_dummy_id>
491 In-Reply-To: <dummy_test_message_id>
492 X-Roundup-Name: Roundup issue tracker
493 X-Roundup-Loop: hello
494 X-Roundup-Issue-Status: chatting
495 Content-Transfer-Encoding: quoted-printable
498 Contrary, Mary <mary@test.test> added the comment:
500 This is a second followup
502 ----------
503 status: unread -> chatting
505 _______________________________________________________________________
506 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
507 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
508 _______________________________________________________________________
509 ''')
511     def testFollowup(self):
512         self.doNewIssue()
514         self._handle_mail('''Content-Type: text/plain;
515   charset="iso-8859-1"
516 From: richard <richard@test.test>
517 To: issue_tracker@your.tracker.email.domain.example
518 Message-Id: <followup_dummy_id>
519 In-Reply-To: <dummy_test_message_id>
520 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
522 This is a followup
523 ''')
524         l = self.db.issue.get('1', 'nosy')
525         l.sort()
526         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
527             self.john_id])
529         self.compareMessages(self._get_mail(),
530 '''FROM: roundup-admin@your.tracker.email.domain.example
531 TO: chef@bork.bork.bork, john@test.test, mary@test.test
532 Content-Type: text/plain; charset="utf-8"
533 Subject: [issue1] Testing...
534 To: chef@bork.bork.bork, john@test.test, mary@test.test
535 From: richard <issue_tracker@your.tracker.email.domain.example>
536 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
537 MIME-Version: 1.0
538 Message-Id: <followup_dummy_id>
539 In-Reply-To: <dummy_test_message_id>
540 X-Roundup-Name: Roundup issue tracker
541 X-Roundup-Loop: hello
542 X-Roundup-Issue-Status: chatting
543 Content-Transfer-Encoding: quoted-printable
546 richard <richard@test.test> added the comment:
548 This is a followup
550 ----------
551 assignedto:  -> mary
552 nosy: +john, mary
553 status: unread -> chatting
555 _______________________________________________________________________
556 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
557 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
558 _______________________________________________________________________
559 ''')
561     def testPropertyChangeOnly(self):
562         self.doNewIssue()
563         oldvalues = self.db.getnode('issue', '1').copy()
564         oldvalues['assignedto'] = None
565         # reconstruct old behaviour: This would reuse the
566         # database-handle from the doNewIssue above which has committed
567         # as user "Chef". So we close and reopen the db as that user.
568         self.db.close()
569         self.db = self.instance.open('Chef')
570         self.db.issue.set('1', assignedto=self.chef_id)
571         self.db.commit()
572         self.db.issue.nosymessage('1', None, oldvalues)
574         new_mail = ""
575         for line in self._get_mail().split("\n"):
576             if "Message-Id: " in line:
577                 continue
578             if "Date: " in line:
579                 continue
580             new_mail += line+"\n"
582         self.compareMessages(new_mail, """
583 FROM: roundup-admin@your.tracker.email.domain.example
584 TO: chef@bork.bork.bork, richard@test.test
585 Content-Type: text/plain; charset="utf-8"
586 Subject: [issue1] Testing...
587 To: chef@bork.bork.bork, richard@test.test
588 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
589 X-Roundup-Name: Roundup issue tracker
590 X-Roundup-Loop: hello
591 X-Roundup-Issue-Status: unread
592 X-Roundup-Version: 1.3.3
593 MIME-Version: 1.0
594 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
595 Content-Transfer-Encoding: quoted-printable
598 Change by Bork, Chef <chef@bork.bork.bork>:
601 ----------
602 assignedto:  -> Chef
604 _______________________________________________________________________
605 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
606 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
607 _______________________________________________________________________
608 """)
611     #
612     # FOLLOWUP TITLE MATCH
613     #
614     def testFollowupTitleMatch(self):
615         self.doNewIssue()
616         self._handle_mail('''Content-Type: text/plain;
617   charset="iso-8859-1"
618 From: richard <richard@test.test>
619 To: issue_tracker@your.tracker.email.domain.example
620 Message-Id: <followup_dummy_id>
621 Subject: Re: Testing... [assignedto=mary; nosy=+john]
623 This is a followup
624 ''')
625         self.compareMessages(self._get_mail(),
626 '''FROM: roundup-admin@your.tracker.email.domain.example
627 TO: chef@bork.bork.bork, john@test.test, mary@test.test
628 Content-Type: text/plain; charset="utf-8"
629 Subject: [issue1] Testing...
630 To: chef@bork.bork.bork, john@test.test, mary@test.test
631 From: richard <issue_tracker@your.tracker.email.domain.example>
632 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
633 MIME-Version: 1.0
634 Message-Id: <followup_dummy_id>
635 In-Reply-To: <dummy_test_message_id>
636 X-Roundup-Name: Roundup issue tracker
637 X-Roundup-Loop: hello
638 X-Roundup-Issue-Status: chatting
639 Content-Transfer-Encoding: quoted-printable
642 richard <richard@test.test> added the comment:
644 This is a followup
646 ----------
647 assignedto:  -> mary
648 nosy: +john, mary
649 status: unread -> chatting
651 _______________________________________________________________________
652 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
653 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
654 _______________________________________________________________________
655 ''')
657     def testFollowupTitleMatchMultiRe(self):
658         nodeid1 = self.doNewIssue()
659         nodeid2 = self._handle_mail('''Content-Type: text/plain;
660   charset="iso-8859-1"
661 From: richard <richard@test.test>
662 To: issue_tracker@your.tracker.email.domain.example
663 Message-Id: <followup_dummy_id>
664 Subject: Re: Testing... [assignedto=mary; nosy=+john]
666 This is a followup
667 ''')
669         nodeid3 = self._handle_mail('''Content-Type: text/plain;
670   charset="iso-8859-1"
671 From: richard <richard@test.test>
672 To: issue_tracker@your.tracker.email.domain.example
673 Message-Id: <followup2_dummy_id>
674 Subject: Ang: Re: Testing...
676 This is a followup
677 ''')
678         self.assertEqual(nodeid1, nodeid2)
679         self.assertEqual(nodeid1, nodeid3)
681     def testFollowupTitleMatchNever(self):
682         nodeid = self.doNewIssue()
683         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
684         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
685   charset="iso-8859-1"
686 From: richard <richard@test.test>
687 To: issue_tracker@your.tracker.email.domain.example
688 Message-Id: <followup_dummy_id>
689 Subject: Re: Testing...
691 This is a followup
692 '''), nodeid)
694     def testFollowupTitleMatchNeverInterval(self):
695         nodeid = self.doNewIssue()
696         # force failure of the interval
697         time.sleep(2)
698         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
699         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
700   charset="iso-8859-1"
701 From: richard <richard@test.test>
702 To: issue_tracker@your.tracker.email.domain.example
703 Message-Id: <followup_dummy_id>
704 Subject: Re: Testing...
706 This is a followup
707 '''), nodeid)
710     def testFollowupTitleMatchInterval(self):
711         nodeid = self.doNewIssue()
712         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
713         self.assertEqual(self._handle_mail('''Content-Type: text/plain;
714   charset="iso-8859-1"
715 From: richard <richard@test.test>
716 To: issue_tracker@your.tracker.email.domain.example
717 Message-Id: <followup_dummy_id>
718 Subject: Re: Testing...
720 This is a followup
721 '''), nodeid)
724     def testFollowupNosyAuthor(self):
725         self.doNewIssue()
726         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
727         self._handle_mail('''Content-Type: text/plain;
728   charset="iso-8859-1"
729 From: john@test.test
730 To: issue_tracker@your.tracker.email.domain.example
731 Message-Id: <followup_dummy_id>
732 In-Reply-To: <dummy_test_message_id>
733 Subject: [issue1] Testing...
735 This is a followup
736 ''')
738         self.compareMessages(self._get_mail(),
739 '''FROM: roundup-admin@your.tracker.email.domain.example
740 TO: chef@bork.bork.bork, richard@test.test
741 Content-Type: text/plain; charset="utf-8"
742 Subject: [issue1] Testing...
743 To: chef@bork.bork.bork, richard@test.test
744 From: John Doe <issue_tracker@your.tracker.email.domain.example>
745 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
746 MIME-Version: 1.0
747 Message-Id: <followup_dummy_id>
748 In-Reply-To: <dummy_test_message_id>
749 X-Roundup-Name: Roundup issue tracker
750 X-Roundup-Loop: hello
751 X-Roundup-Issue-Status: chatting
752 Content-Transfer-Encoding: quoted-printable
755 John Doe <john@test.test> added the comment:
757 This is a followup
759 ----------
760 nosy: +john
761 status: unread -> chatting
763 _______________________________________________________________________
764 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
765 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
766 _______________________________________________________________________
768 ''')
770     def testFollowupNosyRecipients(self):
771         self.doNewIssue()
772         self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
773         self._handle_mail('''Content-Type: text/plain;
774   charset="iso-8859-1"
775 From: richard@test.test
776 To: issue_tracker@your.tracker.email.domain.example
777 Cc: john@test.test
778 Message-Id: <followup_dummy_id>
779 In-Reply-To: <dummy_test_message_id>
780 Subject: [issue1] Testing...
782 This is a followup
783 ''')
784         self.compareMessages(self._get_mail(),
785 '''FROM: roundup-admin@your.tracker.email.domain.example
786 TO: chef@bork.bork.bork
787 Content-Type: text/plain; charset="utf-8"
788 Subject: [issue1] Testing...
789 To: chef@bork.bork.bork
790 From: richard <issue_tracker@your.tracker.email.domain.example>
791 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
792 MIME-Version: 1.0
793 Message-Id: <followup_dummy_id>
794 In-Reply-To: <dummy_test_message_id>
795 X-Roundup-Name: Roundup issue tracker
796 X-Roundup-Loop: hello
797 X-Roundup-Issue-Status: chatting
798 Content-Transfer-Encoding: quoted-printable
801 richard <richard@test.test> added the comment:
803 This is a followup
805 ----------
806 nosy: +john
807 status: unread -> chatting
809 _______________________________________________________________________
810 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
811 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
812 _______________________________________________________________________
814 ''')
816     def testFollowupNosyAuthorAndCopy(self):
817         self.doNewIssue()
818         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
819         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
820         self._handle_mail('''Content-Type: text/plain;
821   charset="iso-8859-1"
822 From: john@test.test
823 To: issue_tracker@your.tracker.email.domain.example
824 Message-Id: <followup_dummy_id>
825 In-Reply-To: <dummy_test_message_id>
826 Subject: [issue1] Testing...
828 This is a followup
829 ''')
830         self.compareMessages(self._get_mail(),
831 '''FROM: roundup-admin@your.tracker.email.domain.example
832 TO: chef@bork.bork.bork, john@test.test, richard@test.test
833 Content-Type: text/plain; charset="utf-8"
834 Subject: [issue1] Testing...
835 To: chef@bork.bork.bork, john@test.test, richard@test.test
836 From: John Doe <issue_tracker@your.tracker.email.domain.example>
837 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
838 MIME-Version: 1.0
839 Message-Id: <followup_dummy_id>
840 In-Reply-To: <dummy_test_message_id>
841 X-Roundup-Name: Roundup issue tracker
842 X-Roundup-Loop: hello
843 X-Roundup-Issue-Status: chatting
844 Content-Transfer-Encoding: quoted-printable
847 John Doe <john@test.test> added the comment:
849 This is a followup
851 ----------
852 nosy: +john
853 status: unread -> chatting
855 _______________________________________________________________________
856 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
857 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
858 _______________________________________________________________________
860 ''')
862     def testFollowupNoNosyAuthor(self):
863         self.doNewIssue()
864         self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
865         self._handle_mail('''Content-Type: text/plain;
866   charset="iso-8859-1"
867 From: john@test.test
868 To: issue_tracker@your.tracker.email.domain.example
869 Message-Id: <followup_dummy_id>
870 In-Reply-To: <dummy_test_message_id>
871 Subject: [issue1] Testing...
873 This is a followup
874 ''')
875         self.compareMessages(self._get_mail(),
876 '''FROM: roundup-admin@your.tracker.email.domain.example
877 TO: chef@bork.bork.bork, richard@test.test
878 Content-Type: text/plain; charset="utf-8"
879 Subject: [issue1] Testing...
880 To: chef@bork.bork.bork, richard@test.test
881 From: John Doe <issue_tracker@your.tracker.email.domain.example>
882 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
883 MIME-Version: 1.0
884 Message-Id: <followup_dummy_id>
885 In-Reply-To: <dummy_test_message_id>
886 X-Roundup-Name: Roundup issue tracker
887 X-Roundup-Loop: hello
888 X-Roundup-Issue-Status: chatting
889 Content-Transfer-Encoding: quoted-printable
892 John Doe <john@test.test> added the comment:
894 This is a followup
896 ----------
897 status: unread -> chatting
899 _______________________________________________________________________
900 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
901 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
902 _______________________________________________________________________
904 ''')
906     def testFollowupNoNosyRecipients(self):
907         self.doNewIssue()
908         self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
909         self._handle_mail('''Content-Type: text/plain;
910   charset="iso-8859-1"
911 From: richard@test.test
912 To: issue_tracker@your.tracker.email.domain.example
913 Cc: john@test.test
914 Message-Id: <followup_dummy_id>
915 In-Reply-To: <dummy_test_message_id>
916 Subject: [issue1] Testing...
918 This is a followup
919 ''')
920         self.compareMessages(self._get_mail(),
921 '''FROM: roundup-admin@your.tracker.email.domain.example
922 TO: chef@bork.bork.bork
923 Content-Type: text/plain; charset="utf-8"
924 Subject: [issue1] Testing...
925 To: chef@bork.bork.bork
926 From: richard <issue_tracker@your.tracker.email.domain.example>
927 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
928 MIME-Version: 1.0
929 Message-Id: <followup_dummy_id>
930 In-Reply-To: <dummy_test_message_id>
931 X-Roundup-Name: Roundup issue tracker
932 X-Roundup-Loop: hello
933 X-Roundup-Issue-Status: chatting
934 Content-Transfer-Encoding: quoted-printable
937 richard <richard@test.test> added the comment:
939 This is a followup
941 ----------
942 status: unread -> chatting
944 _______________________________________________________________________
945 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
946 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
947 _______________________________________________________________________
949 ''')
951     def testFollowupEmptyMessage(self):
952         self.doNewIssue()
954         self._handle_mail('''Content-Type: text/plain;
955   charset="iso-8859-1"
956 From: richard <richard@test.test>
957 To: issue_tracker@your.tracker.email.domain.example
958 Message-Id: <followup_dummy_id>
959 In-Reply-To: <dummy_test_message_id>
960 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
962 ''')
963         l = self.db.issue.get('1', 'nosy')
964         l.sort()
965         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
966             self.john_id])
968         # should be no file created (ie. no message)
969         assert not os.path.exists(SENDMAILDEBUG)
971     def testFollowupEmptyMessageNoSubject(self):
972         self.doNewIssue()
974         self._handle_mail('''Content-Type: text/plain;
975   charset="iso-8859-1"
976 From: richard <richard@test.test>
977 To: issue_tracker@your.tracker.email.domain.example
978 Message-Id: <followup_dummy_id>
979 In-Reply-To: <dummy_test_message_id>
980 Subject: [issue1] [assignedto=mary; nosy=+john]
982 ''')
983         l = self.db.issue.get('1', 'nosy')
984         l.sort()
985         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
986             self.john_id])
988         # should be no file created (ie. no message)
989         assert not os.path.exists(SENDMAILDEBUG)
991     def testNosyRemove(self):
992         self.doNewIssue()
994         self._handle_mail('''Content-Type: text/plain;
995   charset="iso-8859-1"
996 From: richard <richard@test.test>
997 To: issue_tracker@your.tracker.email.domain.example
998 Message-Id: <followup_dummy_id>
999 In-Reply-To: <dummy_test_message_id>
1000 Subject: [issue1] Testing... [nosy=-richard]
1002 ''')
1003         l = self.db.issue.get('1', 'nosy')
1004         l.sort()
1005         self.assertEqual(l, [self.chef_id])
1007         # NO NOSY MESSAGE SHOULD BE SENT!
1008         assert not os.path.exists(SENDMAILDEBUG)
1010     def testNewUserAuthor(self):
1012         l = self.db.user.list()
1013         l.sort()
1014         message = '''Content-Type: text/plain;
1015   charset="iso-8859-1"
1016 From: fubar <fubar@bork.bork.bork>
1017 To: issue_tracker@your.tracker.email.domain.example
1018 Message-Id: <dummy_test_message_id>
1019 Subject: [issue] Testing...
1021 This is a test submission of a new issue.
1022 '''
1023         def hook (db, **kw):
1024             ''' set up callback for db open '''
1025             db.security.role['anonymous'].permissions=[]
1026             anonid = db.user.lookup('anonymous')
1027             db.user.set(anonid, roles='Anonymous')
1028         self.instance.schema_hook = hook
1029         try:
1030             self._handle_mail(message)
1031         except Unauthorized, value:
1032             body_diff = self.compareMessages(str(value), """
1033 You are not a registered user.
1035 Unknown address: fubar@bork.bork.bork
1036 """)
1038             assert not body_diff, body_diff
1040         else:
1041             raise AssertionError, "Unathorized not raised when handling mail"
1044         def hook (db, **kw):
1045             ''' set up callback for db open '''
1046             # Add Web Access role to anonymous, and try again to make sure
1047             # we get a "please register at:" message this time.
1048             p = [
1049                 db.security.getPermission('Create', 'user'),
1050                 db.security.getPermission('Web Access', None),
1051             ]
1052             db.security.role['anonymous'].permissions=p
1053         self.instance.schema_hook = hook
1054         try:
1055             self._handle_mail(message)
1056         except Unauthorized, value:
1057             body_diff = self.compareMessages(str(value), """
1058 You are not a registered user. Please register at:
1060 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1062 ...before sending mail to the tracker.
1064 Unknown address: fubar@bork.bork.bork
1065 """)
1067             assert not body_diff, body_diff
1069         else:
1070             raise AssertionError, "Unathorized not raised when handling mail"
1072         # Make sure list of users is the same as before.
1073         m = self.db.user.list()
1074         m.sort()
1075         self.assertEqual(l, m)
1077         def hook (db, **kw):
1078             ''' set up callback for db open '''
1079             # now with the permission
1080             p = [
1081                 db.security.getPermission('Create', 'user'),
1082                 db.security.getPermission('Email Access', None),
1083             ]
1084             db.security.role['anonymous'].permissions=p
1085         self.instance.schema_hook = hook
1086         self._handle_mail(message)
1087         m = self.db.user.list()
1088         m.sort()
1089         self.assertNotEqual(l, m)
1091     def testNewUserAuthorHighBit(self):
1092         l = set(self.db.user.list())
1093         # From: name has Euro symbol in it
1094         message = '''Content-Type: text/plain;
1095   charset="iso-8859-1"
1096 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1097 To: issue_tracker@your.tracker.email.domain.example
1098 Message-Id: <dummy_test_message_id>
1099 Subject: [issue] Testing...
1101 This is a test submission of a new issue.
1102 '''
1103         def hook (db, **kw):
1104             ''' set up callback for db open '''
1105             p = [
1106                 db.security.getPermission('Create', 'user'),
1107                 db.security.getPermission('Email Access', None),
1108             ]
1109             db.security.role['anonymous'].permissions=p
1110         self.instance.schema_hook = hook
1111         self._handle_mail(message)
1112         m = set(self.db.user.list())
1113         new = list(m - l)[0]
1114         name = self.db.user.get(new, 'realname')
1115         self.assertEquals(name, 'H€llo')
1117     def testUnknownUser(self):
1118         l = set(self.db.user.list())
1119         message = '''Content-Type: text/plain;
1120   charset="iso-8859-1"
1121 From: Nonexisting User <nonexisting@bork.bork.bork>
1122 To: issue_tracker@your.tracker.email.domain.example
1123 Message-Id: <dummy_test_message_id>
1124 Subject: [issue] Testing nonexisting user...
1126 This is a test submission of a new issue.
1127 '''
1128         self.db.close()
1129         handler = self.instance.MailGW(self.instance)
1130         # we want a bounce message:
1131         handler.trapExceptions = 1
1132         ret = handler.main(StringIO(message))
1133         self.compareMessages(self._get_mail(),
1134 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1135 TO: nonexisting@bork.bork.bork
1136 From nobody Tue Jul 14 12:04:11 2009
1137 Content-Type: multipart/mixed; boundary="===============0639262320=="
1138 MIME-Version: 1.0
1139 Subject: Failed issue tracker submission
1140 To: nonexisting@bork.bork.bork
1141 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1142 Date: Tue, 14 Jul 2009 12:04:11 +0000
1143 Precedence: bulk
1144 X-Roundup-Name: Roundup issue tracker
1145 X-Roundup-Loop: hello
1146 X-Roundup-Version: 1.4.8
1147 MIME-Version: 1.0
1149 --===============0639262320==
1150 Content-Type: text/plain; charset="us-ascii"
1151 MIME-Version: 1.0
1152 Content-Transfer-Encoding: 7bit
1156 You are not a registered user.
1158 Unknown address: nonexisting@bork.bork.bork
1160 --===============0639262320==
1161 Content-Type: text/plain; charset="us-ascii"
1162 MIME-Version: 1.0
1163 Content-Transfer-Encoding: 7bit
1165 Content-Type: text/plain;
1166   charset="iso-8859-1"
1167 From: Nonexisting User <nonexisting@bork.bork.bork>
1168 To: issue_tracker@your.tracker.email.domain.example
1169 Message-Id: <dummy_test_message_id>
1170 Subject: [issue] Testing nonexisting user...
1172 This is a test submission of a new issue.
1174 --===============0639262320==--
1175 ''')
1177     def testEnc01(self):
1178         self.doNewIssue()
1179         self._handle_mail('''Content-Type: text/plain;
1180   charset="iso-8859-1"
1181 From: mary <mary@test.test>
1182 To: issue_tracker@your.tracker.email.domain.example
1183 Message-Id: <followup_dummy_id>
1184 In-Reply-To: <dummy_test_message_id>
1185 Subject: [issue1] Testing...
1186 Content-Type: text/plain;
1187         charset="iso-8859-1"
1188 Content-Transfer-Encoding: quoted-printable
1190 A message with encoding (encoded oe =F6)
1192 ''')
1193         self.compareMessages(self._get_mail(),
1194 '''FROM: roundup-admin@your.tracker.email.domain.example
1195 TO: chef@bork.bork.bork, richard@test.test
1196 Content-Type: text/plain; charset="utf-8"
1197 Subject: [issue1] Testing...
1198 To: chef@bork.bork.bork, richard@test.test
1199 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1200 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1201 MIME-Version: 1.0
1202 Message-Id: <followup_dummy_id>
1203 In-Reply-To: <dummy_test_message_id>
1204 X-Roundup-Name: Roundup issue tracker
1205 X-Roundup-Loop: hello
1206 X-Roundup-Issue-Status: chatting
1207 Content-Transfer-Encoding: quoted-printable
1210 Contrary, Mary <mary@test.test> added the comment:
1212 A message with encoding (encoded oe =C3=B6)
1214 ----------
1215 status: unread -> chatting
1217 _______________________________________________________________________
1218 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1219 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1220 _______________________________________________________________________
1221 ''')
1223     def testEncNonUTF8(self):
1224         self.doNewIssue()
1225         self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1226         self._handle_mail('''Content-Type: text/plain;
1227   charset="iso-8859-1"
1228 From: mary <mary@test.test>
1229 To: issue_tracker@your.tracker.email.domain.example
1230 Message-Id: <followup_dummy_id>
1231 In-Reply-To: <dummy_test_message_id>
1232 Subject: [issue1] Testing...
1233 Content-Type: text/plain;
1234         charset="iso-8859-1"
1235 Content-Transfer-Encoding: quoted-printable
1237 A message with encoding (encoded oe =F6)
1239 ''')
1240         self.compareMessages(self._get_mail(),
1241 '''FROM: roundup-admin@your.tracker.email.domain.example
1242 TO: chef@bork.bork.bork, richard@test.test
1243 Content-Type: text/plain; charset="iso-8859-1"
1244 Subject: [issue1] Testing...
1245 To: chef@bork.bork.bork, richard@test.test
1246 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1247 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1248 MIME-Version: 1.0
1249 Message-Id: <followup_dummy_id>
1250 In-Reply-To: <dummy_test_message_id>
1251 X-Roundup-Name: Roundup issue tracker
1252 X-Roundup-Loop: hello
1253 X-Roundup-Issue-Status: chatting
1254 Content-Transfer-Encoding: quoted-printable
1257 Contrary, Mary <mary@test.test> added the comment:
1259 A message with encoding (encoded oe =F6)
1261 ----------
1262 status: unread -> chatting
1264 _______________________________________________________________________
1265 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1266 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1267 _______________________________________________________________________
1268 ''')
1271     def testMultipartEnc01(self):
1272         self.doNewIssue()
1273         self._handle_mail('''Content-Type: text/plain;
1274   charset="iso-8859-1"
1275 From: mary <mary@test.test>
1276 To: issue_tracker@your.tracker.email.domain.example
1277 Message-Id: <followup_dummy_id>
1278 In-Reply-To: <dummy_test_message_id>
1279 Subject: [issue1] Testing...
1280 Content-Type: multipart/mixed;
1281         boundary="----_=_NextPart_000_01"
1283 This message is in MIME format. Since your mail reader does not understand
1284 this format, some or all of this message may not be legible.
1286 ------_=_NextPart_000_01
1287 Content-Type: text/plain;
1288         charset="iso-8859-1"
1289 Content-Transfer-Encoding: quoted-printable
1291 A message with first part encoded (encoded oe =F6)
1293 ''')
1294         self.compareMessages(self._get_mail(),
1295 '''FROM: roundup-admin@your.tracker.email.domain.example
1296 TO: chef@bork.bork.bork, richard@test.test
1297 Content-Type: text/plain; charset="utf-8"
1298 Subject: [issue1] Testing...
1299 To: chef@bork.bork.bork, richard@test.test
1300 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1301 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1302 MIME-Version: 1.0
1303 Message-Id: <followup_dummy_id>
1304 In-Reply-To: <dummy_test_message_id>
1305 X-Roundup-Name: Roundup issue tracker
1306 X-Roundup-Loop: hello
1307 X-Roundup-Issue-Status: chatting
1308 Content-Transfer-Encoding: quoted-printable
1311 Contrary, Mary <mary@test.test> added the comment:
1313 A message with first part encoded (encoded oe =C3=B6)
1315 ----------
1316 status: unread -> chatting
1318 _______________________________________________________________________
1319 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1320 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1321 _______________________________________________________________________
1322 ''')
1324     def testContentDisposition(self):
1325         self.doNewIssue()
1326         self._handle_mail('''Content-Type: text/plain;
1327   charset="iso-8859-1"
1328 From: mary <mary@test.test>
1329 To: issue_tracker@your.tracker.email.domain.example
1330 Message-Id: <followup_dummy_id>
1331 In-Reply-To: <dummy_test_message_id>
1332 Subject: [issue1] Testing...
1333 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
1334 Content-Disposition: inline
1337 --bCsyhTFzCvuiizWE
1338 Content-Type: text/plain; charset=us-ascii
1339 Content-Disposition: inline
1341 test attachment binary
1343 --bCsyhTFzCvuiizWE
1344 Content-Type: application/octet-stream
1345 Content-Disposition: attachment; filename="main.dvi"
1346 Content-Transfer-Encoding: base64
1348 SnVzdCBhIHRlc3QgAQo=
1350 --bCsyhTFzCvuiizWE--
1351 ''')
1352         messages = self.db.issue.get('1', 'messages')
1353         messages.sort()
1354         file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
1355         self.assertEqual(file.name, 'main.dvi')
1356         self.assertEqual(file.content, 'Just a test \001\n')
1358     def testFollowupStupidQuoting(self):
1359         self.doNewIssue()
1361         self._handle_mail('''Content-Type: text/plain;
1362   charset="iso-8859-1"
1363 From: richard <richard@test.test>
1364 To: issue_tracker@your.tracker.email.domain.example
1365 Message-Id: <followup_dummy_id>
1366 In-Reply-To: <dummy_test_message_id>
1367 Subject: Re: "[issue1] Testing... "
1369 This is a followup
1370 ''')
1371         self.compareMessages(self._get_mail(),
1372 '''FROM: roundup-admin@your.tracker.email.domain.example
1373 TO: chef@bork.bork.bork
1374 Content-Type: text/plain; charset="utf-8"
1375 Subject: [issue1] Testing...
1376 To: chef@bork.bork.bork
1377 From: richard <issue_tracker@your.tracker.email.domain.example>
1378 Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1379 MIME-Version: 1.0
1380 Message-Id: <followup_dummy_id>
1381 In-Reply-To: <dummy_test_message_id>
1382 X-Roundup-Name: Roundup issue tracker
1383 X-Roundup-Loop: hello
1384 X-Roundup-Issue-Status: chatting
1385 Content-Transfer-Encoding: quoted-printable
1388 richard <richard@test.test> added the comment:
1390 This is a followup
1392 ----------
1393 status: unread -> chatting
1395 _______________________________________________________________________
1396 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1397 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1398 _______________________________________________________________________
1399 ''')
1401     def testEmailQuoting(self):
1402         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
1403         self.innerTestQuoting('''This is a followup
1404 ''')
1406     def testEmailQuotingRemove(self):
1407         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
1408         self.innerTestQuoting('''Blah blah wrote:
1409 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
1410 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
1413 This is a followup
1414 ''')
1416     def innerTestQuoting(self, expect):
1417         nodeid = self.doNewIssue()
1419         messages = self.db.issue.get(nodeid, 'messages')
1421         self._handle_mail('''Content-Type: text/plain;
1422   charset="iso-8859-1"
1423 From: richard <richard@test.test>
1424 To: issue_tracker@your.tracker.email.domain.example
1425 Message-Id: <followup_dummy_id>
1426 In-Reply-To: <dummy_test_message_id>
1427 Subject: Re: [issue1] Testing...
1429 Blah blah wrote:
1430 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
1431 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
1434 This is a followup
1435 ''')
1436         # figure the new message id
1437         newmessages = self.db.issue.get(nodeid, 'messages')
1438         for msg in messages:
1439             newmessages.remove(msg)
1440         messageid = newmessages[0]
1442         self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
1444     def testUserLookup(self):
1445         i = self.db.user.create(username='user1', address='user1@foo.com')
1446         self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
1447         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
1448         i = self.db.user.create(username='user2', address='USER2@foo.com')
1449         self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
1450         self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
1452     def testUserAlternateLookup(self):
1453         i = self.db.user.create(username='user1', address='user1@foo.com',
1454                                 alternate_addresses='user1@bar.com')
1455         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
1456         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
1458     def testUserCreate(self):
1459         i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
1460         self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
1462     def testRFC2822(self):
1463         ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
1464         unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
1465         unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
1466         self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
1467         self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
1469     def testRegistrationConfirmation(self):
1470         otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
1471         self.db.getOTKManager().set(otk, username='johannes')
1472         self._handle_mail('''Content-Type: text/plain;
1473   charset="iso-8859-1"
1474 From: Chef <chef@bork.bork.bork>
1475 To: issue_tracker@your.tracker.email.domain.example
1476 Cc: richard@test.test
1477 Message-Id: <dummy_test_message_id>
1478 Subject: Re: Complete your registration to Roundup issue tracker
1479  -- key %s
1481 This is a test confirmation of registration.
1482 ''' % otk)
1483         self.db.user.lookup('johannes')
1485     def testFollowupOnNonIssue(self):
1486         self.db.keyword.create(name='Foo')
1487         self._handle_mail('''Content-Type: text/plain;
1488   charset="iso-8859-1"
1489 From: richard <richard@test.test>
1490 To: issue_tracker@your.tracker.email.domain.example
1491 Message-Id: <followup_dummy_id>
1492 In-Reply-To: <dummy_test_message_id>
1493 Subject: [keyword1] Testing... [name=Bar]
1495 ''')
1496         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1498     def testResentFrom(self):
1499         nodeid = self._handle_mail('''Content-Type: text/plain;
1500   charset="iso-8859-1"
1501 From: Chef <chef@bork.bork.bork>
1502 Resent-From: mary <mary@test.test>
1503 To: issue_tracker@your.tracker.email.domain.example
1504 Cc: richard@test.test
1505 Message-Id: <dummy_test_message_id>
1506 Subject: [issue] Testing...
1508 This is a test submission of a new issue.
1509 ''')
1510         assert not os.path.exists(SENDMAILDEBUG)
1511         l = self.db.issue.get(nodeid, 'nosy')
1512         l.sort()
1513         self.assertEqual(l, [self.richard_id, self.mary_id])
1514         return nodeid
1516     def testDejaVu(self):
1517         self.assertRaises(IgnoreLoop, self._handle_mail,
1518             '''Content-Type: text/plain;
1519   charset="iso-8859-1"
1520 From: Chef <chef@bork.bork.bork>
1521 X-Roundup-Loop: hello
1522 To: issue_tracker@your.tracker.email.domain.example
1523 Cc: richard@test.test
1524 Message-Id: <dummy_test_message_id>
1525 Subject: Re: [issue] Testing...
1527 Hi, I've been mis-configured to loop messages back to myself.
1528 ''')
1530     def testItsBulkStupid(self):
1531         self.assertRaises(IgnoreBulk, self._handle_mail,
1532             '''Content-Type: text/plain;
1533   charset="iso-8859-1"
1534 From: Chef <chef@bork.bork.bork>
1535 Precedence: bulk
1536 To: issue_tracker@your.tracker.email.domain.example
1537 Cc: richard@test.test
1538 Message-Id: <dummy_test_message_id>
1539 Subject: Re: [issue] Testing...
1541 Hi, I'm on holidays, and this is a dumb auto-responder.
1542 ''')
1544     def testAutoReplyEmailsAreIgnored(self):
1545         self.assertRaises(IgnoreBulk, self._handle_mail,
1546             '''Content-Type: text/plain;
1547   charset="iso-8859-1"
1548 From: Chef <chef@bork.bork.bork>
1549 To: issue_tracker@your.tracker.email.domain.example
1550 Cc: richard@test.test
1551 Message-Id: <dummy_test_message_id>
1552 Subject: Re: [issue] Out of office AutoReply: Back next week
1554 Hi, I am back in the office next week
1555 ''')
1557     def testNoSubject(self):
1558         self.assertRaises(MailUsageError, self._handle_mail,
1559             '''Content-Type: text/plain;
1560   charset="iso-8859-1"
1561 From: Chef <chef@bork.bork.bork>
1562 To: issue_tracker@your.tracker.email.domain.example
1563 Cc: richard@test.test
1564 Reply-To: chef@bork.bork.bork
1565 Message-Id: <dummy_test_message_id>
1567 ''')
1569     #
1570     # TEST FOR INVALID DESIGNATOR HANDLING
1571     #
1572     def testInvalidDesignator(self):
1573         self.assertRaises(MailUsageError, self._handle_mail,
1574             '''Content-Type: text/plain;
1575   charset="iso-8859-1"
1576 From: Chef <chef@bork.bork.bork>
1577 To: issue_tracker@your.tracker.email.domain.example
1578 Subject: [frobulated] testing
1579 Cc: richard@test.test
1580 Reply-To: chef@bork.bork.bork
1581 Message-Id: <dummy_test_message_id>
1583 ''')
1584         self.assertRaises(MailUsageError, self._handle_mail,
1585             '''Content-Type: text/plain;
1586   charset="iso-8859-1"
1587 From: Chef <chef@bork.bork.bork>
1588 To: issue_tracker@your.tracker.email.domain.example
1589 Subject: [issue12345] testing
1590 Cc: richard@test.test
1591 Reply-To: chef@bork.bork.bork
1592 Message-Id: <dummy_test_message_id>
1594 ''')
1596     def testInvalidClassLoose(self):
1597         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1598         nodeid = self._handle_mail('''Content-Type: text/plain;
1599   charset="iso-8859-1"
1600 From: Chef <chef@bork.bork.bork>
1601 To: issue_tracker@your.tracker.email.domain.example
1602 Subject: [frobulated] testing
1603 Cc: richard@test.test
1604 Reply-To: chef@bork.bork.bork
1605 Message-Id: <dummy_test_message_id>
1607 ''')
1608         assert not os.path.exists(SENDMAILDEBUG)
1609         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1610             '[frobulated] testing')
1612     def testInvalidClassLooseReply(self):
1613         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1614         nodeid = self._handle_mail('''Content-Type: text/plain;
1615   charset="iso-8859-1"
1616 From: Chef <chef@bork.bork.bork>
1617 To: issue_tracker@your.tracker.email.domain.example
1618 Subject: Re: [frobulated] testing
1619 Cc: richard@test.test
1620 Reply-To: chef@bork.bork.bork
1621 Message-Id: <dummy_test_message_id>
1623 ''')
1624         assert not os.path.exists(SENDMAILDEBUG)
1625         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1626             '[frobulated] testing')
1628     def testInvalidClassLoose(self):
1629         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1630         nodeid = self._handle_mail('''Content-Type: text/plain;
1631   charset="iso-8859-1"
1632 From: Chef <chef@bork.bork.bork>
1633 To: issue_tracker@your.tracker.email.domain.example
1634 Subject: [issue1234] testing
1635 Cc: richard@test.test
1636 Reply-To: chef@bork.bork.bork
1637 Message-Id: <dummy_test_message_id>
1639 ''')
1640         assert not os.path.exists(SENDMAILDEBUG)
1641         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1642             '[issue1234] testing')
1644     def testClassLooseOK(self):
1645         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1646         self.db.keyword.create(name='Foo')
1647         nodeid = self._handle_mail('''Content-Type: text/plain;
1648   charset="iso-8859-1"
1649 From: Chef <chef@bork.bork.bork>
1650 To: issue_tracker@your.tracker.email.domain.example
1651 Subject: [keyword1] Testing... [name=Bar]
1652 Cc: richard@test.test
1653 Reply-To: chef@bork.bork.bork
1654 Message-Id: <dummy_test_message_id>
1656 ''')
1657         assert not os.path.exists(SENDMAILDEBUG)
1658         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1660     def testClassStrictInvalid(self):
1661         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
1662         self.instance.config.MAILGW_DEFAULT_CLASS = ''
1664         message = '''Content-Type: text/plain;
1665   charset="iso-8859-1"
1666 From: Chef <chef@bork.bork.bork>
1667 To: issue_tracker@your.tracker.email.domain.example
1668 Subject: Testing...
1669 Cc: richard@test.test
1670 Reply-To: chef@bork.bork.bork
1671 Message-Id: <dummy_test_message_id>
1673 '''
1674         self.assertRaises(MailUsageError, self._handle_mail, message)
1676     def testClassStrictValid(self):
1677         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
1678         self.instance.config.MAILGW_DEFAULT_CLASS = ''
1680         nodeid = self._handle_mail('''Content-Type: text/plain;
1681   charset="iso-8859-1"
1682 From: Chef <chef@bork.bork.bork>
1683 To: issue_tracker@your.tracker.email.domain.example
1684 Subject: [issue] Testing...
1685 Cc: richard@test.test
1686 Reply-To: chef@bork.bork.bork
1687 Message-Id: <dummy_test_message_id>
1689 ''')
1691         assert not os.path.exists(SENDMAILDEBUG)
1692         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
1694     #
1695     # TEST FOR INVALID COMMANDS HANDLING
1696     #
1697     def testInvalidCommands(self):
1698         self.assertRaises(MailUsageError, self._handle_mail,
1699             '''Content-Type: text/plain;
1700   charset="iso-8859-1"
1701 From: Chef <chef@bork.bork.bork>
1702 To: issue_tracker@your.tracker.email.domain.example
1703 Subject: testing [frobulated]
1704 Cc: richard@test.test
1705 Reply-To: chef@bork.bork.bork
1706 Message-Id: <dummy_test_message_id>
1708 ''')
1710     def testInvalidCommandPassthrough(self):
1711         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
1712         nodeid = self._handle_mail('''Content-Type: text/plain;
1713   charset="iso-8859-1"
1714 From: Chef <chef@bork.bork.bork>
1715 To: issue_tracker@your.tracker.email.domain.example
1716 Subject: testing [frobulated]
1717 Cc: richard@test.test
1718 Reply-To: chef@bork.bork.bork
1719 Message-Id: <dummy_test_message_id>
1721 ''')
1722         assert not os.path.exists(SENDMAILDEBUG)
1723         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1724             'testing [frobulated]')
1726     def testInvalidCommandPassthroughLoose(self):
1727         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
1728         nodeid = self._handle_mail('''Content-Type: text/plain;
1729   charset="iso-8859-1"
1730 From: Chef <chef@bork.bork.bork>
1731 To: issue_tracker@your.tracker.email.domain.example
1732 Subject: testing [frobulated]
1733 Cc: richard@test.test
1734 Reply-To: chef@bork.bork.bork
1735 Message-Id: <dummy_test_message_id>
1737 ''')
1738         assert not os.path.exists(SENDMAILDEBUG)
1739         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1740             'testing [frobulated]')
1742     def testInvalidCommandPassthroughLooseOK(self):
1743         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
1744         nodeid = self._handle_mail('''Content-Type: text/plain;
1745   charset="iso-8859-1"
1746 From: Chef <chef@bork.bork.bork>
1747 To: issue_tracker@your.tracker.email.domain.example
1748 Subject: testing [assignedto=mary]
1749 Cc: richard@test.test
1750 Reply-To: chef@bork.bork.bork
1751 Message-Id: <dummy_test_message_id>
1753 ''')
1754         assert not os.path.exists(SENDMAILDEBUG)
1755         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
1756         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
1758     def testCommandDelimiters(self):
1759         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1760         nodeid = self._handle_mail('''Content-Type: text/plain;
1761   charset="iso-8859-1"
1762 From: Chef <chef@bork.bork.bork>
1763 To: issue_tracker@your.tracker.email.domain.example
1764 Subject: testing {assignedto=mary}
1765 Cc: richard@test.test
1766 Reply-To: chef@bork.bork.bork
1767 Message-Id: <dummy_test_message_id>
1769 ''')
1770         assert not os.path.exists(SENDMAILDEBUG)
1771         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
1772         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
1774     def testPrefixDelimiters(self):
1775         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1776         self.db.keyword.create(name='Foo')
1777         self._handle_mail('''Content-Type: text/plain;
1778   charset="iso-8859-1"
1779 From: richard <richard@test.test>
1780 To: issue_tracker@your.tracker.email.domain.example
1781 Message-Id: <followup_dummy_id>
1782 In-Reply-To: <dummy_test_message_id>
1783 Subject: {keyword1} Testing... {name=Bar}
1785 ''')
1786         assert not os.path.exists(SENDMAILDEBUG)
1787         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1789     def testCommandDelimitersIgnore(self):
1790         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1791         nodeid = self._handle_mail('''Content-Type: text/plain;
1792   charset="iso-8859-1"
1793 From: Chef <chef@bork.bork.bork>
1794 To: issue_tracker@your.tracker.email.domain.example
1795 Subject: testing [assignedto=mary]
1796 Cc: richard@test.test
1797 Reply-To: chef@bork.bork.bork
1798 Message-Id: <dummy_test_message_id>
1800 ''')
1801         assert not os.path.exists(SENDMAILDEBUG)
1802         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1803             'testing [assignedto=mary]')
1804         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
1806     def testReplytoMatch(self):
1807         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1808         nodeid = self.doNewIssue()
1809         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1810   charset="iso-8859-1"
1811 From: Chef <chef@bork.bork.bork>
1812 To: issue_tracker@your.tracker.email.domain.example
1813 Message-Id: <dummy_test_message_id2>
1814 In-Reply-To: <dummy_test_message_id>
1815 Subject: Testing...
1817 Followup message.
1818 ''')
1820         nodeid3 = self._handle_mail('''Content-Type: text/plain;
1821   charset="iso-8859-1"
1822 From: Chef <chef@bork.bork.bork>
1823 To: issue_tracker@your.tracker.email.domain.example
1824 Message-Id: <dummy_test_message_id3>
1825 In-Reply-To: <dummy_test_message_id2>
1826 Subject: Testing...
1828 Yet another message in the same thread/issue.
1829 ''')
1831         self.assertEqual(nodeid, nodeid2)
1832         self.assertEqual(nodeid, nodeid3)
1834     def testHelpSubject(self):
1835         message = '''Content-Type: text/plain;
1836   charset="iso-8859-1"
1837 From: Chef <chef@bork.bork.bork>
1838 To: issue_tracker@your.tracker.email.domain.example
1839 Message-Id: <dummy_test_message_id2>
1840 In-Reply-To: <dummy_test_message_id>
1841 Subject: hElp
1844 '''
1845         self.assertRaises(MailUsageHelp, self._handle_mail, message)
1847     def testMaillistSubject(self):
1848         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
1849         self.db.keyword.create(name='Foo')
1850         self._handle_mail('''Content-Type: text/plain;
1851   charset="iso-8859-1"
1852 From: Chef <chef@bork.bork.bork>
1853 To: issue_tracker@your.tracker.email.domain.example
1854 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
1855 Cc: richard@test.test
1856 Reply-To: chef@bork.bork.bork
1857 Message-Id: <dummy_test_message_id>
1859 ''')
1861         assert not os.path.exists(SENDMAILDEBUG)
1862         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1864     def testUnknownPrefixSubject(self):
1865         self.db.keyword.create(name='Foo')
1866         self._handle_mail('''Content-Type: text/plain;
1867   charset="iso-8859-1"
1868 From: Chef <chef@bork.bork.bork>
1869 To: issue_tracker@your.tracker.email.domain.example
1870 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
1871 Cc: richard@test.test
1872 Reply-To: chef@bork.bork.bork
1873 Message-Id: <dummy_test_message_id>
1875 ''')
1877         assert not os.path.exists(SENDMAILDEBUG)
1878         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1880     def testIssueidLast(self):
1881         nodeid1 = self.doNewIssue()
1882         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1883   charset="iso-8859-1"
1884 From: mary <mary@test.test>
1885 To: issue_tracker@your.tracker.email.domain.example
1886 Message-Id: <followup_dummy_id>
1887 In-Reply-To: <dummy_test_message_id>
1888 Subject: New title [issue1]
1890 This is a second followup
1891 ''')
1893         assert nodeid1 == nodeid2
1894         self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
1897 def test_suite():
1898     suite = unittest.TestSuite()
1899     suite.addTest(unittest.makeSuite(MailgwTestCase))
1900     return suite
1902 if __name__ == '__main__':
1903     runner = unittest.TextTestRunner()
1904     unittest.main(testRunner=runner)
1906 # vim: set filetype=python sts=4 sw=4 et si :