Code

new mailgw config option subject_updates_title, see discussion
[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
30 import memorydb
32 class Message(rfc822.Message):
33     """String-based Message class with equivalence test."""
34     def __init__(self, s):
35         rfc822.Message.__init__(self, StringIO(s.strip()))
37     def __eq__(self, other):
38         return (self.dict == other.dict and
39                 self.fp.read() == other.fp.read())
41 class Tracker(object):
42     def open(self, journaltag):
43         return self.db
45 class DiffHelper:
46     def compareMessages(self, new, old):
47         """Compare messages for semantic equivalence."""
48         new, old = Message(new), Message(old)
50         # all Roundup-generated messages have "Precedence: bulk"
51         old['Precedence'] = 'bulk'
53         # don't try to compare the date
54         del new['date'], old['date']
56         if not new == old:
57             res = []
59             replace = {}
60             for key in new.keys():
61                 if key.startswith('from '):
62                     # skip the unix from line
63                     continue
64                 if key.lower() == 'x-roundup-version':
65                     # version changes constantly, so handle it specially
66                     if new[key] != __version__:
67                         res.append('  %s: %r != %r' % (key, __version__,
68                             new[key]))
69                 elif key.lower() == 'content-type' and 'boundary=' in new[key]:
70                     # handle mime messages
71                     newmime = new[key].split('=',1)[-1].strip('"')
72                     oldmime = old.get(key, '').split('=',1)[-1].strip('"')
73                     replace ['--' + newmime] = '--' + oldmime
74                     replace ['--' + newmime + '--'] = '--' + oldmime + '--'
75                 elif new.get(key, '') != old.get(key, ''):
76                     res.append('  %s: %r != %r' % (key, old.get(key, ''),
77                         new.get(key, '')))
79             body_diff = self.compareStrings(new.fp.read(), old.fp.read(),
80                 replace=replace)
81             if body_diff:
82                 res.append('')
83                 res.extend(body_diff)
85             if res:
86                 res.insert(0, 'Generated message not correct (diff follows, expected vs. actual):')
87                 raise AssertionError, '\n'.join(res)
89     def compareStrings(self, s2, s1, replace={}):
90         '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
91            the first to be the "original" but in the calls in this file,
92            the second arg is the original. Ho hum.
93            Do replacements over the replace dict -- used for mime boundary
94         '''
95         l1 = s1.strip().split('\n')
96         l2 = [replace.get(i,i) for i in s2.strip().split('\n')]
97         if l1 == l2:
98             return
99         s = difflib.SequenceMatcher(None, l1, l2)
100         res = []
101         for value, s1s, s1e, s2s, s2e in s.get_opcodes():
102             if value == 'equal':
103                 for i in range(s1s, s1e):
104                     res.append('  %s'%l1[i])
105             elif value == 'delete':
106                 for i in range(s1s, s1e):
107                     res.append('- %s'%l1[i])
108             elif value == 'insert':
109                 for i in range(s2s, s2e):
110                     res.append('+ %s'%l2[i])
111             elif value == 'replace':
112                 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
113                     res.append('- %s'%l1[i])
114                     res.append('+ %s'%l2[j])
116         return res
118 class MailgwTestCase(unittest.TestCase, DiffHelper):
119     count = 0
120     schema = 'classic'
121     def setUp(self):
122         MailgwTestCase.count = MailgwTestCase.count + 1
124         # and open the database / "instance"
125         self.db = memorydb.create('admin')
126         self.instance = Tracker()
127         self.instance.db = self.db
128         self.instance.config = self.db.config
129         self.instance.MailGW = MailGW
131         self.chef_id = self.db.user.create(username='Chef',
132             address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
133         self.richard_id = self.db.user.create(username='richard',
134             address='richard@test.test', roles='User')
135         self.mary_id = self.db.user.create(username='mary',
136             address='mary@test.test', roles='User', realname='Contrary, Mary')
137         self.john_id = self.db.user.create(username='john',
138             address='john@test.test', roles='User', realname='John Doe',
139             alternate_addresses='jondoe@test.test\njohn.doe@test.test')
140         self.rgg_id = self.db.user.create(username='rgg',
141             address='rgg@test.test', roles='User')
143     def tearDown(self):
144         if os.path.exists(SENDMAILDEBUG):
145             os.remove(SENDMAILDEBUG)
146         self.db.close()
148     def _create_mailgw(self, message):
149         class MailGW(self.instance.MailGW):
150             def handle_message(self, message):
151                 return self._handle_message(message)
152         handler = MailGW(self.instance)
153         handler.db = self.db
154         return handler
156     def _handle_mail(self, message):
157         handler = self._create_mailgw(message)
158         handler.trapExceptions = 0
159         return handler.main(StringIO(message))
161     def _get_mail(self):
162         f = open(SENDMAILDEBUG)
163         try:
164             return f.read()
165         finally:
166             f.close()
168     def testEmptyMessage(self):
169         nodeid = self._handle_mail('''Content-Type: text/plain;
170   charset="iso-8859-1"
171 From: Chef <chef@bork.bork.bork>
172 To: issue_tracker@your.tracker.email.domain.example
173 Cc: richard@test.test
174 Reply-To: chef@bork.bork.bork
175 Message-Id: <dummy_test_message_id>
176 Subject: [issue] Testing...
178 ''')
179         assert not os.path.exists(SENDMAILDEBUG)
180         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
182     def testMessageWithFromInIt(self):
183         nodeid = self._handle_mail('''Content-Type: text/plain;
184   charset="iso-8859-1"
185 From: Chef <chef@bork.bork.bork>
186 To: issue_tracker@your.tracker.email.domain.example
187 Cc: richard@test.test
188 Reply-To: chef@bork.bork.bork
189 Message-Id: <dummy_test_message_id>
190 Subject: [issue] Testing...
192 From here to there!
193 ''')
194         assert not os.path.exists(SENDMAILDEBUG)
195         msgid = self.db.issue.get(nodeid, 'messages')[0]
196         self.assertEqual(self.db.msg.get(msgid, 'content'), 'From here to there!')
198     def doNewIssue(self):
199         nodeid = self._handle_mail('''Content-Type: text/plain;
200   charset="iso-8859-1"
201 From: Chef <chef@bork.bork.bork>
202 To: issue_tracker@your.tracker.email.domain.example
203 Cc: richard@test.test
204 Message-Id: <dummy_test_message_id>
205 Subject: [issue] Testing...
207 This is a test submission of a new issue.
208 ''')
209         assert not os.path.exists(SENDMAILDEBUG)
210         l = self.db.issue.get(nodeid, 'nosy')
211         l.sort()
212         self.assertEqual(l, [self.chef_id, self.richard_id])
213         return nodeid
215     def testNewIssue(self):
216         self.doNewIssue()
218     def testNewIssueNosy(self):
219         self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes'
220         nodeid = self._handle_mail('''Content-Type: text/plain;
221   charset="iso-8859-1"
222 From: Chef <chef@bork.bork.bork>
223 To: issue_tracker@your.tracker.email.domain.example
224 Cc: richard@test.test
225 Message-Id: <dummy_test_message_id>
226 Subject: [issue] Testing...
228 This is a test submission of a new issue.
229 ''')
230         assert not os.path.exists(SENDMAILDEBUG)
231         l = self.db.issue.get(nodeid, 'nosy')
232         l.sort()
233         self.assertEqual(l, [self.chef_id, self.richard_id])
235     def testAlternateAddress(self):
236         self._handle_mail('''Content-Type: text/plain;
237   charset="iso-8859-1"
238 From: John Doe <john.doe@test.test>
239 To: issue_tracker@your.tracker.email.domain.example
240 Message-Id: <dummy_test_message_id>
241 Subject: [issue] Testing...
243 This is a test submission of a new issue.
244 ''')
245         userlist = self.db.user.list()
246         assert not os.path.exists(SENDMAILDEBUG)
247         self.assertEqual(userlist, self.db.user.list(),
248             "user created when it shouldn't have been")
250     def testNewIssueNoClass(self):
251         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 Cc: richard@test.test
256 Message-Id: <dummy_test_message_id>
257 Subject: Testing...
259 This is a test submission of a new issue.
260 ''')
261         assert not os.path.exists(SENDMAILDEBUG)
263     def testNewIssueAuthMsg(self):
264         # TODO: fix the damn config - this is apalling
265         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
266         self._handle_mail('''Content-Type: text/plain;
267   charset="iso-8859-1"
268 From: Chef <chef@bork.bork.bork>
269 To: issue_tracker@your.tracker.email.domain.example
270 Message-Id: <dummy_test_message_id>
271 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
273 This is a test submission of a new issue.
274 ''')
275         self.compareMessages(self._get_mail(),
276 '''FROM: roundup-admin@your.tracker.email.domain.example
277 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
278 Content-Type: text/plain; charset="utf-8"
279 Subject: [issue1] Testing...
280 To: chef@bork.bork.bork, mary@test.test, richard@test.test
281 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
282 Reply-To: Roundup issue tracker
283  <issue_tracker@your.tracker.email.domain.example>
284 MIME-Version: 1.0
285 Message-Id: <dummy_test_message_id>
286 X-Roundup-Name: Roundup issue tracker
287 X-Roundup-Loop: hello
288 X-Roundup-Issue-Status: unread
289 Content-Transfer-Encoding: quoted-printable
292 New submission from Bork, Chef <chef@bork.bork.bork>:
294 This is a test submission of a new issue.
296 ----------
297 assignedto: richard
298 messages: 1
299 nosy: Chef, mary, richard
300 status: unread
301 title: Testing...
303 _______________________________________________________________________
304 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
305 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
306 _______________________________________________________________________
307 ''')
309     def testNewIssueNoAuthorInfo(self):
310         self.db.config.MAIL_ADD_AUTHORINFO = 'no'
311         self._handle_mail('''Content-Type: text/plain;
312   charset="iso-8859-1"
313 From: Chef <chef@bork.bork.bork>
314 To: issue_tracker@your.tracker.email.domain.example
315 Message-Id: <dummy_test_message_id>
316 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
318 This is a test submission of a new issue.
319 ''')
320         self.compareMessages(self._get_mail(),
321 '''FROM: roundup-admin@your.tracker.email.domain.example
322 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
323 Content-Type: text/plain; charset="utf-8"
324 Subject: [issue1] Testing...
325 To: mary@test.test, richard@test.test
326 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
327 Reply-To: Roundup issue tracker
328  <issue_tracker@your.tracker.email.domain.example>
329 MIME-Version: 1.0
330 Message-Id: <dummy_test_message_id>
331 X-Roundup-Name: Roundup issue tracker
332 X-Roundup-Loop: hello
333 X-Roundup-Issue-Status: unread
334 Content-Transfer-Encoding: quoted-printable
336 This is a test submission of a new issue.
338 ----------
339 assignedto: richard
340 messages: 1
341 nosy: Chef, mary, richard
342 status: unread
343 title: Testing...
345 _______________________________________________________________________
346 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
347 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
348 _______________________________________________________________________
349 ''')
351     def testNewIssueNoAuthorEmail(self):
352         self.db.config.MAIL_ADD_AUTHOREMAIL = 'no'
353         self._handle_mail('''Content-Type: text/plain;
354   charset="iso-8859-1"
355 From: Chef <chef@bork.bork.bork>
356 To: issue_tracker@your.tracker.email.domain.example
357 Message-Id: <dummy_test_message_id>
358 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
360 This is a test submission of a new issue.
361 ''')
362         self.compareMessages(self._get_mail(),
363 '''FROM: roundup-admin@your.tracker.email.domain.example
364 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
365 Content-Type: text/plain; charset="utf-8"
366 Subject: [issue1] Testing...
367 To: mary@test.test, richard@test.test
368 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
369 Reply-To: Roundup issue tracker
370  <issue_tracker@your.tracker.email.domain.example>
371 MIME-Version: 1.0
372 Message-Id: <dummy_test_message_id>
373 X-Roundup-Name: Roundup issue tracker
374 X-Roundup-Loop: hello
375 X-Roundup-Issue-Status: unread
376 Content-Transfer-Encoding: quoted-printable
378 New submission from Bork, Chef:
380 This is a test submission of a new issue.
382 ----------
383 assignedto: richard
384 messages: 1
385 nosy: Chef, mary, richard
386 status: unread
387 title: Testing...
389 _______________________________________________________________________
390 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
391 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
392 _______________________________________________________________________
393 ''')
395     multipart_msg = '''From: mary <mary@test.test>
396 To: issue_tracker@your.tracker.email.domain.example
397 Message-Id: <followup_dummy_id>
398 In-Reply-To: <dummy_test_message_id>
399 Subject: [issue1] Testing...
400 Content-Type: multipart/mixed; boundary="bxyzzy"
401 Content-Disposition: inline
404 --bxyzzy
405 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE"
406 Content-Disposition: inline
408 --bCsyhTFzCvuiizWE
409 Content-Type: text/plain; charset=us-ascii
410 Content-Disposition: inline
412 test attachment first text/plain
414 --bCsyhTFzCvuiizWE
415 Content-Type: application/octet-stream
416 Content-Disposition: attachment; filename="first.dvi"
417 Content-Transfer-Encoding: base64
419 SnVzdCBhIHRlc3QgAQo=
421 --bCsyhTFzCvuiizWE
422 Content-Type: text/plain; charset=us-ascii
423 Content-Disposition: inline
425 test attachment second text/plain
427 --bCsyhTFzCvuiizWE
428 Content-Type: text/html
429 Content-Disposition: inline
431 <html>
432 to be ignored.
433 </html>
435 --bCsyhTFzCvuiizWE--
437 --bxyzzy
438 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF"
439 Content-Disposition: inline
441 --bCsyhTFzCvuiizWF
442 Content-Type: text/plain; charset=us-ascii
443 Content-Disposition: inline
445 test attachment third text/plain
447 --bCsyhTFzCvuiizWF
448 Content-Type: application/octet-stream
449 Content-Disposition: attachment; filename="second.dvi"
450 Content-Transfer-Encoding: base64
452 SnVzdCBhIHRlc3QK
454 --bCsyhTFzCvuiizWF--
456 --bxyzzy--
457 '''
459     def testMultipartKeepAlternatives(self):
460         self.doNewIssue()
461         self._handle_mail(self.multipart_msg)
462         messages = self.db.issue.get('1', 'messages')
463         messages.sort()
464         msg = self.db.msg.getnode (messages[-1])
465         assert(len(msg.files) == 5)
466         names = {0 : 'first.dvi', 4 : 'second.dvi'}
467         content = {3 : 'test attachment third text/plain\n',
468                    4 : 'Just a test\n'}
469         for n, id in enumerate (msg.files):
470             f = self.db.file.getnode (id)
471             self.assertEqual(f.name, names.get (n, 'unnamed'))
472             if n in content :
473                 self.assertEqual(f.content, content [n])
474         self.assertEqual(msg.content, 'test attachment second text/plain')
476     def testMultipartDropAlternatives(self):
477         self.doNewIssue()
478         self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
479         self._handle_mail(self.multipart_msg)
480         messages = self.db.issue.get('1', 'messages')
481         messages.sort()
482         msg = self.db.msg.getnode (messages[-1])
483         assert(len(msg.files) == 2)
484         names = {1 : 'second.dvi'}
485         content = {0 : 'test attachment third text/plain\n',
486                    1 : 'Just a test\n'}
487         for n, id in enumerate (msg.files):
488             f = self.db.file.getnode (id)
489             self.assertEqual(f.name, names.get (n, 'unnamed'))
490             if n in content :
491                 self.assertEqual(f.content, content [n])
492         self.assertEqual(msg.content, 'test attachment second text/plain')
494     def testSimpleFollowup(self):
495         self.doNewIssue()
496         self._handle_mail('''Content-Type: text/plain;
497   charset="iso-8859-1"
498 From: mary <mary@test.test>
499 To: issue_tracker@your.tracker.email.domain.example
500 Message-Id: <followup_dummy_id>
501 In-Reply-To: <dummy_test_message_id>
502 Subject: [issue1] Testing...
504 This is a second followup
505 ''')
506         self.compareMessages(self._get_mail(),
507 '''FROM: roundup-admin@your.tracker.email.domain.example
508 TO: chef@bork.bork.bork, richard@test.test
509 Content-Type: text/plain; charset="utf-8"
510 Subject: [issue1] Testing...
511 To: chef@bork.bork.bork, richard@test.test
512 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
513 Reply-To: Roundup issue tracker
514  <issue_tracker@your.tracker.email.domain.example>
515 MIME-Version: 1.0
516 Message-Id: <followup_dummy_id>
517 In-Reply-To: <dummy_test_message_id>
518 X-Roundup-Name: Roundup issue tracker
519 X-Roundup-Loop: hello
520 X-Roundup-Issue-Status: chatting
521 Content-Transfer-Encoding: quoted-printable
524 Contrary, Mary <mary@test.test> added the comment:
526 This is a second followup
528 ----------
529 status: unread -> chatting
531 _______________________________________________________________________
532 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
533 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
534 _______________________________________________________________________
535 ''')
537     def testFollowup(self):
538         self.doNewIssue()
540         self._handle_mail('''Content-Type: text/plain;
541   charset="iso-8859-1"
542 From: richard <richard@test.test>
543 To: issue_tracker@your.tracker.email.domain.example
544 Message-Id: <followup_dummy_id>
545 In-Reply-To: <dummy_test_message_id>
546 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
548 This is a followup
549 ''')
550         l = self.db.issue.get('1', 'nosy')
551         l.sort()
552         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
553             self.john_id])
555         self.compareMessages(self._get_mail(),
556 '''FROM: roundup-admin@your.tracker.email.domain.example
557 TO: chef@bork.bork.bork, john@test.test, mary@test.test
558 Content-Type: text/plain; charset="utf-8"
559 Subject: [issue1] Testing...
560 To: chef@bork.bork.bork, john@test.test, mary@test.test
561 From: richard <issue_tracker@your.tracker.email.domain.example>
562 Reply-To: Roundup issue tracker
563  <issue_tracker@your.tracker.email.domain.example>
564 MIME-Version: 1.0
565 Message-Id: <followup_dummy_id>
566 In-Reply-To: <dummy_test_message_id>
567 X-Roundup-Name: Roundup issue tracker
568 X-Roundup-Loop: hello
569 X-Roundup-Issue-Status: chatting
570 Content-Transfer-Encoding: quoted-printable
573 richard <richard@test.test> added the comment:
575 This is a followup
577 ----------
578 assignedto:  -> mary
579 nosy: +john, mary
580 status: unread -> chatting
582 _______________________________________________________________________
583 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
584 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
585 _______________________________________________________________________
586 ''')
588     def testFollowupNoSubjectChange(self):
589         self.db.config.MAILGW_SUBJECT_UPDATES_TITLE = 'no'
590         self.doNewIssue()
592         self._handle_mail('''Content-Type: text/plain;
593   charset="iso-8859-1"
594 From: richard <richard@test.test>
595 To: issue_tracker@your.tracker.email.domain.example
596 Message-Id: <followup_dummy_id>
597 In-Reply-To: <dummy_test_message_id>
598 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john]
600 This is a followup
601 ''')
602         l = self.db.issue.get('1', 'nosy')
603         l.sort()
604         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
605             self.john_id])
607         self.compareMessages(self._get_mail(),
608 '''FROM: roundup-admin@your.tracker.email.domain.example
609 TO: chef@bork.bork.bork, john@test.test, mary@test.test
610 Content-Type: text/plain; charset="utf-8"
611 Subject: [issue1] Testing...
612 To: chef@bork.bork.bork, john@test.test, mary@test.test
613 From: richard <issue_tracker@your.tracker.email.domain.example>
614 Reply-To: Roundup issue tracker
615  <issue_tracker@your.tracker.email.domain.example>
616 MIME-Version: 1.0
617 Message-Id: <followup_dummy_id>
618 In-Reply-To: <dummy_test_message_id>
619 X-Roundup-Name: Roundup issue tracker
620 X-Roundup-Loop: hello
621 X-Roundup-Issue-Status: chatting
622 Content-Transfer-Encoding: quoted-printable
625 richard <richard@test.test> added the comment:
627 This is a followup
629 ----------
630 assignedto:  -> mary
631 nosy: +john, mary
632 status: unread -> chatting
634 _______________________________________________________________________
635 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
636 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
637 _______________________________________________________________________
638 ''')
639         self.assertEqual(self.db.issue.get('1','title'), 'Testing...')
641     def testFollowupExplicitSubjectChange(self):
642         self.doNewIssue()
644         self._handle_mail('''Content-Type: text/plain;
645   charset="iso-8859-1"
646 From: richard <richard@test.test>
647 To: issue_tracker@your.tracker.email.domain.example
648 Message-Id: <followup_dummy_id>
649 In-Reply-To: <dummy_test_message_id>
650 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john; title=new title]
652 This is a followup
653 ''')
654         l = self.db.issue.get('1', 'nosy')
655         l.sort()
656         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
657             self.john_id])
659         self.compareMessages(self._get_mail(),
660 '''FROM: roundup-admin@your.tracker.email.domain.example
661 TO: chef@bork.bork.bork, john@test.test, mary@test.test
662 Content-Type: text/plain; charset="utf-8"
663 Subject: [issue1] new title
664 To: chef@bork.bork.bork, john@test.test, mary@test.test
665 From: richard <issue_tracker@your.tracker.email.domain.example>
666 Reply-To: Roundup issue tracker
667  <issue_tracker@your.tracker.email.domain.example>
668 MIME-Version: 1.0
669 Message-Id: <followup_dummy_id>
670 In-Reply-To: <dummy_test_message_id>
671 X-Roundup-Name: Roundup issue tracker
672 X-Roundup-Loop: hello
673 X-Roundup-Issue-Status: chatting
674 Content-Transfer-Encoding: quoted-printable
677 richard <richard@test.test> added the comment:
679 This is a followup
681 ----------
682 assignedto:  -> mary
683 nosy: +john, mary
684 status: unread -> chatting
685 title: Testing... -> new title
687 _______________________________________________________________________
688 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
689 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
690 _______________________________________________________________________
691 ''')
693     def testNosyGeneration(self):
694         self.db.issue.create(title='test')
696         # create a nosy message
697         msg = self.db.msg.create(content='This is a test',
698             author=self.richard_id, messageid='<dummy_test_message_id>')
699         self.db.journaltag = 'richard'
700         l = self.db.issue.create(title='test', messages=[msg],
701             nosy=[self.chef_id, self.mary_id, self.john_id])
703         self.compareMessages(self._get_mail(),
704 '''FROM: roundup-admin@your.tracker.email.domain.example
705 TO: chef@bork.bork.bork, john@test.test, mary@test.test
706 Content-Type: text/plain; charset="utf-8"
707 Subject: [issue2] test
708 To: chef@bork.bork.bork, john@test.test, mary@test.test
709 From: richard <issue_tracker@your.tracker.email.domain.example>
710 Reply-To: Roundup issue tracker
711  <issue_tracker@your.tracker.email.domain.example>
712 MIME-Version: 1.0
713 Message-Id: <dummy_test_message_id>
714 X-Roundup-Name: Roundup issue tracker
715 X-Roundup-Loop: hello
716 X-Roundup-Issue-Status: unread
717 Content-Transfer-Encoding: quoted-printable
720 New submission from richard <richard@test.test>:
722 This is a test
724 ----------
725 messages: 1
726 nosy: Chef, john, mary, richard
727 status: unread
728 title: test
730 _______________________________________________________________________
731 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
732 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue2>
733 _______________________________________________________________________
734 ''')
736     def testPropertyChangeOnly(self):
737         self.doNewIssue()
738         oldvalues = self.db.getnode('issue', '1').copy()
739         oldvalues['assignedto'] = None
740         # reconstruct old behaviour: This would reuse the
741         # database-handle from the doNewIssue above which has committed
742         # as user "Chef". So we close and reopen the db as that user.
743         #self.db.close() actually don't close 'cos this empties memorydb
744         self.db = self.instance.open('Chef')
745         self.db.issue.set('1', assignedto=self.chef_id)
746         self.db.commit()
747         self.db.issue.nosymessage('1', None, oldvalues)
749         new_mail = ""
750         for line in self._get_mail().split("\n"):
751             if "Message-Id: " in line:
752                 continue
753             if "Date: " in line:
754                 continue
755             new_mail += line+"\n"
757         self.compareMessages(new_mail, """
758 FROM: roundup-admin@your.tracker.email.domain.example
759 TO: chef@bork.bork.bork, richard@test.test
760 Content-Type: text/plain; charset="utf-8"
761 Subject: [issue1] Testing...
762 To: chef@bork.bork.bork, richard@test.test
763 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
764 X-Roundup-Name: Roundup issue tracker
765 X-Roundup-Loop: hello
766 X-Roundup-Issue-Status: unread
767 X-Roundup-Version: 1.3.3
768 In-Reply-To: <dummy_test_message_id>
769 MIME-Version: 1.0
770 Reply-To: Roundup issue tracker
771  <issue_tracker@your.tracker.email.domain.example>
772 Content-Transfer-Encoding: quoted-printable
775 Change by Bork, Chef <chef@bork.bork.bork>:
778 ----------
779 assignedto:  -> Chef
781 _______________________________________________________________________
782 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
783 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
784 _______________________________________________________________________
785 """)
788     #
789     # FOLLOWUP TITLE MATCH
790     #
791     def testFollowupTitleMatch(self):
792         self.doNewIssue()
793         self._handle_mail('''Content-Type: text/plain;
794   charset="iso-8859-1"
795 From: richard <richard@test.test>
796 To: issue_tracker@your.tracker.email.domain.example
797 Message-Id: <followup_dummy_id>
798 Subject: Re: Testing... [assignedto=mary; nosy=+john]
800 This is a followup
801 ''')
802         self.compareMessages(self._get_mail(),
803 '''FROM: roundup-admin@your.tracker.email.domain.example
804 TO: chef@bork.bork.bork, john@test.test, mary@test.test
805 Content-Type: text/plain; charset="utf-8"
806 Subject: [issue1] Testing...
807 To: chef@bork.bork.bork, john@test.test, mary@test.test
808 From: richard <issue_tracker@your.tracker.email.domain.example>
809 Reply-To: Roundup issue tracker
810  <issue_tracker@your.tracker.email.domain.example>
811 MIME-Version: 1.0
812 Message-Id: <followup_dummy_id>
813 In-Reply-To: <dummy_test_message_id>
814 X-Roundup-Name: Roundup issue tracker
815 X-Roundup-Loop: hello
816 X-Roundup-Issue-Status: chatting
817 Content-Transfer-Encoding: quoted-printable
820 richard <richard@test.test> added the comment:
822 This is a followup
824 ----------
825 assignedto:  -> mary
826 nosy: +john, mary
827 status: unread -> chatting
829 _______________________________________________________________________
830 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
831 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
832 _______________________________________________________________________
833 ''')
835     def testFollowupTitleMatchMultiRe(self):
836         nodeid1 = self.doNewIssue()
837         nodeid2 = self._handle_mail('''Content-Type: text/plain;
838   charset="iso-8859-1"
839 From: richard <richard@test.test>
840 To: issue_tracker@your.tracker.email.domain.example
841 Message-Id: <followup_dummy_id>
842 Subject: Re: Testing... [assignedto=mary; nosy=+john]
844 This is a followup
845 ''')
847         nodeid3 = self._handle_mail('''Content-Type: text/plain;
848   charset="iso-8859-1"
849 From: richard <richard@test.test>
850 To: issue_tracker@your.tracker.email.domain.example
851 Message-Id: <followup2_dummy_id>
852 Subject: Ang: Re: Testing...
854 This is a followup
855 ''')
856         self.assertEqual(nodeid1, nodeid2)
857         self.assertEqual(nodeid1, nodeid3)
859     def testFollowupTitleMatchNever(self):
860         nodeid = self.doNewIssue()
861         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
862         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
863   charset="iso-8859-1"
864 From: richard <richard@test.test>
865 To: issue_tracker@your.tracker.email.domain.example
866 Message-Id: <followup_dummy_id>
867 Subject: Re: Testing...
869 This is a followup
870 '''), nodeid)
872     def testFollowupTitleMatchNeverInterval(self):
873         nodeid = self.doNewIssue()
874         # force failure of the interval
875         time.sleep(2)
876         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
877         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
878   charset="iso-8859-1"
879 From: richard <richard@test.test>
880 To: issue_tracker@your.tracker.email.domain.example
881 Message-Id: <followup_dummy_id>
882 Subject: Re: Testing...
884 This is a followup
885 '''), nodeid)
888     def testFollowupTitleMatchInterval(self):
889         nodeid = self.doNewIssue()
890         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
891         self.assertEqual(self._handle_mail('''Content-Type: text/plain;
892   charset="iso-8859-1"
893 From: richard <richard@test.test>
894 To: issue_tracker@your.tracker.email.domain.example
895 Message-Id: <followup_dummy_id>
896 Subject: Re: Testing...
898 This is a followup
899 '''), nodeid)
902     def testFollowupNosyAuthor(self):
903         self.doNewIssue()
904         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
905         self._handle_mail('''Content-Type: text/plain;
906   charset="iso-8859-1"
907 From: john@test.test
908 To: issue_tracker@your.tracker.email.domain.example
909 Message-Id: <followup_dummy_id>
910 In-Reply-To: <dummy_test_message_id>
911 Subject: [issue1] Testing...
913 This is a followup
914 ''')
916         self.compareMessages(self._get_mail(),
917 '''FROM: roundup-admin@your.tracker.email.domain.example
918 TO: chef@bork.bork.bork, richard@test.test
919 Content-Type: text/plain; charset="utf-8"
920 Subject: [issue1] Testing...
921 To: chef@bork.bork.bork, richard@test.test
922 From: John Doe <issue_tracker@your.tracker.email.domain.example>
923 Reply-To: Roundup issue tracker
924  <issue_tracker@your.tracker.email.domain.example>
925 MIME-Version: 1.0
926 Message-Id: <followup_dummy_id>
927 In-Reply-To: <dummy_test_message_id>
928 X-Roundup-Name: Roundup issue tracker
929 X-Roundup-Loop: hello
930 X-Roundup-Issue-Status: chatting
931 Content-Transfer-Encoding: quoted-printable
934 John Doe <john@test.test> added the comment:
936 This is a followup
938 ----------
939 nosy: +john
940 status: unread -> chatting
942 _______________________________________________________________________
943 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
944 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
945 _______________________________________________________________________
947 ''')
949     def testFollowupNosyRecipients(self):
950         self.doNewIssue()
951         self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
952         self._handle_mail('''Content-Type: text/plain;
953   charset="iso-8859-1"
954 From: richard@test.test
955 To: issue_tracker@your.tracker.email.domain.example
956 Cc: john@test.test
957 Message-Id: <followup_dummy_id>
958 In-Reply-To: <dummy_test_message_id>
959 Subject: [issue1] Testing...
961 This is a followup
962 ''')
963         self.compareMessages(self._get_mail(),
964 '''FROM: roundup-admin@your.tracker.email.domain.example
965 TO: chef@bork.bork.bork
966 Content-Type: text/plain; charset="utf-8"
967 Subject: [issue1] Testing...
968 To: chef@bork.bork.bork
969 From: richard <issue_tracker@your.tracker.email.domain.example>
970 Reply-To: Roundup issue tracker
971  <issue_tracker@your.tracker.email.domain.example>
972 MIME-Version: 1.0
973 Message-Id: <followup_dummy_id>
974 In-Reply-To: <dummy_test_message_id>
975 X-Roundup-Name: Roundup issue tracker
976 X-Roundup-Loop: hello
977 X-Roundup-Issue-Status: chatting
978 Content-Transfer-Encoding: quoted-printable
981 richard <richard@test.test> added the comment:
983 This is a followup
985 ----------
986 nosy: +john
987 status: unread -> chatting
989 _______________________________________________________________________
990 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
991 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
992 _______________________________________________________________________
994 ''')
996     def testFollowupNosyAuthorAndCopy(self):
997         self.doNewIssue()
998         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
999         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
1000         self._handle_mail('''Content-Type: text/plain;
1001   charset="iso-8859-1"
1002 From: john@test.test
1003 To: issue_tracker@your.tracker.email.domain.example
1004 Message-Id: <followup_dummy_id>
1005 In-Reply-To: <dummy_test_message_id>
1006 Subject: [issue1] Testing...
1008 This is a followup
1009 ''')
1010         self.compareMessages(self._get_mail(),
1011 '''FROM: roundup-admin@your.tracker.email.domain.example
1012 TO: chef@bork.bork.bork, john@test.test, richard@test.test
1013 Content-Type: text/plain; charset="utf-8"
1014 Subject: [issue1] Testing...
1015 To: chef@bork.bork.bork, john@test.test, richard@test.test
1016 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1017 Reply-To: Roundup issue tracker
1018  <issue_tracker@your.tracker.email.domain.example>
1019 MIME-Version: 1.0
1020 Message-Id: <followup_dummy_id>
1021 In-Reply-To: <dummy_test_message_id>
1022 X-Roundup-Name: Roundup issue tracker
1023 X-Roundup-Loop: hello
1024 X-Roundup-Issue-Status: chatting
1025 Content-Transfer-Encoding: quoted-printable
1028 John Doe <john@test.test> added the comment:
1030 This is a followup
1032 ----------
1033 nosy: +john
1034 status: unread -> chatting
1036 _______________________________________________________________________
1037 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1038 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1039 _______________________________________________________________________
1041 ''')
1043     def testFollowupNoNosyAuthor(self):
1044         self.doNewIssue()
1045         self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
1046         self._handle_mail('''Content-Type: text/plain;
1047   charset="iso-8859-1"
1048 From: john@test.test
1049 To: issue_tracker@your.tracker.email.domain.example
1050 Message-Id: <followup_dummy_id>
1051 In-Reply-To: <dummy_test_message_id>
1052 Subject: [issue1] Testing...
1054 This is a followup
1055 ''')
1056         self.compareMessages(self._get_mail(),
1057 '''FROM: roundup-admin@your.tracker.email.domain.example
1058 TO: chef@bork.bork.bork, richard@test.test
1059 Content-Type: text/plain; charset="utf-8"
1060 Subject: [issue1] Testing...
1061 To: chef@bork.bork.bork, richard@test.test
1062 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1063 Reply-To: Roundup issue tracker
1064  <issue_tracker@your.tracker.email.domain.example>
1065 MIME-Version: 1.0
1066 Message-Id: <followup_dummy_id>
1067 In-Reply-To: <dummy_test_message_id>
1068 X-Roundup-Name: Roundup issue tracker
1069 X-Roundup-Loop: hello
1070 X-Roundup-Issue-Status: chatting
1071 Content-Transfer-Encoding: quoted-printable
1074 John Doe <john@test.test> added the comment:
1076 This is a followup
1078 ----------
1079 status: unread -> chatting
1081 _______________________________________________________________________
1082 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1083 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1084 _______________________________________________________________________
1086 ''')
1088     def testFollowupNoNosyRecipients(self):
1089         self.doNewIssue()
1090         self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
1091         self._handle_mail('''Content-Type: text/plain;
1092   charset="iso-8859-1"
1093 From: richard@test.test
1094 To: issue_tracker@your.tracker.email.domain.example
1095 Cc: john@test.test
1096 Message-Id: <followup_dummy_id>
1097 In-Reply-To: <dummy_test_message_id>
1098 Subject: [issue1] Testing...
1100 This is a followup
1101 ''')
1102         self.compareMessages(self._get_mail(),
1103 '''FROM: roundup-admin@your.tracker.email.domain.example
1104 TO: chef@bork.bork.bork
1105 Content-Type: text/plain; charset="utf-8"
1106 Subject: [issue1] Testing...
1107 To: chef@bork.bork.bork
1108 From: richard <issue_tracker@your.tracker.email.domain.example>
1109 Reply-To: Roundup issue tracker
1110  <issue_tracker@your.tracker.email.domain.example>
1111 MIME-Version: 1.0
1112 Message-Id: <followup_dummy_id>
1113 In-Reply-To: <dummy_test_message_id>
1114 X-Roundup-Name: Roundup issue tracker
1115 X-Roundup-Loop: hello
1116 X-Roundup-Issue-Status: chatting
1117 Content-Transfer-Encoding: quoted-printable
1120 richard <richard@test.test> added the comment:
1122 This is a followup
1124 ----------
1125 status: unread -> chatting
1127 _______________________________________________________________________
1128 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1129 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1130 _______________________________________________________________________
1132 ''')
1134     def testFollowupEmptyMessage(self):
1135         self.doNewIssue()
1137         self._handle_mail('''Content-Type: text/plain;
1138   charset="iso-8859-1"
1139 From: richard <richard@test.test>
1140 To: issue_tracker@your.tracker.email.domain.example
1141 Message-Id: <followup_dummy_id>
1142 In-Reply-To: <dummy_test_message_id>
1143 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1145 ''')
1146         l = self.db.issue.get('1', 'nosy')
1147         l.sort()
1148         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1149             self.john_id])
1151         # should be no file created (ie. no message)
1152         assert not os.path.exists(SENDMAILDEBUG)
1154     def testFollowupEmptyMessageNoSubject(self):
1155         self.doNewIssue()
1157         self._handle_mail('''Content-Type: text/plain;
1158   charset="iso-8859-1"
1159 From: richard <richard@test.test>
1160 To: issue_tracker@your.tracker.email.domain.example
1161 Message-Id: <followup_dummy_id>
1162 In-Reply-To: <dummy_test_message_id>
1163 Subject: [issue1] [assignedto=mary; nosy=+john]
1165 ''')
1166         l = self.db.issue.get('1', 'nosy')
1167         l.sort()
1168         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1169             self.john_id])
1171         # should be no file created (ie. no message)
1172         assert not os.path.exists(SENDMAILDEBUG)
1174     def testNosyRemove(self):
1175         self.doNewIssue()
1177         self._handle_mail('''Content-Type: text/plain;
1178   charset="iso-8859-1"
1179 From: richard <richard@test.test>
1180 To: issue_tracker@your.tracker.email.domain.example
1181 Message-Id: <followup_dummy_id>
1182 In-Reply-To: <dummy_test_message_id>
1183 Subject: [issue1] Testing... [nosy=-richard]
1185 ''')
1186         l = self.db.issue.get('1', 'nosy')
1187         l.sort()
1188         self.assertEqual(l, [self.chef_id])
1190         # NO NOSY MESSAGE SHOULD BE SENT!
1191         assert not os.path.exists(SENDMAILDEBUG)
1193     def testNewUserAuthor(self):
1194         self.db.commit()
1195         l = self.db.user.list()
1196         l.sort()
1197         message = '''Content-Type: text/plain;
1198   charset="iso-8859-1"
1199 From: fubar <fubar@bork.bork.bork>
1200 To: issue_tracker@your.tracker.email.domain.example
1201 Message-Id: <dummy_test_message_id>
1202 Subject: [issue] Testing...
1204 This is a test submission of a new issue.
1205 '''
1206         self.db.security.role['anonymous'].permissions=[]
1207         anonid = self.db.user.lookup('anonymous')
1208         self.db.user.set(anonid, roles='Anonymous')
1209         try:
1210             self._handle_mail(message)
1211         except Unauthorized, value:
1212             body_diff = self.compareMessages(str(value), """
1213 You are not a registered user.
1215 Unknown address: fubar@bork.bork.bork
1216 """)
1217             assert not body_diff, body_diff
1218         else:
1219             raise AssertionError, "Unathorized not raised when handling mail"
1221         # Add Web Access role to anonymous, and try again to make sure
1222         # we get a "please register at:" message this time.
1223         p = [
1224             self.db.security.getPermission('Register', 'user'),
1225             self.db.security.getPermission('Web Access', None),
1226         ]
1227         self.db.security.role['anonymous'].permissions=p
1228         try:
1229             self._handle_mail(message)
1230         except Unauthorized, value:
1231             body_diff = self.compareMessages(str(value), """
1232 You are not a registered user. Please register at:
1234 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1236 ...before sending mail to the tracker.
1238 Unknown address: fubar@bork.bork.bork
1239 """)
1240             assert not body_diff, body_diff
1241         else:
1242             raise AssertionError, "Unathorized not raised when handling mail"
1244         # Make sure list of users is the same as before.
1245         m = self.db.user.list()
1246         m.sort()
1247         self.assertEqual(l, m)
1249         # now with the permission
1250         p = [
1251             self.db.security.getPermission('Register', 'user'),
1252             self.db.security.getPermission('Email Access', None),
1253         ]
1254         self.db.security.role['anonymous'].permissions=p
1255         self._handle_mail(message)
1256         m = self.db.user.list()
1257         m.sort()
1258         self.assertNotEqual(l, m)
1260     def testNewUserAuthorEncodedName(self):
1261         l = set(self.db.user.list())
1262         # From: name has Euro symbol in it
1263         message = '''Content-Type: text/plain;
1264   charset="iso-8859-1"
1265 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1266 To: issue_tracker@your.tracker.email.domain.example
1267 Message-Id: <dummy_test_message_id>
1268 Subject: [issue] Testing...
1270 This is a test submission of a new issue.
1271 '''
1272         p = [
1273             self.db.security.getPermission('Register', 'user'),
1274             self.db.security.getPermission('Email Access', None),
1275             self.db.security.getPermission('Create', 'issue'),
1276             self.db.security.getPermission('Create', 'msg'),
1277         ]
1278         self.db.security.role['anonymous'].permissions = p
1279         self._handle_mail(message)
1280         m = set(self.db.user.list())
1281         new = list(m - l)[0]
1282         name = self.db.user.get(new, 'realname')
1283         self.assertEquals(name, 'H€llo')
1285     def testUnknownUser(self):
1286         l = set(self.db.user.list())
1287         message = '''Content-Type: text/plain;
1288   charset="iso-8859-1"
1289 From: Nonexisting User <nonexisting@bork.bork.bork>
1290 To: issue_tracker@your.tracker.email.domain.example
1291 Message-Id: <dummy_test_message_id>
1292 Subject: [issue] Testing nonexisting user...
1294 This is a test submission of a new issue.
1295 '''
1296         handler = self._create_mailgw(message)
1297         # we want a bounce message:
1298         handler.trapExceptions = 1
1299         ret = handler.main(StringIO(message))
1300         self.compareMessages(self._get_mail(),
1301 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1302 TO: nonexisting@bork.bork.bork
1303 From nobody Tue Jul 14 12:04:11 2009
1304 Content-Type: multipart/mixed; boundary="===============0639262320=="
1305 MIME-Version: 1.0
1306 Subject: Failed issue tracker submission
1307 To: nonexisting@bork.bork.bork
1308 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1309 Date: Tue, 14 Jul 2009 12:04:11 +0000
1310 Precedence: bulk
1311 X-Roundup-Name: Roundup issue tracker
1312 X-Roundup-Loop: hello
1313 X-Roundup-Version: 1.4.8
1314 MIME-Version: 1.0
1316 --===============0639262320==
1317 Content-Type: text/plain; charset="us-ascii"
1318 MIME-Version: 1.0
1319 Content-Transfer-Encoding: 7bit
1323 You are not a registered user. Please register at:
1325 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1327 ...before sending mail to the tracker.
1329 Unknown address: nonexisting@bork.bork.bork
1331 --===============0639262320==
1332 Content-Type: text/plain; charset="us-ascii"
1333 MIME-Version: 1.0
1334 Content-Transfer-Encoding: 7bit
1336 Content-Type: text/plain;
1337   charset="iso-8859-1"
1338 From: Nonexisting User <nonexisting@bork.bork.bork>
1339 To: issue_tracker@your.tracker.email.domain.example
1340 Message-Id: <dummy_test_message_id>
1341 Subject: [issue] Testing nonexisting user...
1343 This is a test submission of a new issue.
1345 --===============0639262320==--
1346 ''')
1348     def testEnc01(self):
1349         self.db.user.set(self.mary_id,
1350             realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode
1351             ('latin-1').encode('utf-8'))
1352         self.doNewIssue()
1353         self._handle_mail('''Content-Type: text/plain;
1354   charset="iso-8859-1"
1355 From: mary <mary@test.test>
1356 To: issue_tracker@your.tracker.email.domain.example
1357 Message-Id: <followup_dummy_id>
1358 In-Reply-To: <dummy_test_message_id>
1359 Subject: [issue1] Testing...
1360 Content-Type: text/plain;
1361         charset="iso-8859-1"
1362 Content-Transfer-Encoding: quoted-printable
1364 A message with encoding (encoded oe =F6)
1366 ''')
1367         self.compareMessages(self._get_mail(),
1368 '''FROM: roundup-admin@your.tracker.email.domain.example
1369 TO: chef@bork.bork.bork, richard@test.test
1370 Content-Type: text/plain; charset="utf-8"
1371 Subject: [issue1] Testing...
1372 To: chef@bork.bork.bork, richard@test.test
1373 From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?=
1374  <issue_tracker@your.tracker.email.domain.example>
1375 Reply-To: Roundup issue tracker
1376  <issue_tracker@your.tracker.email.domain.example>
1377 MIME-Version: 1.0
1378 Message-Id: <followup_dummy_id>
1379 In-Reply-To: <dummy_test_message_id>
1380 X-Roundup-Name: Roundup issue tracker
1381 X-Roundup-Loop: hello
1382 X-Roundup-Issue-Status: chatting
1383 Content-Transfer-Encoding: quoted-printable
1386 =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary <mary@test.test> added the=
1387  comment:
1389 A message with encoding (encoded oe =C3=B6)
1391 ----------
1392 status: unread -> chatting
1394 _______________________________________________________________________
1395 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1396 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1397 _______________________________________________________________________
1398 ''')
1400     def testEncNonUTF8(self):
1401         self.doNewIssue()
1402         self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1403         self._handle_mail('''Content-Type: text/plain;
1404   charset="iso-8859-1"
1405 From: mary <mary@test.test>
1406 To: issue_tracker@your.tracker.email.domain.example
1407 Message-Id: <followup_dummy_id>
1408 In-Reply-To: <dummy_test_message_id>
1409 Subject: [issue1] Testing...
1410 Content-Type: text/plain;
1411         charset="iso-8859-1"
1412 Content-Transfer-Encoding: quoted-printable
1414 A message with encoding (encoded oe =F6)
1416 ''')
1417         self.compareMessages(self._get_mail(),
1418 '''FROM: roundup-admin@your.tracker.email.domain.example
1419 TO: chef@bork.bork.bork, richard@test.test
1420 Content-Type: text/plain; charset="iso-8859-1"
1421 Subject: [issue1] Testing...
1422 To: chef@bork.bork.bork, richard@test.test
1423 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1424 Reply-To: Roundup issue tracker
1425  <issue_tracker@your.tracker.email.domain.example>
1426 MIME-Version: 1.0
1427 Message-Id: <followup_dummy_id>
1428 In-Reply-To: <dummy_test_message_id>
1429 X-Roundup-Name: Roundup issue tracker
1430 X-Roundup-Loop: hello
1431 X-Roundup-Issue-Status: chatting
1432 Content-Transfer-Encoding: quoted-printable
1435 Contrary, Mary <mary@test.test> added the comment:
1437 A message with encoding (encoded oe =F6)
1439 ----------
1440 status: unread -> chatting
1442 _______________________________________________________________________
1443 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1444 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1445 _______________________________________________________________________
1446 ''')
1449     def testMultipartEnc01(self):
1450         self.doNewIssue()
1451         self._handle_mail('''Content-Type: text/plain;
1452   charset="iso-8859-1"
1453 From: mary <mary@test.test>
1454 To: issue_tracker@your.tracker.email.domain.example
1455 Message-Id: <followup_dummy_id>
1456 In-Reply-To: <dummy_test_message_id>
1457 Subject: [issue1] Testing...
1458 Content-Type: multipart/mixed;
1459         boundary="----_=_NextPart_000_01"
1461 This message is in MIME format. Since your mail reader does not understand
1462 this format, some or all of this message may not be legible.
1464 ------_=_NextPart_000_01
1465 Content-Type: text/plain;
1466         charset="iso-8859-1"
1467 Content-Transfer-Encoding: quoted-printable
1469 A message with first part encoded (encoded oe =F6)
1471 ''')
1472         self.compareMessages(self._get_mail(),
1473 '''FROM: roundup-admin@your.tracker.email.domain.example
1474 TO: chef@bork.bork.bork, richard@test.test
1475 Content-Type: text/plain; charset="utf-8"
1476 Subject: [issue1] Testing...
1477 To: chef@bork.bork.bork, richard@test.test
1478 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1479 Reply-To: Roundup issue tracker
1480  <issue_tracker@your.tracker.email.domain.example>
1481 MIME-Version: 1.0
1482 Message-Id: <followup_dummy_id>
1483 In-Reply-To: <dummy_test_message_id>
1484 X-Roundup-Name: Roundup issue tracker
1485 X-Roundup-Loop: hello
1486 X-Roundup-Issue-Status: chatting
1487 Content-Transfer-Encoding: quoted-printable
1490 Contrary, Mary <mary@test.test> added the comment:
1492 A message with first part encoded (encoded oe =C3=B6)
1494 ----------
1495 status: unread -> chatting
1497 _______________________________________________________________________
1498 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1499 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1500 _______________________________________________________________________
1501 ''')
1503     def testContentDisposition(self):
1504         self.doNewIssue()
1505         self._handle_mail('''Content-Type: text/plain;
1506   charset="iso-8859-1"
1507 From: mary <mary@test.test>
1508 To: issue_tracker@your.tracker.email.domain.example
1509 Message-Id: <followup_dummy_id>
1510 In-Reply-To: <dummy_test_message_id>
1511 Subject: [issue1] Testing...
1512 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
1513 Content-Disposition: inline
1516 --bCsyhTFzCvuiizWE
1517 Content-Type: text/plain; charset=us-ascii
1518 Content-Disposition: inline
1520 test attachment binary
1522 --bCsyhTFzCvuiizWE
1523 Content-Type: application/octet-stream
1524 Content-Disposition: attachment; filename="main.dvi"
1525 Content-Transfer-Encoding: base64
1527 SnVzdCBhIHRlc3QgAQo=
1529 --bCsyhTFzCvuiizWE--
1530 ''')
1531         messages = self.db.issue.get('1', 'messages')
1532         messages.sort()
1533         file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
1534         self.assertEqual(file.name, 'main.dvi')
1535         self.assertEqual(file.content, 'Just a test \001\n')
1537     def testFollowupStupidQuoting(self):
1538         self.doNewIssue()
1540         self._handle_mail('''Content-Type: text/plain;
1541   charset="iso-8859-1"
1542 From: richard <richard@test.test>
1543 To: issue_tracker@your.tracker.email.domain.example
1544 Message-Id: <followup_dummy_id>
1545 In-Reply-To: <dummy_test_message_id>
1546 Subject: Re: "[issue1] Testing... "
1548 This is a followup
1549 ''')
1550         self.compareMessages(self._get_mail(),
1551 '''FROM: roundup-admin@your.tracker.email.domain.example
1552 TO: chef@bork.bork.bork
1553 Content-Type: text/plain; charset="utf-8"
1554 Subject: [issue1] Testing...
1555 To: chef@bork.bork.bork
1556 From: richard <issue_tracker@your.tracker.email.domain.example>
1557 Reply-To: Roundup issue tracker
1558  <issue_tracker@your.tracker.email.domain.example>
1559 MIME-Version: 1.0
1560 Message-Id: <followup_dummy_id>
1561 In-Reply-To: <dummy_test_message_id>
1562 X-Roundup-Name: Roundup issue tracker
1563 X-Roundup-Loop: hello
1564 X-Roundup-Issue-Status: chatting
1565 Content-Transfer-Encoding: quoted-printable
1568 richard <richard@test.test> added the comment:
1570 This is a followup
1572 ----------
1573 status: unread -> chatting
1575 _______________________________________________________________________
1576 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1577 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1578 _______________________________________________________________________
1579 ''')
1581     def testEmailQuoting(self):
1582         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
1583         self.innerTestQuoting('''This is a followup
1584 ''')
1586     def testEmailQuotingRemove(self):
1587         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
1588         self.innerTestQuoting('''Blah blah wrote:
1589 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
1590 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
1593 This is a followup
1594 ''')
1596     def innerTestQuoting(self, expect):
1597         nodeid = self.doNewIssue()
1599         messages = self.db.issue.get(nodeid, 'messages')
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: Re: [issue1] Testing...
1609 Blah blah wrote:
1610 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
1611 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
1614 This is a followup
1615 ''')
1616         # figure the new message id
1617         newmessages = self.db.issue.get(nodeid, 'messages')
1618         for msg in messages:
1619             newmessages.remove(msg)
1620         messageid = newmessages[0]
1622         self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
1624     def testUserLookup(self):
1625         i = self.db.user.create(username='user1', address='user1@foo.com')
1626         self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
1627         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
1628         i = self.db.user.create(username='user2', address='USER2@foo.com')
1629         self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
1630         self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
1632     def testUserAlternateLookup(self):
1633         i = self.db.user.create(username='user1', address='user1@foo.com',
1634                                 alternate_addresses='user1@bar.com')
1635         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
1636         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
1638     def testUserCreate(self):
1639         i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
1640         self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
1642     def testRFC2822(self):
1643         ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
1644         unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
1645         unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
1646         self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
1647         self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
1649     def testRegistrationConfirmation(self):
1650         otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
1651         self.db.getOTKManager().set(otk, username='johannes')
1652         self._handle_mail('''Content-Type: text/plain;
1653   charset="iso-8859-1"
1654 From: Chef <chef@bork.bork.bork>
1655 To: issue_tracker@your.tracker.email.domain.example
1656 Cc: richard@test.test
1657 Message-Id: <dummy_test_message_id>
1658 Subject: Re: Complete your registration to Roundup issue tracker
1659  -- key %s
1661 This is a test confirmation of registration.
1662 ''' % otk)
1663         self.db.user.lookup('johannes')
1665     def testFollowupOnNonIssue(self):
1666         self.db.keyword.create(name='Foo')
1667         self._handle_mail('''Content-Type: text/plain;
1668   charset="iso-8859-1"
1669 From: richard <richard@test.test>
1670 To: issue_tracker@your.tracker.email.domain.example
1671 Message-Id: <followup_dummy_id>
1672 In-Reply-To: <dummy_test_message_id>
1673 Subject: [keyword1] Testing... [name=Bar]
1675 ''')
1676         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1678     def testResentFrom(self):
1679         nodeid = self._handle_mail('''Content-Type: text/plain;
1680   charset="iso-8859-1"
1681 From: Chef <chef@bork.bork.bork>
1682 Resent-From: mary <mary@test.test>
1683 To: issue_tracker@your.tracker.email.domain.example
1684 Cc: richard@test.test
1685 Message-Id: <dummy_test_message_id>
1686 Subject: [issue] Testing...
1688 This is a test submission of a new issue.
1689 ''')
1690         assert not os.path.exists(SENDMAILDEBUG)
1691         l = self.db.issue.get(nodeid, 'nosy')
1692         l.sort()
1693         self.assertEqual(l, [self.richard_id, self.mary_id])
1694         return nodeid
1696     def testDejaVu(self):
1697         self.assertRaises(IgnoreLoop, self._handle_mail,
1698             '''Content-Type: text/plain;
1699   charset="iso-8859-1"
1700 From: Chef <chef@bork.bork.bork>
1701 X-Roundup-Loop: hello
1702 To: issue_tracker@your.tracker.email.domain.example
1703 Cc: richard@test.test
1704 Message-Id: <dummy_test_message_id>
1705 Subject: Re: [issue] Testing...
1707 Hi, I've been mis-configured to loop messages back to myself.
1708 ''')
1710     def testItsBulkStupid(self):
1711         self.assertRaises(IgnoreBulk, self._handle_mail,
1712             '''Content-Type: text/plain;
1713   charset="iso-8859-1"
1714 From: Chef <chef@bork.bork.bork>
1715 Precedence: bulk
1716 To: issue_tracker@your.tracker.email.domain.example
1717 Cc: richard@test.test
1718 Message-Id: <dummy_test_message_id>
1719 Subject: Re: [issue] Testing...
1721 Hi, I'm on holidays, and this is a dumb auto-responder.
1722 ''')
1724     def testAutoReplyEmailsAreIgnored(self):
1725         self.assertRaises(IgnoreBulk, self._handle_mail,
1726             '''Content-Type: text/plain;
1727   charset="iso-8859-1"
1728 From: Chef <chef@bork.bork.bork>
1729 To: issue_tracker@your.tracker.email.domain.example
1730 Cc: richard@test.test
1731 Message-Id: <dummy_test_message_id>
1732 Subject: Re: [issue] Out of office AutoReply: Back next week
1734 Hi, I am back in the office next week
1735 ''')
1737     def testNoSubject(self):
1738         self.assertRaises(MailUsageError, self._handle_mail,
1739             '''Content-Type: text/plain;
1740   charset="iso-8859-1"
1741 From: Chef <chef@bork.bork.bork>
1742 To: issue_tracker@your.tracker.email.domain.example
1743 Cc: richard@test.test
1744 Reply-To: chef@bork.bork.bork
1745 Message-Id: <dummy_test_message_id>
1747 ''')
1749     #
1750     # TEST FOR INVALID DESIGNATOR HANDLING
1751     #
1752     def testInvalidDesignator(self):
1753         self.assertRaises(MailUsageError, self._handle_mail,
1754             '''Content-Type: text/plain;
1755   charset="iso-8859-1"
1756 From: Chef <chef@bork.bork.bork>
1757 To: issue_tracker@your.tracker.email.domain.example
1758 Subject: [frobulated] testing
1759 Cc: richard@test.test
1760 Reply-To: chef@bork.bork.bork
1761 Message-Id: <dummy_test_message_id>
1763 ''')
1764         self.assertRaises(MailUsageError, self._handle_mail,
1765             '''Content-Type: text/plain;
1766   charset="iso-8859-1"
1767 From: Chef <chef@bork.bork.bork>
1768 To: issue_tracker@your.tracker.email.domain.example
1769 Subject: [issue12345] testing
1770 Cc: richard@test.test
1771 Reply-To: chef@bork.bork.bork
1772 Message-Id: <dummy_test_message_id>
1774 ''')
1776     def testInvalidClassLoose(self):
1777         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1778         nodeid = self._handle_mail('''Content-Type: text/plain;
1779   charset="iso-8859-1"
1780 From: Chef <chef@bork.bork.bork>
1781 To: issue_tracker@your.tracker.email.domain.example
1782 Subject: [frobulated] testing
1783 Cc: richard@test.test
1784 Reply-To: chef@bork.bork.bork
1785 Message-Id: <dummy_test_message_id>
1787 ''')
1788         assert not os.path.exists(SENDMAILDEBUG)
1789         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1790             '[frobulated] testing')
1792     def testInvalidClassLooseReply(self):
1793         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1794         nodeid = self._handle_mail('''Content-Type: text/plain;
1795   charset="iso-8859-1"
1796 From: Chef <chef@bork.bork.bork>
1797 To: issue_tracker@your.tracker.email.domain.example
1798 Subject: Re: [frobulated] testing
1799 Cc: richard@test.test
1800 Reply-To: chef@bork.bork.bork
1801 Message-Id: <dummy_test_message_id>
1803 ''')
1804         assert not os.path.exists(SENDMAILDEBUG)
1805         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1806             '[frobulated] testing')
1808     def testInvalidClassLoose(self):
1809         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1810         nodeid = self._handle_mail('''Content-Type: text/plain;
1811   charset="iso-8859-1"
1812 From: Chef <chef@bork.bork.bork>
1813 To: issue_tracker@your.tracker.email.domain.example
1814 Subject: [issue1234] testing
1815 Cc: richard@test.test
1816 Reply-To: chef@bork.bork.bork
1817 Message-Id: <dummy_test_message_id>
1819 ''')
1820         assert not os.path.exists(SENDMAILDEBUG)
1821         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1822             '[issue1234] testing')
1824     def testClassLooseOK(self):
1825         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1826         self.db.keyword.create(name='Foo')
1827         nodeid = self._handle_mail('''Content-Type: text/plain;
1828   charset="iso-8859-1"
1829 From: Chef <chef@bork.bork.bork>
1830 To: issue_tracker@your.tracker.email.domain.example
1831 Subject: [keyword1] Testing... [name=Bar]
1832 Cc: richard@test.test
1833 Reply-To: chef@bork.bork.bork
1834 Message-Id: <dummy_test_message_id>
1836 ''')
1837         assert not os.path.exists(SENDMAILDEBUG)
1838         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1840     def testClassStrictInvalid(self):
1841         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
1842         self.instance.config.MAILGW_DEFAULT_CLASS = ''
1844         message = '''Content-Type: text/plain;
1845   charset="iso-8859-1"
1846 From: Chef <chef@bork.bork.bork>
1847 To: issue_tracker@your.tracker.email.domain.example
1848 Subject: Testing...
1849 Cc: richard@test.test
1850 Reply-To: chef@bork.bork.bork
1851 Message-Id: <dummy_test_message_id>
1853 '''
1854         self.assertRaises(MailUsageError, self._handle_mail, message)
1856     def testClassStrictValid(self):
1857         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
1858         self.instance.config.MAILGW_DEFAULT_CLASS = ''
1860         nodeid = self._handle_mail('''Content-Type: text/plain;
1861   charset="iso-8859-1"
1862 From: Chef <chef@bork.bork.bork>
1863 To: issue_tracker@your.tracker.email.domain.example
1864 Subject: [issue] Testing...
1865 Cc: richard@test.test
1866 Reply-To: chef@bork.bork.bork
1867 Message-Id: <dummy_test_message_id>
1869 ''')
1871         assert not os.path.exists(SENDMAILDEBUG)
1872         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
1874     #
1875     # TEST FOR INVALID COMMANDS HANDLING
1876     #
1877     def testInvalidCommands(self):
1878         self.assertRaises(MailUsageError, self._handle_mail,
1879             '''Content-Type: text/plain;
1880   charset="iso-8859-1"
1881 From: Chef <chef@bork.bork.bork>
1882 To: issue_tracker@your.tracker.email.domain.example
1883 Subject: testing [frobulated]
1884 Cc: richard@test.test
1885 Reply-To: chef@bork.bork.bork
1886 Message-Id: <dummy_test_message_id>
1888 ''')
1890     def testInvalidCommandPassthrough(self):
1891         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
1892         nodeid = self._handle_mail('''Content-Type: text/plain;
1893   charset="iso-8859-1"
1894 From: Chef <chef@bork.bork.bork>
1895 To: issue_tracker@your.tracker.email.domain.example
1896 Subject: testing [frobulated]
1897 Cc: richard@test.test
1898 Reply-To: chef@bork.bork.bork
1899 Message-Id: <dummy_test_message_id>
1901 ''')
1902         assert not os.path.exists(SENDMAILDEBUG)
1903         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1904             'testing [frobulated]')
1906     def testInvalidCommandPassthroughLoose(self):
1907         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
1908         nodeid = self._handle_mail('''Content-Type: text/plain;
1909   charset="iso-8859-1"
1910 From: Chef <chef@bork.bork.bork>
1911 To: issue_tracker@your.tracker.email.domain.example
1912 Subject: testing [frobulated]
1913 Cc: richard@test.test
1914 Reply-To: chef@bork.bork.bork
1915 Message-Id: <dummy_test_message_id>
1917 ''')
1918         assert not os.path.exists(SENDMAILDEBUG)
1919         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1920             'testing [frobulated]')
1922     def testInvalidCommandPassthroughLooseOK(self):
1923         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
1924         nodeid = self._handle_mail('''Content-Type: text/plain;
1925   charset="iso-8859-1"
1926 From: Chef <chef@bork.bork.bork>
1927 To: issue_tracker@your.tracker.email.domain.example
1928 Subject: testing [assignedto=mary]
1929 Cc: richard@test.test
1930 Reply-To: chef@bork.bork.bork
1931 Message-Id: <dummy_test_message_id>
1933 ''')
1934         assert not os.path.exists(SENDMAILDEBUG)
1935         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
1936         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
1938     def testCommandDelimiters(self):
1939         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1940         nodeid = self._handle_mail('''Content-Type: text/plain;
1941   charset="iso-8859-1"
1942 From: Chef <chef@bork.bork.bork>
1943 To: issue_tracker@your.tracker.email.domain.example
1944 Subject: testing {assignedto=mary}
1945 Cc: richard@test.test
1946 Reply-To: chef@bork.bork.bork
1947 Message-Id: <dummy_test_message_id>
1949 ''')
1950         assert not os.path.exists(SENDMAILDEBUG)
1951         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
1952         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
1954     def testPrefixDelimiters(self):
1955         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1956         self.db.keyword.create(name='Foo')
1957         self._handle_mail('''Content-Type: text/plain;
1958   charset="iso-8859-1"
1959 From: richard <richard@test.test>
1960 To: issue_tracker@your.tracker.email.domain.example
1961 Message-Id: <followup_dummy_id>
1962 In-Reply-To: <dummy_test_message_id>
1963 Subject: {keyword1} Testing... {name=Bar}
1965 ''')
1966         assert not os.path.exists(SENDMAILDEBUG)
1967         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1969     def testCommandDelimitersIgnore(self):
1970         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1971         nodeid = self._handle_mail('''Content-Type: text/plain;
1972   charset="iso-8859-1"
1973 From: Chef <chef@bork.bork.bork>
1974 To: issue_tracker@your.tracker.email.domain.example
1975 Subject: testing [assignedto=mary]
1976 Cc: richard@test.test
1977 Reply-To: chef@bork.bork.bork
1978 Message-Id: <dummy_test_message_id>
1980 ''')
1981         assert not os.path.exists(SENDMAILDEBUG)
1982         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1983             'testing [assignedto=mary]')
1984         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
1986     def testReplytoMatch(self):
1987         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1988         nodeid = self.doNewIssue()
1989         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1990   charset="iso-8859-1"
1991 From: Chef <chef@bork.bork.bork>
1992 To: issue_tracker@your.tracker.email.domain.example
1993 Message-Id: <dummy_test_message_id2>
1994 In-Reply-To: <dummy_test_message_id>
1995 Subject: Testing...
1997 Followup message.
1998 ''')
2000         nodeid3 = self._handle_mail('''Content-Type: text/plain;
2001   charset="iso-8859-1"
2002 From: Chef <chef@bork.bork.bork>
2003 To: issue_tracker@your.tracker.email.domain.example
2004 Message-Id: <dummy_test_message_id3>
2005 In-Reply-To: <dummy_test_message_id2>
2006 Subject: Testing...
2008 Yet another message in the same thread/issue.
2009 ''')
2011         self.assertEqual(nodeid, nodeid2)
2012         self.assertEqual(nodeid, nodeid3)
2014     def testHelpSubject(self):
2015         message = '''Content-Type: text/plain;
2016   charset="iso-8859-1"
2017 From: Chef <chef@bork.bork.bork>
2018 To: issue_tracker@your.tracker.email.domain.example
2019 Message-Id: <dummy_test_message_id2>
2020 In-Reply-To: <dummy_test_message_id>
2021 Subject: hElp
2024 '''
2025         self.assertRaises(MailUsageHelp, self._handle_mail, message)
2027     def testMaillistSubject(self):
2028         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
2029         self.db.keyword.create(name='Foo')
2030         self._handle_mail('''Content-Type: text/plain;
2031   charset="iso-8859-1"
2032 From: Chef <chef@bork.bork.bork>
2033 To: issue_tracker@your.tracker.email.domain.example
2034 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
2035 Cc: richard@test.test
2036 Reply-To: chef@bork.bork.bork
2037 Message-Id: <dummy_test_message_id>
2039 ''')
2041         assert not os.path.exists(SENDMAILDEBUG)
2042         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2044     def testUnknownPrefixSubject(self):
2045         self.db.keyword.create(name='Foo')
2046         self._handle_mail('''Content-Type: text/plain;
2047   charset="iso-8859-1"
2048 From: Chef <chef@bork.bork.bork>
2049 To: issue_tracker@your.tracker.email.domain.example
2050 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
2051 Cc: richard@test.test
2052 Reply-To: chef@bork.bork.bork
2053 Message-Id: <dummy_test_message_id>
2055 ''')
2057         assert not os.path.exists(SENDMAILDEBUG)
2058         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2060     def testOneCharSubject(self):
2061         message = '''Content-Type: text/plain;
2062   charset="iso-8859-1"
2063 From: Chef <chef@bork.bork.bork>
2064 To: issue_tracker@your.tracker.email.domain.example
2065 Subject: b
2066 Cc: richard@test.test
2067 Reply-To: chef@bork.bork.bork
2068 Message-Id: <dummy_test_message_id>
2070 '''
2071         try:
2072             self._handle_mail(message)
2073         except MailUsageError:
2074             self.fail('MailUsageError raised')
2076     def testIssueidLast(self):
2077         nodeid1 = self.doNewIssue()
2078         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2079   charset="iso-8859-1"
2080 From: mary <mary@test.test>
2081 To: issue_tracker@your.tracker.email.domain.example
2082 Message-Id: <followup_dummy_id>
2083 In-Reply-To: <dummy_test_message_id>
2084 Subject: New title [issue1]
2086 This is a second followup
2087 ''')
2089         assert nodeid1 == nodeid2
2090         self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
2092     def testSecurityMessagePermissionContent(self):
2093         id = self.doNewIssue()
2094         issue = self.db.issue.getnode (id)
2095         self.db.security.addRole(name='Nomsg')
2096         self.db.security.addPermissionToRole('Nomsg', 'Email Access')
2097         for cl in 'issue', 'file', 'keyword':
2098             for p in 'View', 'Edit', 'Create':
2099                 self.db.security.addPermissionToRole('Nomsg', p, cl)
2100         self.db.user.set(self.mary_id, roles='Nomsg')
2101         nodeid = self._handle_mail('''Content-Type: text/plain;
2102   charset="iso-8859-1"
2103 From: Chef <chef@bork.bork.bork>
2104 To: issue_tracker@your.tracker.email.domain.example
2105 Message-Id: <dummy_test_message_id_2>
2106 Subject: [issue%(id)s] Testing... [nosy=+mary]
2108 Just a test reply
2109 '''%locals())
2110         assert os.path.exists(SENDMAILDEBUG)
2111         self.compareMessages(self._get_mail(),
2112 '''FROM: roundup-admin@your.tracker.email.domain.example
2113 TO: chef@bork.bork.bork, richard@test.test
2114 Content-Type: text/plain; charset="utf-8"
2115 Subject: [issue1] Testing...
2116 To: richard@test.test
2117 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
2118 Reply-To: Roundup issue tracker
2119  <issue_tracker@your.tracker.email.domain.example>
2120 MIME-Version: 1.0
2121 Message-Id: <dummy_test_message_id_2>
2122 In-Reply-To: <dummy_test_message_id>
2123 X-Roundup-Name: Roundup issue tracker
2124 X-Roundup-Loop: hello
2125 X-Roundup-Issue-Status: chatting
2126 Content-Transfer-Encoding: quoted-printable
2129 Bork, Chef <chef@bork.bork.bork> added the comment:
2131 Just a test reply
2133 ----------
2134 nosy: +mary
2135 status: unread -> chatting
2137 _______________________________________________________________________
2138 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2139 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2140 _______________________________________________________________________
2141 ''')
2143     def testOutlookAttachment(self):
2144         message = '''X-MimeOLE: Produced By Microsoft Exchange V6.5
2145 Content-class: urn:content-classes:message
2146 MIME-Version: 1.0
2147 Content-Type: multipart/mixed;
2148         boundary="----_=_NextPart_001_01CACA65.40A51CBC"
2149 Subject: Example of a failed outlook attachment e-mail
2150 Date: Tue, 23 Mar 2010 01:43:44 -0700
2151 Message-ID: <CA37F17219784343816CA6613D2E339205E7D0F9@nrcwstexb1.nrc.ca>
2152 X-MS-Has-Attach: yes
2153 X-MS-TNEF-Correlator: 
2154 Thread-Topic: Example of a failed outlook attachment e-mail
2155 Thread-Index: AcrKJo/t3pUBBwTpSwWNE3LE67UBDQ==
2156 From: "Hugh" <richard@test.test>
2157 To: <richard@test.test>
2158 X-OriginalArrivalTime: 23 Mar 2010 08:45:57.0350 (UTC) FILETIME=[41893860:01CACA65]
2160 This is a multi-part message in MIME format.
2162 ------_=_NextPart_001_01CACA65.40A51CBC
2163 Content-Type: multipart/alternative;
2164         boundary="----_=_NextPart_002_01CACA65.40A51CBC"
2167 ------_=_NextPart_002_01CACA65.40A51CBC
2168 Content-Type: text/plain;
2169         charset="us-ascii"
2170 Content-Transfer-Encoding: quoted-printable
2173 Hi Richard,
2175 I suppose this isn't the exact message that was sent but is a resend of
2176 one of my trial messages that failed.  For your benefit I changed the
2177 subject line and am adding these words to the message body.  Should
2178 still be as problematic, but if you like I can resend an exact copy of a
2179 failed message changing nothing except putting your address instead of
2180 our tracker.
2182 Thanks very much for taking time to look into this.  Much appreciated.
2184  <<battery backup>>=20
2186 ------_=_NextPart_002_01CACA65.40A51CBC
2187 Content-Type: text/html;
2188         charset="us-ascii"
2189 Content-Transfer-Encoding: quoted-printable
2191 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2192 <HTML>
2193 <HEAD>
2194 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2195 charset=3Dus-ascii">
2196 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2197 6.5.7654.12">
2198 <TITLE>Example of a failed outlook attachment e-mail</TITLE>
2199 </HEAD>
2200 <BODY>
2201 <!-- Converted from text/rtf format -->
2202 <BR>
2204 <P><FONT SIZE=3D2 FACE=3D"Arial">Hi Richard,</FONT>
2205 </P>
2207 <P><FONT SIZE=3D2 FACE=3D"Arial">I suppose this isn't the exact message =
2208 that was sent but is a resend of one of my trial messages that =
2209 failed.&nbsp; For your benefit I changed the subject line and am adding =
2210 these words to the message body.&nbsp; Should still be as problematic, =
2211 but if you like I can resend an exact copy of a failed message changing =
2212 nothing except putting your address instead of our tracker.</FONT></P>
2214 <P><FONT SIZE=3D2 FACE=3D"Arial">Thanks very much for taking time to =
2215 look into this.&nbsp; Much appreciated.</FONT>
2216 </P>
2217 <BR>
2219 <P><FONT FACE=3D"Arial" SIZE=3D2 COLOR=3D"#000000"> &lt;&lt;battery =
2220 backup&gt;&gt; </FONT>
2221 </P>
2223 </BODY>
2224 </HTML>
2225 ------_=_NextPart_002_01CACA65.40A51CBC--
2227 ------_=_NextPart_001_01CACA65.40A51CBC
2228 Content-Type: message/rfc822
2229 Content-Transfer-Encoding: 7bit
2231 X-MimeOLE: Produced By Microsoft Exchange V6.5
2232 MIME-Version: 1.0
2233 Content-Type: multipart/alternative;
2234         boundary="----_=_NextPart_003_01CAC15A.29717800"
2235 X-OriginalArrivalTime: 11 Mar 2010 20:33:51.0249 (UTC) FILETIME=[28FEE010:01CAC15A]
2236 Content-class: urn:content-classes:message
2237 Subject: battery backup
2238 Date: Thu, 11 Mar 2010 13:33:43 -0700
2239 Message-ID: <p06240809c7bf02f9624c@[128.114.22.203]>
2240 X-MS-Has-Attach: 
2241 X-MS-TNEF-Correlator: 
2242 Thread-Topic: battery backup
2243 Thread-Index: AcrBWimtulTrSvBdQ2CcfZ8lyQdxmQ==
2244 From: "Jerry" <jerry@test.test>
2245 To: "Hugh" <hugh@test.test>
2247 This is a multi-part message in MIME format.
2249 ------_=_NextPart_003_01CAC15A.29717800
2250 Content-Type: text/plain;
2251         charset="iso-8859-1"
2252 Content-Transfer-Encoding: quoted-printable
2254 Dear Hugh,
2255         A car batter has an energy capacity of ~ 500Wh.  A UPS=20
2256 battery is worse than this.
2258 if we need to provied 100kW for 30 minutes that will take 100 car=20
2259 batteries.  This seems like an awful lot of batteries.
2261 Of course I like your idea of making the time 1 minute, so we get to=20
2262 a more modest number of batteries
2264 Jerry
2267 ------_=_NextPart_003_01CAC15A.29717800
2268 Content-Type: text/html;
2269         charset="iso-8859-1"
2270 Content-Transfer-Encoding: quoted-printable
2272 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2273 <HTML>
2274 <HEAD>
2275 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2276 charset=3Diso-8859-1">
2277 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2278 6.5.7654.12">
2279 <TITLE>battery backup</TITLE>
2280 </HEAD>
2281 <BODY>
2282 <!-- Converted from text/plain format -->
2284 <P><FONT SIZE=3D2>Dear Hugh,</FONT>
2286 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT SIZE=3D2>A car =
2287 batter has an energy capacity of ~ 500Wh.&nbsp; A UPS </FONT>
2289 <BR><FONT SIZE=3D2>battery is worse than this.</FONT>
2290 </P>
2292 <P><FONT SIZE=3D2>if we need to provied 100kW for 30 minutes that will =
2293 take 100 car </FONT>
2295 <BR><FONT SIZE=3D2>batteries.&nbsp; This seems like an awful lot of =
2296 batteries.</FONT>
2297 </P>
2299 <P><FONT SIZE=3D2>Of course I like your idea of making the time 1 =
2300 minute, so we get to </FONT>
2302 <BR><FONT SIZE=3D2>a more modest number of batteries</FONT>
2303 </P>
2305 <P><FONT SIZE=3D2>Jerry</FONT>
2306 </P>
2308 </BODY>
2309 </HTML>
2310 ------_=_NextPart_003_01CAC15A.29717800--
2312 ------_=_NextPart_001_01CACA65.40A51CBC--
2313 '''
2314         nodeid = self._handle_mail(message)
2315         assert not os.path.exists(SENDMAILDEBUG)
2316         msgid = self.db.issue.get(nodeid, 'messages')[0]
2317         self.assert_(self.db.msg.get(msgid, 'content').startswith('Hi Richard'))
2318         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1', '2'])
2319         fileid = self.db.msg.get(msgid, 'files')[0]
2320         self.assertEqual(self.db.file.get(fileid, 'type'), 'text/html')
2321         fileid = self.db.msg.get(msgid, 'files')[1]
2322         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2324     def testForwardedMessageAttachment(self):
2325         message = '''Return-Path: <rgg@test.test>
2326 Received: from localhost(127.0.0.1), claiming to be "[115.130.26.69]"
2327 via SMTP by localhost, id smtpdAAApLaWrq; Tue Apr 13 23:10:05 2010
2328 Message-ID: <4BC4F9C7.50409@test.test>
2329 Date: Wed, 14 Apr 2010 09:09:59 +1000
2330 From: Rupert Goldie <rgg@test.test>
2331 User-Agent: Thunderbird 2.0.0.24 (Windows/20100228)
2332 MIME-Version: 1.0
2333 To: ekit issues <issues@test.test>
2334 Subject: [Fwd: PHP ERROR (fb)] post limit reached
2335 Content-Type: multipart/mixed; boundary="------------000807090608060304010403"
2337 This is a multi-part message in MIME format.
2338 --------------000807090608060304010403
2339 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
2340 Content-Transfer-Encoding: 7bit
2342 Catch this exception and log it without emailing.
2344 --------------000807090608060304010403
2345 Content-Type: message/rfc822; name="PHP ERROR (fb).eml"
2346 Content-Transfer-Encoding: 7bit
2347 Content-Disposition: inline; filename="PHP ERROR (fb).eml"
2349 Return-Path: <ektravj@test.test>
2350 X-Sieve: CMU Sieve 2.2
2351 via SMTP by crown.off.ekorp.com, id smtpdAAA1JaW1o; Tue Apr 13 23:01:04 2010
2352 X-Virus-Scanned: by amavisd-new at ekit.com
2353 To: facebook-errors@test.test
2354 From: ektravj@test.test
2355 Subject: PHP ERROR (fb)
2356 Message-Id: <20100413230100.D601D27E84@mail2.elax3.ekorp.com>
2357 Date: Tue, 13 Apr 2010 23:01:00 +0000 (UTC)
2359 [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
2360 Stack trace:
2361 #0 /app/01/www/virtual/fb.ekit.com/htdocs/gateway/ekit/feed/index.php(178): fb_exceptions(Object(FacebookRestClientException))
2362 #1 {main}
2363  thrown in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php on line 280
2366 --------------000807090608060304010403--
2367 '''
2368         nodeid = self._handle_mail(message)
2369         assert not os.path.exists(SENDMAILDEBUG)
2370         msgid = self.db.issue.get(nodeid, 'messages')[0]
2371         self.assertEqual(self.db.msg.get(msgid, 'content'),
2372             'Catch this exception and log it without emailing.')
2373         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1'])
2374         fileid = self.db.msg.get(msgid, 'files')[0]
2375         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2377 def test_suite():
2378     suite = unittest.TestSuite()
2379     suite.addTest(unittest.makeSuite(MailgwTestCase))
2380     return suite
2382 if __name__ == '__main__':
2383     runner = unittest.TextTestRunner()
2384     unittest.main(testRunner=runner)
2386 # vim: set filetype=python sts=4 sw=4 et si :