~mailman-coders/mailman/3.0

« back to all changes in this revision

Viewing changes to src/mailman/app/tests/test_subscriptions.py

  • Committer: Barry Warsaw
  • Date: 2015-04-16 02:52:34 UTC
  • mfrom: (7319.1.6 restsub)
  • Revision ID: barry@list.org-20150416025234-h1g2hllh0cmye6to
Plumb the subscription policy through to the membership subscription REST API.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
    MemberRole, MembershipIsBannedError, MissingPreferredAddressError)
35
35
from mailman.interfaces.pending import IPendings
36
36
from mailman.interfaces.subscriptions import (
37
 
    MissingUserError, ISubscriptionService)
 
37
    ISubscriptionService, MissingUserError, TokenOwner)
38
38
from mailman.testing.helpers import LogFileMark, get_queue_messages
39
39
from mailman.testing.layers import ConfigLayer
40
40
from mailman.interfaces.mailinglist import SubscriptionPolicy
45
45
 
46
46
 
47
47
 
48
 
class TestJoin(unittest.TestCase):
49
 
    layer = ConfigLayer
50
 
 
51
 
    def setUp(self):
52
 
        self._mlist = create_list('test@example.com')
53
 
        self._service = getUtility(ISubscriptionService)
54
 
 
55
 
    def test_join_user_with_bogus_id(self):
56
 
        # When `subscriber` is a missing user id, an exception is raised.
57
 
        with self.assertRaises(MissingUserError) as cm:
58
 
            self._service.join('test.example.com', uuid.UUID(int=99))
59
 
        self.assertEqual(cm.exception.user_id, uuid.UUID(int=99))
60
 
 
61
 
    def test_join_user_with_invalid_email_address(self):
62
 
        # When `subscriber` is a string that is not an email address, an
63
 
        # exception is raised.
64
 
        with self.assertRaises(InvalidEmailAddressError) as cm:
65
 
            self._service.join('test.example.com', 'bogus')
66
 
        self.assertEqual(cm.exception.email, 'bogus')
67
 
 
68
 
    def test_missing_preferred_address(self):
69
 
        # A user cannot join a mailing list if they have no preferred address.
70
 
        anne = self._service.join(
71
 
            'test.example.com', 'anne@example.com', 'Anne Person')
72
 
        # Try to join Anne as a user with a different role.  Her user has no
73
 
        # preferred address, so this will fail.
74
 
        self.assertRaises(MissingPreferredAddressError,
75
 
                          self._service.join,
76
 
                          'test.example.com', anne.user.user_id,
77
 
                          role=MemberRole.owner)
78
 
 
79
 
 
80
 
 
81
48
class TestSubscriptionWorkflow(unittest.TestCase):
82
49
    layer = ConfigLayer
83
50
    maxDiff = None
88
55
        self._anne = 'anne@example.com'
89
56
        self._user_manager = getUtility(IUserManager)
90
57
 
 
58
    def test_start_state(self):
 
59
        # The workflow starts with no tokens or member.
 
60
        workflow = SubscriptionWorkflow(self._mlist)
 
61
        self.assertIsNone(workflow.token)
 
62
        self.assertEqual(workflow.token_owner, TokenOwner.no_one)
 
63
        self.assertIsNone(workflow.member)
 
64
 
91
65
    def test_user_or_address_required(self):
92
66
        # The `subscriber` attribute must be a user or address.
93
67
        workflow = SubscriptionWorkflow(self._mlist)
229
203
 
230
204
    def test_confirmation_checks_confirm_pre_confirmed(self):
231
205
        # The subscription policy requires user confirmation, but their
232
 
        # subscription is pre-confirmed.
 
206
        # subscription is pre-confirmed.  Since moderation is not required,
 
207
        # the user will be immediately subscribed.
233
208
        self._mlist.subscription_policy = SubscriptionPolicy.confirm
234
209
        anne = self._user_manager.create_address(self._anne)
235
210
        workflow = SubscriptionWorkflow(self._mlist, anne,
236
211
                                        pre_verified=True,
237
212
                                        pre_confirmed=True)
238
213
        workflow.run_thru('confirmation_checks')
 
214
        with patch.object(workflow, '_step_do_subscription') as step:
 
215
            next(workflow)
 
216
        step.assert_called_once_with()
 
