~sumanah/mailman/mailman

« back to all changes in this revision

Viewing changes to src/mailman/rest/tests/test_moderation.py

  • Committer: Sumana Harihareswara
  • Date: 2015-04-20 01:29:41 UTC
  • mfrom: (7273.2.59 trunk)
  • Revision ID: sumanah@panix.com-20150420012941-wc4y4fjativpdxj3
merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""REST moderation tests."""
19
19
 
20
20
__all__ = [
21
 
    'TestModeration',
 
21
    'TestPostModeration',
 
22
    'TestSubscriptionModeration',
22
23
    ]
23
24
 
24
25
 
25
26
import unittest
26
27
 
27
28
from mailman.app.lifecycle import create_list
28
 
from mailman.app.moderator import hold_message, hold_subscription
29
 
from mailman.config import config
 
29
from mailman.app.moderator import hold_message
30
30
from mailman.database.transaction import transaction
31
 
from mailman.interfaces.member import DeliveryMode
 
31
from mailman.interfaces.mailinglist import SubscriptionPolicy
 
32
from mailman.interfaces.registrar import IRegistrar
 
33
from mailman.interfaces.usermanager import IUserManager
32
34
from mailman.testing.helpers import (
33
 
    call_api, specialized_message_from_string as mfs)
 
35
    call_api, get_queue_messages, specialized_message_from_string as mfs)
34
36
from mailman.testing.layers import RESTLayer
 
37
from mailman.utilities.datetime import now
35
38
from urllib.error import HTTPError
 
39
from zope.component import getUtility
36
40
 
37
41
 
38
42
 
39
 
class TestModeration(unittest.TestCase):
 
43
class TestPostModeration(unittest.TestCase):
40
44
    layer = RESTLayer
41
45
 
42
46
    def setUp(self):
70
74
            call_api('http://localhost:9001/3.0/lists/ant@example.com/held/99')
71
75
        self.assertEqual(cm.exception.code, 404)
72
76
 
73
 
    def test_subscription_request_as_held_message(self):
74
 
        # Provide the request id of a subscription request using the held
75
 
        # message API returns a not-found even though the request id is
76
 
        # in the database.
77
 
        held_id = hold_message(self._mlist, self._msg)
78
 
        subscribe_id = hold_subscription(
79
 
            self._mlist, 'bperson@example.net', 'Bart Person', 'xyz',
80
 
            DeliveryMode.regular, 'en')
81
 
        config.db.store.commit()
82
 
        url = 'http://localhost:9001/3.0/lists/ant@example.com/held/{0}'
83
 
        with self.assertRaises(HTTPError) as cm:
84
 
            call_api(url.format(subscribe_id))
85
 
        self.assertEqual(cm.exception.code, 404)
86
 
        # But using the held_id returns a valid response.
87
 
        response, content = call_api(url.format(held_id))
88
 
        self.assertEqual(response['message_id'], '<alpha>')
89
 
 
90
77
    def test_bad_held_message_action(self):
91
78
        # POSTing to a held message with a bad action.
92
79
        held_id = hold_message(self._mlist, self._msg)
97
84
        self.assertEqual(cm.exception.msg,
98
85
                         b'Cannot convert parameters: action')
99
86
 
100
 
    def test_bad_subscription_request_id(self):
101
 
        # Bad request when request_id is not an integer.
102
 
        with self.assertRaises(HTTPError) as cm:
103
 
            call_api('http://localhost:9001/3.0/lists/ant@example.com/'
104
 
                     'requests/bogus')
105
 
        self.assertEqual(cm.exception.code, 400)
106
 
 
107
 
    def test_missing_subscription_request_id(self):
108
 
        # Bad request when the request_id is not in the database.
109
 
        with self.assertRaises(HTTPError) as cm:
110
 
            call_api('http://localhost:9001/3.0/lists/ant@example.com/'
111
 
                     'requests/99')
112
 
        self.assertEqual(cm.exception.code, 404)
113
 
 
114
 
    def test_bad_subscription_action(self):
115
 
        # POSTing to a held message with a bad action.
116
 
        held_id = hold_subscription(
117
 
            self._mlist, 'cperson@example.net', 'Cris Person', 'xyz',
118
 
            DeliveryMode.regular, 'en')
119
 
        config.db.store.commit()
120
 
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{0}'
121
 
        with self.assertRaises(HTTPError) as cm:
122
 
            call_api(url.format(held_id), {'action': 'bogus'})
123
 
        self.assertEqual(cm.exception.code, 400)
124
 
        self.assertEqual(cm.exception.msg,
125
 
                         b'Cannot convert parameters: action')
126
 
 
127
87
    def test_discard(self):
128
88
        # Discarding a message removes it from the moderation queue.
129
89
        with transaction():
136
96
        with self.assertRaises(HTTPError) as cm:
137
97
            call_api(url, dict(action='discard'))
138
98
        self.assertEqual(cm.exception.code, 404)
 
99
 
 
100
 
 
101
 
 
102
class TestSubscriptionModeration(unittest.TestCase):
 
103
    layer = RESTLayer
 
104
    maxDiff = None
 
105
 
 
106
    def setUp(self):
 
107
        with transaction():
 
108
            self._mlist = create_list('ant@example.com')
 
109
            self._registrar = IRegistrar(self._mlist)
 
110
            manager = getUtility(IUserManager)
 
111
            self._anne = manager.create_address(
 
112
                'anne@example.com', 'Anne Person')
 
113
            self._bart = manager.make_user(
 
114
                'bart@example.com', 'Bart Person')
 
115
            preferred = list(self._bart.addresses)[0]
 
116
            preferred.verified_on = now()
 
117
            self._bart.preferred_address = preferred
 
118
 
 
119
    def test_no_such_list(self):
 
120
        # Try to get the requests of a nonexistent list.
 
121
        with self.assertRaises(HTTPError) as cm:
 
122
            call_api('http://localhost:9001/3.0/lists/bee@example.com/'
 
123
                     'requests')
 
124
        self.assertEqual(cm.exception.code, 404)
 
125
 
 
126
    def test_no_such_subscription_token(self):
 
127
        # Bad request when the token is not in the database.
 
128
        with self.assertRaises(HTTPError) as cm:
 
129
            call_api('http://localhost:9001/3.0/lists/ant@example.com/'
 
130
                     'requests/missing')
 
131
        self.assertEqual(cm.exception.code, 404)
 
132
 
 
133
    def test_bad_subscription_action(self):
 
134
        # POSTing to a held message with a bad action.
 
135
        token, token_owner, member = self._registrar.register(self._anne)
 
136
        # Anne's subscription request got held.
 
137
        self.assertIsNone(member)
 
138
        # Let's try to handle her request, but with a bogus action.
 
139
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
140
        with self.assertRaises(HTTPError) as cm:
 
141
            call_api(url.format(token), dict(
 
142
                action='bogus',
 
143
                ))
 
144
        self.assertEqual(cm.exception.code, 400)
 
145
        self.assertEqual(cm.exception.msg,
 
146
                         b'Cannot convert parameters: action')
 
147
 
 
148
    def test_list_held_requests(self):
 
149
        # We can view all the held requests.
 
150
        with transaction():
 
151
            token_1, token_owner, member = self._registrar.register(self._anne)
 
152
            # Anne's subscription request got held.
 
153
            self.assertIsNotNone(token_1)
 
154
            self.assertIsNone(member)
 
155
            token_2, token_owner, member = self._registrar.register(self._bart)
 
156
            self.assertIsNotNone(token_2)
 
157
            self.assertIsNone(member)
 
158
        content, response = call_api(
 
159
            'http://localhost:9001/3.0/lists/ant@example.com/requests')
 
160
        self.assertEqual(response.status, 200)
 
161
        self.assertEqual(content['total_size'], 2)
 
162
        tokens = set(json['token'] for json in content['entries'])
 
163
        self.assertEqual(tokens, {token_1, token_2})
 
164
        emails = set(json['email'] for json in content['entries'])
 
165
        self.assertEqual(emails, {'anne@example.com', 'bart@example.com'})
 
166
 
 
167
    def test_individual_request(self):
 
168
        # We can view an individual request.
 
169
        with transaction():
 
170
            token, token_owner, member = self._registrar.register(self._anne)
 
171
            # Anne's subscription request got held.
 
172
            self.assertIsNotNone(token)
 
173
            self.assertIsNone(member)
 
174
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
175
        content, response = call_api(url.format(token))
 
176
        self.assertEqual(response.status, 200)
 
177
        self.assertEqual(content['token'], token)
 
178
        self.assertEqual(content['token_owner'], token_owner.name)
 
179
        self.assertEqual(content['email'], 'anne@example.com')
 
180
 
 
181
    def test_accept(self):
 
182
        # POST to the request to accept it.
 
183
        with transaction():
 
184
            token, token_owner, member = self._registrar.register(self._anne)
 
185
        # Anne's subscription request got held.
 
186
        self.assertIsNone(member)
 
187
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
188
        content, response = call_api(url.format(token), dict(
 
189
            action='accept',
 
190
            ))
 
191
        self.assertEqual(response.status, 204)
 
192
        # Anne is a member.
 
193
        self.assertEqual(
 
194
            self._mlist.members.get_member('anne@example.com').address,
 
195
            self._anne)
 
196
        # The request URL no longer exists.
 
197
        with self.assertRaises(HTTPError) as cm:
 
198
            call_api(url.format(token), dict(
 
199
                action='accept',
 
200
                ))
 
201
        self.assertEqual(cm.exception.code, 404)
 
202
 
 
203
    def test_accept_bad_token(self):
 
204
        # Try to accept a request with a bogus token.
 
205
        with self.assertRaises(HTTPError) as cm:
 
206
            call_api('http://localhost:9001/3.0/lists/ant@example.com'
 
207
                     '/requests/bogus',
 
208
                     dict(action='accept'))
 
209
        self.assertEqual(cm.exception.code, 404)
 
210
 
 
211
    def test_accept_by_moderator_clears_request_queue(self):
 
212
        # After accepting a message held for moderator approval, there are no
 
213
        # more requests to handle.
 
214
        #
 
215
        # We start with nothing in the queue.
 
216
        content, response = call_api(
 
217
            'http://localhost:9001/3.0/lists/ant@example.com/requests')
 
218
        self.assertEqual(content['total_size'], 0)
 
219
        # Anne tries to subscribe to a list that only requests moderator
 
220
        # approval.
 
221
        with transaction():
 
222
            self._mlist.subscription_policy = SubscriptionPolicy.moderate
 
223
            token, token_owner, member = self._registrar.register(
 
224
                self._anne,
 
225
                pre_verified=True, pre_confirmed=True)
 
226
        # There's now one request in the queue, and it's waiting on moderator
 
227
        # approval.
 
228
        content, response = call_api(
 
229
            'http://localhost:9001/3.0/lists/ant@example.com/requests')
 
230
        self.assertEqual(content['total_size'], 1)
 
231
        json = content['entries'][0]
 
232
        self.assertEqual(json['token_owner'], 'moderator')
 
233
        self.assertEqual(json['email'], 'anne@example.com')
 
234
        # The moderator approves the request.
 
235
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
236
        content, response = call_api(url.format(token), {'action': 'accept'})
 
237
        self.assertEqual(response.status, 204)
 
238
        # And now the request queue is empty.
 
239
        content, response = call_api(
 
240
            'http://localhost:9001/3.0/lists/ant@example.com/requests')
 
241
        self.assertEqual(content['total_size'], 0)
 
242
 
 
243
    def test_discard(self):
 
244
        # POST to the request to discard it.
 
245
        with transaction():
 
246
            token, token_owner, member = self._registrar.register(self._anne)
 
247
        # Anne's subscription request got held.
 
248
        self.assertIsNone(member)
 
249
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
250
        content, response = call_api(url.format(token), dict(
 
251
            action='discard',
 
252
            ))
 
253
        self.assertEqual(response.status, 204)
 
254
        # Anne is not a member.
 
255
        self.assertIsNone(self._mlist.members.get_member('anne@example.com'))
 
256
        # The request URL no longer exists.
 
257
        with self.assertRaises(HTTPError) as cm:
 
258
            call_api(url.format(token), dict(
 
259
                action='discard',
 
260
                ))
 
261
        self.assertEqual(cm.exception.code, 404)
 
262
 
 
263
    def test_defer(self):
 
264
        # Defer the decision for some other moderator.
 
265
        with transaction():
 
266
            token, token_owner, member = self._registrar.register(self._anne)
 
267
        # Anne's subscription request got held.
 
268
        self.assertIsNone(member)
 
269
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
270
        content, response = call_api(url.format(token), dict(
 
271
            action='defer',
 
272
            ))
 
273
        self.assertEqual(response.status, 204)
 
274
        # Anne is not a member.
 
275
        self.assertIsNone(self._mlist.members.get_member('anne@example.com'))
 
276
        # The request URL still exists.
 
277
        content, response = call_api(url.format(token), dict(
 
278
            action='defer',
 
279
            ))
 
280
        self.assertEqual(response.status, 204)
 
281
        # And now we can accept it.
 
282
        content, response = call_api(url.format(token), dict(
 
283
            action='accept',
 
284
            ))
 
285
        self.assertEqual(response.status, 204)
 
286
        # Anne is a member.
 
287
        self.assertEqual(
 
288
            self._mlist.members.get_member('anne@example.com').address,
 
289
            self._anne)
 
290
        # The request URL no longer exists.
 
291
        with self.assertRaises(HTTPError) as cm:
 
292
            call_api(url.format(token), dict(
 
293
                action='accept',
 
294
                ))
 
295
        self.assertEqual(cm.exception.code, 404)
 
296
 
 
297
    def test_defer_bad_token(self):
 
298
        # Try to accept a request with a bogus token.
 
299
        with self.assertRaises(HTTPError) as cm:
 
300
            call_api('http://localhost:9001/3.0/lists/ant@example.com'
 
301
                     '/requests/bogus',
 
302
                     dict(action='defer'))
 
303
        self.assertEqual(cm.exception.code, 404)
 
304
 
 
305
    def test_reject(self):
 
306
        # POST to the request to reject it.  This leaves a bounce message in
 
307
        # the virgin queue.
 
308
        with transaction():
 
309
            token, token_owner, member = self._registrar.register(self._anne)
 
310
        # Anne's subscription request got held.
 
311
        self.assertIsNone(member)
 
312
        # Clear out the virgin queue, which currently contains the
 
313
        # confirmation message sent to Anne.
 
314
        get_queue_messages('virgin')
 
315
        url = 'http://localhost:9001/3.0/lists/ant@example.com/requests/{}'
 
316
        content, response = call_api(url.format(token), dict(
 
317
            action='reject',
 
318
            ))
 
319
        self.assertEqual(response.status, 204)
 
320
        # Anne is not a member.
 
321
        self.assertIsNone(self._mlist.members.get_member('anne@example.com'))
 
322
        # The request URL no longer exists.
 
323
        with self.assertRaises(HTTPError) as cm:
 
324
            call_api(url.format(token), dict(
 
325
                action='reject',
 
326
                ))
 
327
        self.assertEqual(cm.exception.code, 404)
 
328
        # And the rejection message to Anne is now in the virgin queue.
 
329
        items = get_queue_messages('virgin')
 
330
        self.assertEqual(len(items), 1)
 
331
        message = items[0].msg
 
332
        self.assertEqual(message['From'], 'ant-bounces@example.com')
 
333
        self.assertEqual(message['To'], 'anne@example.com')
 
334
        self.assertEqual(message['Subject'],
 
335
                         'Request to mailing list "Ant" rejected')
 
336
 
 
337
    def test_reject_bad_token(self):
 
338
        # Try to accept a request with a bogus token.
 
339
        with self.assertRaises(HTTPError) as cm:
 
340
            call_api('http://localhost:9001/3.0/lists/ant@example.com'
 
341
                     '/requests/bogus',
 
342
                     dict(action='reject'))
 
343
        self.assertEqual(cm.exception.code, 404)