Merge lp:~allenap/launchpad/bug-mail-case-sensitive-domain-bug-48464-fix into lp:launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: 12057
Proposed branch: lp:~allenap/launchpad/bug-mail-case-sensitive-domain-bug-48464-fix
Merge into: lp:launchpad
Prerequisite: lp:~allenap/launchpad/bug-mail-case-sensitive-domain-bug-48464-refactor
Diff against target: 163 lines (+65/-44)
3 files modified
lib/lp/services/mail/handlers.py (+19/-35)
lib/lp/services/mail/tests/incomingmail.txt (+5/-3)
lib/lp/services/mail/tests/test_handlers.py (+41/-6)
To merge this branch: bzr merge lp:~allenap/launchpad/bug-mail-case-sensitive-domain-bug-48464-fix
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+43547@code.launchpad.net

Commit message

[r=abentley][ui=none][bug=48464] Treat target domains on incoming mail without regard to case.

Description of the change

This makes MailHandler case-insensitive to domains, moves its doctests into unit tests, and updates the integration test, incomingmail.txt, to demonstrate case-insensitivity.

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

The code looks good.

Please add docs for all the tests. The copyright date for test_handlers should be 2009-2010, not just 2010.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/services/mail/handlers.py'
2--- lib/lp/services/mail/handlers.py 2010-12-13 20:43:45 +0000
3+++ lib/lp/services/mail/handlers.py 2010-12-13 20:43:46 +0000
4@@ -2,6 +2,9 @@
5 # GNU Affero General Public License version 3 (see the file LICENSE).
6
7 __metaclass__ = type
8+__all__ = [
9+ "mail_handlers",
10+ ]
11
12 from canonical.config import config
13 from lp.answers.mail.handler import AnswerTrackerHandler
14@@ -13,50 +16,31 @@
15 class MailHandlers:
16 """All the registered mail handlers."""
17
18+ DEFAULT = (
19+ (config.launchpad.bugs_domain, MaloneHandler),
20+ (config.launchpad.specs_domain, SpecificationHandler),
21+ (config.answertracker.email_domain, AnswerTrackerHandler),
22+ # XXX flacoste 2007-04-23 Backward compatibility for old domain.
23+ # We probably want to remove it in the future.
24+ ('support.launchpad.net', AnswerTrackerHandler),
25+ (config.launchpad.code_domain, CodeHandler),
26+ )
27+
28 def __init__(self):
29- self._handlers = {
30- config.launchpad.bugs_domain: MaloneHandler(),
31- config.launchpad.specs_domain: SpecificationHandler(),
32- config.answertracker.email_domain: AnswerTrackerHandler(),
33- # XXX flacoste 2007-04-23 Backward compatibility for old domain.
34- # We probably want to remove it in the future.
35- 'support.launchpad.net': AnswerTrackerHandler(),
36- config.launchpad.code_domain: CodeHandler(),
37- }
38+ self._handlers = {}
39+ for domain, handler_factory in self.DEFAULT:
40+ self.add(domain, handler_factory())
41
42 def get(self, domain):
43 """Return the handler for the given email domain.
44
45 Return None if no such handler exists.
46-
47- >>> handlers = MailHandlers()
48- >>> handlers.get('bugs.launchpad.net') #doctest: +ELLIPSIS
49- <...MaloneHandler...>
50- >>> handlers.get('no.such.domain') is None
51- True
52 """
53- return self._handlers.get(domain)
54+ return self._handlers.get(domain.lower())
55
56 def add(self, domain, handler):
57- """Adds a handler for a domain.
58-
59- >>> handlers = MailHandlers()
60- >>> handlers.get('some.domain') is None
61- True
62- >>> handler = object()
63- >>> handlers.add('some.domain', handler)
64- >>> handlers.get('some.domain') is handler
65- True
66-
67- If there already is a handler for the domain, the old one will
68- get overwritten:
69-
70- >>> new_handler = object()
71- >>> handlers.add('some.domain', new_handler)
72- >>> handlers.get('some.domain') is new_handler
73- True
74- """
75- self._handlers[domain] = handler
76+ """Adds a handler for a domain."""
77+ self._handlers[domain.lower()] = handler
78
79
80 mail_handlers = MailHandlers()
81
82=== modified file 'lib/lp/services/mail/tests/incomingmail.txt'
83--- lib/lp/services/mail/tests/incomingmail.txt 2010-12-13 20:43:45 +0000
84+++ lib/lp/services/mail/tests/incomingmail.txt 2010-12-13 20:43:46 +0000
85@@ -50,17 +50,19 @@
86 >>> from canonical.testing.layers import LaunchpadZopelessLayer
87 >>> from canonical.launchpad.mail import sendmail as original_sendmail
88
89-For these examples, we don't want the Precedence header added.
90+For these examples, we don't want the Precedence header added. Domains
91+are treated without regard to case: for incoming mail, foo.com and
92+FOO.COM are treated equivalently.
93
94 >>> def sendmail(msg, to_addrs=None):
95 ... return original_sendmail(msg, to_addrs=to_addrs, bulk=False)
96
97 >>> LaunchpadZopelessLayer.switchDbUser('launchpad')
98 >>> msgids = {'foo.com': [], 'bar.com': [], 'baz.com': []}
99- >>> for domain in ('foo.com', 'bar.com', 'foo.com', 'baz.com'):
100+ >>> for domain in ('foo.com', 'bar.com', 'FOO.COM', 'baz.com'):
101 ... msg = read_test_message('signed_detached.txt')
102 ... msg.replace_header('To', '123@%s' % domain)
103- ... msgids[domain].append("<%s>" % sendmail(msg))
104+ ... msgids[domain.lower()].append("<%s>" % sendmail(msg))
105
106 handleMail will check the timestamp on signed messages, but the signatures
107 on our testdata are old, and in these tests we don't care to be told.
108
109=== modified file 'lib/lp/services/mail/tests/test_handlers.py'
110--- lib/lp/services/mail/tests/test_handlers.py 2010-12-13 20:43:45 +0000
111+++ lib/lp/services/mail/tests/test_handlers.py 2010-12-13 20:43:46 +0000
112@@ -1,10 +1,45 @@
113-# Copyright 2009 Canonical Ltd. This software is licensed under the
114+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
115 # GNU Affero General Public License version 3 (see the file LICENSE).
116
117 __metaclass__ = type
118
119-from doctest import DocTestSuite
120-
121-
122-def test_suite():
123- return DocTestSuite('lp.services.mail.handlers')
124+from lp.services.mail.handlers import MailHandlers
125+from lp.testing import TestCase
126+
127+
128+class TestMailHandlers(TestCase):
129+ """Tests for the `MailHandlers` class."""
130+
131+ def test_get(self):
132+ # MailHandlers.get() should return the registered handler for the
133+ # given domain.
134+ handlers = MailHandlers()
135+ self.assertIsNot(None, handlers.get("bugs.launchpad.net"))
136+ self.assertIs(None, handlers.get("no.such.domain"))
137+
138+ def test_get_is_case_insensitive(self):
139+ # The domain passed to get() is treated case-insentitively.
140+ handlers = MailHandlers()
141+ handler = object()
142+ handlers.add("some.domain", handler)
143+ self.assertIs(handler, handlers.get("some.domain"))
144+ self.assertIs(handler, handlers.get("SOME.DOMAIN"))
145+ self.assertIs(handler, handlers.get("Some.Domain"))
146+
147+ def test_add_for_new_domain(self):
148+ # MailHandlers.add() registers a handler for the given domain.
149+ handlers = MailHandlers()
150+ self.assertIs(None, handlers.get("some.domain"))
151+ handler = object()
152+ handlers.add("some.domain", handler)
153+ self.assertIs(handler, handlers.get("some.domain"))
154+
155+ def test_add_for_existing_domain(self):
156+ # When adding a new handler for an already congfigured domain, the
157+ # existing handler is overwritten.
158+ handlers = MailHandlers()
159+ handler1 = object()
160+ handlers.add("some.domain", handler1)
161+ handler2 = object()
162+ handlers.add("some.domain", handler2)
163+ self.assertIs(handler2, handlers.get("some.domain"))