217
 
 
218
    def test_confirmation_checks_confirm_then_moderate_pre_confirmed(self):
 
219
        # The subscription policy requires user confirmation, but their
 
220
        # subscription is pre-confirmed.  Since moderation is required, that
 
221
        # check will be performed.
 
222
        self._mlist.subscription_policy = \
 
223
          SubscriptionPolicy.confirm_then_moderate
 
224
        anne = self._user_manager.create_address(self._anne)
 
225
        workflow = SubscriptionWorkflow(self._mlist, anne,
 
226
                                        pre_verified=True,
 
227
                                        pre_confirmed=True)
 
228
        workflow.run_thru('confirmation_checks')
239
229
        with patch.object(workflow, '_step_moderation_checks') as step:
240
230
            next(workflow)
241
231
        step.assert_called_once_with()
311
301
        # Anne is now a member of the mailing list.
312
302
        member = self._mlist.regular_members.get_member(self._anne)
313
303
        self.assertEqual(member.address, anne)
 
304
        self.assertEqual(workflow.member, member)
 
305
        # No further token is needed.
 
306
        self.assertIsNone(workflow.token)
 
307
        self.assertEqual(workflow.token_owner, TokenOwner.no_one)
314
308
 
315
309
    def test_do_subscription_pre_approved(self):
316
310
        # An moderation-requiring subscription policy plus a pre-verified and
326
320
        # Anne is now a member of the mailing list.
327
321
        member = self._mlist.regular_members.get_member(self._anne)
328
322
        self.assertEqual(member.address, anne)
 
323
        self.assertEqual(workflow.member, member)
 
324
        # No further token is needed.
 
325
        self.assertIsNone(workflow.token)
 
326
        self.assertEqual(workflow.token_owner, TokenOwner.no_one)
329
327
 
330
328
    def test_do_subscription_pre_approved_pre_confirmed(self):
331
329
        # An moderation-requiring subscription policy plus a pre-verified and
343
341
        # Anne is now a member of the mailing list.
344
342
        member = self._mlist.regular_members.get_member(self._anne)
345
343
        self.assertEqual(member.address, anne)
 
344
        self.assertEqual(workflow.member, member)
 
345
        # No further token is needed.
 
346
        self.assertIsNone(workflow.token)
 
347
        self.assertEqual(workflow.token_owner, TokenOwner.no_one)
346
348
 
347
349
    def test_do_subscription_cleanups(self):
348
350
        # Once the user is subscribed, the token, and its associated pending
360
362
        # Anne is now a member of the mailing list.
361
363
        member = self._mlist.regular_members.get_member(self._anne)
362
364
        self.assertEqual(member.address, anne)
 
365
        self.assertEqual(workflow.member, member)
363
366
        # The workflow is done, so it has no token.
364
367
        self.assertIsNone(workflow.token)
 
368
        self.assertEqual(workflow.token_owner, TokenOwner.no_one)
365
369
        # The pendable associated with the token has been evicted.
366
370
        self.assertIsNone(getUtility(IPendings).confirm(token, expunge=False))
367
371
        # There is no saved workflow associated with the token.  This shows up
384
388
        # The user is not currently subscribed to the mailing list.
385
389
        member = self._mlist.regular_members.get_member(self._anne)
386
390
        self.assertIsNone(member)
 
391
        self.assertIsNone(workflow.member)
 
392
        # The token is owned by the moderator.
 
393
        self.assertIsNotNone(workflow.token)
 
394
        self.assertEqual(workflow.token_owner, TokenOwner.moderator)
387
395
        # Create a new workflow with the previous workflow's save token, and
388
396
        # restore its state.  This models an approved subscription and should
389
397
        # result in the user getting subscribed.
394
402
        # Now the user is subscribed to the mailing list.
395
403
        member = self._mlist.regular_members.get_member(self._anne)
396
404
        self.assertEqual(member.address, anne)
 
405
        self.assertEqual(approved_workflow.member, member)
 
406
        # No further token is needed.
 
407
        self.assertIsNone(approved_workflow.token)
 
408
        self.assertEqual(approved_workflow.token_owner, TokenOwner.no_one)
397
409
 
398
410
    def test_get_moderator_approval_log_on_hold(self):
399
411
        # When the subscription is held for moderator approval, a message is
