1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
from doctest import DocTestSuite
import time
import unittest
from zope.interface import (
directlyProvidedBy,
directlyProvides,
)
from canonical.launchpad.webapp.interaction import get_current_principal
from canonical.testing.layers import DatabaseFunctionalLayer
from canonical.launchpad.helpers import get_contact_email_addresses
from lp.services.mail.helpers import (
ensure_not_weakly_authenticated,
ensure_sane_signature_timestamp,
get_person_or_team,
IncomingEmailError,
parse_commands,
)
from lp.services.mail.interfaces import (
EmailProcessingError,
IWeaklyAuthenticatedPrincipal,
)
from lp.testing import (
login_person,
TestCase,
TestCaseWithFactory,
)
class TestParseCommands(TestCase):
"""Test the ParseCommands function."""
def test_parse_commandsEmpty(self):
"""Empty messages have no commands."""
self.assertEqual([], parse_commands('', ['command']))
def test_parse_commandsNoIndent(self):
"""Commands with no indent are not commands."""
self.assertEqual([], parse_commands('command', ['command']))
def test_parse_commandsSpaceIndent(self):
"""Commands indented with spaces are recognized."""
self.assertEqual(
[('command', [])], parse_commands(' command', ['command']))
def test_parse_commands_args(self):
"""Commands indented with spaces are recognized."""
self.assertEqual(
[('command', ['arg1', 'arg2'])],
parse_commands(' command arg1 arg2', ['command']))
def test_parse_commands_args_quoted(self):
"""Commands indented with spaces are recognized."""
self.assertEqual(
[('command', ['"arg1', 'arg2"'])],
parse_commands(' command "arg1 arg2"', ['command']))
def test_parse_commandsTabIndent(self):
"""Commands indented with tabs are recognized.
(Tabs? What are we, make?)
"""
self.assertEqual(
[('command', [])], parse_commands('\tcommand', ['command']))
def test_parse_commandsDone(self):
"""The 'done' pseudo-command halts processing."""
self.assertEqual(
[('command', []), ('command', [])],
parse_commands(' command\n command', ['command']))
self.assertEqual(
[('command', [])],
parse_commands(' command\n done\n command', ['command']))
# done takes no arguments.
self.assertEqual(
[('command', []), ('command', [])],
parse_commands(' command\n done commands\n command', ['command']))
def test_parse_commands_optional_colons(self):
"""Colons at the end of commands are accepted and stripped."""
self.assertEqual(
[('command', ['arg1', 'arg2'])],
parse_commands(' command: arg1 arg2', ['command']))
self.assertEqual(
[('command', [])],
parse_commands(' command:', ['command']))
class TestEnsureSaneSignatureTimestamp(unittest.TestCase):
"""Tests for ensure_sane_signature_timestamp"""
def test_too_old_timestamp(self):
# signature timestamps shouldn't be too old
now = time.time()
one_week = 60 * 60 * 24 * 7
self.assertRaises(
IncomingEmailError, ensure_sane_signature_timestamp,
now - one_week, 'bug report')
def test_future_timestamp(self):
# signature timestamps shouldn't be (far) in the future
now = time.time()
one_week = 60 * 60 * 24 * 7
self.assertRaises(
IncomingEmailError, ensure_sane_signature_timestamp,
now + one_week, 'bug report')
def test_near_future_timestamp(self):
# signature timestamps in the near future are OK
now = time.time()
one_minute = 60
# this should not raise an exception
ensure_sane_signature_timestamp(now + one_minute, 'bug report')
def test_recent_timestamp(self):
# signature timestamps in the recent past are OK
now = time.time()
one_hour = 60 * 60
# this should not raise an exception
ensure_sane_signature_timestamp(now - one_hour, 'bug report')
class TestEnsureNotWeaklyAuthenticated(TestCaseWithFactory):
"""Test the ensure_not_weakly_authenticated function."""
layer = DatabaseFunctionalLayer
def setUp(self):
TestCaseWithFactory.setUp(self, 'test@canonical.com')
self.eric = self.factory.makePerson(name='eric')
login_person(self.eric)
def test_normal_user(self):
# If the current principal doesn't provide
# IWeaklyAuthenticatedPrincipal, then we are good.
signed_msg = self.factory.makeSignedMessage()
ensure_not_weakly_authenticated(signed_msg, 'test case')
def _setWeakPrincipal(self):
# Get the current principal to provide IWeaklyAuthenticatedPrincipal
# this is set when the message is unsigned or the signature doesn't
# match a key that the person has.
cur_principal = get_current_principal()
directlyProvides(
cur_principal, directlyProvidedBy(cur_principal),
IWeaklyAuthenticatedPrincipal)
def test_weakly_authenticated_no_sig(self):
signed_msg = self.factory.makeSignedMessage()
self.assertIs(None, signed_msg.signature)
self._setWeakPrincipal()
error = self.assertRaises(
IncomingEmailError,
ensure_not_weakly_authenticated,
signed_msg, 'test')
self.assertEqual(
"The message you sent included commands to modify the test,\n"
"but you didn't sign the message with your OpenPGP key.\n",
error.message)
def test_weakly_authenticated_with_sig(self):
signed_msg = self.factory.makeSignedMessage()
signed_msg.signature = 'fakesig'
self._setWeakPrincipal()
error = self.assertRaises(
IncomingEmailError,
ensure_not_weakly_authenticated,
signed_msg, 'test')
self.assertEqual(
"The message you sent included commands to modify the test,\n"
"but your OpenPGP key isn't imported into Launchpad. "
"Please go to\n"
"http://launchpad.dev/~eric/+editpgpkeys to import your key.\n",
error.message)
class TestGetPersonOrTeam(TestCaseWithFactory):
"""Test the get_person_or_team function."""
layer = DatabaseFunctionalLayer
def setUp(self):
TestCaseWithFactory.setUp(self, 'test@canonical.com')
def test_by_name(self):
# The user's launchpad name can be used to get them.
eric = self.factory.makePerson(name="eric")
self.assertEqual(eric, get_person_or_team('eric'))
def test_by_email(self):
# The user's launchpad name can be used to get them.
eric = self.factory.makePerson(email="eric@example.com")
self.assertEqual(eric, get_person_or_team('eric@example.com'))
def test_not_found(self):
# An unknown user raises an EmailProcessingError.
error = self.assertRaises(
EmailProcessingError,
get_person_or_team,
'unknown-user')
self.assertEqual(
"There's no such person with the specified name or email: "
"unknown-user\n", str(error))
def test_team_by_name(self):
# A team can also be gotten by name.
owner = self.factory.makePerson()
team = self.factory.makeTeam(owner=owner, name='fooix-devs')
self.assertEqual(team, get_person_or_team('fooix-devs'))
def test_team_by_email(self):
# The team's contact email address can also be used to get the team.
owner = self.factory.makePerson()
team = self.factory.makeTeam(
owner=owner, email='fooix-devs@lists.example.com')
self.assertEqual(
team, get_person_or_team('fooix-devs@lists.example.com'))
class getContactEmailAddresses(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
def test_user_with_preferredemail(self):
user = self.factory.makePerson(
email='user@canonical.com', name='user',)
result = get_contact_email_addresses(user)
self.assertEqual(set(['user@canonical.com']), result)
def test_suite():
suite = DocTestSuite('lp.services.mail.helpers')
suite.addTests(unittest.TestLoader().loadTestsFromName(__name__))
return suite
|