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