Merge lp:~gary/launchpad/bug650343-2 into lp:launchpad
- bug650343-2
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Leonard Richardson | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 11705 | ||||
Proposed branch: | lp:~gary/launchpad/bug650343-2 | ||||
Merge into: | lp:launchpad | ||||
Prerequisite: | lp:~gary/launchpad/bug650343 | ||||
Diff against target: |
666 lines (+133/-125) 10 files modified
cronscripts/process-mail.py (+2/-1) lib/canonical/launchpad/doc/emailauthentication.txt (+7/-4) lib/canonical/launchpad/ftests/test_system_documentation.py (+55/-77) lib/canonical/launchpad/mail/handlers.py (+1/-1) lib/lp/blueprints/doc/spec-mail-exploder.txt (+1/-1) lib/lp/services/mail/incoming.py (+2/-2) lib/lp/services/mail/tests/incomingmail.txt (+6/-6) lib/lp/services/mail/tests/test_dkim.py (+31/-26) lib/lp/services/mail/tests/test_incoming.py (+24/-2) lib/lp/services/mail/tests/test_signedmessage.py (+4/-5) |
||||
To merge this branch: | bzr merge lp:~gary/launchpad/bug650343-2 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Leonard Richardson (community) | Approve | ||
Review via email: mp+38224@code.launchpad.net |
Commit message
Add X-Launchpad-
Description of the change
In the reviews for https:/
While we're speaking of lint, here's the output.
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
cronscripts/
lib/canonical
lib/canonical
lib/canonical
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
./cronscripts/
9: '_pythonpath' imported but unused
./lib/canonical
155: want exceeds 78 characters.
The ./cronscripts/
The request for ./lib/canonical
Tests passed on ec2 with the current branch, except for a Windmill error that appears to be spurious.
Other than that, the only thing I know to mention is that I did not move the files I touched for this move (lib/canonical/
Thank you.
Preview Diff
1 | === modified file 'cronscripts/process-mail.py' | |||
2 | --- cronscripts/process-mail.py 2010-10-03 15:30:06 +0000 | |||
3 | +++ cronscripts/process-mail.py 2010-10-14 20:59:59 +0000 | |||
4 | @@ -13,7 +13,7 @@ | |||
5 | 13 | from canonical.config import config | 13 | from canonical.config import config |
6 | 14 | from lp.services.scripts.base import ( | 14 | from lp.services.scripts.base import ( |
7 | 15 | LaunchpadCronScript, LaunchpadScriptFailure) | 15 | LaunchpadCronScript, LaunchpadScriptFailure) |
9 | 16 | from canonical.launchpad.mail.incoming import handleMail | 16 | from lp.services.mail.incoming import handleMail |
10 | 17 | from canonical.launchpad.interfaces import IMailBox | 17 | from canonical.launchpad.interfaces import IMailBox |
11 | 18 | 18 | ||
12 | 19 | 19 | ||
13 | @@ -21,6 +21,7 @@ | |||
14 | 21 | usage = """%prog [options] | 21 | usage = """%prog [options] |
15 | 22 | 22 | ||
16 | 23 | """ + __doc__ | 23 | """ + __doc__ |
17 | 24 | |||
18 | 24 | def main(self): | 25 | def main(self): |
19 | 25 | try: | 26 | try: |
20 | 26 | handleMail(self.txn) | 27 | handleMail(self.txn) |
21 | 27 | 28 | ||
22 | === modified file 'lib/canonical/launchpad/doc/emailauthentication.txt' | |||
23 | --- lib/canonical/launchpad/doc/emailauthentication.txt 2010-10-04 19:50:45 +0000 | |||
24 | +++ lib/canonical/launchpad/doc/emailauthentication.txt 2010-10-14 20:59:59 +0000 | |||
25 | @@ -1,9 +1,10 @@ | |||
27 | 1 | = Authentication of Emails = | 1 | Authentication of Emails |
28 | 2 | ======================== | ||
29 | 2 | 3 | ||
30 | 3 | When an email arrives in Launchpad the user who sent it needs to be | 4 | When an email arrives in Launchpad the user who sent it needs to be |
31 | 4 | authenticated. This is done by authenticateEmail: | 5 | authenticated. This is done by authenticateEmail: |
32 | 5 | 6 | ||
34 | 6 | >>> from canonical.launchpad.mail.incoming import authenticateEmail | 7 | >>> from lp.services.mail.incoming import authenticateEmail |
35 | 7 | 8 | ||
36 | 8 | The only way of authenticating the user is by looking at the OpenPGP | 9 | The only way of authenticating the user is by looking at the OpenPGP |
37 | 9 | signature. First we have to import the OpenPGP keys we will use in the | 10 | signature. First we have to import the OpenPGP keys we will use in the |
38 | @@ -159,7 +160,8 @@ | |||
39 | 159 | Sample Person | 160 | Sample Person |
40 | 160 | 161 | ||
41 | 161 | 162 | ||
43 | 162 | == IWeaklyAuthenticatedPrinipal == | 163 | IWeaklyAuthenticatedPrincipal |
44 | 164 | ----------------------------- | ||
45 | 163 | 165 | ||
46 | 164 | It's a huge difference to signing an email with a key that is associated | 166 | It's a huge difference to signing an email with a key that is associated |
47 | 165 | with the authenticated Person, and signing it with a key that isn't | 167 | with the authenticated Person, and signing it with a key that isn't |
48 | @@ -172,7 +174,8 @@ | |||
49 | 172 | 174 | ||
50 | 173 | An unsigned email: | 175 | An unsigned email: |
51 | 174 | 176 | ||
53 | 175 | >>> from canonical.launchpad.interfaces import IWeaklyAuthenticatedPrincipal | 177 | >>> from canonical.launchpad.interfaces import ( |
54 | 178 | ... IWeaklyAuthenticatedPrincipal) | ||
55 | 176 | >>> msg = read_test_message('unsigned_multipart.txt') | 179 | >>> msg = read_test_message('unsigned_multipart.txt') |
56 | 177 | >>> principal = authenticateEmail(msg) | 180 | >>> principal = authenticateEmail(msg) |
57 | 178 | >>> IWeaklyAuthenticatedPrincipal.providedBy(principal) | 181 | >>> IWeaklyAuthenticatedPrincipal.providedBy(principal) |
58 | 179 | 182 | ||
59 | === modified file 'lib/canonical/launchpad/ftests/test_system_documentation.py' | |||
60 | --- lib/canonical/launchpad/ftests/test_system_documentation.py 2010-10-03 15:30:06 +0000 | |||
61 | +++ lib/canonical/launchpad/ftests/test_system_documentation.py 2010-10-14 20:59:59 +0000 | |||
62 | @@ -52,6 +52,7 @@ | |||
63 | 52 | 52 | ||
64 | 53 | here = os.path.dirname(os.path.realpath(__file__)) | 53 | here = os.path.dirname(os.path.realpath(__file__)) |
65 | 54 | 54 | ||
66 | 55 | |||
67 | 55 | def lobotomize_stevea(): | 56 | def lobotomize_stevea(): |
68 | 56 | """Set SteveA's email address' status to NEW. | 57 | """Set SteveA's email address' status to NEW. |
69 | 57 | 58 | ||
70 | @@ -79,17 +80,20 @@ | |||
71 | 79 | LaunchpadZopelessLayer.switchDbUser('poexport') | 80 | LaunchpadZopelessLayer.switchDbUser('poexport') |
72 | 80 | setUp(test) | 81 | setUp(test) |
73 | 81 | 82 | ||
74 | 83 | |||
75 | 82 | def poExportTearDown(test): | 84 | def poExportTearDown(test): |
76 | 83 | """Tear down the PO export script tests.""" | 85 | """Tear down the PO export script tests.""" |
77 | 84 | # XXX sinzui 2007-11-14: | 86 | # XXX sinzui 2007-11-14: |
78 | 85 | # This function is not needed. The test should be switched to tearDown. | 87 | # This function is not needed. The test should be switched to tearDown. |
79 | 86 | tearDown(test) | 88 | tearDown(test) |
80 | 87 | 89 | ||
81 | 90 | |||
82 | 88 | def uploaderSetUp(test): | 91 | def uploaderSetUp(test): |
83 | 89 | """setup the package uploader script tests.""" | 92 | """setup the package uploader script tests.""" |
84 | 90 | setUp(test) | 93 | setUp(test) |
85 | 91 | LaunchpadZopelessLayer.switchDbUser('uploader') | 94 | LaunchpadZopelessLayer.switchDbUser('uploader') |
86 | 92 | 95 | ||
87 | 96 | |||
88 | 93 | def uploaderTearDown(test): | 97 | def uploaderTearDown(test): |
89 | 94 | """Tear down the package uploader script tests.""" | 98 | """Tear down the package uploader script tests.""" |
90 | 95 | # XXX sinzui 2007-11-14: | 99 | # XXX sinzui 2007-11-14: |
91 | @@ -115,6 +119,7 @@ | |||
92 | 115 | # This function is not needed. The test should be switched to tearDown. | 119 | # This function is not needed. The test should be switched to tearDown. |
93 | 116 | tearDown(test) | 120 | tearDown(test) |
94 | 117 | 121 | ||
95 | 122 | |||
96 | 118 | def uploadQueueSetUp(test): | 123 | def uploadQueueSetUp(test): |
97 | 119 | lobotomize_stevea() | 124 | lobotomize_stevea() |
98 | 120 | test_dbuser = config.uploadqueue.dbuser | 125 | test_dbuser = config.uploadqueue.dbuser |
99 | @@ -127,6 +132,7 @@ | |||
100 | 127 | """Clean up any Zope registrations.""" | 132 | """Clean up any Zope registrations.""" |
101 | 128 | cleanUp() | 133 | cleanUp() |
102 | 129 | 134 | ||
103 | 135 | |||
104 | 130 | def _createUbuntuBugTaskLinkedToQuestion(): | 136 | def _createUbuntuBugTaskLinkedToQuestion(): |
105 | 131 | """Get the id of an Ubuntu bugtask linked to a question. | 137 | """Get the id of an Ubuntu bugtask linked to a question. |
106 | 132 | 138 | ||
107 | @@ -154,10 +160,13 @@ | |||
108 | 154 | pop_notifications() | 160 | pop_notifications() |
109 | 155 | return ubuntu_bugtask.id | 161 | return ubuntu_bugtask.id |
110 | 156 | 162 | ||
111 | 163 | |||
112 | 157 | def bugLinkedToQuestionSetUp(test): | 164 | def bugLinkedToQuestionSetUp(test): |
113 | 158 | """Setup the question and linked bug for testing.""" | 165 | """Setup the question and linked bug for testing.""" |
114 | 166 | |||
115 | 159 | def get_bugtask_linked_to_question(): | 167 | def get_bugtask_linked_to_question(): |
116 | 160 | return getUtility(IBugTaskSet).get(bugtask_id) | 168 | return getUtility(IBugTaskSet).get(bugtask_id) |
117 | 169 | |||
118 | 161 | setUp(test) | 170 | setUp(test) |
119 | 162 | bugtask_id = _createUbuntuBugTaskLinkedToQuestion() | 171 | bugtask_id = _createUbuntuBugTaskLinkedToQuestion() |
120 | 163 | test.globs['get_bugtask_linked_to_question'] = ( | 172 | test.globs['get_bugtask_linked_to_question'] = ( |
121 | @@ -166,6 +175,7 @@ | |||
122 | 166 | # interaction in the test. | 175 | # interaction in the test. |
123 | 167 | login('no-priv@canonical.com') | 176 | login('no-priv@canonical.com') |
124 | 168 | 177 | ||
125 | 178 | |||
126 | 169 | def uploaderBugLinkedToQuestionSetUp(test): | 179 | def uploaderBugLinkedToQuestionSetUp(test): |
127 | 170 | LaunchpadZopelessLayer.switchDbUser('launchpad') | 180 | LaunchpadZopelessLayer.switchDbUser('launchpad') |
128 | 171 | bugLinkedToQuestionSetUp(test) | 181 | bugLinkedToQuestionSetUp(test) |
129 | @@ -173,6 +183,7 @@ | |||
130 | 173 | uploaderSetUp(test) | 183 | uploaderSetUp(test) |
131 | 174 | login(ANONYMOUS) | 184 | login(ANONYMOUS) |
132 | 175 | 185 | ||
133 | 186 | |||
134 | 176 | def uploadQueueBugLinkedToQuestionSetUp(test): | 187 | def uploadQueueBugLinkedToQuestionSetUp(test): |
135 | 177 | LaunchpadZopelessLayer.switchDbUser('launchpad') | 188 | LaunchpadZopelessLayer.switchDbUser('launchpad') |
136 | 178 | bugLinkedToQuestionSetUp(test) | 189 | bugLinkedToQuestionSetUp(test) |
137 | @@ -185,8 +196,7 @@ | |||
138 | 185 | special = { | 196 | special = { |
139 | 186 | # No setup or teardown at all, since it is demonstrating these features. | 197 | # No setup or teardown at all, since it is demonstrating these features. |
140 | 187 | 'old-testing.txt': LayeredDocFileSuite( | 198 | 'old-testing.txt': LayeredDocFileSuite( |
143 | 188 | '../doc/old-testing.txt', layer=FunctionalLayer | 199 | '../doc/old-testing.txt', layer=FunctionalLayer), |
142 | 189 | ), | ||
144 | 190 | 200 | ||
145 | 191 | 'autodecorate.txt': | 201 | 'autodecorate.txt': |
146 | 192 | LayeredDocFileSuite('../doc/autodecorate.txt', layer=BaseLayer), | 202 | LayeredDocFileSuite('../doc/autodecorate.txt', layer=BaseLayer), |
147 | @@ -194,84 +204,70 @@ | |||
148 | 194 | 204 | ||
149 | 195 | # And this test want minimal environment too. | 205 | # And this test want minimal environment too. |
150 | 196 | 'package-relationship.txt': LayeredDocFileSuite( | 206 | 'package-relationship.txt': LayeredDocFileSuite( |
154 | 197 | '../doc/package-relationship.txt', | 207 | '../doc/package-relationship.txt', |
155 | 198 | stdout_logging=False, layer=None | 208 | stdout_logging=False, layer=None), |
153 | 199 | ), | ||
156 | 200 | 209 | ||
157 | 201 | 'webservice-configuration.txt': LayeredDocFileSuite( | 210 | 'webservice-configuration.txt': LayeredDocFileSuite( |
161 | 202 | '../doc/webservice-configuration.txt', | 211 | '../doc/webservice-configuration.txt', |
162 | 203 | setUp=setGlobs, tearDown=layerlessTearDown, layer=None | 212 | setUp=setGlobs, tearDown=layerlessTearDown, layer=None), |
160 | 204 | ), | ||
163 | 205 | 213 | ||
164 | 206 | 214 | ||
165 | 207 | # POExport stuff is Zopeless and connects as a different database user. | 215 | # POExport stuff is Zopeless and connects as a different database user. |
166 | 208 | # poexport-distroseries-(date-)tarball.txt is excluded, since they add | 216 | # poexport-distroseries-(date-)tarball.txt is excluded, since they add |
167 | 209 | # data to the database as well. | 217 | # data to the database as well. |
168 | 210 | 'message.txt': LayeredDocFileSuite( | 218 | 'message.txt': LayeredDocFileSuite( |
172 | 211 | '../doc/message.txt', | 219 | '../doc/message.txt', |
173 | 212 | setUp=setUp, tearDown=tearDown, layer=LaunchpadFunctionalLayer | 220 | setUp=setUp, tearDown=tearDown, layer=LaunchpadFunctionalLayer), |
171 | 213 | ), | ||
174 | 214 | 'close-account.txt': LayeredDocFileSuite( | 221 | 'close-account.txt': LayeredDocFileSuite( |
178 | 215 | '../doc/close-account.txt', setUp=setUp, tearDown=tearDown, | 222 | '../doc/close-account.txt', setUp=setUp, tearDown=tearDown, |
179 | 216 | layer=LaunchpadZopelessLayer | 223 | layer=LaunchpadZopelessLayer), |
177 | 217 | ), | ||
180 | 218 | 'launchpadform.txt': LayeredDocFileSuite( | 224 | 'launchpadform.txt': LayeredDocFileSuite( |
185 | 219 | '../doc/launchpadform.txt', | 225 | '../doc/launchpadform.txt', |
186 | 220 | setUp=setUp, tearDown=tearDown, | 226 | setUp=setUp, tearDown=tearDown, |
187 | 221 | layer=LaunchpadFunctionalLayer | 227 | layer=LaunchpadFunctionalLayer), |
184 | 222 | ), | ||
188 | 223 | 'launchpadformharness.txt': LayeredDocFileSuite( | 228 | 'launchpadformharness.txt': LayeredDocFileSuite( |
193 | 224 | '../doc/launchpadformharness.txt', | 229 | '../doc/launchpadformharness.txt', |
194 | 225 | setUp=setUp, tearDown=tearDown, | 230 | setUp=setUp, tearDown=tearDown, |
195 | 226 | layer=LaunchpadFunctionalLayer | 231 | layer=LaunchpadFunctionalLayer), |
192 | 227 | ), | ||
196 | 228 | 'uri.txt': LayeredDocFileSuite( | 232 | 'uri.txt': LayeredDocFileSuite( |
201 | 229 | '../doc/uri.txt', | 233 | '../doc/uri.txt', |
202 | 230 | setUp=setUp, tearDown=tearDown, | 234 | setUp=setUp, tearDown=tearDown, |
203 | 231 | layer=FunctionalLayer | 235 | layer=FunctionalLayer), |
200 | 232 | ), | ||
204 | 233 | 'notification-text-escape.txt': LayeredDocFileSuite( | 236 | 'notification-text-escape.txt': LayeredDocFileSuite( |
210 | 234 | '../doc/notification-text-escape.txt', | 237 | '../doc/notification-text-escape.txt', |
211 | 235 | setUp=test_notifications.setUp, | 238 | setUp=test_notifications.setUp, |
212 | 236 | tearDown=test_notifications.tearDown, | 239 | tearDown=test_notifications.tearDown, |
213 | 237 | stdout_logging=False, layer=None | 240 | stdout_logging=False, layer=None), |
209 | 238 | ), | ||
214 | 239 | # This test is actually run twice to prove that the AppServerLayer | 241 | # This test is actually run twice to prove that the AppServerLayer |
215 | 240 | # properly isolates the database between tests. | 242 | # properly isolates the database between tests. |
216 | 241 | 'launchpadlib.txt': LayeredDocFileSuite( | 243 | 'launchpadlib.txt': LayeredDocFileSuite( |
217 | 242 | '../doc/launchpadlib.txt', | 244 | '../doc/launchpadlib.txt', |
218 | 243 | layer=AppServerLayer, | 245 | layer=AppServerLayer, |
221 | 244 | setUp=browser.setUp, tearDown=browser.tearDown, | 246 | setUp=browser.setUp, tearDown=browser.tearDown,), |
220 | 245 | ), | ||
222 | 246 | 'launchpadlib2.txt': LayeredDocFileSuite( | 247 | 'launchpadlib2.txt': LayeredDocFileSuite( |
223 | 247 | '../doc/launchpadlib.txt', | 248 | '../doc/launchpadlib.txt', |
224 | 248 | layer=AppServerLayer, | 249 | layer=AppServerLayer, |
227 | 249 | setUp=browser.setUp, tearDown=browser.tearDown, | 250 | setUp=browser.setUp, tearDown=browser.tearDown,), |
226 | 250 | ), | ||
228 | 251 | # XXX gary 2008-12-08 bug=306246 bug=305858: Disabled test because of | 251 | # XXX gary 2008-12-08 bug=306246 bug=305858: Disabled test because of |
229 | 252 | # multiple spurious problems with layer and test. | 252 | # multiple spurious problems with layer and test. |
230 | 253 | # 'google-service-stub.txt': LayeredDocFileSuite( | 253 | # 'google-service-stub.txt': LayeredDocFileSuite( |
234 | 254 | # '../doc/google-service-stub.txt', | 254 | # '../doc/google-service-stub.txt', |
235 | 255 | # layer=GoogleServiceLayer, | 255 | # layer=GoogleServiceLayer,), |
233 | 256 | # ), | ||
236 | 257 | 'canonical_url.txt': LayeredDocFileSuite( | 256 | 'canonical_url.txt': LayeredDocFileSuite( |
242 | 258 | '../doc/canonical_url.txt', | 257 | '../doc/canonical_url.txt', |
243 | 259 | setUp=setUp, | 258 | setUp=setUp, |
244 | 260 | tearDown=tearDown, | 259 | tearDown=tearDown, |
245 | 261 | layer=FunctionalLayer, | 260 | layer=FunctionalLayer,), |
241 | 262 | ), | ||
246 | 263 | 'google-searchservice.txt': LayeredDocFileSuite( | 261 | 'google-searchservice.txt': LayeredDocFileSuite( |
251 | 264 | '../doc/google-searchservice.txt', | 262 | '../doc/google-searchservice.txt', |
252 | 265 | setUp=setUp, tearDown=tearDown, | 263 | setUp=setUp, tearDown=tearDown, |
253 | 266 | layer=GoogleLaunchpadFunctionalLayer, | 264 | layer=GoogleLaunchpadFunctionalLayer,), |
250 | 267 | ), | ||
254 | 268 | } | 265 | } |
255 | 269 | 266 | ||
256 | 270 | 267 | ||
257 | 271 | class ProcessMailLayer(LaunchpadZopelessLayer): | 268 | class ProcessMailLayer(LaunchpadZopelessLayer): |
258 | 272 | """Layer containing the tests running inside process-mail.py.""" | 269 | """Layer containing the tests running inside process-mail.py.""" |
259 | 273 | 270 | ||
260 | 274 | |||
261 | 275 | @classmethod | 271 | @classmethod |
262 | 276 | def testSetUp(cls): | 272 | def testSetUp(cls): |
263 | 277 | """Fixture replicating the process-mail.py environment. | 273 | """Fixture replicating the process-mail.py environment. |
264 | @@ -287,7 +283,7 @@ | |||
265 | 287 | """Tear down the test fixture.""" | 283 | """Tear down the test fixture.""" |
266 | 288 | setSecurityPolicy(cls._old_policy) | 284 | setSecurityPolicy(cls._old_policy) |
267 | 289 | 285 | ||
269 | 290 | doctests_without_logging = [ | 286 | doctests = [ |
270 | 291 | # XXX gary 2008-12-06 bug=305856: Spurious test failure | 287 | # XXX gary 2008-12-06 bug=305856: Spurious test failure |
271 | 292 | # discovered on buildbot, build 40. Note that, to completely | 288 | # discovered on buildbot, build 40. Note that, to completely |
272 | 293 | # disable the test from running, the filename has been changed | 289 | # disable the test from running, the filename has been changed |
273 | @@ -299,21 +295,16 @@ | |||
274 | 299 | '../doc/emailauthentication.txt', | 295 | '../doc/emailauthentication.txt', |
275 | 300 | ] | 296 | ] |
276 | 301 | 297 | ||
277 | 302 | doctests_with_logging = [ | ||
278 | 303 | '../doc/incomingmail.txt', | ||
279 | 304 | ] | ||
280 | 305 | |||
281 | 306 | @classmethod | 298 | @classmethod |
282 | 307 | def addTestsToSpecial(cls): | 299 | def addTestsToSpecial(cls): |
283 | 308 | """Adds all the tests related to process-mail.py to special""" | 300 | """Adds all the tests related to process-mail.py to special""" |
292 | 309 | for filepath in cls.doctests_without_logging: | 301 | for filepath in cls.doctests: |
293 | 310 | filename = os.path.basename(filepath) | 302 | filename = os.path.basename(filepath) |
294 | 311 | special[filename] = cls.createLayeredDocFileSuite(filepath) | 303 | special[filename] = LayeredDocFileSuite( |
295 | 312 | 304 | filepath, | |
296 | 313 | for filepath in cls.doctests_with_logging: | 305 | setUp=setUp, tearDown=tearDown, |
297 | 314 | filename = os.path.basename(filepath) | 306 | layer=cls, |
298 | 315 | special[filename] = cls.createLayeredDocFileSuite( | 307 | stdout_logging=False) |
291 | 316 | filepath, stdout_logging=True) | ||
299 | 317 | 308 | ||
300 | 318 | # Adds a copy of some bug doctests that will be run with | 309 | # Adds a copy of some bug doctests that will be run with |
301 | 319 | # the processmail user. | 310 | # the processmail user. |
302 | @@ -337,16 +328,6 @@ | |||
303 | 337 | layer=cls, | 328 | layer=cls, |
304 | 338 | stdout_logging=False) | 329 | stdout_logging=False) |
305 | 339 | 330 | ||
306 | 340 | @classmethod | ||
307 | 341 | def createLayeredDocFileSuite(cls, filename, stdout_logging=False): | ||
308 | 342 | """Helper to create a doctest using this layer.""" | ||
309 | 343 | return LayeredDocFileSuite( | ||
310 | 344 | filename, | ||
311 | 345 | setUp=setUp, tearDown=tearDown, | ||
312 | 346 | layer=cls, | ||
313 | 347 | stdout_logging=stdout_logging, | ||
314 | 348 | stdout_logging_level=logging.WARNING) | ||
315 | 349 | |||
316 | 350 | 331 | ||
317 | 351 | ProcessMailLayer.addTestsToSpecial() | 332 | ProcessMailLayer.addTestsToSpecial() |
318 | 352 | 333 | ||
319 | @@ -360,15 +341,13 @@ | |||
320 | 360 | suite.addTest(special_suite) | 341 | suite.addTest(special_suite) |
321 | 361 | 342 | ||
322 | 362 | testsdir = os.path.abspath( | 343 | testsdir = os.path.abspath( |
325 | 363 | os.path.normpath(os.path.join(here, '..', 'doc')) | 344 | os.path.normpath(os.path.join(here, '..', 'doc'))) |
324 | 364 | ) | ||
326 | 365 | 345 | ||
327 | 366 | # Add tests using default setup/teardown | 346 | # Add tests using default setup/teardown |
328 | 367 | filenames = [filename | 347 | filenames = [filename |
329 | 368 | for filename in os.listdir(testsdir) | 348 | for filename in os.listdir(testsdir) |
330 | 369 | if filename.lower().endswith('.txt') | 349 | if filename.lower().endswith('.txt') |
333 | 370 | and filename not in special | 350 | and filename not in special] |
332 | 371 | ] | ||
334 | 372 | # Sort the list to give a predictable order. We do this because when | 351 | # Sort the list to give a predictable order. We do this because when |
335 | 373 | # tests interfere with each other, the varying orderings that os.listdir | 352 | # tests interfere with each other, the varying orderings that os.listdir |
336 | 374 | # gives on different people's systems make reproducing and debugging | 353 | # gives on different people's systems make reproducing and debugging |
337 | @@ -383,8 +362,7 @@ | |||
338 | 383 | layer=LaunchpadFunctionalLayer, | 362 | layer=LaunchpadFunctionalLayer, |
339 | 384 | # 'icky way of running doctests with __future__ imports | 363 | # 'icky way of running doctests with __future__ imports |
340 | 385 | globs={'with_statement': with_statement}, | 364 | globs={'with_statement': with_statement}, |
343 | 386 | stdout_logging_level=logging.WARNING | 365 | stdout_logging_level=logging.WARNING) |
342 | 387 | ) | ||
344 | 388 | suite.addTest(one_test) | 366 | suite.addTest(one_test) |
345 | 389 | 367 | ||
346 | 390 | return suite | 368 | return suite |
347 | 391 | 369 | ||
348 | === modified file 'lib/canonical/launchpad/mail/handlers.py' | |||
349 | --- lib/canonical/launchpad/mail/handlers.py 2010-10-03 15:30:06 +0000 | |||
350 | +++ lib/canonical/launchpad/mail/handlers.py 2010-10-14 20:59:59 +0000 | |||
351 | @@ -61,7 +61,7 @@ | |||
352 | 61 | 61 | ||
353 | 62 | def extract_signature_timestamp(signed_msg): | 62 | def extract_signature_timestamp(signed_msg): |
354 | 63 | # break import cycle | 63 | # break import cycle |
356 | 64 | from canonical.launchpad.mail.incoming import ( | 64 | from lp.services.mail.incoming import ( |
357 | 65 | canonicalise_line_endings) | 65 | canonicalise_line_endings) |
358 | 66 | signature = getUtility(IGPGHandler).getVerifiedSignature( | 66 | signature = getUtility(IGPGHandler).getVerifiedSignature( |
359 | 67 | canonicalise_line_endings(signed_msg.signedContent), | 67 | canonicalise_line_endings(signed_msg.signedContent), |
360 | 68 | 68 | ||
361 | === modified file 'lib/lp/blueprints/doc/spec-mail-exploder.txt' | |||
362 | --- lib/lp/blueprints/doc/spec-mail-exploder.txt 2010-10-14 20:59:48 +0000 | |||
363 | +++ lib/lp/blueprints/doc/spec-mail-exploder.txt 2010-10-14 20:59:59 +0000 | |||
364 | @@ -269,7 +269,7 @@ | |||
365 | 269 | >>> moin_change['Sender'] = 'webmaster@ubuntu.com' | 269 | >>> moin_change['Sender'] = 'webmaster@ubuntu.com' |
366 | 270 | 270 | ||
367 | 271 | >>> from lp.services.mail.sendmail import sendmail | 271 | >>> from lp.services.mail.sendmail import sendmail |
369 | 272 | >>> from canonical.launchpad.mail.incoming import handleMail | 272 | >>> from lp.services.mail.incoming import handleMail |
370 | 273 | >>> sendmail(moin_change, bulk=False) | 273 | >>> sendmail(moin_change, bulk=False) |
371 | 274 | '...' | 274 | '...' |
372 | 275 | 275 | ||
373 | 276 | 276 | ||
374 | === renamed file 'lib/canonical/launchpad/mail/incoming.py' => 'lib/lp/services/mail/incoming.py' | |||
375 | --- lib/canonical/launchpad/mail/incoming.py 2010-10-14 20:59:48 +0000 | |||
376 | +++ lib/lp/services/mail/incoming.py 2010-10-14 20:59:59 +0000 | |||
377 | @@ -325,7 +325,7 @@ | |||
378 | 325 | mail = signed_message_from_string(raw_mail) | 325 | mail = signed_message_from_string(raw_mail) |
379 | 326 | except email.Errors.MessageError, error: | 326 | except email.Errors.MessageError, error: |
380 | 327 | mailbox.delete(mail_id) | 327 | mailbox.delete(mail_id) |
382 | 328 | log = logging.getLogger('canonical.launchpad.mail') | 328 | log = logging.getLogger('lp.services.mail') |
383 | 329 | log.warn( | 329 | log.warn( |
384 | 330 | "Couldn't convert email to email.Message: %s" % ( | 330 | "Couldn't convert email to email.Message: %s" % ( |
385 | 331 | file_alias_url, ), | 331 | file_alias_url, ), |
386 | @@ -369,7 +369,7 @@ | |||
387 | 369 | if 'X-Launchpad-Original-To' in mail: | 369 | if 'X-Launchpad-Original-To' in mail: |
388 | 370 | addresses = [mail['X-Launchpad-Original-To']] | 370 | addresses = [mail['X-Launchpad-Original-To']] |
389 | 371 | else: | 371 | else: |
391 | 372 | log = logging.getLogger('canonical.launchpad.mail') | 372 | log = logging.getLogger('lp.services.mail') |
392 | 373 | log.warn( | 373 | log.warn( |
393 | 374 | "No X-Launchpad-Original-To header was present " | 374 | "No X-Launchpad-Original-To header was present " |
394 | 375 | "in email: %s" % | 375 | "in email: %s" % |
395 | 376 | 376 | ||
396 | === renamed file 'lib/canonical/launchpad/doc/incomingmail.txt' => 'lib/lp/services/mail/tests/incomingmail.txt' | |||
397 | --- lib/canonical/launchpad/doc/incomingmail.txt 2010-10-14 20:59:48 +0000 | |||
398 | +++ lib/lp/services/mail/tests/incomingmail.txt 2010-10-14 20:59:59 +0000 | |||
399 | @@ -4,7 +4,7 @@ | |||
400 | 4 | When an email is sent to Launchpad we need to handle it somehow. This | 4 | When an email is sent to Launchpad we need to handle it somehow. This |
401 | 5 | is done by handleEmails: | 5 | is done by handleEmails: |
402 | 6 | 6 | ||
404 | 7 | >>> from canonical.launchpad.mail.incoming import handleMail | 7 | >>> from lp.services.mail.incoming import handleMail |
405 | 8 | 8 | ||
406 | 9 | Basically what it does is to open the Launchpad mail box, and for each | 9 | Basically what it does is to open the Launchpad mail box, and for each |
407 | 10 | message it: | 10 | message it: |
408 | @@ -88,10 +88,10 @@ | |||
409 | 88 | header is missing.) | 88 | header is missing.) |
410 | 89 | 89 | ||
411 | 90 | >>> handleMail(zopeless_transaction) | 90 | >>> handleMail(zopeless_transaction) |
416 | 91 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | 91 | WARNING:lp.services.mail:No X-Launchpad-Original-To header was present ... |
417 | 92 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | 92 | WARNING:lp.services.mail:No X-Launchpad-Original-To header was present ... |
418 | 93 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | 93 | WARNING:lp.services.mail:No X-Launchpad-Original-To header was present ... |
419 | 94 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | 94 | WARNING:lp.services.mail:No X-Launchpad-Original-To header was present ... |
420 | 95 | 95 | ||
421 | 96 | Now we can see that each handler handled the emails sent to its domain: | 96 | Now we can see that each handler handled the emails sent to its domain: |
422 | 97 | 97 | ||
423 | @@ -389,7 +389,7 @@ | |||
424 | 389 | >>> len(stub.test_emails) | 389 | >>> len(stub.test_emails) |
425 | 390 | 2 | 390 | 2 |
426 | 391 | 391 | ||
428 | 392 | >>> handleMail(transaction) | 392 | >>> handleMail(zopeless_transaction) |
429 | 393 | ERROR:...:Upload to Librarian failed... | 393 | ERROR:...:Upload to Librarian failed... |
430 | 394 | ... | 394 | ... |
431 | 395 | UploadFailed: ...Connection refused... | 395 | UploadFailed: ...Connection refused... |
432 | 396 | 396 | ||
433 | === modified file 'lib/lp/services/mail/tests/test_dkim.py' | |||
434 | --- lib/lp/services/mail/tests/test_dkim.py 2010-08-20 20:31:18 +0000 | |||
435 | +++ lib/lp/services/mail/tests/test_dkim.py 2010-10-14 20:59:59 +0000 | |||
436 | @@ -11,14 +11,11 @@ | |||
437 | 11 | 11 | ||
438 | 12 | import dkim | 12 | import dkim |
439 | 13 | import dns.resolver | 13 | import dns.resolver |
440 | 14 | from zope.component import getUtility | ||
441 | 15 | 14 | ||
442 | 16 | from canonical.launchpad.interfaces.mail import IWeaklyAuthenticatedPrincipal | 15 | from canonical.launchpad.interfaces.mail import IWeaklyAuthenticatedPrincipal |
448 | 17 | from canonical.launchpad.mail import ( | 16 | from canonical.launchpad.mail import signed_message_from_string |
449 | 18 | incoming, | 17 | from lp.services.mail import incoming |
450 | 19 | signed_message_from_string, | 18 | from lp.services.mail.incoming import authenticateEmail |
446 | 20 | ) | ||
447 | 21 | from canonical.launchpad.mail.incoming import authenticateEmail | ||
451 | 22 | from canonical.testing.layers import DatabaseFunctionalLayer | 19 | from canonical.testing.layers import DatabaseFunctionalLayer |
452 | 23 | from lp.testing import TestCaseWithFactory | 20 | from lp.testing import TestCaseWithFactory |
453 | 24 | 21 | ||
454 | @@ -77,33 +74,36 @@ | |||
455 | 77 | 74 | ||
456 | 78 | def fake_signing(self, plain_message, canonicalize=None): | 75 | def fake_signing(self, plain_message, canonicalize=None): |
457 | 79 | if canonicalize is None: | 76 | if canonicalize is None: |
459 | 80 | canonicalize = (dkim.Relaxed, dkim.Relaxed) | 77 | canonicalize = (dkim.Relaxed, dkim.Relaxed) |
460 | 81 | dkim_line = dkim.sign(plain_message, | 78 | dkim_line = dkim.sign(plain_message, |
462 | 82 | selector='example', | 79 | selector='example', |
463 | 83 | domain='canonical.com', | 80 | domain='canonical.com', |
464 | 84 | privkey=sample_privkey, | 81 | privkey=sample_privkey, |
465 | 85 | debuglog=self._log_output, | 82 | debuglog=self._log_output, |
468 | 86 | canonicalize=canonicalize | 83 | canonicalize=canonicalize) |
467 | 87 | ) | ||
469 | 88 | assert dkim_line[-1] == '\n' | 84 | assert dkim_line[-1] == '\n' |
470 | 89 | return dkim_line + plain_message | 85 | return dkim_line + plain_message |
471 | 90 | 86 | ||
472 | 91 | def monkeypatch_dns(self): | 87 | def monkeypatch_dns(self): |
473 | 92 | self._dns_responses = {} | 88 | self._dns_responses = {} |
474 | 89 | |||
475 | 93 | def my_lookup(name): | 90 | def my_lookup(name): |
476 | 94 | try: | 91 | try: |
477 | 95 | return self._dns_responses[name] | 92 | return self._dns_responses[name] |
478 | 96 | except KeyError: | 93 | except KeyError: |
479 | 97 | raise dns.resolver.NXDOMAIN() | 94 | raise dns.resolver.NXDOMAIN() |
480 | 95 | |||
481 | 98 | orig_dnstxt = dkim.dnstxt | 96 | orig_dnstxt = dkim.dnstxt |
482 | 99 | dkim.dnstxt = my_lookup | 97 | dkim.dnstxt = my_lookup |
483 | 98 | |||
484 | 100 | def restore(): | 99 | def restore(): |
485 | 101 | dkim.dnstxt = orig_dnstxt | 100 | dkim.dnstxt = orig_dnstxt |
486 | 101 | |||
487 | 102 | self.addCleanup(restore) | 102 | self.addCleanup(restore) |
488 | 103 | 103 | ||
489 | 104 | def get_dkim_log(self): | 104 | def get_dkim_log(self): |
490 | 105 | return self._log_output.getvalue() | 105 | return self._log_output.getvalue() |
492 | 106 | 106 | ||
493 | 107 | def assertStronglyAuthenticated(self, principal, signed_message): | 107 | def assertStronglyAuthenticated(self, principal, signed_message): |
494 | 108 | if IWeaklyAuthenticatedPrincipal.providedBy(principal): | 108 | if IWeaklyAuthenticatedPrincipal.providedBy(principal): |
495 | 109 | self.fail('expected strong authentication; got weak:\n' | 109 | self.fail('expected strong authentication; got weak:\n' |
496 | @@ -157,8 +157,10 @@ | |||
497 | 157 | self._dns_responses['example._domainkey.canonical.com.'] = \ | 157 | self._dns_responses['example._domainkey.canonical.com.'] = \ |
498 | 158 | sample_dns | 158 | sample_dns |
499 | 159 | saved_domains = incoming._trusted_dkim_domains[:] | 159 | saved_domains = incoming._trusted_dkim_domains[:] |
500 | 160 | |||
501 | 160 | def restore(): | 161 | def restore(): |
502 | 161 | incoming._trusted_dkim_domains = saved_domains | 162 | incoming._trusted_dkim_domains = saved_domains |
503 | 163 | |||
504 | 162 | self.addCleanup(restore) | 164 | self.addCleanup(restore) |
505 | 163 | incoming._trusted_dkim_domains = [] | 165 | incoming._trusted_dkim_domains = [] |
506 | 164 | principal = authenticateEmail( | 166 | principal = authenticateEmail( |
507 | @@ -185,14 +187,15 @@ | |||
508 | 185 | 'steve.alexander@ubuntulinux.com') | 187 | 'steve.alexander@ubuntulinux.com') |
509 | 186 | 188 | ||
510 | 187 | def test_dkim_changed_from_address(self): | 189 | def test_dkim_changed_from_address(self): |
515 | 188 | # if the address part of the message has changed, it's detected. we | 190 | # If the address part of the message has changed, it's detected. |
516 | 189 | # still treat this as weakly authenticated by the purported From-header | 191 | # We still treat this as weakly authenticated by the purported |
517 | 190 | # sender, though perhaps in future we would prefer to reject these | 192 | # From-header sender, though perhaps in future we would prefer |
518 | 191 | # messages. | 193 | # to reject these messages. |
519 | 192 | signed_message = self.fake_signing(plain_content) | 194 | signed_message = self.fake_signing(plain_content) |
520 | 193 | self._dns_responses['example._domainkey.canonical.com.'] = \ | 195 | self._dns_responses['example._domainkey.canonical.com.'] = \ |
521 | 194 | sample_dns | 196 | sample_dns |
523 | 195 | fiddled_message = signed_message.replace('From: Foo Bar <foo.bar@canonical.com>', | 197 | fiddled_message = signed_message.replace( |
524 | 198 | 'From: Foo Bar <foo.bar@canonical.com>', | ||
525 | 196 | 'From: Carlos <carlos@canonical.com>') | 199 | 'From: Carlos <carlos@canonical.com>') |
526 | 197 | principal = authenticateEmail( | 200 | principal = authenticateEmail( |
527 | 198 | signed_message_from_string(fiddled_message)) | 201 | signed_message_from_string(fiddled_message)) |
528 | @@ -202,22 +205,23 @@ | |||
529 | 202 | 'carlos@canonical.com') | 205 | 'carlos@canonical.com') |
530 | 203 | 206 | ||
531 | 204 | def test_dkim_changed_from_realname(self): | 207 | def test_dkim_changed_from_realname(self): |
533 | 205 | # if the real name part of the message has changed, it's detected | 208 | # If the real name part of the message has changed, it's detected. |
534 | 206 | signed_message = self.fake_signing(plain_content) | 209 | signed_message = self.fake_signing(plain_content) |
535 | 207 | self._dns_responses['example._domainkey.canonical.com.'] = \ | 210 | self._dns_responses['example._domainkey.canonical.com.'] = \ |
536 | 208 | sample_dns | 211 | sample_dns |
538 | 209 | fiddled_message = signed_message.replace('From: Foo Bar <foo.bar@canonical.com>', | 212 | fiddled_message = signed_message.replace( |
539 | 213 | 'From: Foo Bar <foo.bar@canonical.com>', | ||
540 | 210 | 'From: Evil Foo <foo.bar@canonical.com>') | 214 | 'From: Evil Foo <foo.bar@canonical.com>') |
541 | 211 | principal = authenticateEmail( | 215 | principal = authenticateEmail( |
542 | 212 | signed_message_from_string(fiddled_message)) | 216 | signed_message_from_string(fiddled_message)) |
544 | 213 | # we don't care about the real name for determining the principal | 217 | # We don't care about the real name for determining the principal. |
545 | 214 | self.assertWeaklyAuthenticated(principal, fiddled_message) | 218 | self.assertWeaklyAuthenticated(principal, fiddled_message) |
546 | 215 | self.assertEqual(principal.person.preferredemail.email, | 219 | self.assertEqual(principal.person.preferredemail.email, |
547 | 216 | 'foo.bar@canonical.com') | 220 | 'foo.bar@canonical.com') |
548 | 217 | 221 | ||
549 | 218 | def test_dkim_nxdomain(self): | 222 | def test_dkim_nxdomain(self): |
552 | 219 | # if there's no DNS entry for the pubkey | 223 | # If there's no DNS entry for the pubkey it should be handled |
553 | 220 | # it should be handled decently | 224 | # decently. |
554 | 221 | signed_message = self.fake_signing(plain_content) | 225 | signed_message = self.fake_signing(plain_content) |
555 | 222 | principal = authenticateEmail( | 226 | principal = authenticateEmail( |
556 | 223 | signed_message_from_string(signed_message)) | 227 | signed_message_from_string(signed_message)) |
557 | @@ -226,18 +230,19 @@ | |||
558 | 226 | 'foo.bar@canonical.com') | 230 | 'foo.bar@canonical.com') |
559 | 227 | 231 | ||
560 | 228 | def test_dkim_message_unsigned(self): | 232 | def test_dkim_message_unsigned(self): |
562 | 229 | # degenerate case: no signature treated as weakly authenticated | 233 | # This is a degenerate case: a message with no signature is |
563 | 234 | # treated as weakly authenticated. | ||
564 | 235 | # The library doesn't log anything if there's no header at all. | ||
565 | 230 | principal = authenticateEmail( | 236 | principal = authenticateEmail( |
566 | 231 | signed_message_from_string(plain_content)) | 237 | signed_message_from_string(plain_content)) |
567 | 232 | self.assertWeaklyAuthenticated(principal, plain_content) | 238 | self.assertWeaklyAuthenticated(principal, plain_content) |
568 | 233 | self.assertEqual(principal.person.preferredemail.email, | 239 | self.assertEqual(principal.person.preferredemail.email, |
569 | 234 | 'foo.bar@canonical.com') | 240 | 'foo.bar@canonical.com') |
570 | 235 | # the library doesn't log anything if there's no header at all | ||
571 | 236 | 241 | ||
572 | 237 | def test_dkim_body_mismatch(self): | 242 | def test_dkim_body_mismatch(self): |
576 | 238 | # The message message has a syntactically valid DKIM signature that | 243 | # The message has a syntactically valid DKIM signature that |
577 | 239 | # doesn't actually correspond to what was signed. We log something about | 244 | # doesn't actually correspond to what was signed. We log |
578 | 240 | # this but we don't want to drop the message. | 245 | # something about this but we don't want to drop the message. |
579 | 241 | signed_message = self.fake_signing(plain_content) | 246 | signed_message = self.fake_signing(plain_content) |
580 | 242 | signed_message += 'blah blah' | 247 | signed_message += 'blah blah' |
581 | 243 | self._dns_responses['example._domainkey.canonical.com.'] = \ | 248 | self._dns_responses['example._domainkey.canonical.com.'] = \ |
582 | 244 | 249 | ||
583 | === renamed file 'lib/canonical/launchpad/mail/tests/test_incoming.py' => 'lib/lp/services/mail/tests/test_incoming.py' | |||
584 | --- lib/canonical/launchpad/mail/tests/test_incoming.py 2010-10-04 19:50:45 +0000 | |||
585 | +++ lib/lp/services/mail/tests/test_incoming.py 2010-10-14 20:59:59 +0000 | |||
586 | @@ -2,13 +2,19 @@ | |||
587 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
588 | 3 | 3 | ||
589 | 4 | from doctest import DocTestSuite | 4 | from doctest import DocTestSuite |
590 | 5 | import logging | ||
591 | 5 | import os | 6 | import os |
592 | 6 | import unittest | 7 | import unittest |
593 | 7 | 8 | ||
594 | 8 | import transaction | 9 | import transaction |
595 | 9 | 10 | ||
596 | 11 | from zope.security.management import setSecurityPolicy | ||
597 | 12 | |||
598 | 13 | from canonical.config import config | ||
599 | 10 | from canonical.launchpad.mail.ftests.helpers import testmails_path | 14 | from canonical.launchpad.mail.ftests.helpers import testmails_path |
601 | 11 | from canonical.launchpad.mail.incoming import ( | 15 | from canonical.launchpad.testing.systemdocs import LayeredDocFileSuite |
602 | 16 | from canonical.launchpad.webapp.authorization import LaunchpadSecurityPolicy | ||
603 | 17 | from lp.services.mail.incoming import ( | ||
604 | 12 | handleMail, | 18 | handleMail, |
605 | 13 | MailErrorUtility, | 19 | MailErrorUtility, |
606 | 14 | ) | 20 | ) |
607 | @@ -77,10 +83,26 @@ | |||
608 | 77 | self.assertEqual(old_oops.id, current_oops.id) | 83 | self.assertEqual(old_oops.id, current_oops.id) |
609 | 78 | 84 | ||
610 | 79 | 85 | ||
611 | 86 | def setUp(test): | ||
612 | 87 | test._old_policy = setSecurityPolicy(LaunchpadSecurityPolicy) | ||
613 | 88 | LaunchpadZopelessLayer.switchDbUser(config.processmail.dbuser) | ||
614 | 89 | |||
615 | 90 | |||
616 | 91 | def tearDown(test): | ||
617 | 92 | setSecurityPolicy(test._old_policy) | ||
618 | 93 | |||
619 | 94 | |||
620 | 80 | def test_suite(): | 95 | def test_suite(): |
621 | 81 | suite = unittest.TestSuite() | 96 | suite = unittest.TestSuite() |
622 | 82 | suite.addTest(unittest.TestLoader().loadTestsFromName(__name__)) | 97 | suite.addTest(unittest.TestLoader().loadTestsFromName(__name__)) |
624 | 83 | suite.addTest(DocTestSuite('canonical.launchpad.mail.incoming')) | 98 | suite.addTest(DocTestSuite('lp.services.mail.incoming')) |
625 | 99 | suite.addTest( | ||
626 | 100 | LayeredDocFileSuite( | ||
627 | 101 | 'incomingmail.txt', | ||
628 | 102 | setUp=setUp, | ||
629 | 103 | tearDown=tearDown, | ||
630 | 104 | layer=LaunchpadZopelessLayer, | ||
631 | 105 | stdout_logging_level=logging.WARNING)) | ||
632 | 84 | return suite | 106 | return suite |
633 | 85 | 107 | ||
634 | 86 | 108 | ||
635 | 87 | 109 | ||
636 | === modified file 'lib/lp/services/mail/tests/test_signedmessage.py' | |||
637 | --- lib/lp/services/mail/tests/test_signedmessage.py 2010-08-20 20:31:18 +0000 | |||
638 | +++ lib/lp/services/mail/tests/test_signedmessage.py 2010-10-14 20:59:59 +0000 | |||
639 | @@ -25,18 +25,18 @@ | |||
640 | 25 | from canonical.launchpad.interfaces.gpghandler import IGPGHandler | 25 | from canonical.launchpad.interfaces.gpghandler import IGPGHandler |
641 | 26 | from canonical.launchpad.interfaces.mail import IWeaklyAuthenticatedPrincipal | 26 | from canonical.launchpad.interfaces.mail import IWeaklyAuthenticatedPrincipal |
642 | 27 | from canonical.launchpad.mail import signed_message_from_string | 27 | from canonical.launchpad.mail import signed_message_from_string |
644 | 28 | from canonical.launchpad.mail.incoming import ( | 28 | from canonical.testing.layers import DatabaseFunctionalLayer |
645 | 29 | from lp.registry.interfaces.person import IPersonSet | ||
646 | 30 | from lp.services.mail.incoming import ( | ||
647 | 29 | authenticateEmail, | 31 | authenticateEmail, |
648 | 30 | canonicalise_line_endings, | 32 | canonicalise_line_endings, |
649 | 31 | ) | 33 | ) |
650 | 32 | from canonical.testing.layers import DatabaseFunctionalLayer | ||
651 | 33 | from lp.registry.interfaces.person import IPersonSet | ||
652 | 34 | from lp.testing import TestCaseWithFactory | 34 | from lp.testing import TestCaseWithFactory |
653 | 35 | from lp.testing.factory import GPGSigningContext | 35 | from lp.testing.factory import GPGSigningContext |
654 | 36 | 36 | ||
655 | 37 | 37 | ||
656 | 38 | class TestSignedMessage(TestCaseWithFactory): | 38 | class TestSignedMessage(TestCaseWithFactory): |
658 | 39 | """Test SignedMessage class correctly extracts and verifies the GPG signatures.""" | 39 | "Test SignedMessage class correctly extracts and verifies GPG signatures." |
659 | 40 | 40 | ||
660 | 41 | layer = DatabaseFunctionalLayer | 41 | layer = DatabaseFunctionalLayer |
661 | 42 | 42 | ||
662 | @@ -154,4 +154,3 @@ | |||
663 | 154 | 154 | ||
664 | 155 | def test_suite(): | 155 | def test_suite(): |
665 | 156 | return unittest.TestLoader().loadTestsFromName(__name__) | 156 | return unittest.TestLoader().loadTestsFromName(__name__) |
666 | 157 |
Good progress. Approved with minor changes discussed on IRC: reword "no signature is treated" and fix "message message" typo.