1 # Copyright (C) 2016-2023 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <https://www.gnu.org/licenses/>.
18 """Test the Join and Leave commands."""
22 from mailman
.app
.lifecycle
import create_list
23 from mailman
.commands
.eml_membership
import Join
, Leave
24 from mailman
.email
.message
import Message
25 from mailman
.interfaces
.bans
import IBanManager
26 from mailman
.interfaces
.mailinglist
import SubscriptionPolicy
27 from mailman
.interfaces
.member
import DeliveryMode
28 from mailman
.interfaces
.pending
import IPendings
29 from mailman
.interfaces
.subscriptions
import ISubscriptionManager
30 from mailman
.interfaces
.usermanager
import IUserManager
31 from mailman
.runners
.command
import Results
32 from mailman
.testing
.helpers
import set_preferred
33 from mailman
.testing
.layers
import ConfigLayer
34 from zope
.component
import getUtility
37 class TestLeave(unittest
.TestCase
):
41 self
._mlist
= create_list('ant@example.com')
42 self
._command
= Leave()
44 def test_confirm_leave_not_a_member(self
):
45 self
._mlist
.unsubscription_policy
= SubscriptionPolicy
.confirm
46 # Try to unsubscribe someone who is not a member. Anne is a real
47 # user, with a validated address, but she is not a member of the
49 anne
= getUtility(IUserManager
).create_user('anne@example.com')
51 # Initiate an unsubscription.
53 msg
['From'] = 'anne@example.com'
55 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
57 str(results
).splitlines()[-1],
58 'leave: anne@example.com is not a member of ant@example.com')
60 def test_leave_no_sender(self
):
61 # Initiate an unsubscription with no sender in the message.
64 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
66 str(results
).splitlines()[-1],
67 'leave: No valid email address found to unsubscribe')
69 def test_leave_no_user(self
):
70 # Initiate an unsubscription for a non-user.
72 msg
['From'] = 'anne@example.com'
74 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
76 str(results
).splitlines()[-1],
77 'No registered user for email address: anne@example.com')
80 class TestJoin(unittest
.TestCase
):
84 self
._mlist
= create_list('ant@example.com')
85 self
._command
= Join()
87 def test_join_successful(self
):
88 # Subscribe a member via join.
90 msg
['From'] = 'anne@example.com'
92 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
93 self
.assertIn('Confirmation email sent to anne@example.com',
96 def test_join_rfc2047_display(self
):
97 # Subscribe a member with RFC 2047 encoded display name via join.
99 msg
['From'] = '=?utf-8?q?Anne?= <anne@example.com>'
101 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
102 self
.assertIn('Confirmation email sent to Anne <anne@example.com>',
104 # Check the pending confirmation.
105 pendings
= list(getUtility(IPendings
).find(self
._mlist
,
108 self
.assertEqual(1, len(pendings
))
109 token
= pendings
[0][0]
110 pended
= getUtility(IPendings
).confirm(token
, expunge
=False)
111 self
.assertEqual('Anne', pended
['display_name'])
112 self
.assertEqual('anne@example.com', pended
['email'])
114 def test_join_rfc2047_display_with_comma(self
):
115 # Subscribe a member with RFC 2047 encoded display name containing a
116 # comma and non-ascii via join.
118 msg
['From'] = '=?utf-8?q?J=C3=BCnk=2C_Anne?= <anne@example.com>'
120 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
121 self
.assertIn('Confirmation email sent to =?utf-8?b?SsO8bmssIEFubmU=?='
122 ' <anne@example.com>', str(results
))
123 # Check the pending confirmation.
124 pendings
= list(getUtility(IPendings
).find(self
._mlist
,
127 self
.assertEqual(1, len(pendings
))
128 token
= pendings
[0][0]
129 pended
= getUtility(IPendings
).confirm(token
, expunge
=False)
130 self
.assertEqual('Jünk, Anne', pended
['display_name'])
131 self
.assertEqual('anne@example.com', pended
['email'])
133 def test_join_digest(self
):
134 # Subscribe a member to digest via join.
136 msg
['From'] = 'anne@example.com'
138 self
._command
.process(self
._mlist
, msg
, {}, ('digest=mime',), results
)
139 self
.assertIn('Confirmation email sent to anne@example.com',
142 def test_join_digest_works(self
):
143 # Subscribe a member to digest via join and verify it works.
144 # Set things so the subscribe works now.
145 self
._mlist
.subscription_policy
= SubscriptionPolicy
.open
146 # Create a verified address for Anne.
147 user
= getUtility(IUserManager
).make_user('anne@example.com', 'Anne')
148 address
= user
.addresses
[0]
149 address
.verified_on
= address
.registered_on
151 msg
['From'] = 'anne@example.com'
153 self
._command
.process(self
._mlist
, msg
, {}, ('digest=mime',), results
)
155 members
= list(self
._mlist
.members
.members
)
156 self
.assertEqual(1, len(members
))
157 self
.assertEqual('anne@example.com', members
[0].address
.email
)
158 self
.assertEqual(DeliveryMode
.mime_digests
, members
[0].delivery_mode
)
160 def test_join_other(self
):
161 # Subscribe a different address via join.
163 msg
['From'] = 'anne@example.com'
165 self
._command
.process(self
._mlist
, msg
, {},
166 ('address=bob@example.com',), results
)
167 self
.assertIn('Confirmation email sent to bob@example.com',
170 def test_join_other_bogus(self
):
171 # Try to subscribe a bogus different address via join.
173 msg
['From'] = 'anne@example.com'
175 self
._command
.process(self
._mlist
, msg
, {},
176 ('address=bogus',), results
)
177 self
.assertIn('Invalid email address: bogus', str(results
))
179 def test_join_bad_argument(self
):
180 # Try to subscribe a member with a bad argument via join.
182 msg
['From'] = 'anne@example.com'
184 self
._command
.process(self
._mlist
, msg
, {}, ('digest=bogus',), results
)
185 self
.assertIn('bad argument: digest=bogus', str(results
))
187 def test_join_bad_argument_name(self
):
188 # Try to subscribe a member with a bad argument via join.
190 msg
['From'] = 'anne@example.com'
192 self
._command
.process(self
._mlist
, msg
, {}, ('reg=bogus',), results
)
193 self
.assertIn('bad argument: reg=bogus', str(results
))
195 def test_join_bad_argument_no_equal(self
):
196 # Try to subscribe a member with a bad argument via join.
198 msg
['From'] = 'anne@example.com'
200 self
._command
.process(self
._mlist
, msg
, {}, ('digest',), results
)
201 self
.assertIn('bad argument: digest', str(results
))
203 def test_join_already_a_member(self
):
204 # Try to subscribe someone who is already a member. Anne is a real
205 # user, with a validated address, but she is not a member of the
207 anne
= getUtility(IUserManager
).create_user('anne@example.com')
209 # First subscribe anne.
210 ISubscriptionManager(self
._mlist
).register(anne
, pre_verified
=True,
213 # Then initiate a subscription.
215 msg
['From'] = 'anne@example.com'
217 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
219 str(results
).splitlines()[-1],
220 'anne@example.com is already a member of '
221 'mailing list ant@example.com')
223 def test_join_banned(self
):
224 # Try to subscribe someone who is banned. Anne is a real
225 # user, with a validated address, but she is not a member of the
226 # mailing list and is banned from joining.
227 # Add anne to the ban list.
228 IBanManager(self
._mlist
).ban('anne@example.com')
229 # Then initiate a subscription.
231 msg
['From'] = 'anne@example.com'
233 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
235 str(results
).splitlines()[-1],
236 'anne@example.com is not allowed to subscribe to ant@example.com')
238 def test_join_pending(self
):
239 self
._mlist
.subscription_policy
= SubscriptionPolicy
.confirm
240 # Try to subscribe someone who already has a subscription pending.
241 # Anne is a real user, with a validated address, who already has a
242 # pending subscription for this mailing list.
243 anne
= getUtility(IUserManager
).create_user('anne@example.com')
245 # Initiate a subscription.
246 ISubscriptionManager(self
._mlist
).register(anne
)
247 # And try to subscribe.
249 msg
['From'] = 'anne@example.com'
251 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
253 str(results
).splitlines()[-1],
254 'anne@example.com has a pending subscription for ant@example.com')
256 def test_join_posting_address(self
):
257 # Try to subscribe the list posting address.
259 msg
['From'] = self
._mlist
.posting_address
261 self
._command
.process(self
._mlist
, msg
, {}, (), results
)
263 str(results
).splitlines()[-1],
264 'List posting address not allowed')