1 #
2 # Copyright (c) 2001 Richard Jones, richard@bofh.asn.au.
3 # This module is free software, and you may redistribute it and/or modify
4 # under the same terms as Python, so long as this copyright message and
5 # disclaimer are retained in their original form.
6 #
7 # This module is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 #
11 # $Id: test_mailgw.py,v 1.15 2002-03-19 06:37:00 richard Exp $
13 import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys, difflib
15 from roundup.mailgw import MailGW
16 from roundup import init, instance
18 # TODO: make this output only enough equal lines for context, not all of
19 # them
20 class DiffHelper:
21 def compareStrings(self, s2, s1):
22 '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
23 the first to be the "original" but in the calls in this file,
24 the second arg is the original. Ho hum.
25 '''
26 if s1 == s2:
27 return
28 l1=s1.split('\n')
29 l2=s2.split('\n')
30 s = difflib.SequenceMatcher(None, l1, l2)
31 res = ['Generated message not correct (diff follows):']
32 for value, s1s, s1e, s2s, s2e in s.get_opcodes():
33 if value == 'equal':
34 for i in range(s1s, s1e):
35 res.append(' %s'%l1[i])
36 elif value == 'delete':
37 for i in range(s1s, s1e):
38 res.append('- %s'%l1[i])
39 elif value == 'insert':
40 for i in range(s2s, s2e):
41 res.append('+ %s'%l2[i])
42 elif value == 'replace':
43 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
44 res.append('- %s'%l1[i])
45 res.append('+ %s'%l2[j])
47 raise AssertionError, '\n'.join(res)
49 class MailgwTestCase(unittest.TestCase, DiffHelper):
50 count = 0
51 schema = 'classic'
52 def setUp(self):
53 MailgwTestCase.count = MailgwTestCase.count + 1
54 self.dirname = '_test_%s'%self.count
55 try:
56 shutil.rmtree(self.dirname)
57 except OSError, error:
58 if error.errno not in (errno.ENOENT, errno.ESRCH): raise
59 # create the instance
60 init.init(self.dirname, self.schema, 'anydbm', 'sekrit')
61 # check we can load the package
62 self.instance = instance.open(self.dirname)
63 # and open the database
64 self.db = self.instance.open('sekrit')
65 self.db.user.create(username='Chef', address='chef@bork.bork.bork')
66 self.db.user.create(username='richard', address='richard@test')
67 self.db.user.create(username='mary', address='mary@test')
68 self.db.user.create(username='john', address='john@test',
69 alternate_addresses='jondoe@test\njohn.doe@test')
71 def tearDown(self):
72 if os.path.exists(os.environ['SENDMAILDEBUG']):
73 os.remove(os.environ['SENDMAILDEBUG'])
74 try:
75 shutil.rmtree(self.dirname)
76 except OSError, error:
77 if error.errno not in (errno.ENOENT, errno.ESRCH): raise
79 def testNewIssue(self):
80 message = cStringIO.StringIO('''Content-Type: text/plain;
81 charset="iso-8859-1"
82 From: Chef <chef@bork.bork.bork
83 To: issue_tracker@fill.me.in.
84 Cc: richard@test
85 Message-Id: <dummy_test_message_id>
86 Subject: [issue] Testing...
88 This is a test submission of a new issue.
89 ''')
90 handler = self.instance.MailGW(self.instance, self.db)
91 handler.main(message)
92 if os.path.exists(os.environ['SENDMAILDEBUG']):
93 error = open(os.environ['SENDMAILDEBUG']).read()
94 self.assertEqual('no error', error)
96 def testAlternateAddress(self):
97 message = cStringIO.StringIO('''Content-Type: text/plain;
98 charset="iso-8859-1"
99 From: John Doe <john.doe@test>
100 To: issue_tracker@fill.me.in.
101 Message-Id: <dummy_test_message_id>
102 Subject: [issue] Testing...
104 This is a test submission of a new issue.
105 ''')
106 userlist = self.db.user.list()
107 handler = self.instance.MailGW(self.instance, self.db)
108 handler.main(message)
109 if os.path.exists(os.environ['SENDMAILDEBUG']):
110 error = open(os.environ['SENDMAILDEBUG']).read()
111 self.assertEqual('no error', error)
112 self.assertEqual(userlist, self.db.user.list(),
113 "user created when it shouldn't have been")
115 def testNewIssueNoClass(self):
116 message = cStringIO.StringIO('''Content-Type: text/plain;
117 charset="iso-8859-1"
118 From: Chef <chef@bork.bork.bork
119 To: issue_tracker@fill.me.in.
120 Cc: richard@test
121 Message-Id: <dummy_test_message_id>
122 Subject: Testing...
124 This is a test submission of a new issue.
125 ''')
126 handler = self.instance.MailGW(self.instance, self.db)
127 handler.main(message)
128 if os.path.exists(os.environ['SENDMAILDEBUG']):
129 error = open(os.environ['SENDMAILDEBUG']).read()
130 self.assertEqual('no error', error)
132 def testNewIssueAuthMsg(self):
133 message = cStringIO.StringIO('''Content-Type: text/plain;
134 charset="iso-8859-1"
135 From: Chef <chef@bork.bork.bork
136 To: issue_tracker@fill.me.in.
137 Message-Id: <dummy_test_message_id>
138 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
140 This is a test submission of a new issue.
141 ''')
142 handler = self.instance.MailGW(self.instance, self.db)
143 # TODO: fix the damn config - this is apalling
144 self.db.config.MESSAGES_TO_AUTHOR = 'yes'
145 handler.main(message)
147 self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
148 '''FROM: roundup-admin@fill.me.in.
149 TO: chef@bork.bork.bork, mary@test, richard@test
150 Content-Type: text/plain
151 Subject: [issue1] Testing...
152 To: chef@bork.bork.bork, mary@test, richard@test
153 From: Chef <issue_tracker@fill.me.in.>
154 Reply-To: Roundup issue tracker <issue_tracker@fill.me.in.>
155 MIME-Version: 1.0
156 Message-Id: <dummy_test_message_id>
157 X-Roundup-Name: Roundup issue tracker
158 Content-Transfer-Encoding: quoted-printable
161 New submission from Chef <chef@bork.bork.bork>:
163 This is a test submission of a new issue.
166 ----------
167 assignedto: richard
168 messages: 1
169 nosy: mary, Chef, richard
170 status: unread
171 title: Testing...
172 ___________________________________________________
173 "Roundup issue tracker" <issue_tracker@fill.me.in.>
174 http://some.useful.url/issue1
175 ___________________________________________________
176 ''')
178 # BUG
179 # def testMultipart(self):
180 # '''With more than one part'''
181 # see MultipartEnc tests: but if there is more than one part
182 # we return a multipart/mixed and the boundary contains
183 # the ip address of the test machine.
185 # BUG should test some binary attamchent too.
187 def testFollowup(self):
188 self.testNewIssue()
189 message = cStringIO.StringIO('''Content-Type: text/plain;
190 charset="iso-8859-1"
191 From: richard <richard@test>
192 To: issue_tracker@fill.me.in.
193 Message-Id: <followup_dummy_id>
194 In-Reply-To: <dummy_test_message_id>
195 Subject: [issue1] Testing... [assignedto=mary; nosy=john]
197 This is a followup
198 ''')
199 handler = self.instance.MailGW(self.instance, self.db)
200 handler.main(message)
202 self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
203 '''FROM: roundup-admin@fill.me.in.
204 TO: chef@bork.bork.bork, mary@test, john@test
205 Content-Type: text/plain
206 Subject: [issue1] Testing...
207 To: chef@bork.bork.bork, mary@test, john@test
208 From: richard <issue_tracker@fill.me.in.>
209 Reply-To: Roundup issue tracker <issue_tracker@fill.me.in.>
210 MIME-Version: 1.0
211 Message-Id: <followup_dummy_id>
212 In-Reply-To: <dummy_test_message_id>
213 X-Roundup-Name: Roundup issue tracker
214 Content-Transfer-Encoding: quoted-printable
217 richard <richard@test> added the comment:
219 This is a followup
222 ----------
223 assignedto: -> mary
224 nosy: +mary, john
225 status: unread -> chatting
226 ___________________________________________________
227 "Roundup issue tracker" <issue_tracker@fill.me.in.>
228 http://some.useful.url/issue1
229 ___________________________________________________
230 ''')
232 def testFollowup2(self):
233 self.testNewIssue()
234 message = cStringIO.StringIO('''Content-Type: text/plain;
235 charset="iso-8859-1"
236 From: mary <mary@test>
237 To: issue_tracker@fill.me.in.
238 Message-Id: <followup_dummy_id>
239 In-Reply-To: <dummy_test_message_id>
240 Subject: [issue1] Testing...
242 This is a second followup
243 ''')
244 handler = self.instance.MailGW(self.instance, self.db)
245 handler.main(message)
246 self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
247 '''FROM: roundup-admin@fill.me.in.
248 TO: chef@bork.bork.bork, richard@test
249 Content-Type: text/plain
250 Subject: [issue1] Testing...
251 To: chef@bork.bork.bork, richard@test
252 From: mary <issue_tracker@fill.me.in.>
253 Reply-To: Roundup issue tracker <issue_tracker@fill.me.in.>
254 MIME-Version: 1.0
255 Message-Id: <followup_dummy_id>
256 In-Reply-To: <dummy_test_message_id>
257 X-Roundup-Name: Roundup issue tracker
258 Content-Transfer-Encoding: quoted-printable
261 mary <mary@test> added the comment:
263 This is a second followup
266 ----------
267 status: unread -> chatting
268 ___________________________________________________
269 "Roundup issue tracker" <issue_tracker@fill.me.in.>
270 http://some.useful.url/issue1
271 ___________________________________________________
272 ''')
274 def testFollowupTitleMatch(self):
275 self.testNewIssue()
276 message = cStringIO.StringIO('''Content-Type: text/plain;
277 charset="iso-8859-1"
278 From: richard <richard@test>
279 To: issue_tracker@fill.me.in.
280 Message-Id: <followup_dummy_id>
281 In-Reply-To: <dummy_test_message_id>
282 Subject: Re: Testing... [assignedto=mary; nosy=john]
284 This is a followup
285 ''')
286 handler = self.instance.MailGW(self.instance, self.db)
287 handler.main(message)
289 self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
290 '''FROM: roundup-admin@fill.me.in.
291 TO: chef@bork.bork.bork, mary@test, john@test
292 Content-Type: text/plain
293 Subject: [issue1] Testing...
294 To: chef@bork.bork.bork, mary@test, john@test
295 From: richard <issue_tracker@fill.me.in.>
296 Reply-To: Roundup issue tracker <issue_tracker@fill.me.in.>
297 MIME-Version: 1.0
298 Message-Id: <followup_dummy_id>
299 In-Reply-To: <dummy_test_message_id>
300 X-Roundup-Name: Roundup issue tracker
301 Content-Transfer-Encoding: quoted-printable
304 richard <richard@test> added the comment:
306 This is a followup
309 ----------
310 assignedto: -> mary
311 nosy: +mary, john
312 status: unread -> chatting
313 ___________________________________________________
314 "Roundup issue tracker" <issue_tracker@fill.me.in.>
315 http://some.useful.url/issue1
316 ___________________________________________________
317 ''')
319 def testEnc01(self):
320 self.testNewIssue()
321 message = cStringIO.StringIO('''Content-Type: text/plain;
322 charset="iso-8859-1"
323 From: mary <mary@test>
324 To: issue_tracker@fill.me.in.
325 Message-Id: <followup_dummy_id>
326 In-Reply-To: <dummy_test_message_id>
327 Subject: [issue1] Testing...
328 Content-Type: text/plain;
329 charset="iso-8859-1"
330 Content-Transfer-Encoding: quoted-printable
332 A message with encoding (encoded oe =F6)
334 ''')
335 handler = self.instance.MailGW(self.instance, self.db)
336 handler.main(message)
337 self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
338 '''FROM: roundup-admin@fill.me.in.
339 TO: chef@bork.bork.bork, richard@test
340 Content-Type: text/plain
341 Subject: [issue1] Testing...
342 To: chef@bork.bork.bork, richard@test
343 From: mary <issue_tracker@fill.me.in.>
344 Reply-To: Roundup issue tracker <issue_tracker@fill.me.in.>
345 MIME-Version: 1.0
346 Message-Id: <followup_dummy_id>
347 In-Reply-To: <dummy_test_message_id>
348 X-Roundup-Name: Roundup issue tracker
349 Content-Transfer-Encoding: quoted-printable
352 mary <mary@test> added the comment:
354 A message with encoding (encoded oe =F6)
356 ----------
357 status: unread -> chatting
358 ___________________________________________________
359 "Roundup issue tracker" <issue_tracker@fill.me.in.>
360 http://some.useful.url/issue1
361 ___________________________________________________
362 ''')
365 def testMultipartEnc01(self):
366 self.testNewIssue()
367 message = cStringIO.StringIO('''Content-Type: text/plain;
368 charset="iso-8859-1"
369 From: mary <mary@test>
370 To: issue_tracker@fill.me.in.
371 Message-Id: <followup_dummy_id>
372 In-Reply-To: <dummy_test_message_id>
373 Subject: [issue1] Testing...
374 Content-Type: multipart/mixed;
375 boundary="----_=_NextPart_000_01"
377 This message is in MIME format. Since your mail reader does not understand
378 this format, some or all of this message may not be legible.
380 ------_=_NextPart_000_01
381 Content-Type: text/plain;
382 charset="iso-8859-1"
383 Content-Transfer-Encoding: quoted-printable
385 A message with first part encoded (encoded oe =F6)
387 ''')
388 handler = self.instance.MailGW(self.instance, self.db)
389 handler.main(message)
390 self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
391 '''FROM: roundup-admin@fill.me.in.
392 TO: chef@bork.bork.bork, richard@test
393 Content-Type: text/plain
394 Subject: [issue1] Testing...
395 To: chef@bork.bork.bork, richard@test
396 From: mary <issue_tracker@fill.me.in.>
397 Reply-To: Roundup issue tracker <issue_tracker@fill.me.in.>
398 MIME-Version: 1.0
399 Message-Id: <followup_dummy_id>
400 In-Reply-To: <dummy_test_message_id>
401 X-Roundup-Name: Roundup issue tracker
402 Content-Transfer-Encoding: quoted-printable
405 mary <mary@test> added the comment:
407 A message with first part encoded (encoded oe =F6)
409 ----------
410 status: unread -> chatting
411 ___________________________________________________
412 "Roundup issue tracker" <issue_tracker@fill.me.in.>
413 http://some.useful.url/issue1
414 ___________________________________________________
415 ''')
417 class ExtMailgwTestCase(MailgwTestCase):
418 schema = 'extended'
420 def suite():
421 l = [unittest.makeSuite(MailgwTestCase, 'test'),
422 # unittest.makeSuite(ExtMailgwTestCase, 'test')
423 ]
424 return unittest.TestSuite(l)
427 #
428 # $Log: not supported by cvs2svn $
429 # Revision 1.14 2002/03/18 18:32:00 rochecompaan
430 # All messages sent to the nosy list are now encoded as quoted-printable.
431 #
432 # Revision 1.13 2002/02/15 07:08:45 richard
433 # . Alternate email addresses are now available for users. See the MIGRATION
434 # file for info on how to activate the feature.
435 #
436 # Revision 1.12 2002/02/15 00:13:38 richard
437 # . #503204 ] mailgw needs a default class
438 # - partially done - the setting of additional properties can wait for a
439 # better configuration system.
440 #
441 # Revision 1.11 2002/02/14 23:38:12 richard
442 # Fixed the unit tests for the mailgw re: the x-roundup-name header.
443 # Also made the test runner more user-friendly:
444 # ./run_tests - detect all tests in test/test_<name>.py and run them
445 # ./run_tests <name> - run only test/test_<name>.py
446 # eg ./run_tests mailgw - run the mailgw test from test/test_mailgw.py
447 #
448 # Revision 1.10 2002/02/12 08:08:55 grubert
449 # . Clean up mail handling, multipart handling.
450 #
451 # Revision 1.9 2002/02/05 14:15:29 grubert
452 # . respect encodings in non multipart messages.
453 #
454 # Revision 1.8 2002/02/04 09:40:21 grubert
455 # . add test for multipart messages with first part being encoded.
456 #
457 # Revision 1.7 2002/01/22 11:54:45 rochecompaan
458 # Fixed status change in mail gateway.
459 #
460 # Revision 1.6 2002/01/21 10:05:48 rochecompaan
461 # Feature:
462 # . the mail gateway now responds with an error message when invalid
463 # values for arguments are specified for link or multilink properties
464 # . modified unit test to check nosy and assignedto when specified as
465 # arguments
466 #
467 # Fixed:
468 # . fixed setting nosy as argument in subject line
469 #
470 # Revision 1.5 2002/01/15 00:12:40 richard
471 # #503340 ] creating issue with [asignedto=p.ohly]
472 #
473 # Revision 1.4 2002/01/14 07:12:15 richard
474 # removed file writing from tests...
475 #
476 # Revision 1.3 2002/01/14 02:20:15 richard
477 # . changed all config accesses so they access either the instance or the
478 # config attriubute on the db. This means that all config is obtained from
479 # instance_config instead of the mish-mash of classes. This will make
480 # switching to a ConfigParser setup easier too, I hope.
481 #
482 # At a minimum, this makes migration a _little_ easier (a lot easier in the
483 # 0.5.0 switch, I hope!)
484 #
485 # Revision 1.2 2002/01/11 23:22:29 richard
486 # . #502437 ] rogue reactor and unittest
487 # in short, the nosy reactor was modifying the nosy list. That code had
488 # been there for a long time, and I suspsect it was there because we
489 # weren't generating the nosy list correctly in other places of the code.
490 # We're now doing that, so the nosy-modifying code can go away from the
491 # nosy reactor.
492 #
493 # Revision 1.1 2002/01/02 02:31:38 richard
494 # Sorry for the huge checkin message - I was only intending to implement #496356
495 # but I found a number of places where things had been broken by transactions:
496 # . modified ROUNDUPDBSENDMAILDEBUG to be SENDMAILDEBUG and hold a filename
497 # for _all_ roundup-generated smtp messages to be sent to.
498 # . the transaction cache had broken the roundupdb.Class set() reactors
499 # . newly-created author users in the mailgw weren't being committed to the db
500 #
501 # Stuff that made it into CHANGES.txt (ie. the stuff I was actually working
502 # on when I found that stuff :):
503 # . #496356 ] Use threading in messages
504 # . detectors were being registered multiple times
505 # . added tests for mailgw
506 # . much better attaching of erroneous messages in the mail gateway
507 #
508 #
509 #
510 #
511 # vim: set filetype=python ts=4 sw=4 et si