Code

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