X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=test%2Ftest_mailgw.py;h=ca3419f90510d7bc361dc0fd6d2a000b0a181e21;hb=8b8b6f5da468851e86eaf6a1920ed56a4875f257;hp=d524b787f73e3875eaa39b3879fdc399f0f6e646;hpb=e9a26effed8495aa409a4bca11c514af91525048;p=roundup.git diff --git a/test/test_mailgw.py b/test/test_mailgw.py index d524b78..ca3419f 100644 --- a/test/test_mailgw.py +++ b/test/test_mailgw.py @@ -1,3 +1,4 @@ +# -*- encoding: utf-8 -*- # # Copyright (c) 2001 Richard Jones, richard@bofh.asn.au. # This module is free software, and you may redistribute it and/or modify @@ -8,39 +9,95 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # -# $Id: test_mailgw.py,v 1.45 2003-04-27 02:16:47 richard Exp $ +# $Id: test_mailgw.py,v 1.96 2008-08-19 01:40:59 richard Exp $ -import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys, difflib -import rfc822 +# TODO: test bcc -# Note: Should parse emails according to RFC2822 instead of performing a -# literal string comparision. Parsing the messages allows the tests to work for -# any legal serialization of an email. -#try : -# import email -#except ImportError : -# import rfc822 as email +import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time -from roundup.mailgw import MailGW, Unauthorized, uidFromAddress -from roundup import init, instance +from cStringIO import StringIO + +if not os.environ.has_key('SENDMAILDEBUG'): + os.environ['SENDMAILDEBUG'] = 'mail-test.log' +SENDMAILDEBUG = os.environ['SENDMAILDEBUG'] + +from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, \ + parseContent, IgnoreLoop, IgnoreBulk, MailUsageError, MailUsageHelp +from roundup import init, instance, password, rfc2822, __version__ +from roundup.anypy.sets_ import set + +#import db_test_base +import memorydb + +class Message(rfc822.Message): + """String-based Message class with equivalence test.""" + def __init__(self, s): + rfc822.Message.__init__(self, StringIO(s.strip())) + + def __eq__(self, other): + return (self.dict == other.dict and + self.fp.read() == other.fp.read()) + +class Tracker(object): + def open(self, journaltag): + return self.db -# TODO: make this output only enough equal lines for context, not all of -# them class DiffHelper: - def compareStrings(self, s2, s1): + def compareMessages(self, new, old): + """Compare messages for semantic equivalence.""" + new, old = Message(new), Message(old) + + # all Roundup-generated messages have "Precedence: bulk" + old['Precedence'] = 'bulk' + + # don't try to compare the date + del new['date'], old['date'] + + if not new == old: + res = [] + + replace = {} + for key in new.keys(): + if key.startswith('from '): + # skip the unix from line + continue + if key.lower() == 'x-roundup-version': + # version changes constantly, so handle it specially + if new[key] != __version__: + res.append(' %s: %r != %r' % (key, __version__, + new[key])) + elif key.lower() == 'content-type' and 'boundary=' in new[key]: + # handle mime messages + newmime = new[key].split('=',1)[-1].strip('"') + oldmime = old.get(key, '').split('=',1)[-1].strip('"') + replace ['--' + newmime] = '--' + oldmime + replace ['--' + newmime + '--'] = '--' + oldmime + '--' + elif new.get(key, '') != old.get(key, ''): + res.append(' %s: %r != %r' % (key, old.get(key, ''), + new.get(key, ''))) + + body_diff = self.compareStrings(new.fp.read(), old.fp.read(), + replace=replace) + if body_diff: + res.append('') + res.extend(body_diff) + + if res: + res.insert(0, 'Generated message not correct (diff follows, expected vs. actual):') + raise AssertionError, '\n'.join(res) + + def compareStrings(self, s2, s1, replace={}): '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants the first to be the "original" but in the calls in this file, the second arg is the original. Ho hum. + Do replacements over the replace dict -- used for mime boundary ''' - # we have to special-case the Date: header here 'cos we can't test - # for it properly - l1=s1.strip().split('\n') - l2=[x for x in s2.strip().split('\n') if not x.startswith('Date: ')] + l1 = s1.strip().split('\n') + l2 = [replace.get(i,i) for i in s2.strip().split('\n')] if l1 == l2: return - s = difflib.SequenceMatcher(None, l1, l2) - res = ['Generated message not correct (diff follows):'] + res = [] for value, s1s, s1e, s2s, s2e in s.get_opcodes(): if value == 'equal': for i in range(s1s, s1e): @@ -56,83 +113,93 @@ class DiffHelper: res.append('- %s'%l1[i]) res.append('+ %s'%l2[j]) - raise AssertionError, '\n'.join(res) + return res class MailgwTestCase(unittest.TestCase, DiffHelper): count = 0 schema = 'classic' def setUp(self): MailgwTestCase.count = MailgwTestCase.count + 1 - self.dirname = '_test_mailgw_%s'%self.count - try: - shutil.rmtree(self.dirname) - except OSError, error: - if error.errno not in (errno.ENOENT, errno.ESRCH): raise - # create the instance - init.install(self.dirname, 'templates/classic') - init.write_select_db(self.dirname, 'anydbm') - init.initialise(self.dirname, 'sekrit') - # check we can load the package - self.instance = instance.open(self.dirname) - # and open the database - self.db = self.instance.open('admin') - self.db.user.create(username='Chef', address='chef@bork.bork.bork', - realname='Bork, Chef', roles='User') - self.db.user.create(username='richard', address='richard@test', - roles='User') - self.db.user.create(username='mary', address='mary@test', - roles='User', realname='Contrary, Mary') - self.db.user.create(username='john', address='john@test', - alternate_addresses='jondoe@test\njohn.doe@test', roles='User', - realname='John Doe') + + # and open the database / "instance" + self.db = memorydb.create('admin') + self.instance = Tracker() + self.instance.db = self.db + self.instance.config = self.db.config + self.instance.MailGW = MailGW + + self.chef_id = self.db.user.create(username='Chef', + address='chef@bork.bork.bork', realname='Bork, Chef', roles='User') + self.richard_id = self.db.user.create(username='richard', + address='richard@test.test', roles='User') + self.mary_id = self.db.user.create(username='mary', + address='mary@test.test', roles='User', realname='Contrary, Mary') + self.john_id = self.db.user.create(username='john', + address='john@test.test', roles='User', realname='John Doe', + alternate_addresses='jondoe@test.test\njohn.doe@test.test') def tearDown(self): - if os.path.exists(os.environ['SENDMAILDEBUG']): - os.remove(os.environ['SENDMAILDEBUG']) + if os.path.exists(SENDMAILDEBUG): + os.remove(SENDMAILDEBUG) self.db.close() + + def _handle_mail(self, message): + handler = self.instance.MailGW(self.instance) + handler.trapExceptions = 0 + return handler.main(StringIO(message)) + + def _get_mail(self): + f = open(SENDMAILDEBUG) try: - shutil.rmtree(self.dirname) - except OSError, error: - if error.errno not in (errno.ENOENT, errno.ESRCH): raise + return f.read() + finally: + f.close() def testEmptyMessage(self): - message = cStringIO.StringIO('''Content-Type: text/plain; + nodeid = self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" From: Chef To: issue_tracker@your.tracker.email.domain.example -Cc: richard@test +Cc: richard@test.test +Reply-To: chef@bork.bork.bork Message-Id: Subject: [issue] Testing... ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - nodeid = handler.main(message) - if os.path.exists(os.environ['SENDMAILDEBUG']): - error = open(os.environ['SENDMAILDEBUG']).read() - self.assertEqual('no error', error) + assert not os.path.exists(SENDMAILDEBUG) self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...') + def testMessageWithFromInIt(self): + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: +Subject: [issue] Testing... + +From here to there! +''') + assert not os.path.exists(SENDMAILDEBUG) + msgid = self.db.issue.get(nodeid, 'messages')[0] + self.assertEqual(self.db.msg.get(msgid, 'content'), 'From here to there!') + def doNewIssue(self): - message = cStringIO.StringIO('''Content-Type: text/plain; + nodeid = self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" From: Chef To: issue_tracker@your.tracker.email.domain.example -Cc: richard@test +Cc: richard@test.test Message-Id: Subject: [issue] Testing... This is a test submission of a new issue. ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - nodeid = handler.main(message) - if os.path.exists(os.environ['SENDMAILDEBUG']): - error = open(os.environ['SENDMAILDEBUG']).read() - self.assertEqual('no error', error) + assert not os.path.exists(SENDMAILDEBUG) l = self.db.issue.get(nodeid, 'nosy') l.sort() - self.assertEqual(l, ['3', '4']) + self.assertEqual(l, [self.chef_id, self.richard_id]) return nodeid def testNewIssue(self): @@ -140,30 +207,25 @@ This is a test submission of a new issue. def testNewIssueNosy(self): self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes' - message = cStringIO.StringIO('''Content-Type: text/plain; + nodeid = self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" From: Chef To: issue_tracker@your.tracker.email.domain.example -Cc: richard@test +Cc: richard@test.test Message-Id: Subject: [issue] Testing... This is a test submission of a new issue. ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - nodeid = handler.main(message) - if os.path.exists(os.environ['SENDMAILDEBUG']): - error = open(os.environ['SENDMAILDEBUG']).read() - self.assertEqual('no error', error) + assert not os.path.exists(SENDMAILDEBUG) l = self.db.issue.get(nodeid, 'nosy') l.sort() - self.assertEqual(l, ['3', '4']) + self.assertEqual(l, [self.chef_id, self.richard_id]) def testAlternateAddress(self): - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: John Doe +From: John Doe To: issue_tracker@your.tracker.email.domain.example Message-Id: Subject: [issue] Testing... @@ -171,35 +233,27 @@ Subject: [issue] Testing... This is a test submission of a new issue. ''') userlist = self.db.user.list() - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - if os.path.exists(os.environ['SENDMAILDEBUG']): - error = open(os.environ['SENDMAILDEBUG']).read() - self.assertEqual('no error', error) + assert not os.path.exists(SENDMAILDEBUG) self.assertEqual(userlist, self.db.user.list(), "user created when it shouldn't have been") def testNewIssueNoClass(self): - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" From: Chef To: issue_tracker@your.tracker.email.domain.example -Cc: richard@test +Cc: richard@test.test Message-Id: Subject: Testing... This is a test submission of a new issue. ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - if os.path.exists(os.environ['SENDMAILDEBUG']): - error = open(os.environ['SENDMAILDEBUG']).read() - self.assertEqual('no error', error) + assert not os.path.exists(SENDMAILDEBUG) def testNewIssueAuthMsg(self): - message = cStringIO.StringIO('''Content-Type: text/plain; + # TODO: fix the damn config - this is apalling + self.db.config.MESSAGES_TO_AUTHOR = 'yes' + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" From: Chef To: issue_tracker@your.tracker.email.domain.example @@ -208,24 +262,20 @@ Subject: [issue] Testing... [nosy=mary; assignedto=richard] This is a test submission of a new issue. ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - # TODO: fix the damn config - this is apalling - self.db.config.MESSAGES_TO_AUTHOR = 'yes' - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, mary@test, richard@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, mary@test.test, richard@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, mary@test, richard@test +To: chef@bork.bork.bork, mary@test.test, richard@test.test From: "Bork, Chef" -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: unread Content-Transfer-Encoding: quoted-printable @@ -239,26 +289,203 @@ messages: 1 nosy: Chef, mary, richard status: unread title: Testing... + +_______________________________________________________________________ +Roundup issue tracker + +_______________________________________________________________________ +''') + + def testNewIssueNoAuthorInfo(self): + self.db.config.MAIL_ADD_AUTHORINFO = 'no' + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: [issue] Testing... [nosy=mary; assignedto=richard] + +This is a test submission of a new issue. +''') + self.compareMessages(self._get_mail(), +'''FROM: roundup-admin@your.tracker.email.domain.example +TO: chef@bork.bork.bork, mary@test.test, richard@test.test +Content-Type: text/plain; charset="utf-8" +Subject: [issue1] Testing... +To: mary@test.test, richard@test.test +From: "Bork, Chef" +Reply-To: Roundup issue tracker + +MIME-Version: 1.0 +Message-Id: +X-Roundup-Name: Roundup issue tracker +X-Roundup-Loop: hello +X-Roundup-Issue-Status: unread +Content-Transfer-Encoding: quoted-printable + +This is a test submission of a new issue. + +---------- +assignedto: richard +messages: 1 +nosy: Chef, mary, richard +status: unread +title: Testing... + +_______________________________________________________________________ +Roundup issue tracker + +_______________________________________________________________________ +''') + + def testNewIssueNoAuthorEmail(self): + self.db.config.MAIL_ADD_AUTHOREMAIL = 'no' + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: [issue] Testing... [nosy=mary; assignedto=richard] + +This is a test submission of a new issue. +''') + self.compareMessages(self._get_mail(), +'''FROM: roundup-admin@your.tracker.email.domain.example +TO: chef@bork.bork.bork, mary@test.test, richard@test.test +Content-Type: text/plain; charset="utf-8" +Subject: [issue1] Testing... +To: mary@test.test, richard@test.test +From: "Bork, Chef" +Reply-To: Roundup issue tracker + +MIME-Version: 1.0 +Message-Id: +X-Roundup-Name: Roundup issue tracker +X-Roundup-Loop: hello +X-Roundup-Issue-Status: unread +Content-Transfer-Encoding: quoted-printable + +New submission from Bork, Chef: + +This is a test submission of a new issue. + +---------- +assignedto: richard +messages: 1 +nosy: Chef, mary, richard +status: unread +title: Testing... + _______________________________________________________________________ Roundup issue tracker _______________________________________________________________________ ''') - # BUG - # def testMultipart(self): - # '''With more than one part''' - # see MultipartEnc tests: but if there is more than one part - # we return a multipart/mixed and the boundary contains - # the ip address of the test machine. + multipart_msg = '''From: mary +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: [issue1] Testing... +Content-Type: multipart/mixed; boundary="bxyzzy" +Content-Disposition: inline + + +--bxyzzy +Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE" +Content-Disposition: inline + +--bCsyhTFzCvuiizWE +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline + +test attachment first text/plain + +--bCsyhTFzCvuiizWE +Content-Type: application/octet-stream +Content-Disposition: attachment; filename="first.dvi" +Content-Transfer-Encoding: base64 + +SnVzdCBhIHRlc3QgAQo= + +--bCsyhTFzCvuiizWE +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline + +test attachment second text/plain + +--bCsyhTFzCvuiizWE +Content-Type: text/html +Content-Disposition: inline + + +to be ignored. + + +--bCsyhTFzCvuiizWE-- + +--bxyzzy +Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF" +Content-Disposition: inline + +--bCsyhTFzCvuiizWF +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline + +test attachment third text/plain + +--bCsyhTFzCvuiizWF +Content-Type: application/octet-stream +Content-Disposition: attachment; filename="second.dvi" +Content-Transfer-Encoding: base64 + +SnVzdCBhIHRlc3QK + +--bCsyhTFzCvuiizWF-- + +--bxyzzy-- +''' - # BUG should test some binary attamchent too. + def testMultipartKeepAlternatives(self): + self.doNewIssue() + self._handle_mail(self.multipart_msg) + messages = self.db.issue.get('1', 'messages') + messages.sort() + msg = self.db.msg.getnode (messages[-1]) + assert(len(msg.files) == 5) + names = {0 : 'first.dvi', 4 : 'second.dvi'} + content = {3 : 'test attachment third text/plain\n', + 4 : 'Just a test\n'} + for n, id in enumerate (msg.files): + f = self.db.file.getnode (id) + self.assertEqual(f.name, names.get (n, 'unnamed')) + if n in content : + self.assertEqual(f.content, content [n]) + self.assertEqual(msg.content, 'test attachment second text/plain') + + def testMultipartDropAlternatives(self): + self.doNewIssue() + self.db.config.MAILGW_IGNORE_ALTERNATIVES = True + self._handle_mail(self.multipart_msg) + messages = self.db.issue.get('1', 'messages') + messages.sort() + msg = self.db.msg.getnode (messages[-1]) + assert(len(msg.files) == 2) + names = {1 : 'second.dvi'} + content = {0 : 'test attachment third text/plain\n', + 1 : 'Just a test\n'} + for n, id in enumerate (msg.files): + f = self.db.file.getnode (id) + self.assertEqual(f.name, names.get (n, 'unnamed')) + if n in content : + self.assertEqual(f.content, content [n]) + self.assertEqual(msg.content, 'test attachment second text/plain') def testSimpleFollowup(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: mary +From: mary To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -266,31 +493,31 @@ Subject: [issue1] Testing... This is a second followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, richard@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, richard@test +To: chef@bork.bork.bork, richard@test.test From: "Contrary, Mary" -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -Contrary, Mary added the comment: +Contrary, Mary added the comment: This is a second followup ---------- status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -300,9 +527,9 @@ _______________________________________________________________________ def testFollowup(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard +From: richard To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -310,30 +537,30 @@ Subject: [issue1] Testing... [assignedto=mary; nosy=+john] This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) l = self.db.issue.get('1', 'nosy') l.sort() - self.assertEqual(l, ['3', '4', '5', '6']) + self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id, + self.john_id]) - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, john@test, mary@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, john@test.test, mary@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, john@test, mary@test +To: chef@bork.bork.bork, john@test.test, mary@test.test From: richard -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -richard added the comment: +richard added the comment: This is a followup @@ -341,45 +568,97 @@ This is a followup assignedto: -> mary nosy: +john, mary status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker _______________________________________________________________________ ''') + def testPropertyChangeOnly(self): + self.doNewIssue() + oldvalues = self.db.getnode('issue', '1').copy() + oldvalues['assignedto'] = None + # reconstruct old behaviour: This would reuse the + # database-handle from the doNewIssue above which has committed + # as user "Chef". So we close and reopen the db as that user. + self.db.close() + self.db = self.instance.open('Chef') + self.db.issue.set('1', assignedto=self.chef_id) + self.db.commit() + self.db.issue.nosymessage('1', None, oldvalues) + + new_mail = "" + for line in self._get_mail().split("\n"): + if "Message-Id: " in line: + continue + if "Date: " in line: + continue + new_mail += line+"\n" + + self.compareMessages(new_mail, """ +FROM: roundup-admin@your.tracker.email.domain.example +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" +Subject: [issue1] Testing... +To: chef@bork.bork.bork, richard@test.test +From: "Bork, Chef" +X-Roundup-Name: Roundup issue tracker +X-Roundup-Loop: hello +X-Roundup-Issue-Status: unread +X-Roundup-Version: 1.3.3 +MIME-Version: 1.0 +Reply-To: Roundup issue tracker + +Content-Transfer-Encoding: quoted-printable + + +Change by Bork, Chef : + + +---------- +assignedto: -> Chef + +_______________________________________________________________________ +Roundup issue tracker + +_______________________________________________________________________ +""") + + + # + # FOLLOWUP TITLE MATCH + # def testFollowupTitleMatch(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard +From: richard To: issue_tracker@your.tracker.email.domain.example Message-Id: -In-Reply-To: Subject: Re: Testing... [assignedto=mary; nosy=+john] This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, john@test, mary@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, john@test.test, mary@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, john@test, mary@test +To: chef@bork.bork.bork, john@test.test, mary@test.test From: richard -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -richard added the comment: +richard added the comment: This is a followup @@ -387,18 +666,86 @@ This is a followup assignedto: -> mary nosy: +john, mary status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker _______________________________________________________________________ ''') + def testFollowupTitleMatchMultiRe(self): + nodeid1 = self.doNewIssue() + nodeid2 = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: Re: Testing... [assignedto=mary; nosy=+john] + +This is a followup +''') + + nodeid3 = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: Ang: Re: Testing... + +This is a followup +''') + self.assertEqual(nodeid1, nodeid2) + self.assertEqual(nodeid1, nodeid3) + + def testFollowupTitleMatchNever(self): + nodeid = self.doNewIssue() + self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never' + self.assertNotEqual(self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: Re: Testing... + +This is a followup +'''), nodeid) + + def testFollowupTitleMatchNeverInterval(self): + nodeid = self.doNewIssue() + # force failure of the interval + time.sleep(2) + self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01' + self.assertNotEqual(self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: Re: Testing... + +This is a followup +'''), nodeid) + + + def testFollowupTitleMatchInterval(self): + nodeid = self.doNewIssue() + self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d' + self.assertEqual(self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: Re: Testing... + +This is a followup +'''), nodeid) + + def testFollowupNosyAuthor(self): self.doNewIssue() self.db.config.ADD_AUTHOR_TO_NOSY = 'yes' - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: john@test +From: john@test.test To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -406,33 +753,33 @@ Subject: [issue1] Testing... This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, richard@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, richard@test +To: chef@bork.bork.bork, richard@test.test From: John Doe -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -John Doe added the comment: +John Doe added the comment: This is a followup ---------- nosy: +john status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -443,44 +790,43 @@ _______________________________________________________________________ def testFollowupNosyRecipients(self): self.doNewIssue() self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes' - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard@test +From: richard@test.test To: issue_tracker@your.tracker.email.domain.example -Cc: john@test +Cc: john@test.test Message-Id: In-Reply-To: Subject: [issue1] Testing... This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example TO: chef@bork.bork.bork -Content-Type: text/plain; charset=utf-8 +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... To: chef@bork.bork.bork From: richard -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -richard added the comment: +richard added the comment: This is a followup ---------- nosy: +john status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -492,9 +838,9 @@ _______________________________________________________________________ self.doNewIssue() self.db.config.ADD_AUTHOR_TO_NOSY = 'yes' self.db.config.MESSAGES_TO_AUTHOR = 'yes' - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: john@test +From: john@test.test To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -502,33 +848,32 @@ Subject: [issue1] Testing... This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, john@test, richard@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, john@test.test, richard@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, john@test, richard@test +To: chef@bork.bork.bork, john@test.test, richard@test.test From: John Doe -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -John Doe added the comment: +John Doe added the comment: This is a followup ---------- nosy: +john status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -539,9 +884,9 @@ _______________________________________________________________________ def testFollowupNoNosyAuthor(self): self.doNewIssue() self.instance.config.ADD_AUTHOR_TO_NOSY = 'no' - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: john@test +From: john@test.test To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -549,32 +894,31 @@ Subject: [issue1] Testing... This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, richard@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, richard@test +To: chef@bork.bork.bork, richard@test.test From: John Doe -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -John Doe added the comment: +John Doe added the comment: This is a followup ---------- status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -585,43 +929,42 @@ _______________________________________________________________________ def testFollowupNoNosyRecipients(self): self.doNewIssue() self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no' - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard@test +From: richard@test.test To: issue_tracker@your.tracker.email.domain.example -Cc: john@test +Cc: john@test.test Message-Id: In-Reply-To: Subject: [issue1] Testing... This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example TO: chef@bork.bork.bork -Content-Type: text/plain; charset=utf-8 +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... To: chef@bork.bork.bork From: richard -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -richard added the comment: +richard added the comment: This is a followup ---------- status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -632,58 +975,66 @@ _______________________________________________________________________ def testFollowupEmptyMessage(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard +From: richard To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: Subject: [issue1] Testing... [assignedto=mary; nosy=+john] ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) l = self.db.issue.get('1', 'nosy') l.sort() - self.assertEqual(l, ['3', '4', '5', '6']) + self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id, + self.john_id]) + + # should be no file created (ie. no message) + assert not os.path.exists(SENDMAILDEBUG) + + def testFollowupEmptyMessageNoSubject(self): + self.doNewIssue() + + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: [issue1] [assignedto=mary; nosy=+john] + +''') + l = self.db.issue.get('1', 'nosy') + l.sort() + self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id, + self.john_id]) # should be no file created (ie. no message) - assert not os.path.exists(os.environ['SENDMAILDEBUG']) + assert not os.path.exists(SENDMAILDEBUG) def testNosyRemove(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard +From: richard To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: Subject: [issue1] Testing... [nosy=-richard] ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) l = self.db.issue.get('1', 'nosy') l.sort() - self.assertEqual(l, ['3']) + self.assertEqual(l, [self.chef_id]) # NO NOSY MESSAGE SHOULD BE SENT! - self.assert_(not os.path.exists(os.environ['SENDMAILDEBUG'])) + assert not os.path.exists(SENDMAILDEBUG) def testNewUserAuthor(self): - # first without the permission - # heh... just ignore the API for a second ;) - self.db.security.role['anonymous'].permissions=[] - anonid = self.db.user.lookup('anonymous') - self.db.user.set(anonid, roles='Anonymous') - - self.db.security.hasPermission('Email Registration', anonid) l = self.db.user.list() l.sort() - s = '''Content-Type: text/plain; + message = '''Content-Type: text/plain; charset="iso-8859-1" From: fubar To: issue_tracker@your.tracker.email.domain.example @@ -692,66 +1043,243 @@ Subject: [issue] Testing... This is a test submission of a new issue. ''' - message = cStringIO.StringIO(s) - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - self.assertRaises(Unauthorized, handler.main, message) + self.db.security.role['anonymous'].permissions=[] + anonid = self.db.user.lookup('anonymous') + self.db.user.set(anonid, roles='Anonymous') + try: + self._handle_mail(message) + except Unauthorized, value: + body_diff = self.compareMessages(str(value), """ +You are not a registered user. + +Unknown address: fubar@bork.bork.bork +""") + assert not body_diff, body_diff + else: + raise AssertionError, "Unathorized not raised when handling mail" + + # Add Web Access role to anonymous, and try again to make sure + # we get a "please register at:" message this time. + p = [ + self.db.security.getPermission('Register', 'user'), + self.db.security.getPermission('Web Access', None), + ] + self.db.security.role['anonymous'].permissions=p + try: + self._handle_mail(message) + except Unauthorized, value: + body_diff = self.compareMessages(str(value), """ +You are not a registered user. Please register at: + +http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register + +...before sending mail to the tracker. + +Unknown address: fubar@bork.bork.bork +""") + assert not body_diff, body_diff + else: + raise AssertionError, "Unathorized not raised when handling mail" + + # Make sure list of users is the same as before. m = self.db.user.list() m.sort() self.assertEqual(l, m) # now with the permission - p = self.db.security.getPermission('Email Registration') - self.db.security.role['anonymous'].permissions=[p] - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - message = cStringIO.StringIO(s) - handler.main(message) + p = [ + self.db.security.getPermission('Register', 'user'), + self.db.security.getPermission('Email Access', None), + ] + self.db.security.role['anonymous'].permissions=p + self._handle_mail(message) m = self.db.user.list() m.sort() self.assertNotEqual(l, m) - def testEnc01(self): - self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + def testNewUserAuthorEncodedName(self): + l = set(self.db.user.list()) + # From: name has Euro symbol in it + message = '''Content-Type: text/plain; charset="iso-8859-1" -From: mary +From: =?utf8?b?SOKCrGxsbw==?= To: issue_tracker@your.tracker.email.domain.example -Message-Id: -In-Reply-To: -Subject: [issue1] Testing... -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable +Message-Id: +Subject: [issue] Testing... -A message with encoding (encoded oe =F6) +This is a test submission of a new issue. +''' + p = [ + self.db.security.getPermission('Register', 'user'), + self.db.security.getPermission('Email Access', None), + self.db.security.getPermission('Create', 'issue'), + self.db.security.getPermission('Create', 'msg'), + ] + self.db.security.role['anonymous'].permissions = p + self._handle_mail(message) + m = set(self.db.user.list()) + new = list(m - l)[0] + name = self.db.user.get(new, 'realname') + self.assertEquals(name, 'H€llo') + + def testUnknownUser(self): + l = set(self.db.user.list()) + message = '''Content-Type: text/plain; + charset="iso-8859-1" +From: Nonexisting User +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: [issue] Testing nonexisting user... -''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), -'''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, richard@test -Content-Type: text/plain; charset=utf-8 -Subject: [issue1] Testing... -To: chef@bork.bork.bork, richard@test -From: "Contrary, Mary" -Reply-To: Roundup issue tracker +This is a test submission of a new issue. +''' + self.db.close() + handler = self.instance.MailGW(self.instance) + # we want a bounce message: + handler.trapExceptions = 1 + ret = handler.main(StringIO(message)) + self.compareMessages(self._get_mail(), +'''FROM: Roundup issue tracker +TO: nonexisting@bork.bork.bork +From nobody Tue Jul 14 12:04:11 2009 +Content-Type: multipart/mixed; boundary="===============0639262320==" +MIME-Version: 1.0 +Subject: Failed issue tracker submission +To: nonexisting@bork.bork.bork +From: Roundup issue tracker +Date: Tue, 14 Jul 2009 12:04:11 +0000 +Precedence: bulk +X-Roundup-Name: Roundup issue tracker +X-Roundup-Loop: hello +X-Roundup-Version: 1.4.8 +MIME-Version: 1.0 + +--===============0639262320== +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + + + +You are not a registered user. Please register at: + +http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register + +...before sending mail to the tracker. + +Unknown address: nonexisting@bork.bork.bork + +--===============0639262320== +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +Content-Type: text/plain; + charset="iso-8859-1" +From: Nonexisting User +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: [issue] Testing nonexisting user... + +This is a test submission of a new issue. + +--===============0639262320==-- +''') + + def testEnc01(self): + self.db.user.set(self.mary_id, + realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode + ('latin-1').encode('utf-8')) + self.doNewIssue() + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: mary +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: [issue1] Testing... +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + +A message with encoding (encoded oe =F6) + +''') + self.compareMessages(self._get_mail(), +'''FROM: roundup-admin@your.tracker.email.domain.example +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" +Subject: [issue1] Testing... +To: chef@bork.bork.bork, richard@test.test +From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?= + +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -Contrary, Mary added the comment: +=C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary added the= + comment: A message with encoding (encoded oe =C3=B6) ---------- status: unread -> chatting + +_______________________________________________________________________ +Roundup issue tracker + +_______________________________________________________________________ +''') + + def testEncNonUTF8(self): + self.doNewIssue() + self.instance.config.EMAIL_CHARSET = 'iso-8859-1' + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: mary +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: [issue1] Testing... +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable + +A message with encoding (encoded oe =F6) + +''') + self.compareMessages(self._get_mail(), +'''FROM: roundup-admin@your.tracker.email.domain.example +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="iso-8859-1" +Subject: [issue1] Testing... +To: chef@bork.bork.bork, richard@test.test +From: "Contrary, Mary" +Reply-To: Roundup issue tracker + +MIME-Version: 1.0 +Message-Id: +In-Reply-To: +X-Roundup-Name: Roundup issue tracker +X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting +Content-Transfer-Encoding: quoted-printable + + +Contrary, Mary added the comment: + +A message with encoding (encoded oe =F6) + +---------- +status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -761,9 +1289,9 @@ _______________________________________________________________________ def testMultipartEnc01(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: mary +From: mary To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -782,31 +1310,31 @@ Content-Transfer-Encoding: quoted-printable A message with first part encoded (encoded oe =F6) ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example -TO: chef@bork.bork.bork, richard@test -Content-Type: text/plain; charset=utf-8 +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... -To: chef@bork.bork.bork, richard@test +To: chef@bork.bork.bork, richard@test.test From: "Contrary, Mary" -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -Contrary, Mary added the comment: +Contrary, Mary added the comment: A message with first part encoded (encoded oe =C3=B6) ---------- status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -815,45 +1343,44 @@ _______________________________________________________________________ def testContentDisposition(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: mary +From: mary To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: Subject: [issue1] Testing... -Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE" -Content-Disposition: inline - - ---bCsyhTFzCvuiizWE -Content-Type: text/plain; charset=us-ascii -Content-Disposition: inline +Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE" +Content-Disposition: inline -test attachment binary ---bCsyhTFzCvuiizWE -Content-Type: application/octet-stream -Content-Disposition: attachment; filename="main.dvi" +--bCsyhTFzCvuiizWE +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline -xxxxxx +test attachment binary + +--bCsyhTFzCvuiizWE +Content-Type: application/octet-stream +Content-Disposition: attachment; filename="main.dvi" +Content-Transfer-Encoding: base64 + +SnVzdCBhIHRlc3QgAQo= --bCsyhTFzCvuiizWE-- ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) messages = self.db.issue.get('1', 'messages') messages.sort() - file = self.db.msg.get(messages[-1], 'files')[0] - self.assertEqual(self.db.file.get(file, 'name'), 'main.dvi') + file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0]) + self.assertEqual(file.name, 'main.dvi') + self.assertEqual(file.content, 'Just a test \001\n') def testFollowupStupidQuoting(self): self.doNewIssue() - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard +From: richard To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -861,32 +1388,31 @@ Subject: Re: "[issue1] Testing... " This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - - self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(), + self.compareMessages(self._get_mail(), '''FROM: roundup-admin@your.tracker.email.domain.example TO: chef@bork.bork.bork -Content-Type: text/plain; charset=utf-8 +Content-Type: text/plain; charset="utf-8" Subject: [issue1] Testing... To: chef@bork.bork.bork From: richard -Reply-To: Roundup issue tracker +Reply-To: Roundup issue tracker + MIME-Version: 1.0 Message-Id: In-Reply-To: X-Roundup-Name: Roundup issue tracker X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting Content-Transfer-Encoding: quoted-printable -richard added the comment: +richard added the comment: This is a followup ---------- status: unread -> chatting + _______________________________________________________________________ Roundup issue tracker @@ -913,9 +1439,9 @@ This is a followup messages = self.db.issue.get(nodeid, 'messages') - message = cStringIO.StringIO('''Content-Type: text/plain; + self._handle_mail('''Content-Type: text/plain; charset="iso-8859-1" -From: richard +From: richard To: issue_tracker@your.tracker.email.domain.example Message-Id: In-Reply-To: @@ -928,17 +1454,13 @@ Blah blah wrote: This is a followup ''') - handler = self.instance.MailGW(self.instance, self.db) - handler.trapExceptions = 0 - handler.main(message) - # figure the new message id newmessages = self.db.issue.get(nodeid, 'messages') for msg in messages: newmessages.remove(msg) messageid = newmessages[0] - self.compareStrings(self.db.msg.get(messageid, 'content'), expect) + self.compareMessages(self.db.msg.get(messageid, 'content'), expect) def testUserLookup(self): i = self.db.user.create(username='user1', address='user1@foo.com') @@ -948,14 +1470,508 @@ This is a followup self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i) self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i) + def testUserAlternateLookup(self): + i = self.db.user.create(username='user1', address='user1@foo.com', + alternate_addresses='user1@bar.com') + self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i) + self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i) + def testUserCreate(self): i = uidFromAddress(self.db, ('', 'user@foo.com'), 1) self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i) -def suite(): - l = [unittest.makeSuite(MailgwTestCase), - ] - return unittest.TestSuite(l) + def testRFC2822(self): + ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks" + unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9' + unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?=' + self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header) + self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded) + + def testRegistrationConfirmation(self): + otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL" + self.db.getOTKManager().set(otk, username='johannes') + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Message-Id: +Subject: Re: Complete your registration to Roundup issue tracker + -- key %s + +This is a test confirmation of registration. +''' % otk) + self.db.user.lookup('johannes') + + def testFollowupOnNonIssue(self): + self.db.keyword.create(name='Foo') + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: [keyword1] Testing... [name=Bar] + +''') + self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar') + + def testResentFrom(self): + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +Resent-From: mary +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Message-Id: +Subject: [issue] Testing... + +This is a test submission of a new issue. +''') + assert not os.path.exists(SENDMAILDEBUG) + l = self.db.issue.get(nodeid, 'nosy') + l.sort() + self.assertEqual(l, [self.richard_id, self.mary_id]) + return nodeid + + def testDejaVu(self): + self.assertRaises(IgnoreLoop, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +X-Roundup-Loop: hello +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Message-Id: +Subject: Re: [issue] Testing... + +Hi, I've been mis-configured to loop messages back to myself. +''') + + def testItsBulkStupid(self): + self.assertRaises(IgnoreBulk, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +Precedence: bulk +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Message-Id: +Subject: Re: [issue] Testing... + +Hi, I'm on holidays, and this is a dumb auto-responder. +''') + + def testAutoReplyEmailsAreIgnored(self): + self.assertRaises(IgnoreBulk, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Message-Id: +Subject: Re: [issue] Out of office AutoReply: Back next week + +Hi, I am back in the office next week +''') + + def testNoSubject(self): + self.assertRaises(MailUsageError, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + + # + # TEST FOR INVALID DESIGNATOR HANDLING + # + def testInvalidDesignator(self): + self.assertRaises(MailUsageError, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [frobulated] testing +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + self.assertRaises(MailUsageError, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [issue12345] testing +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + + def testInvalidClassLoose(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [frobulated] testing +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), + '[frobulated] testing') + + def testInvalidClassLooseReply(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: Re: [frobulated] testing +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), + '[frobulated] testing') + + def testInvalidClassLoose(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [issue1234] testing +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), + '[issue1234] testing') + + def testClassLooseOK(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose' + self.db.keyword.create(name='Foo') + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [keyword1] Testing... [name=Bar] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar') + + def testClassStrictInvalid(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict' + self.instance.config.MAILGW_DEFAULT_CLASS = '' + + message = '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: Testing... +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''' + self.assertRaises(MailUsageError, self._handle_mail, message) + + def testClassStrictValid(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict' + self.instance.config.MAILGW_DEFAULT_CLASS = '' + + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [issue] Testing... +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...') + + # + # TEST FOR INVALID COMMANDS HANDLING + # + def testInvalidCommands(self): + self.assertRaises(MailUsageError, self._handle_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: testing [frobulated] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + + def testInvalidCommandPassthrough(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: testing [frobulated] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), + 'testing [frobulated]') + + def testInvalidCommandPassthroughLoose(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: testing [frobulated] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), + 'testing [frobulated]') + + def testInvalidCommandPassthroughLooseOK(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: testing [assignedto=mary] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing') + self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id) + + def testCommandDelimiters(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: testing {assignedto=mary} +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing') + self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id) + + def testPrefixDelimiters(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}' + self.db.keyword.create(name='Foo') + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: richard +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: {keyword1} Testing... {name=Bar} + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar') + + def testCommandDelimitersIgnore(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}' + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: testing [assignedto=mary] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.issue.get(nodeid, 'title'), + 'testing [assignedto=mary]') + self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None) + + def testReplytoMatch(self): + self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose' + nodeid = self.doNewIssue() + nodeid2 = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: Testing... + +Followup message. +''') + + nodeid3 = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: Testing... + +Yet another message in the same thread/issue. +''') + + self.assertEqual(nodeid, nodeid2) + self.assertEqual(nodeid, nodeid3) + + def testHelpSubject(self): + message = '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: hElp + + +''' + self.assertRaises(MailUsageHelp, self._handle_mail, message) + + def testMaillistSubject(self): + self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]' + self.db.keyword.create(name='Foo') + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar') + + def testUnknownPrefixSubject(self): + self.db.keyword.create(name='Foo') + self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar] +Cc: richard@test.test +Reply-To: chef@bork.bork.bork +Message-Id: + +''') + + assert not os.path.exists(SENDMAILDEBUG) + self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar') + + def testIssueidLast(self): + nodeid1 = self.doNewIssue() + nodeid2 = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: mary +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: New title [issue1] + +This is a second followup +''') + + assert nodeid1 == nodeid2 + self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...") + + def testSecurityMessagePermissionContent(self): + id = self.doNewIssue() + issue = self.db.issue.getnode (id) + self.db.security.addRole(name='Nomsg') + self.db.security.addPermissionToRole('Nomsg', 'Email Access') + for cl in 'issue', 'file', 'keyword': + for p in 'View', 'Edit', 'Create': + self.db.security.addPermissionToRole('Nomsg', p, cl) + self.db.user.set(self.mary_id, roles='Nomsg') + nodeid = self._handle_mail('''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +Subject: [issue%(id)s] Testing... [nosy=+mary] + +Just a test reply +'''%locals()) + assert os.path.exists(SENDMAILDEBUG) + self.compareMessages(self._get_mail(), +'''FROM: roundup-admin@your.tracker.email.domain.example +TO: chef@bork.bork.bork, richard@test.test +Content-Type: text/plain; charset="utf-8" +Subject: [issue1] Testing... +To: richard@test.test +From: "Bork, Chef" +Reply-To: Roundup issue tracker + +MIME-Version: 1.0 +Message-Id: +X-Roundup-Name: Roundup issue tracker +X-Roundup-Loop: hello +X-Roundup-Issue-Status: chatting +Content-Transfer-Encoding: quoted-printable + + +Bork, Chef added the comment: + +Just a test reply + +---------- +nosy: +mary +status: unread -> chatting + +_______________________________________________________________________ +Roundup issue tracker + +_______________________________________________________________________ +''') + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(MailgwTestCase)) + return suite +if __name__ == '__main__': + runner = unittest.TextTestRunner() + unittest.main(testRunner=runner) -# vim: set filetype=python ts=4 sw=4 et si +# vim: set filetype=python sts=4 sw=4 et si :