529
541
        self.assertIsNone(anne.verified_on)
530
542
        workflow = SubscriptionWorkflow(self._mlist, anne)
531
543
        list(workflow)
532
 
        self.assertIsNone(self._mlist.regular_members.get_member(self._anne))
 
544
        # Anne is not yet a member.
 
545
        member = self._mlist.regular_members.get_member(self._anne)
 
546
        self.assertIsNone(member)
 
547
        self.assertIsNone(workflow.member)
 
548
        # The token is owned by the subscriber.
 
549
        self.assertIsNotNone(workflow.token)
 
550
        self.assertEqual(workflow.token_owner, TokenOwner.subscriber)
 
551
        # Confirm.
533
552
        confirm_workflow = SubscriptionWorkflow(self._mlist)
534
553
        confirm_workflow.token = workflow.token
535
554
        confirm_workflow.restore()
536
555
        list(confirm_workflow)
537
556
        self.assertIsNotNone(anne.verified_on)
538
 
        self.assertEqual(
539
 
            self._mlist.regular_members.get_member(self._anne).address, anne)
 
557
        # Anne is now a member.
 
558
        member = self._mlist.regular_members.get_member(self._anne)
 
559
        self.assertEqual(member.address, anne)
 
560
        self.assertEqual(confirm_workflow.member, member)
 
561
        # No further token is needed.
 
562
        self.assertIsNone(confirm_workflow.token)
 
563
        self.assertEqual(confirm_workflow.token_owner, TokenOwner.no_one)
540
564
 
541
565
    def test_prevent_confirmation_replay_attacks(self):
542
566
        # Ensure that if the workflow requires two confirmations, e.g. first
553
577
        # Anne is not yet a member of the mailing list.
554
578
        member = self._mlist.regular_members.get_member(self._anne)
555
579
        self.assertIsNone(member)
 
580
        self.assertIsNone(workflow.member)
 
581
        # The token is owned by the subscriber.
 
582
        self.assertIsNotNone(workflow.token)
 
583
        self.assertEqual(workflow.token_owner, TokenOwner.subscriber)
556
584
        # The old token will not work for moderator approval.
557
585
        moderator_workflow = SubscriptionWorkflow(self._mlist)
558
586
        moderator_workflow.token = token
559
587
        moderator_workflow.restore()
560
588
        list(moderator_workflow)
 
589
        # The token is owned by the moderator.
 
590
        self.assertIsNotNone(moderator_workflow.token)
 
591
        self.assertEqual(moderator_workflow.token_owner, TokenOwner.moderator)
561
592
        # While we wait for the moderator to approve the subscription, note
562
593
        # that there's a new token for the next steps.
563
594
        self.assertNotEqual(token, moderator_workflow.token)
570
601
        # Anne is still not subscribed.
571
602
        member = self._mlist.regular_members.get_member(self._anne)
572
603
        self.assertIsNone(member)
 
604
        self.assertIsNone(final_workflow.member)
573
605
        # However, if we use the new token, her subscription request will be
574
606
        # approved by the moderator.
575
607
        final_workflow.token = moderator_workflow.token
578
610
        # And now Anne is a member.
579
611
        member = self._mlist.regular_members.get_member(self._anne)
580
612
        self.assertEqual(member.address.email, self._anne)
 
613
        self.assertEqual(final_workflow.member, member)
 
614
        # No further token is needed.
 
615
        self.assertIsNone(final_workflow.token)
 
616
        self.assertEqual(final_workflow.token_owner, TokenOwner.no_one)
 
617
 
 
618
    def test_confirmation_needed_and_pre_confirmed(self):
 
619
        # The subscription policy is 'confirm' but the subscription is
 
620
        # pre-confirmed so the moderation checks can be skipped.
 
621
        self._mlist.subscription_policy = SubscriptionPolicy.confirm
 
622
        anne = self._user_manager.create_address(self._anne)
 
623
        workflow = SubscriptionWorkflow(
 
624
            self._mlist, anne,
 
625
            pre_verified=True, pre_confirmed=True, pre_approved=True)
 
626
        list(workflow)
 
627
        # Anne was subscribed.
 
628
        self.assertIsNone(workflow.token)
 
629
        self.assertEqual(workflow.token_owner, TokenOwner.no_one)
 
630
        self.assertEqual(workflow.member.address, anne)