Code

6b64a30caae047aa1bfa85d54617ea632f614726
[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 testMultipartSeveralAttachmentMessages(self):
629         self.doNewIssue()
630         self._handle_mail(self.multipart_msg)
631         messages = self.db.issue.get('1', 'messages')
632         messages.sort()
633         self.assertEqual(messages[-1], '2')
634         msg = self.db.msg.getnode (messages[-1])
635         self.assertEqual(len(msg.files), 5)
636         issue = self.db.issue.getnode ('1')
637         self.assertEqual(len(issue.files), 5)
638         names = {0 : 'first.dvi', 4 : 'second.dvi'}
639         content = {3 : 'test attachment third text/plain\n',
640                    4 : 'Just a test\n'}
641         for n, id in enumerate (msg.files):
642             f = self.db.file.getnode (id)
643             self.assertEqual(f.name, names.get (n, 'unnamed'))
644             if n in content :
645                 self.assertEqual(f.content, content [n])
646         self.assertEqual(msg.content, 'test attachment second text/plain')
647         self.assertEqual(msg.files, ['1', '2', '3', '4', '5'])
648         self.assertEqual(issue.files, ['1', '2', '3', '4', '5'])
650         self._handle_mail(self.multipart_msg)
651         issue = self.db.issue.getnode ('1')
652         self.assertEqual(len(issue.files), 10)
653         messages = self.db.issue.get('1', 'messages')
654         messages.sort()
655         self.assertEqual(messages[-1], '3')
656         msg = self.db.msg.getnode (messages[-1])
657         self.assertEqual(issue.files, [str(i+1) for i in range(10)])
658         self.assertEqual(msg.files, ['6', '7', '8', '9', '10'])
660     def testMultipartKeepFiles(self):
661         self.doNewIssue()
662         self._handle_mail(self.multipart_msg)
663         messages = self.db.issue.get('1', 'messages')
664         messages.sort()
665         msg = self.db.msg.getnode (messages[-1])
666         self.assertEqual(len(msg.files), 5)
667         issue = self.db.issue.getnode ('1')
668         self.assertEqual(len(issue.files), 5)
669         names = {0 : 'first.dvi', 4 : 'second.dvi'}
670         content = {3 : 'test attachment third text/plain\n',
671                    4 : 'Just a test\n'}
672         for n, id in enumerate (msg.files):
673             f = self.db.file.getnode (id)
674             self.assertEqual(f.name, names.get (n, 'unnamed'))
675             if n in content :
676                 self.assertEqual(f.content, content [n])
677         self.assertEqual(msg.content, 'test attachment second text/plain')
678         self._handle_mail('''From: mary <mary@test.test>
679 To: issue_tracker@your.tracker.email.domain.example
680 Message-Id: <followup_dummy_id2>
681 In-Reply-To: <dummy_test_message_id>
682 Subject: [issue1] Testing...
684 This ist a message without attachment
685 ''')
686         issue = self.db.issue.getnode ('1')
687         self.assertEqual(len(issue.files), 5)
688         self.assertEqual(issue.files, ['1', '2', '3', '4', '5'])
690     def testMultipartDropAlternatives(self):
691         self.doNewIssue()
692         self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
693         self._handle_mail(self.multipart_msg)
694         messages = self.db.issue.get('1', 'messages')
695         messages.sort()
696         msg = self.db.msg.getnode (messages[-1])
697         self.assertEqual(len(msg.files), 2)
698         names = {1 : 'second.dvi'}
699         content = {0 : 'test attachment third text/plain\n',
700                    1 : 'Just a test\n'}
701         for n, id in enumerate (msg.files):
702             f = self.db.file.getnode (id)
703             self.assertEqual(f.name, names.get (n, 'unnamed'))
704             if n in content :
705                 self.assertEqual(f.content, content [n])
706         self.assertEqual(msg.content, 'test attachment second text/plain')
708     def testMultipartCharsetUTF8NoAttach(self):
709         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
710         self.doNewIssue()
711         self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
712         self._handle_mail(self.multipart_msg_latin1)
713         messages = self.db.issue.get('1', 'messages')
714         messages.sort()
715         msg = self.db.msg.getnode (messages[-1])
716         self.assertEqual(len(msg.files), 1)
717         name = 'unnamed'
718         content = '<html>' + c + '</html>\n'
719         for n, id in enumerate (msg.files):
720             f = self.db.file.getnode (id)
721             self.assertEqual(f.name, name)
722             self.assertEqual(f.content, content)
723         self.assertEqual(msg.content, c)
724         self.compareMessages(self._get_mail(),
725 '''FROM: roundup-admin@your.tracker.email.domain.example
726 TO: chef@bork.bork.bork, richard@test.test
727 Content-Type: text/plain; charset="utf-8"
728 Subject: [issue1] Testing...
729 To: chef@bork.bork.bork, richard@test.test
730 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
731 Reply-To: Roundup issue tracker
732  <issue_tracker@your.tracker.email.domain.example>
733 MIME-Version: 1.0
734 Message-Id: <followup_dummy_id>
735 In-Reply-To: <dummy_test_message_id>
736 X-Roundup-Name: Roundup issue tracker
737 X-Roundup-Loop: hello
738 X-Roundup-Issue-Status: chatting
739 X-Roundup-Issue-Files: unnamed
740 Content-Transfer-Encoding: quoted-printable
743 Contrary, Mary <mary@test.test> added the comment:
745 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
746 File 'unnamed' not attached - you can download it from http://tracker.examp=
747 le/cgi-bin/roundup.cgi/bugs/file1.
749 ----------
750 status: unread -> chatting
752 _______________________________________________________________________
753 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
754 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
755 _______________________________________________________________________
756 ''')
758     def testMultipartCharsetLatin1NoAttach(self):
759         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
760         self.doNewIssue()
761         self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
762         self.db.config.MAIL_CHARSET = 'iso-8859-1'
763         self._handle_mail(self.multipart_msg_latin1)
764         messages = self.db.issue.get('1', 'messages')
765         messages.sort()
766         msg = self.db.msg.getnode (messages[-1])
767         self.assertEqual(len(msg.files), 1)
768         name = 'unnamed'
769         content = '<html>' + c + '</html>\n'
770         for n, id in enumerate (msg.files):
771             f = self.db.file.getnode (id)
772             self.assertEqual(f.name, name)
773             self.assertEqual(f.content, content)
774         self.assertEqual(msg.content, c)
775         self.compareMessages(self._get_mail(),
776 '''FROM: roundup-admin@your.tracker.email.domain.example
777 TO: chef@bork.bork.bork, richard@test.test
778 Content-Type: text/plain; charset="iso-8859-1"
779 Subject: [issue1] Testing...
780 To: chef@bork.bork.bork, richard@test.test
781 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
782 Reply-To: Roundup issue tracker
783  <issue_tracker@your.tracker.email.domain.example>
784 MIME-Version: 1.0
785 Message-Id: <followup_dummy_id>
786 In-Reply-To: <dummy_test_message_id>
787 X-Roundup-Name: Roundup issue tracker
788 X-Roundup-Loop: hello
789 X-Roundup-Issue-Status: chatting
790 X-Roundup-Issue-Files: unnamed
791 Content-Transfer-Encoding: quoted-printable
794 Contrary, Mary <mary@test.test> added the comment:
796 umlaut =E4=F6=FC=C4=D6=DC=DF
797 File 'unnamed' not attached - you can download it from http://tracker.examp=
798 le/cgi-bin/roundup.cgi/bugs/file1.
800 ----------
801 status: unread -> chatting
803 _______________________________________________________________________
804 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
805 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
806 _______________________________________________________________________
807 ''')
809     def testMultipartCharsetUTF8AttachFile(self):
810         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
811         self.doNewIssue()
812         self._handle_mail(self.multipart_msg_latin1)
813         messages = self.db.issue.get('1', 'messages')
814         messages.sort()
815         msg = self.db.msg.getnode (messages[-1])
816         self.assertEqual(len(msg.files), 1)
817         name = 'unnamed'
818         content = '<html>' + c + '</html>\n'
819         for n, id in enumerate (msg.files):
820             f = self.db.file.getnode (id)
821             self.assertEqual(f.name, name)
822             self.assertEqual(f.content, content)
823         self.assertEqual(msg.content, c)
824         self.compareMessages(self._get_mail(),
825 '''FROM: roundup-admin@your.tracker.email.domain.example
826 TO: chef@bork.bork.bork, richard@test.test
827 Content-Type: multipart/mixed; boundary="utf-8"
828 Subject: [issue1] Testing...
829 To: chef@bork.bork.bork, richard@test.test
830 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
831 Reply-To: Roundup issue tracker
832  <issue_tracker@your.tracker.email.domain.example>
833 MIME-Version: 1.0
834 Message-Id: <followup_dummy_id>
835 In-Reply-To: <dummy_test_message_id>
836 X-Roundup-Name: Roundup issue tracker
837 X-Roundup-Loop: hello
838 X-Roundup-Issue-Status: chatting
839 X-Roundup-Issue-Files: unnamed
840 Content-Transfer-Encoding: quoted-printable
843 --utf-8
844 MIME-Version: 1.0
845 Content-Type: text/plain; charset="utf-8"
846 Content-Transfer-Encoding: quoted-printable
849 Contrary, Mary <mary@test.test> added the comment:
851 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
853 ----------
854 status: unread -> chatting
856 _______________________________________________________________________
857 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
858 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
859 _______________________________________________________________________
860 --utf-8
861 Content-Type: text/html
862 MIME-Version: 1.0
863 Content-Transfer-Encoding: base64
864 Content-Disposition: attachment;
865  filename="unnamed"
867 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
869 --utf-8--
870 ''')
872     def testMultipartCharsetLatin1AttachFile(self):
873         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
874         self.doNewIssue()
875         self.db.config.MAIL_CHARSET = 'iso-8859-1'
876         self._handle_mail(self.multipart_msg_latin1)
877         messages = self.db.issue.get('1', 'messages')
878         messages.sort()
879         msg = self.db.msg.getnode (messages[-1])
880         self.assertEqual(len(msg.files), 1)
881         name = 'unnamed'
882         content = '<html>' + c + '</html>\n'
883         for n, id in enumerate (msg.files):
884             f = self.db.file.getnode (id)
885             self.assertEqual(f.name, name)
886             self.assertEqual(f.content, content)
887         self.assertEqual(msg.content, c)
888         self.compareMessages(self._get_mail(),
889 '''FROM: roundup-admin@your.tracker.email.domain.example
890 TO: chef@bork.bork.bork, richard@test.test
891 Content-Type: multipart/mixed; boundary="utf-8"
892 Subject: [issue1] Testing...
893 To: chef@bork.bork.bork, richard@test.test
894 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
895 Reply-To: Roundup issue tracker
896  <issue_tracker@your.tracker.email.domain.example>
897 MIME-Version: 1.0
898 Message-Id: <followup_dummy_id>
899 In-Reply-To: <dummy_test_message_id>
900 X-Roundup-Name: Roundup issue tracker
901 X-Roundup-Loop: hello
902 X-Roundup-Issue-Status: chatting
903 X-Roundup-Issue-Files: unnamed
904 Content-Transfer-Encoding: quoted-printable
907 --utf-8
908 MIME-Version: 1.0
909 Content-Type: text/plain; charset="iso-8859-1"
910 Content-Transfer-Encoding: quoted-printable
913 Contrary, Mary <mary@test.test> added the comment:
915 umlaut =E4=F6=FC=C4=D6=DC=DF
917 ----------
918 status: unread -> chatting
920 _______________________________________________________________________
921 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
922 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
923 _______________________________________________________________________
924 --utf-8
925 Content-Type: text/html
926 MIME-Version: 1.0
927 Content-Transfer-Encoding: base64
928 Content-Disposition: attachment;
929  filename="unnamed"
931 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
933 --utf-8--
934 ''')
936     def testMultipartRFC822(self):
937         self.doNewIssue()
938         self._handle_mail(self.multipart_msg_rfc822)
939         messages = self.db.issue.get('1', 'messages')
940         messages.sort()
941         msg = self.db.msg.getnode (messages[-1])
942         self.assertEqual(len(msg.files), 1)
943         name = "Fwd: Original email subject.eml"
944         for n, id in enumerate (msg.files):
945             f = self.db.file.getnode (id)
946             self.assertEqual(f.name, name)
947         self.assertEqual(msg.content, 'First part: Text')
948         self.compareMessages(self._get_mail(),
949 '''TO: chef@bork.bork.bork, richard@test.test
950 Content-Type: text/plain; charset="utf-8"
951 Subject: [issue1] Testing...
952 To: chef@bork.bork.bork, richard@test.test
953 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
954 Reply-To: Roundup issue tracker
955  <issue_tracker@your.tracker.email.domain.example>
956 MIME-Version: 1.0
957 Message-Id: <followup_dummy_id>
958 In-Reply-To: <dummy_test_message_id>
959 X-Roundup-Name: Roundup issue tracker
960 X-Roundup-Loop: hello
961 X-Roundup-Issue-Status: chatting
962 X-Roundup-Issue-Files: Fwd: Original email subject.eml
963 Content-Transfer-Encoding: quoted-printable
966 --utf-8
967 MIME-Version: 1.0
968 Content-Type: text/plain; charset="utf-8"
969 Content-Transfer-Encoding: quoted-printable
972 Contrary, Mary <mary@test.test> added the comment:
974 First part: Text
976 ----------
977 status: unread -> chatting
979 _______________________________________________________________________
980 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
981 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
982 _______________________________________________________________________
983 --utf-8
984 Content-Type: message/rfc822
985 MIME-Version: 1.0
986 Content-Disposition: attachment;
987  filename="Fwd: Original email subject.eml"
989 Message-Id: <followup_dummy_id_2>
990 In-Reply-To: <dummy_test_message_id_2>
991 MIME-Version: 1.0
992 Subject: Fwd: Original email subject
993 Date: Mon, 23 Aug 2010 08:23:33 +0200
994 Content-Type: multipart/alternative; boundary="090500050101020406060002"
996 This is a multi-part message in MIME format.
997 --090500050101020406060002
998 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
999 Content-Transfer-Encoding: 7bit
1001 some text in inner email
1002 ========================
1004 --090500050101020406060002
1005 Content-Type: text/html; charset=ISO-8859-15
1006 Content-Transfer-Encoding: 7bit
1008 <html>
1009 some text in inner email
1010 ========================
1011 </html>
1013 --090500050101020406060002--
1015 --utf-8--
1016 ''')
1018     def testMultipartRFC822Unpack(self):
1019         self.doNewIssue()
1020         self.db.config.MAILGW_UNPACK_RFC822 = True
1021         self._handle_mail(self.multipart_msg_rfc822)
1022         messages = self.db.issue.get('1', 'messages')
1023         messages.sort()
1024         msg = self.db.msg.getnode (messages[-1])
1025         self.assertEqual(len(msg.files), 2)
1026         t = 'some text in inner email\n========================\n'
1027         content = {0 : t, 1 : '<html>\n' + t + '</html>\n'}
1028         for n, id in enumerate (msg.files):
1029             f = self.db.file.getnode (id)
1030             self.assertEqual(f.name, 'unnamed')
1031             if n in content :
1032                 self.assertEqual(f.content, content [n])
1033         self.assertEqual(msg.content, 'First part: Text')
1035     def testSimpleFollowup(self):
1036         self.doNewIssue()
1037         self._handle_mail('''Content-Type: text/plain;
1038   charset="iso-8859-1"
1039 From: mary <mary@test.test>
1040 To: issue_tracker@your.tracker.email.domain.example
1041 Message-Id: <followup_dummy_id>
1042 In-Reply-To: <dummy_test_message_id>
1043 Subject: [issue1] Testing...
1045 This is a second followup
1046 ''')
1047         self.compareMessages(self._get_mail(),
1048 '''FROM: roundup-admin@your.tracker.email.domain.example
1049 TO: chef@bork.bork.bork, richard@test.test
1050 Content-Type: text/plain; charset="utf-8"
1051 Subject: [issue1] Testing...
1052 To: chef@bork.bork.bork, richard@test.test
1053 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1054 Reply-To: Roundup issue tracker
1055  <issue_tracker@your.tracker.email.domain.example>
1056 MIME-Version: 1.0
1057 Message-Id: <followup_dummy_id>
1058 In-Reply-To: <dummy_test_message_id>
1059 X-Roundup-Name: Roundup issue tracker
1060 X-Roundup-Loop: hello
1061 X-Roundup-Issue-Status: chatting
1062 Content-Transfer-Encoding: quoted-printable
1065 Contrary, Mary <mary@test.test> added the comment:
1067 This is a second followup
1069 ----------
1070 status: unread -> chatting
1072 _______________________________________________________________________
1073 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1074 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1075 _______________________________________________________________________
1076 ''')
1078     def testFollowup(self):
1079         self.doNewIssue()
1081         self._handle_mail('''Content-Type: text/plain;
1082   charset="iso-8859-1"
1083 From: richard <richard@test.test>
1084 To: issue_tracker@your.tracker.email.domain.example
1085 Message-Id: <followup_dummy_id>
1086 In-Reply-To: <dummy_test_message_id>
1087 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1089 This is a followup
1090 ''')
1091         l = self.db.issue.get('1', 'nosy')
1092         l.sort()
1093         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1094             self.john_id])
1096         self.compareMessages(self._get_mail(),
1097 '''FROM: roundup-admin@your.tracker.email.domain.example
1098 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1099 Content-Type: text/plain; charset="utf-8"
1100 Subject: [issue1] Testing...
1101 To: chef@bork.bork.bork, john@test.test, mary@test.test
1102 From: richard <issue_tracker@your.tracker.email.domain.example>
1103 Reply-To: Roundup issue tracker
1104  <issue_tracker@your.tracker.email.domain.example>
1105 MIME-Version: 1.0
1106 Message-Id: <followup_dummy_id>
1107 In-Reply-To: <dummy_test_message_id>
1108 X-Roundup-Name: Roundup issue tracker
1109 X-Roundup-Loop: hello
1110 X-Roundup-Issue-Status: chatting
1111 Content-Transfer-Encoding: quoted-printable
1114 richard <richard@test.test> added the comment:
1116 This is a followup
1118 ----------
1119 assignedto:  -> mary
1120 nosy: +john, mary
1121 status: unread -> chatting
1123 _______________________________________________________________________
1124 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1125 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1126 _______________________________________________________________________
1127 ''')
1129     def testFollowupNoSubjectChange(self):
1130         self.db.config.MAILGW_SUBJECT_UPDATES_TITLE = 'no'
1131         self.doNewIssue()
1133         self._handle_mail('''Content-Type: text/plain;
1134   charset="iso-8859-1"
1135 From: richard <richard@test.test>
1136 To: issue_tracker@your.tracker.email.domain.example
1137 Message-Id: <followup_dummy_id>
1138 In-Reply-To: <dummy_test_message_id>
1139 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john]
1141 This is a followup
1142 ''')
1143         l = self.db.issue.get('1', 'nosy')
1144         l.sort()
1145         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1146             self.john_id])
1148         self.compareMessages(self._get_mail(),
1149 '''FROM: roundup-admin@your.tracker.email.domain.example
1150 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1151 Content-Type: text/plain; charset="utf-8"
1152 Subject: [issue1] Testing...
1153 To: chef@bork.bork.bork, john@test.test, mary@test.test
1154 From: richard <issue_tracker@your.tracker.email.domain.example>
1155 Reply-To: Roundup issue tracker
1156  <issue_tracker@your.tracker.email.domain.example>
1157 MIME-Version: 1.0
1158 Message-Id: <followup_dummy_id>
1159 In-Reply-To: <dummy_test_message_id>
1160 X-Roundup-Name: Roundup issue tracker
1161 X-Roundup-Loop: hello
1162 X-Roundup-Issue-Status: chatting
1163 Content-Transfer-Encoding: quoted-printable
1166 richard <richard@test.test> added the comment:
1168 This is a followup
1170 ----------
1171 assignedto:  -> mary
1172 nosy: +john, mary
1173 status: unread -> chatting
1175 _______________________________________________________________________
1176 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1177 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1178 _______________________________________________________________________
1179 ''')
1180         self.assertEqual(self.db.issue.get('1','title'), 'Testing...')
1182     def testFollowupExplicitSubjectChange(self):
1183         self.doNewIssue()
1185         self._handle_mail('''Content-Type: text/plain;
1186   charset="iso-8859-1"
1187 From: richard <richard@test.test>
1188 To: issue_tracker@your.tracker.email.domain.example
1189 Message-Id: <followup_dummy_id>
1190 In-Reply-To: <dummy_test_message_id>
1191 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john; title=new title]
1193 This is a followup
1194 ''')
1195         l = self.db.issue.get('1', 'nosy')
1196         l.sort()
1197         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1198             self.john_id])
1200         self.compareMessages(self._get_mail(),
1201 '''FROM: roundup-admin@your.tracker.email.domain.example
1202 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1203 Content-Type: text/plain; charset="utf-8"
1204 Subject: [issue1] new title
1205 To: chef@bork.bork.bork, john@test.test, mary@test.test
1206 From: richard <issue_tracker@your.tracker.email.domain.example>
1207 Reply-To: Roundup issue tracker
1208  <issue_tracker@your.tracker.email.domain.example>
1209 MIME-Version: 1.0
1210 Message-Id: <followup_dummy_id>
1211 In-Reply-To: <dummy_test_message_id>
1212 X-Roundup-Name: Roundup issue tracker
1213 X-Roundup-Loop: hello
1214 X-Roundup-Issue-Status: chatting
1215 Content-Transfer-Encoding: quoted-printable
1218 richard <richard@test.test> added the comment:
1220 This is a followup
1222 ----------
1223 assignedto:  -> mary
1224 nosy: +john, mary
1225 status: unread -> chatting
1226 title: Testing... -> new title
1228 _______________________________________________________________________
1229 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1230 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1231 _______________________________________________________________________
1232 ''')
1234     def testNosyGeneration(self):
1235         self.db.issue.create(title='test')
1237         # create a nosy message
1238         msg = self.db.msg.create(content='This is a test',
1239             author=self.richard_id, messageid='<dummy_test_message_id>')
1240         self.db.journaltag = 'richard'
1241         l = self.db.issue.create(title='test', messages=[msg],
1242             nosy=[self.chef_id, self.mary_id, self.john_id])
1244         self.compareMessages(self._get_mail(),
1245 '''FROM: roundup-admin@your.tracker.email.domain.example
1246 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1247 Content-Type: text/plain; charset="utf-8"
1248 Subject: [issue2] test
1249 To: chef@bork.bork.bork, john@test.test, mary@test.test
1250 From: richard <issue_tracker@your.tracker.email.domain.example>
1251 Reply-To: Roundup issue tracker
1252  <issue_tracker@your.tracker.email.domain.example>
1253 MIME-Version: 1.0
1254 Message-Id: <dummy_test_message_id>
1255 X-Roundup-Name: Roundup issue tracker
1256 X-Roundup-Loop: hello
1257 X-Roundup-Issue-Status: unread
1258 Content-Transfer-Encoding: quoted-printable
1261 New submission from richard <richard@test.test>:
1263 This is a test
1265 ----------
1266 messages: 1
1267 nosy: Chef, john, mary, richard
1268 status: unread
1269 title: test
1271 _______________________________________________________________________
1272 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1273 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue2>
1274 _______________________________________________________________________
1275 ''')
1277     def testPropertyChangeOnly(self):
1278         self.doNewIssue()
1279         oldvalues = self.db.getnode('issue', '1').copy()
1280         oldvalues['assignedto'] = None
1281         # reconstruct old behaviour: This would reuse the
1282         # database-handle from the doNewIssue above which has committed
1283         # as user "Chef". So we close and reopen the db as that user.
1284         #self.db.close() actually don't close 'cos this empties memorydb
1285         self.db = self.instance.open('Chef')
1286         self.db.issue.set('1', assignedto=self.chef_id)
1287         self.db.commit()
1288         self.db.issue.nosymessage('1', None, oldvalues)
1290         new_mail = ""
1291         for line in self._get_mail().split("\n"):
1292             if "Message-Id: " in line:
1293                 continue
1294             if "Date: " in line:
1295                 continue
1296             new_mail += line+"\n"
1298         self.compareMessages(new_mail, """
1299 FROM: roundup-admin@your.tracker.email.domain.example
1300 TO: chef@bork.bork.bork, richard@test.test
1301 Content-Type: text/plain; charset="utf-8"
1302 Subject: [issue1] Testing...
1303 To: chef@bork.bork.bork, richard@test.test
1304 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
1305 X-Roundup-Name: Roundup issue tracker
1306 X-Roundup-Loop: hello
1307 X-Roundup-Issue-Status: unread
1308 X-Roundup-Version: 1.3.3
1309 In-Reply-To: <dummy_test_message_id>
1310 MIME-Version: 1.0
1311 Reply-To: Roundup issue tracker
1312  <issue_tracker@your.tracker.email.domain.example>
1313 Content-Transfer-Encoding: quoted-printable
1316 Change by Bork, Chef <chef@bork.bork.bork>:
1319 ----------
1320 assignedto:  -> Chef
1322 _______________________________________________________________________
1323 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1324 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1325 _______________________________________________________________________
1326 """)
1329     #
1330     # FOLLOWUP TITLE MATCH
1331     #
1332     def testFollowupTitleMatch(self):
1333         self.doNewIssue()
1334         self._handle_mail('''Content-Type: text/plain;
1335   charset="iso-8859-1"
1336 From: richard <richard@test.test>
1337 To: issue_tracker@your.tracker.email.domain.example
1338 Message-Id: <followup_dummy_id>
1339 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1341 This is a followup
1342 ''')
1343         self.compareMessages(self._get_mail(),
1344 '''FROM: roundup-admin@your.tracker.email.domain.example
1345 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1346 Content-Type: text/plain; charset="utf-8"
1347 Subject: [issue1] Testing...
1348 To: chef@bork.bork.bork, john@test.test, mary@test.test
1349 From: richard <issue_tracker@your.tracker.email.domain.example>
1350 Reply-To: Roundup issue tracker
1351  <issue_tracker@your.tracker.email.domain.example>
1352 MIME-Version: 1.0
1353 Message-Id: <followup_dummy_id>
1354 In-Reply-To: <dummy_test_message_id>
1355 X-Roundup-Name: Roundup issue tracker
1356 X-Roundup-Loop: hello
1357 X-Roundup-Issue-Status: chatting
1358 Content-Transfer-Encoding: quoted-printable
1361 richard <richard@test.test> added the comment:
1363 This is a followup
1365 ----------
1366 assignedto:  -> mary
1367 nosy: +john, mary
1368 status: unread -> chatting
1370 _______________________________________________________________________
1371 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1372 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1373 _______________________________________________________________________
1374 ''')
1376     def testFollowupTitleMatchMultiRe(self):
1377         nodeid1 = self.doNewIssue()
1378         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1379   charset="iso-8859-1"
1380 From: richard <richard@test.test>
1381 To: issue_tracker@your.tracker.email.domain.example
1382 Message-Id: <followup_dummy_id>
1383 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1385 This is a followup
1386 ''')
1388         nodeid3 = self._handle_mail('''Content-Type: text/plain;
1389   charset="iso-8859-1"
1390 From: richard <richard@test.test>
1391 To: issue_tracker@your.tracker.email.domain.example
1392 Message-Id: <followup2_dummy_id>
1393 Subject: Ang: Re: Testing...
1395 This is a followup
1396 ''')
1397         self.assertEqual(nodeid1, nodeid2)
1398         self.assertEqual(nodeid1, nodeid3)
1400     def testFollowupTitleMatchNever(self):
1401         nodeid = self.doNewIssue()
1402         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
1403         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1404   charset="iso-8859-1"
1405 From: richard <richard@test.test>
1406 To: issue_tracker@your.tracker.email.domain.example
1407 Message-Id: <followup_dummy_id>
1408 Subject: Re: Testing...
1410 This is a followup
1411 '''), nodeid)
1413     def testFollowupTitleMatchNeverInterval(self):
1414         nodeid = self.doNewIssue()
1415         # force failure of the interval
1416         time.sleep(2)
1417         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
1418         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1419   charset="iso-8859-1"
1420 From: richard <richard@test.test>
1421 To: issue_tracker@your.tracker.email.domain.example
1422 Message-Id: <followup_dummy_id>
1423 Subject: Re: Testing...
1425 This is a followup
1426 '''), nodeid)
1429     def testFollowupTitleMatchInterval(self):
1430         nodeid = self.doNewIssue()
1431         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
1432         self.assertEqual(self._handle_mail('''Content-Type: text/plain;
1433   charset="iso-8859-1"
1434 From: richard <richard@test.test>
1435 To: issue_tracker@your.tracker.email.domain.example
1436 Message-Id: <followup_dummy_id>
1437 Subject: Re: Testing...
1439 This is a followup
1440 '''), nodeid)
1443     def testFollowupNosyAuthor(self):
1444         self.doNewIssue()
1445         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1446         self._handle_mail('''Content-Type: text/plain;
1447   charset="iso-8859-1"
1448 From: john@test.test
1449 To: issue_tracker@your.tracker.email.domain.example
1450 Message-Id: <followup_dummy_id>
1451 In-Reply-To: <dummy_test_message_id>
1452 Subject: [issue1] Testing...
1454 This is a followup
1455 ''')
1457         self.compareMessages(self._get_mail(),
1458 '''FROM: roundup-admin@your.tracker.email.domain.example
1459 TO: chef@bork.bork.bork, richard@test.test
1460 Content-Type: text/plain; charset="utf-8"
1461 Subject: [issue1] Testing...
1462 To: chef@bork.bork.bork, richard@test.test
1463 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1464 Reply-To: Roundup issue tracker
1465  <issue_tracker@your.tracker.email.domain.example>
1466 MIME-Version: 1.0
1467 Message-Id: <followup_dummy_id>
1468 In-Reply-To: <dummy_test_message_id>
1469 X-Roundup-Name: Roundup issue tracker
1470 X-Roundup-Loop: hello
1471 X-Roundup-Issue-Status: chatting
1472 Content-Transfer-Encoding: quoted-printable
1475 John Doe <john@test.test> added the comment:
1477 This is a followup
1479 ----------
1480 nosy: +john
1481 status: unread -> chatting
1483 _______________________________________________________________________
1484 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1485 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1486 _______________________________________________________________________
1488 ''')
1490     def testFollowupNosyRecipients(self):
1491         self.doNewIssue()
1492         self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
1493         self._handle_mail('''Content-Type: text/plain;
1494   charset="iso-8859-1"
1495 From: richard@test.test
1496 To: issue_tracker@your.tracker.email.domain.example
1497 Cc: john@test.test
1498 Message-Id: <followup_dummy_id>
1499 In-Reply-To: <dummy_test_message_id>
1500 Subject: [issue1] Testing...
1502 This is a followup
1503 ''')
1504         self.compareMessages(self._get_mail(),
1505 '''FROM: roundup-admin@your.tracker.email.domain.example
1506 TO: chef@bork.bork.bork
1507 Content-Type: text/plain; charset="utf-8"
1508 Subject: [issue1] Testing...
1509 To: chef@bork.bork.bork
1510 From: richard <issue_tracker@your.tracker.email.domain.example>
1511 Reply-To: Roundup issue tracker
1512  <issue_tracker@your.tracker.email.domain.example>
1513 MIME-Version: 1.0
1514 Message-Id: <followup_dummy_id>
1515 In-Reply-To: <dummy_test_message_id>
1516 X-Roundup-Name: Roundup issue tracker
1517 X-Roundup-Loop: hello
1518 X-Roundup-Issue-Status: chatting
1519 Content-Transfer-Encoding: quoted-printable
1522 richard <richard@test.test> added the comment:
1524 This is a followup
1526 ----------
1527 nosy: +john
1528 status: unread -> chatting
1530 _______________________________________________________________________
1531 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1532 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1533 _______________________________________________________________________
1535 ''')
1537     def testFollowupNosyAuthorAndCopy(self):
1538         self.doNewIssue()
1539         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1540         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
1541         self._handle_mail('''Content-Type: text/plain;
1542   charset="iso-8859-1"
1543 From: john@test.test
1544 To: issue_tracker@your.tracker.email.domain.example
1545 Message-Id: <followup_dummy_id>
1546 In-Reply-To: <dummy_test_message_id>
1547 Subject: [issue1] Testing...
1549 This is a followup
1550 ''')
1551         self.compareMessages(self._get_mail(),
1552 '''FROM: roundup-admin@your.tracker.email.domain.example
1553 TO: chef@bork.bork.bork, john@test.test, richard@test.test
1554 Content-Type: text/plain; charset="utf-8"
1555 Subject: [issue1] Testing...
1556 To: chef@bork.bork.bork, john@test.test, richard@test.test
1557 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1558 Reply-To: Roundup issue tracker
1559  <issue_tracker@your.tracker.email.domain.example>
1560 MIME-Version: 1.0
1561 Message-Id: <followup_dummy_id>
1562 In-Reply-To: <dummy_test_message_id>
1563 X-Roundup-Name: Roundup issue tracker
1564 X-Roundup-Loop: hello
1565 X-Roundup-Issue-Status: chatting
1566 Content-Transfer-Encoding: quoted-printable
1569 John Doe <john@test.test> added the comment:
1571 This is a followup
1573 ----------
1574 nosy: +john
1575 status: unread -> chatting
1577 _______________________________________________________________________
1578 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1579 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1580 _______________________________________________________________________
1582 ''')
1584     def testFollowupNoNosyAuthor(self):
1585         self.doNewIssue()
1586         self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
1587         self._handle_mail('''Content-Type: text/plain;
1588   charset="iso-8859-1"
1589 From: john@test.test
1590 To: issue_tracker@your.tracker.email.domain.example
1591 Message-Id: <followup_dummy_id>
1592 In-Reply-To: <dummy_test_message_id>
1593 Subject: [issue1] Testing...
1595 This is a followup
1596 ''')
1597         self.compareMessages(self._get_mail(),
1598 '''FROM: roundup-admin@your.tracker.email.domain.example
1599 TO: chef@bork.bork.bork, richard@test.test
1600 Content-Type: text/plain; charset="utf-8"
1601 Subject: [issue1] Testing...
1602 To: chef@bork.bork.bork, richard@test.test
1603 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1604 Reply-To: Roundup issue tracker
1605  <issue_tracker@your.tracker.email.domain.example>
1606 MIME-Version: 1.0
1607 Message-Id: <followup_dummy_id>
1608 In-Reply-To: <dummy_test_message_id>
1609 X-Roundup-Name: Roundup issue tracker
1610 X-Roundup-Loop: hello
1611 X-Roundup-Issue-Status: chatting
1612 Content-Transfer-Encoding: quoted-printable
1615 John Doe <john@test.test> added the comment:
1617 This is a followup
1619 ----------
1620 status: unread -> chatting
1622 _______________________________________________________________________
1623 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1624 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1625 _______________________________________________________________________
1627 ''')
1629     def testFollowupNoNosyRecipients(self):
1630         self.doNewIssue()
1631         self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
1632         self._handle_mail('''Content-Type: text/plain;
1633   charset="iso-8859-1"
1634 From: richard@test.test
1635 To: issue_tracker@your.tracker.email.domain.example
1636 Cc: john@test.test
1637 Message-Id: <followup_dummy_id>
1638 In-Reply-To: <dummy_test_message_id>
1639 Subject: [issue1] Testing...
1641 This is a followup
1642 ''')
1643         self.compareMessages(self._get_mail(),
1644 '''FROM: roundup-admin@your.tracker.email.domain.example
1645 TO: chef@bork.bork.bork
1646 Content-Type: text/plain; charset="utf-8"
1647 Subject: [issue1] Testing...
1648 To: chef@bork.bork.bork
1649 From: richard <issue_tracker@your.tracker.email.domain.example>
1650 Reply-To: Roundup issue tracker
1651  <issue_tracker@your.tracker.email.domain.example>
1652 MIME-Version: 1.0
1653 Message-Id: <followup_dummy_id>
1654 In-Reply-To: <dummy_test_message_id>
1655 X-Roundup-Name: Roundup issue tracker
1656 X-Roundup-Loop: hello
1657 X-Roundup-Issue-Status: chatting
1658 Content-Transfer-Encoding: quoted-printable
1661 richard <richard@test.test> added the comment:
1663 This is a followup
1665 ----------
1666 status: unread -> chatting
1668 _______________________________________________________________________
1669 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1670 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1671 _______________________________________________________________________
1673 ''')
1675     def testFollowupEmptyMessage(self):
1676         self.doNewIssue()
1678         self._handle_mail('''Content-Type: text/plain;
1679   charset="iso-8859-1"
1680 From: richard <richard@test.test>
1681 To: issue_tracker@your.tracker.email.domain.example
1682 Message-Id: <followup_dummy_id>
1683 In-Reply-To: <dummy_test_message_id>
1684 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1686 ''')
1687         l = self.db.issue.get('1', 'nosy')
1688         l.sort()
1689         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1690             self.john_id])
1692         # should be no file created (ie. no message)
1693         assert not os.path.exists(SENDMAILDEBUG)
1695     def testFollowupEmptyMessageNoSubject(self):
1696         self.doNewIssue()
1698         self._handle_mail('''Content-Type: text/plain;
1699   charset="iso-8859-1"
1700 From: richard <richard@test.test>
1701 To: issue_tracker@your.tracker.email.domain.example
1702 Message-Id: <followup_dummy_id>
1703 In-Reply-To: <dummy_test_message_id>
1704 Subject: [issue1] [assignedto=mary; nosy=+john]
1706 ''')
1707         l = self.db.issue.get('1', 'nosy')
1708         l.sort()
1709         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1710             self.john_id])
1712         # should be no file created (ie. no message)
1713         assert not os.path.exists(SENDMAILDEBUG)
1715     def testNosyRemove(self):
1716         self.doNewIssue()
1718         self._handle_mail('''Content-Type: text/plain;
1719   charset="iso-8859-1"
1720 From: richard <richard@test.test>
1721 To: issue_tracker@your.tracker.email.domain.example
1722 Message-Id: <followup_dummy_id>
1723 In-Reply-To: <dummy_test_message_id>
1724 Subject: [issue1] Testing... [nosy=-richard]
1726 ''')
1727         l = self.db.issue.get('1', 'nosy')
1728         l.sort()
1729         self.assertEqual(l, [self.chef_id])
1731         # NO NOSY MESSAGE SHOULD BE SENT!
1732         assert not os.path.exists(SENDMAILDEBUG)
1734     def testNewUserAuthor(self):
1735         self.db.commit()
1736         l = self.db.user.list()
1737         l.sort()
1738         message = '''Content-Type: text/plain;
1739   charset="iso-8859-1"
1740 From: fubar <fubar@bork.bork.bork>
1741 To: issue_tracker@your.tracker.email.domain.example
1742 Message-Id: <dummy_test_message_id>
1743 Subject: [issue] Testing...
1745 This is a test submission of a new issue.
1746 '''
1747         self.db.security.role['anonymous'].permissions=[]
1748         anonid = self.db.user.lookup('anonymous')
1749         self.db.user.set(anonid, roles='Anonymous')
1750         try:
1751             self._handle_mail(message)
1752         except Unauthorized, value:
1753             body_diff = self.compareMessages(str(value), """
1754 You are not a registered user.
1756 Unknown address: fubar@bork.bork.bork
1757 """)
1758             assert not body_diff, body_diff
1759         else:
1760             raise AssertionError, "Unathorized not raised when handling mail"
1762         # Add Web Access role to anonymous, and try again to make sure
1763         # we get a "please register at:" message this time.
1764         p = [
1765             self.db.security.getPermission('Register', 'user'),
1766             self.db.security.getPermission('Web Access', None),
1767         ]
1768         self.db.security.role['anonymous'].permissions=p
1769         try:
1770             self._handle_mail(message)
1771         except Unauthorized, value:
1772             body_diff = self.compareMessages(str(value), """
1773 You are not a registered user. Please register at:
1775 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1777 ...before sending mail to the tracker.
1779 Unknown address: fubar@bork.bork.bork
1780 """)
1781             assert not body_diff, body_diff
1782         else:
1783             raise AssertionError, "Unathorized not raised when handling mail"
1785         # Make sure list of users is the same as before.
1786         m = self.db.user.list()
1787         m.sort()
1788         self.assertEqual(l, m)
1790         # now with the permission
1791         p = [
1792             self.db.security.getPermission('Register', 'user'),
1793             self.db.security.getPermission('Email Access', None),
1794         ]
1795         self.db.security.role['anonymous'].permissions=p
1796         self._handle_mail(message)
1797         m = self.db.user.list()
1798         m.sort()
1799         self.assertNotEqual(l, m)
1801     def testNewUserAuthorEncodedName(self):
1802         l = set(self.db.user.list())
1803         # From: name has Euro symbol in it
1804         message = '''Content-Type: text/plain;
1805   charset="iso-8859-1"
1806 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1807 To: issue_tracker@your.tracker.email.domain.example
1808 Message-Id: <dummy_test_message_id>
1809 Subject: [issue] Testing...
1811 This is a test submission of a new issue.
1812 '''
1813         p = [
1814             self.db.security.getPermission('Register', 'user'),
1815             self.db.security.getPermission('Email Access', None),
1816             self.db.security.getPermission('Create', 'issue'),
1817             self.db.security.getPermission('Create', 'msg'),
1818         ]
1819         self.db.security.role['anonymous'].permissions = p
1820         self._handle_mail(message)
1821         m = set(self.db.user.list())
1822         new = list(m - l)[0]
1823         name = self.db.user.get(new, 'realname')
1824         self.assertEquals(name, 'H€llo')
1826     def testNewUserAuthorMixedEncodedName(self):
1827         l = set(self.db.user.list())
1828         # From: name has Euro symbol in it
1829         message = '''Content-Type: text/plain;
1830   charset="iso-8859-1"
1831 From: Firstname =?utf-8?b?w6TDtsOf?= Last <fubar@bork.bork.bork>
1832 To: issue_tracker@your.tracker.email.domain.example
1833 Message-Id: <dummy_test_message_id>
1834 Subject: [issue] Test =?utf-8?b?w4TDlsOc?= umlauts
1835  X1
1836  X2
1838 This is a test submission of a new issue.
1839 '''
1840         p = [
1841             self.db.security.getPermission('Register', 'user'),
1842             self.db.security.getPermission('Email Access', None),
1843             self.db.security.getPermission('Create', 'issue'),
1844             self.db.security.getPermission('Create', 'msg'),
1845         ]
1846         self.db.security.role['anonymous'].permissions = p
1847         self._handle_mail(message)
1848         title = self.db.issue.get('1', 'title')
1849         self.assertEquals(title, 'Test \xc3\x84\xc3\x96\xc3\x9c umlauts X1 X2')
1850         m = set(self.db.user.list())
1851         new = list(m - l)[0]
1852         name = self.db.user.get(new, 'realname')
1853         self.assertEquals(name, 'Firstname \xc3\xa4\xc3\xb6\xc3\x9f Last')
1855     def testUnknownUser(self):
1856         l = set(self.db.user.list())
1857         message = '''Content-Type: text/plain;
1858   charset="iso-8859-1"
1859 From: Nonexisting User <nonexisting@bork.bork.bork>
1860 To: issue_tracker@your.tracker.email.domain.example
1861 Message-Id: <dummy_test_message_id>
1862 Subject: [issue] Testing nonexisting user...
1864 This is a test submission of a new issue.
1865 '''
1866         handler = self._create_mailgw(message)
1867         # we want a bounce message:
1868         handler.trapExceptions = 1
1869         ret = handler.main(StringIO(message))
1870         self.compareMessages(self._get_mail(),
1871 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1872 TO: nonexisting@bork.bork.bork
1873 From nobody Tue Jul 14 12:04:11 2009
1874 Content-Type: multipart/mixed; boundary="===============0639262320=="
1875 MIME-Version: 1.0
1876 Subject: Failed issue tracker submission
1877 To: nonexisting@bork.bork.bork
1878 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1879 Date: Tue, 14 Jul 2009 12:04:11 +0000
1880 Precedence: bulk
1881 X-Roundup-Name: Roundup issue tracker
1882 X-Roundup-Loop: hello
1883 X-Roundup-Version: 1.4.8
1884 MIME-Version: 1.0
1886 --===============0639262320==
1887 Content-Type: text/plain; charset="us-ascii"
1888 MIME-Version: 1.0
1889 Content-Transfer-Encoding: 7bit
1893 You are not a registered user. Please register at:
1895 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1897 ...before sending mail to the tracker.
1899 Unknown address: nonexisting@bork.bork.bork
1901 --===============0639262320==
1902 Content-Type: text/plain; charset="us-ascii"
1903 MIME-Version: 1.0
1904 Content-Transfer-Encoding: 7bit
1906 Content-Type: text/plain;
1907   charset="iso-8859-1"
1908 From: Nonexisting User <nonexisting@bork.bork.bork>
1909 To: issue_tracker@your.tracker.email.domain.example
1910 Message-Id: <dummy_test_message_id>
1911 Subject: [issue] Testing nonexisting user...
1913 This is a test submission of a new issue.
1915 --===============0639262320==--
1916 ''')
1918     def testEnc01(self):
1919         self.db.user.set(self.mary_id,
1920             realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode
1921             ('latin-1').encode('utf-8'))
1922         self.doNewIssue()
1923         self._handle_mail('''Content-Type: text/plain;
1924   charset="iso-8859-1"
1925 From: mary <mary@test.test>
1926 To: issue_tracker@your.tracker.email.domain.example
1927 Message-Id: <followup_dummy_id>
1928 In-Reply-To: <dummy_test_message_id>
1929 Subject: [issue1] Testing...
1930 Content-Type: text/plain;
1931         charset="iso-8859-1"
1932 Content-Transfer-Encoding: quoted-printable
1934 A message with encoding (encoded oe =F6)
1936 ''')
1937         self.compareMessages(self._get_mail(),
1938 '''FROM: roundup-admin@your.tracker.email.domain.example
1939 TO: chef@bork.bork.bork, richard@test.test
1940 Content-Type: text/plain; charset="utf-8"
1941 Subject: [issue1] Testing...
1942 To: chef@bork.bork.bork, richard@test.test
1943 From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?=
1944  <issue_tracker@your.tracker.email.domain.example>
1945 Reply-To: Roundup issue tracker
1946  <issue_tracker@your.tracker.email.domain.example>
1947 MIME-Version: 1.0
1948 Message-Id: <followup_dummy_id>
1949 In-Reply-To: <dummy_test_message_id>
1950 X-Roundup-Name: Roundup issue tracker
1951 X-Roundup-Loop: hello
1952 X-Roundup-Issue-Status: chatting
1953 Content-Transfer-Encoding: quoted-printable
1956 =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary <mary@test.test> added the=
1957  comment:
1959 A message with encoding (encoded oe =C3=B6)
1961 ----------
1962 status: unread -> chatting
1964 _______________________________________________________________________
1965 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1966 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1967 _______________________________________________________________________
1968 ''')
1970     def testEncNonUTF8(self):
1971         self.doNewIssue()
1972         self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1973         self._handle_mail('''Content-Type: text/plain;
1974   charset="iso-8859-1"
1975 From: mary <mary@test.test>
1976 To: issue_tracker@your.tracker.email.domain.example
1977 Message-Id: <followup_dummy_id>
1978 In-Reply-To: <dummy_test_message_id>
1979 Subject: [issue1] Testing...
1980 Content-Type: text/plain;
1981         charset="iso-8859-1"
1982 Content-Transfer-Encoding: quoted-printable
1984 A message with encoding (encoded oe =F6)
1986 ''')
1987         self.compareMessages(self._get_mail(),
1988 '''FROM: roundup-admin@your.tracker.email.domain.example
1989 TO: chef@bork.bork.bork, richard@test.test
1990 Content-Type: text/plain; charset="iso-8859-1"
1991 Subject: [issue1] Testing...
1992 To: chef@bork.bork.bork, richard@test.test
1993 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1994 Reply-To: Roundup issue tracker
1995  <issue_tracker@your.tracker.email.domain.example>
1996 MIME-Version: 1.0
1997 Message-Id: <followup_dummy_id>
1998 In-Reply-To: <dummy_test_message_id>
1999 X-Roundup-Name: Roundup issue tracker
2000 X-Roundup-Loop: hello
2001 X-Roundup-Issue-Status: chatting
2002 Content-Transfer-Encoding: quoted-printable
2005 Contrary, Mary <mary@test.test> added the comment:
2007 A message with encoding (encoded oe =F6)
2009 ----------
2010 status: unread -> chatting
2012 _______________________________________________________________________
2013 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2014 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2015 _______________________________________________________________________
2016 ''')
2019     def testMultipartEnc01(self):
2020         self.doNewIssue()
2021         self._handle_mail('''Content-Type: text/plain;
2022   charset="iso-8859-1"
2023 From: mary <mary@test.test>
2024 To: issue_tracker@your.tracker.email.domain.example
2025 Message-Id: <followup_dummy_id>
2026 In-Reply-To: <dummy_test_message_id>
2027 Subject: [issue1] Testing...
2028 Content-Type: multipart/mixed;
2029         boundary="----_=_NextPart_000_01"
2031 This message is in MIME format. Since your mail reader does not understand
2032 this format, some or all of this message may not be legible.
2034 ------_=_NextPart_000_01
2035 Content-Type: text/plain;
2036         charset="iso-8859-1"
2037 Content-Transfer-Encoding: quoted-printable
2039 A message with first part encoded (encoded oe =F6)
2041 ''')
2042         self.compareMessages(self._get_mail(),
2043 '''FROM: roundup-admin@your.tracker.email.domain.example
2044 TO: chef@bork.bork.bork, richard@test.test
2045 Content-Type: text/plain; charset="utf-8"
2046 Subject: [issue1] Testing...
2047 To: chef@bork.bork.bork, richard@test.test
2048 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
2049 Reply-To: Roundup issue tracker
2050  <issue_tracker@your.tracker.email.domain.example>
2051 MIME-Version: 1.0
2052 Message-Id: <followup_dummy_id>
2053 In-Reply-To: <dummy_test_message_id>
2054 X-Roundup-Name: Roundup issue tracker
2055 X-Roundup-Loop: hello
2056 X-Roundup-Issue-Status: chatting
2057 Content-Transfer-Encoding: quoted-printable
2060 Contrary, Mary <mary@test.test> added the comment:
2062 A message with first part encoded (encoded oe =C3=B6)
2064 ----------
2065 status: unread -> chatting
2067 _______________________________________________________________________
2068 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2069 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2070 _______________________________________________________________________
2071 ''')
2073     def testContentDisposition(self):
2074         self.doNewIssue()
2075         self._handle_mail('''Content-Type: text/plain;
2076   charset="iso-8859-1"
2077 From: mary <mary@test.test>
2078 To: issue_tracker@your.tracker.email.domain.example
2079 Message-Id: <followup_dummy_id>
2080 In-Reply-To: <dummy_test_message_id>
2081 Subject: [issue1] Testing...
2082 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
2083 Content-Disposition: inline
2086 --bCsyhTFzCvuiizWE
2087 Content-Type: text/plain; charset=us-ascii
2088 Content-Disposition: inline
2090 test attachment binary
2092 --bCsyhTFzCvuiizWE
2093 Content-Type: application/octet-stream
2094 Content-Disposition: attachment; filename="main.dvi"
2095 Content-Transfer-Encoding: base64
2097 SnVzdCBhIHRlc3QgAQo=
2099 --bCsyhTFzCvuiizWE--
2100 ''')
2101         messages = self.db.issue.get('1', 'messages')
2102         messages.sort()
2103         file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
2104         self.assertEqual(file.name, 'main.dvi')
2105         self.assertEqual(file.content, 'Just a test \001\n')
2107     def testFollowupStupidQuoting(self):
2108         self.doNewIssue()
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 This is a followup
2119 ''')
2120         self.compareMessages(self._get_mail(),
2121 '''FROM: roundup-admin@your.tracker.email.domain.example
2122 TO: chef@bork.bork.bork
2123 Content-Type: text/plain; charset="utf-8"
2124 Subject: [issue1] Testing...
2125 To: chef@bork.bork.bork
2126 From: richard <issue_tracker@your.tracker.email.domain.example>
2127 Reply-To: Roundup issue tracker
2128  <issue_tracker@your.tracker.email.domain.example>
2129 MIME-Version: 1.0
2130 Message-Id: <followup_dummy_id>
2131 In-Reply-To: <dummy_test_message_id>
2132 X-Roundup-Name: Roundup issue tracker
2133 X-Roundup-Loop: hello
2134 X-Roundup-Issue-Status: chatting
2135 Content-Transfer-Encoding: quoted-printable
2138 richard <richard@test.test> added the comment:
2140 This is a followup
2142 ----------
2143 status: unread -> chatting
2145 _______________________________________________________________________
2146 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2147 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2148 _______________________________________________________________________
2149 ''')
2151     def testEmailQuoting(self):
2152         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
2153         self.innerTestQuoting('''This is a followup
2154 ''')
2156     def testEmailQuotingRemove(self):
2157         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
2158         self.innerTestQuoting('''Blah blah wrote:
2159 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2160 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2163 This is a followup
2164 ''')
2166     def innerTestQuoting(self, expect):
2167         nodeid = self.doNewIssue()
2169         messages = self.db.issue.get(nodeid, 'messages')
2171         self._handle_mail('''Content-Type: text/plain;
2172   charset="iso-8859-1"
2173 From: richard <richard@test.test>
2174 To: issue_tracker@your.tracker.email.domain.example
2175 Message-Id: <followup_dummy_id>
2176 In-Reply-To: <dummy_test_message_id>
2177 Subject: Re: [issue1] Testing...
2179 Blah blah wrote:
2180 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2181 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2184 This is a followup
2185 ''')
2186         # figure the new message id
2187         newmessages = self.db.issue.get(nodeid, 'messages')
2188         for msg in messages:
2189             newmessages.remove(msg)
2190         messageid = newmessages[0]
2192         self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
2194     def testUserLookup(self):
2195         i = self.db.user.create(username='user1', address='user1@foo.com')
2196         self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
2197         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
2198         i = self.db.user.create(username='user2', address='USER2@foo.com')
2199         self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
2200         self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
2202     def testUserAlternateLookup(self):
2203         i = self.db.user.create(username='user1', address='user1@foo.com',
2204                                 alternate_addresses='user1@bar.com')
2205         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
2206         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
2208     def testUserAlternateSubstringNomatch(self):
2209         i = self.db.user.create(username='user1', address='user1@foo.com',
2210                                 alternate_addresses='x-user1@bar.com')
2211         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), 0)
2212         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), 0)
2214     def testUserCreate(self):
2215         i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
2216         self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
2218     def testRFC2822(self):
2219         ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
2220         unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
2221         unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
2222         self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
2223         self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
2225     def testRegistrationConfirmation(self):
2226         otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
2227         self.db.getOTKManager().set(otk, username='johannes')
2228         self._handle_mail('''Content-Type: text/plain;
2229   charset="iso-8859-1"
2230 From: Chef <chef@bork.bork.bork>
2231 To: issue_tracker@your.tracker.email.domain.example
2232 Cc: richard@test.test
2233 Message-Id: <dummy_test_message_id>
2234 Subject: Re: Complete your registration to Roundup issue tracker
2235  -- key %s
2237 This is a test confirmation of registration.
2238 ''' % otk)
2239         self.db.user.lookup('johannes')
2241     def testFollowupOnNonIssue(self):
2242         self.db.keyword.create(name='Foo')
2243         self._handle_mail('''Content-Type: text/plain;
2244   charset="iso-8859-1"
2245 From: richard <richard@test.test>
2246 To: issue_tracker@your.tracker.email.domain.example
2247 Message-Id: <followup_dummy_id>
2248 In-Reply-To: <dummy_test_message_id>
2249 Subject: [keyword1] Testing... [name=Bar]
2251 ''')
2252         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2254     def testResentFrom(self):
2255         nodeid = self._handle_mail('''Content-Type: text/plain;
2256   charset="iso-8859-1"
2257 From: Chef <chef@bork.bork.bork>
2258 Resent-From: mary <mary@test.test>
2259 To: issue_tracker@your.tracker.email.domain.example
2260 Cc: richard@test.test
2261 Message-Id: <dummy_test_message_id>
2262 Subject: [issue] Testing...
2264 This is a test submission of a new issue.
2265 ''')
2266         assert not os.path.exists(SENDMAILDEBUG)
2267         l = self.db.issue.get(nodeid, 'nosy')
2268         l.sort()
2269         self.assertEqual(l, [self.richard_id, self.mary_id])
2270         return nodeid
2272     def testDejaVu(self):
2273         self.assertRaises(IgnoreLoop, self._handle_mail,
2274             '''Content-Type: text/plain;
2275   charset="iso-8859-1"
2276 From: Chef <chef@bork.bork.bork>
2277 X-Roundup-Loop: hello
2278 To: issue_tracker@your.tracker.email.domain.example
2279 Cc: richard@test.test
2280 Message-Id: <dummy_test_message_id>
2281 Subject: Re: [issue] Testing...
2283 Hi, I've been mis-configured to loop messages back to myself.
2284 ''')
2286     def testItsBulkStupid(self):
2287         self.assertRaises(IgnoreBulk, self._handle_mail,
2288             '''Content-Type: text/plain;
2289   charset="iso-8859-1"
2290 From: Chef <chef@bork.bork.bork>
2291 Precedence: bulk
2292 To: issue_tracker@your.tracker.email.domain.example
2293 Cc: richard@test.test
2294 Message-Id: <dummy_test_message_id>
2295 Subject: Re: [issue] Testing...
2297 Hi, I'm on holidays, and this is a dumb auto-responder.
2298 ''')
2300     def testAutoReplyEmailsAreIgnored(self):
2301         self.assertRaises(IgnoreBulk, self._handle_mail,
2302             '''Content-Type: text/plain;
2303   charset="iso-8859-1"
2304 From: Chef <chef@bork.bork.bork>
2305 To: issue_tracker@your.tracker.email.domain.example
2306 Cc: richard@test.test
2307 Message-Id: <dummy_test_message_id>
2308 Subject: Re: [issue] Out of office AutoReply: Back next week
2310 Hi, I am back in the office next week
2311 ''')
2313     def testNoSubject(self):
2314         self.assertRaises(MailUsageError, self._handle_mail,
2315             '''Content-Type: text/plain;
2316   charset="iso-8859-1"
2317 From: Chef <chef@bork.bork.bork>
2318 To: issue_tracker@your.tracker.email.domain.example
2319 Cc: richard@test.test
2320 Reply-To: chef@bork.bork.bork
2321 Message-Id: <dummy_test_message_id>
2323 ''')
2325     #
2326     # TEST FOR INVALID DESIGNATOR HANDLING
2327     #
2328     def testInvalidDesignator(self):
2329         self.assertRaises(MailUsageError, self._handle_mail,
2330             '''Content-Type: text/plain;
2331   charset="iso-8859-1"
2332 From: Chef <chef@bork.bork.bork>
2333 To: issue_tracker@your.tracker.email.domain.example
2334 Subject: [frobulated] testing
2335 Cc: richard@test.test
2336 Reply-To: chef@bork.bork.bork
2337 Message-Id: <dummy_test_message_id>
2339 ''')
2340         self.assertRaises(MailUsageError, self._handle_mail,
2341             '''Content-Type: text/plain;
2342   charset="iso-8859-1"
2343 From: Chef <chef@bork.bork.bork>
2344 To: issue_tracker@your.tracker.email.domain.example
2345 Subject: [issue12345] testing
2346 Cc: richard@test.test
2347 Reply-To: chef@bork.bork.bork
2348 Message-Id: <dummy_test_message_id>
2350 ''')
2352     def testInvalidClassLoose(self):
2353         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2354         nodeid = self._handle_mail('''Content-Type: text/plain;
2355   charset="iso-8859-1"
2356 From: Chef <chef@bork.bork.bork>
2357 To: issue_tracker@your.tracker.email.domain.example
2358 Subject: [frobulated] testing
2359 Cc: richard@test.test
2360 Reply-To: chef@bork.bork.bork
2361 Message-Id: <dummy_test_message_id>
2363 ''')
2364         assert not os.path.exists(SENDMAILDEBUG)
2365         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2366             '[frobulated] testing')
2368     def testInvalidClassLooseReply(self):
2369         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2370         nodeid = self._handle_mail('''Content-Type: text/plain;
2371   charset="iso-8859-1"
2372 From: Chef <chef@bork.bork.bork>
2373 To: issue_tracker@your.tracker.email.domain.example
2374 Subject: Re: [frobulated] testing
2375 Cc: richard@test.test
2376 Reply-To: chef@bork.bork.bork
2377 Message-Id: <dummy_test_message_id>
2379 ''')
2380         assert not os.path.exists(SENDMAILDEBUG)
2381         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2382             '[frobulated] testing')
2384     def testInvalidClassLoose(self):
2385         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2386         nodeid = self._handle_mail('''Content-Type: text/plain;
2387   charset="iso-8859-1"
2388 From: Chef <chef@bork.bork.bork>
2389 To: issue_tracker@your.tracker.email.domain.example
2390 Subject: [issue1234] testing
2391 Cc: richard@test.test
2392 Reply-To: chef@bork.bork.bork
2393 Message-Id: <dummy_test_message_id>
2395 ''')
2396         assert not os.path.exists(SENDMAILDEBUG)
2397         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2398             '[issue1234] testing')
2400     def testClassLooseOK(self):
2401         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2402         self.db.keyword.create(name='Foo')
2403         nodeid = self._handle_mail('''Content-Type: text/plain;
2404   charset="iso-8859-1"
2405 From: Chef <chef@bork.bork.bork>
2406 To: issue_tracker@your.tracker.email.domain.example
2407 Subject: [keyword1] Testing... [name=Bar]
2408 Cc: richard@test.test
2409 Reply-To: chef@bork.bork.bork
2410 Message-Id: <dummy_test_message_id>
2412 ''')
2413         assert not os.path.exists(SENDMAILDEBUG)
2414         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2416     def testClassStrictInvalid(self):
2417         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2418         self.instance.config.MAILGW_DEFAULT_CLASS = ''
2420         message = '''Content-Type: text/plain;
2421   charset="iso-8859-1"
2422 From: Chef <chef@bork.bork.bork>
2423 To: issue_tracker@your.tracker.email.domain.example
2424 Subject: Testing...
2425 Cc: richard@test.test
2426 Reply-To: chef@bork.bork.bork
2427 Message-Id: <dummy_test_message_id>
2429 '''
2430         self.assertRaises(MailUsageError, self._handle_mail, message)
2432     def testClassStrictValid(self):
2433         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2434         self.instance.config.MAILGW_DEFAULT_CLASS = ''
2436         nodeid = self._handle_mail('''Content-Type: text/plain;
2437   charset="iso-8859-1"
2438 From: Chef <chef@bork.bork.bork>
2439 To: issue_tracker@your.tracker.email.domain.example
2440 Subject: [issue] Testing...
2441 Cc: richard@test.test
2442 Reply-To: chef@bork.bork.bork
2443 Message-Id: <dummy_test_message_id>
2445 ''')
2447         assert not os.path.exists(SENDMAILDEBUG)
2448         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
2450     #
2451     # TEST FOR INVALID COMMANDS HANDLING
2452     #
2453     def testInvalidCommands(self):
2454         self.assertRaises(MailUsageError, self._handle_mail,
2455             '''Content-Type: text/plain;
2456   charset="iso-8859-1"
2457 From: Chef <chef@bork.bork.bork>
2458 To: issue_tracker@your.tracker.email.domain.example
2459 Subject: testing [frobulated]
2460 Cc: richard@test.test
2461 Reply-To: chef@bork.bork.bork
2462 Message-Id: <dummy_test_message_id>
2464 ''')
2466     def testInvalidCommandPassthrough(self):
2467         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
2468         nodeid = self._handle_mail('''Content-Type: text/plain;
2469   charset="iso-8859-1"
2470 From: Chef <chef@bork.bork.bork>
2471 To: issue_tracker@your.tracker.email.domain.example
2472 Subject: testing [frobulated]
2473 Cc: richard@test.test
2474 Reply-To: chef@bork.bork.bork
2475 Message-Id: <dummy_test_message_id>
2477 ''')
2478         assert not os.path.exists(SENDMAILDEBUG)
2479         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2480             'testing [frobulated]')
2482     def testInvalidCommandPassthroughLoose(self):
2483         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2484         nodeid = self._handle_mail('''Content-Type: text/plain;
2485   charset="iso-8859-1"
2486 From: Chef <chef@bork.bork.bork>
2487 To: issue_tracker@your.tracker.email.domain.example
2488 Subject: testing [frobulated]
2489 Cc: richard@test.test
2490 Reply-To: chef@bork.bork.bork
2491 Message-Id: <dummy_test_message_id>
2493 ''')
2494         assert not os.path.exists(SENDMAILDEBUG)
2495         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2496             'testing [frobulated]')
2498     def testInvalidCommandPassthroughLooseOK(self):
2499         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2500         nodeid = self._handle_mail('''Content-Type: text/plain;
2501   charset="iso-8859-1"
2502 From: Chef <chef@bork.bork.bork>
2503 To: issue_tracker@your.tracker.email.domain.example
2504 Subject: testing [assignedto=mary]
2505 Cc: richard@test.test
2506 Reply-To: chef@bork.bork.bork
2507 Message-Id: <dummy_test_message_id>
2509 ''')
2510         assert not os.path.exists(SENDMAILDEBUG)
2511         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2512         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2514     def testCommandDelimiters(self):
2515         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2516         nodeid = self._handle_mail('''Content-Type: text/plain;
2517   charset="iso-8859-1"
2518 From: Chef <chef@bork.bork.bork>
2519 To: issue_tracker@your.tracker.email.domain.example
2520 Subject: testing {assignedto=mary}
2521 Cc: richard@test.test
2522 Reply-To: chef@bork.bork.bork
2523 Message-Id: <dummy_test_message_id>
2525 ''')
2526         assert not os.path.exists(SENDMAILDEBUG)
2527         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2528         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2530     def testPrefixDelimiters(self):
2531         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2532         self.db.keyword.create(name='Foo')
2533         self._handle_mail('''Content-Type: text/plain;
2534   charset="iso-8859-1"
2535 From: richard <richard@test.test>
2536 To: issue_tracker@your.tracker.email.domain.example
2537 Message-Id: <followup_dummy_id>
2538 In-Reply-To: <dummy_test_message_id>
2539 Subject: {keyword1} Testing... {name=Bar}
2541 ''')
2542         assert not os.path.exists(SENDMAILDEBUG)
2543         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2545     def testCommandDelimitersIgnore(self):
2546         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2547         nodeid = self._handle_mail('''Content-Type: text/plain;
2548   charset="iso-8859-1"
2549 From: Chef <chef@bork.bork.bork>
2550 To: issue_tracker@your.tracker.email.domain.example
2551 Subject: testing [assignedto=mary]
2552 Cc: richard@test.test
2553 Reply-To: chef@bork.bork.bork
2554 Message-Id: <dummy_test_message_id>
2556 ''')
2557         assert not os.path.exists(SENDMAILDEBUG)
2558         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2559             'testing [assignedto=mary]')
2560         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
2562     def testReplytoMatch(self):
2563         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2564         nodeid = self.doNewIssue()
2565         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2566   charset="iso-8859-1"
2567 From: Chef <chef@bork.bork.bork>
2568 To: issue_tracker@your.tracker.email.domain.example
2569 Message-Id: <dummy_test_message_id2>
2570 In-Reply-To: <dummy_test_message_id>
2571 Subject: Testing...
2573 Followup message.
2574 ''')
2576         nodeid3 = self._handle_mail('''Content-Type: text/plain;
2577   charset="iso-8859-1"
2578 From: Chef <chef@bork.bork.bork>
2579 To: issue_tracker@your.tracker.email.domain.example
2580 Message-Id: <dummy_test_message_id3>
2581 In-Reply-To: <dummy_test_message_id2>
2582 Subject: Testing...
2584 Yet another message in the same thread/issue.
2585 ''')
2587         self.assertEqual(nodeid, nodeid2)
2588         self.assertEqual(nodeid, nodeid3)
2590     def testHelpSubject(self):
2591         message = '''Content-Type: text/plain;
2592   charset="iso-8859-1"
2593 From: Chef <chef@bork.bork.bork>
2594 To: issue_tracker@your.tracker.email.domain.example
2595 Message-Id: <dummy_test_message_id2>
2596 In-Reply-To: <dummy_test_message_id>
2597 Subject: hElp
2600 '''
2601         self.assertRaises(MailUsageHelp, self._handle_mail, message)
2603     def testMaillistSubject(self):
2604         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
2605         self.db.keyword.create(name='Foo')
2606         self._handle_mail('''Content-Type: text/plain;
2607   charset="iso-8859-1"
2608 From: Chef <chef@bork.bork.bork>
2609 To: issue_tracker@your.tracker.email.domain.example
2610 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
2611 Cc: richard@test.test
2612 Reply-To: chef@bork.bork.bork
2613 Message-Id: <dummy_test_message_id>
2615 ''')
2617         assert not os.path.exists(SENDMAILDEBUG)
2618         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2620     def testUnknownPrefixSubject(self):
2621         self.db.keyword.create(name='Foo')
2622         self._handle_mail('''Content-Type: text/plain;
2623   charset="iso-8859-1"
2624 From: Chef <chef@bork.bork.bork>
2625 To: issue_tracker@your.tracker.email.domain.example
2626 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
2627 Cc: richard@test.test
2628 Reply-To: chef@bork.bork.bork
2629 Message-Id: <dummy_test_message_id>
2631 ''')
2633         assert not os.path.exists(SENDMAILDEBUG)
2634         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2636     def testOneCharSubject(self):
2637         message = '''Content-Type: text/plain;
2638   charset="iso-8859-1"
2639 From: Chef <chef@bork.bork.bork>
2640 To: issue_tracker@your.tracker.email.domain.example
2641 Subject: b
2642 Cc: richard@test.test
2643 Reply-To: chef@bork.bork.bork
2644 Message-Id: <dummy_test_message_id>
2646 '''
2647         try:
2648             self._handle_mail(message)
2649         except MailUsageError:
2650             self.fail('MailUsageError raised')
2652     def testIssueidLast(self):
2653         nodeid1 = self.doNewIssue()
2654         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2655   charset="iso-8859-1"
2656 From: mary <mary@test.test>
2657 To: issue_tracker@your.tracker.email.domain.example
2658 Message-Id: <followup_dummy_id>
2659 In-Reply-To: <dummy_test_message_id>
2660 Subject: New title [issue1]
2662 This is a second followup
2663 ''')
2665         assert nodeid1 == nodeid2
2666         self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
2668     def testSecurityMessagePermissionContent(self):
2669         id = self.doNewIssue()
2670         issue = self.db.issue.getnode (id)
2671         self.db.security.addRole(name='Nomsg')
2672         self.db.security.addPermissionToRole('Nomsg', 'Email Access')
2673         for cl in 'issue', 'file', 'keyword':
2674             for p in 'View', 'Edit', 'Create':
2675                 self.db.security.addPermissionToRole('Nomsg', p, cl)
2676         self.db.user.set(self.mary_id, roles='Nomsg')
2677         nodeid = self._handle_mail('''Content-Type: text/plain;
2678   charset="iso-8859-1"
2679 From: Chef <chef@bork.bork.bork>
2680 To: issue_tracker@your.tracker.email.domain.example
2681 Message-Id: <dummy_test_message_id_2>
2682 Subject: [issue%(id)s] Testing... [nosy=+mary]
2684 Just a test reply
2685 '''%locals())
2686         assert os.path.exists(SENDMAILDEBUG)
2687         self.compareMessages(self._get_mail(),
2688 '''FROM: roundup-admin@your.tracker.email.domain.example
2689 TO: chef@bork.bork.bork, richard@test.test
2690 Content-Type: text/plain; charset="utf-8"
2691 Subject: [issue1] Testing...
2692 To: richard@test.test
2693 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
2694 Reply-To: Roundup issue tracker
2695  <issue_tracker@your.tracker.email.domain.example>
2696 MIME-Version: 1.0
2697 Message-Id: <dummy_test_message_id_2>
2698 In-Reply-To: <dummy_test_message_id>
2699 X-Roundup-Name: Roundup issue tracker
2700 X-Roundup-Loop: hello
2701 X-Roundup-Issue-Status: chatting
2702 Content-Transfer-Encoding: quoted-printable
2705 Bork, Chef <chef@bork.bork.bork> added the comment:
2707 Just a test reply
2709 ----------
2710 nosy: +mary
2711 status: unread -> chatting
2713 _______________________________________________________________________
2714 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2715 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2716 _______________________________________________________________________
2717 ''')
2719     def testOutlookAttachment(self):
2720         message = '''X-MimeOLE: Produced By Microsoft Exchange V6.5
2721 Content-class: urn:content-classes:message
2722 MIME-Version: 1.0
2723 Content-Type: multipart/mixed;
2724         boundary="----_=_NextPart_001_01CACA65.40A51CBC"
2725 Subject: Example of a failed outlook attachment e-mail
2726 Date: Tue, 23 Mar 2010 01:43:44 -0700
2727 Message-ID: <CA37F17219784343816CA6613D2E339205E7D0F9@nrcwstexb1.nrc.ca>
2728 X-MS-Has-Attach: yes
2729 X-MS-TNEF-Correlator: 
2730 Thread-Topic: Example of a failed outlook attachment e-mail
2731 Thread-Index: AcrKJo/t3pUBBwTpSwWNE3LE67UBDQ==
2732 From: "Hugh" <richard@test.test>
2733 To: <richard@test.test>
2734 X-OriginalArrivalTime: 23 Mar 2010 08:45:57.0350 (UTC) FILETIME=[41893860:01CACA65]
2736 This is a multi-part message in MIME format.
2738 ------_=_NextPart_001_01CACA65.40A51CBC
2739 Content-Type: multipart/alternative;
2740         boundary="----_=_NextPart_002_01CACA65.40A51CBC"
2743 ------_=_NextPart_002_01CACA65.40A51CBC
2744 Content-Type: text/plain;
2745         charset="us-ascii"
2746 Content-Transfer-Encoding: quoted-printable
2749 Hi Richard,
2751 I suppose this isn't the exact message that was sent but is a resend of
2752 one of my trial messages that failed.  For your benefit I changed the
2753 subject line and am adding these words to the message body.  Should
2754 still be as problematic, but if you like I can resend an exact copy of a
2755 failed message changing nothing except putting your address instead of
2756 our tracker.
2758 Thanks very much for taking time to look into this.  Much appreciated.
2760  <<battery backup>>=20
2762 ------_=_NextPart_002_01CACA65.40A51CBC
2763 Content-Type: text/html;
2764         charset="us-ascii"
2765 Content-Transfer-Encoding: quoted-printable
2767 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2768 <HTML>
2769 <HEAD>
2770 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2771 charset=3Dus-ascii">
2772 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2773 6.5.7654.12">
2774 <TITLE>Example of a failed outlook attachment e-mail</TITLE>
2775 </HEAD>
2776 <BODY>
2777 <!-- Converted from text/rtf format -->
2778 <BR>
2780 <P><FONT SIZE=3D2 FACE=3D"Arial">Hi Richard,</FONT>
2781 </P>
2783 <P><FONT SIZE=3D2 FACE=3D"Arial">I suppose this isn't the exact message =
2784 that was sent but is a resend of one of my trial messages that =
2785 failed.&nbsp; For your benefit I changed the subject line and am adding =
2786 these words to the message body.&nbsp; Should still be as problematic, =
2787 but if you like I can resend an exact copy of a failed message changing =
2788 nothing except putting your address instead of our tracker.</FONT></P>
2790 <P><FONT SIZE=3D2 FACE=3D"Arial">Thanks very much for taking time to =
2791 look into this.&nbsp; Much appreciated.</FONT>
2792 </P>
2793 <BR>
2795 <P><FONT FACE=3D"Arial" SIZE=3D2 COLOR=3D"#000000"> &lt;&lt;battery =
2796 backup&gt;&gt; </FONT>
2797 </P>
2799 </BODY>
2800 </HTML>
2801 ------_=_NextPart_002_01CACA65.40A51CBC--
2803 ------_=_NextPart_001_01CACA65.40A51CBC
2804 Content-Type: message/rfc822
2805 Content-Transfer-Encoding: 7bit
2807 X-MimeOLE: Produced By Microsoft Exchange V6.5
2808 MIME-Version: 1.0
2809 Content-Type: multipart/alternative;
2810         boundary="----_=_NextPart_003_01CAC15A.29717800"
2811 X-OriginalArrivalTime: 11 Mar 2010 20:33:51.0249 (UTC) FILETIME=[28FEE010:01CAC15A]
2812 Content-class: urn:content-classes:message
2813 Subject: battery backup
2814 Date: Thu, 11 Mar 2010 13:33:43 -0700
2815 Message-ID: <p06240809c7bf02f9624c@[128.114.22.203]>
2816 X-MS-Has-Attach: 
2817 X-MS-TNEF-Correlator: 
2818 Thread-Topic: battery backup
2819 Thread-Index: AcrBWimtulTrSvBdQ2CcfZ8lyQdxmQ==
2820 From: "Jerry" <jerry@test.test>
2821 To: "Hugh" <hugh@test.test>
2823 This is a multi-part message in MIME format.
2825 ------_=_NextPart_003_01CAC15A.29717800
2826 Content-Type: text/plain;
2827         charset="iso-8859-1"
2828 Content-Transfer-Encoding: quoted-printable
2830 Dear Hugh,
2831         A car batter has an energy capacity of ~ 500Wh.  A UPS=20
2832 battery is worse than this.
2834 if we need to provied 100kW for 30 minutes that will take 100 car=20
2835 batteries.  This seems like an awful lot of batteries.
2837 Of course I like your idea of making the time 1 minute, so we get to=20
2838 a more modest number of batteries
2840 Jerry
2843 ------_=_NextPart_003_01CAC15A.29717800
2844 Content-Type: text/html;
2845         charset="iso-8859-1"
2846 Content-Transfer-Encoding: quoted-printable
2848 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2849 <HTML>
2850 <HEAD>
2851 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2852 charset=3Diso-8859-1">
2853 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2854 6.5.7654.12">
2855 <TITLE>battery backup</TITLE>
2856 </HEAD>
2857 <BODY>
2858 <!-- Converted from text/plain format -->
2860 <P><FONT SIZE=3D2>Dear Hugh,</FONT>
2862 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT SIZE=3D2>A car =
2863 batter has an energy capacity of ~ 500Wh.&nbsp; A UPS </FONT>
2865 <BR><FONT SIZE=3D2>battery is worse than this.</FONT>
2866 </P>
2868 <P><FONT SIZE=3D2>if we need to provied 100kW for 30 minutes that will =
2869 take 100 car </FONT>
2871 <BR><FONT SIZE=3D2>batteries.&nbsp; This seems like an awful lot of =
2872 batteries.</FONT>
2873 </P>
2875 <P><FONT SIZE=3D2>Of course I like your idea of making the time 1 =
2876 minute, so we get to </FONT>
2878 <BR><FONT SIZE=3D2>a more modest number of batteries</FONT>
2879 </P>
2881 <P><FONT SIZE=3D2>Jerry</FONT>
2882 </P>
2884 </BODY>
2885 </HTML>
2886 ------_=_NextPart_003_01CAC15A.29717800--
2888 ------_=_NextPart_001_01CACA65.40A51CBC--
2889 '''
2890         nodeid = self._handle_mail(message)
2891         assert not os.path.exists(SENDMAILDEBUG)
2892         msgid = self.db.issue.get(nodeid, 'messages')[0]
2893         self.assert_(self.db.msg.get(msgid, 'content').startswith('Hi Richard'))
2894         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1', '2'])
2895         fileid = self.db.msg.get(msgid, 'files')[0]
2896         self.assertEqual(self.db.file.get(fileid, 'type'), 'text/html')
2897         fileid = self.db.msg.get(msgid, 'files')[1]
2898         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2900     def testForwardedMessageAttachment(self):
2901         message = '''Return-Path: <rgg@test.test>
2902 Received: from localhost(127.0.0.1), claiming to be "[115.130.26.69]"
2903 via SMTP by localhost, id smtpdAAApLaWrq; Tue Apr 13 23:10:05 2010
2904 Message-ID: <4BC4F9C7.50409@test.test>
2905 Date: Wed, 14 Apr 2010 09:09:59 +1000
2906 From: Rupert Goldie <rgg@test.test>
2907 User-Agent: Thunderbird 2.0.0.24 (Windows/20100228)
2908 MIME-Version: 1.0
2909 To: ekit issues <issues@test.test>
2910 Subject: [Fwd: PHP ERROR (fb)] post limit reached
2911 Content-Type: multipart/mixed; boundary="------------000807090608060304010403"
2913 This is a multi-part message in MIME format.
2914 --------------000807090608060304010403
2915 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
2916 Content-Transfer-Encoding: 7bit
2918 Catch this exception and log it without emailing.
2920 --------------000807090608060304010403
2921 Content-Type: message/rfc822; name="PHP ERROR (fb).eml"
2922 Content-Transfer-Encoding: 7bit
2923 Content-Disposition: inline; filename="PHP ERROR (fb).eml"
2925 Return-Path: <ektravj@test.test>
2926 X-Sieve: CMU Sieve 2.2
2927 via SMTP by crown.off.ekorp.com, id smtpdAAA1JaW1o; Tue Apr 13 23:01:04 2010
2928 X-Virus-Scanned: by amavisd-new at ekit.com
2929 To: facebook-errors@test.test
2930 From: ektravj@test.test
2931 Subject: PHP ERROR (fb)
2932 Message-Id: <20100413230100.D601D27E84@mail2.elax3.ekorp.com>
2933 Date: Tue, 13 Apr 2010 23:01:00 +0000 (UTC)
2935 [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
2936 Stack trace:
2937 #0 /app/01/www/virtual/fb.ekit.com/htdocs/gateway/ekit/feed/index.php(178): fb_exceptions(Object(FacebookRestClientException))
2938 #1 {main}
2939  thrown in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php on line 280
2942 --------------000807090608060304010403--
2943 '''
2944         nodeid = self._handle_mail(message)
2945         assert not os.path.exists(SENDMAILDEBUG)
2946         msgid = self.db.issue.get(nodeid, 'messages')[0]
2947         self.assertEqual(self.db.msg.get(msgid, 'content'),
2948             'Catch this exception and log it without emailing.')
2949         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1'])
2950         fileid = self.db.msg.get(msgid, 'files')[0]
2951         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2953 def test_suite():
2954     suite = unittest.TestSuite()
2955     suite.addTest(unittest.makeSuite(MailgwTestCase))
2956     return suite
2958 if __name__ == '__main__':
2959     runner = unittest.TextTestRunner()
2960     unittest.main(testRunner=runner)
2962 # vim: set filetype=python sts=4 sw=4 et si :