Bump copyright year
[mailman-postorious.git] / src / postorius / tests / test_list_forms.py
blobbeaae65de4e638ab9c3305825e78186cc3c56fea
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2017-2022 by the Free Software Foundation, Inc.
4 # This file is part of Postorius.
6 # Postorius is free software: you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free
8 # Software Foundation, either version 3 of the License, or (at your option)
9 # any later version.
11 # Postorius is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 # more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Postorius. If not, see <http://www.gnu.org/licenses/>.
19 import unittest
21 from django.test import TestCase
23 from postorius.forms.list_forms import (
24 ArchiveSettingsForm, ChangeSubscriptionForm, DigestSettingsForm,
25 DMARCMitigationsForm, ListAnonymousSubscribe, ListAutomaticResponsesForm,
26 ListHeaderMatchForm, ListIdentityForm, ListMassRemoval,
27 ListMassSubscription, ListNew, ListSubscribe, MemberModeration,
28 MemberPolicyForm, MessageAcceptanceForm)
29 from postorius.forms.system import AddBanForm
30 from postorius.tests.utils import create_mock_list
33 class TestListSubscribe(TestCase):
35 def test_required_fields_only(self):
36 user_emails = ['bob@example.com', 'anne@example.com']
37 form = ListSubscribe(user_emails, None, None,
38 dict(subscriber='bob@example.com'))
39 self.assertTrue(form.is_valid())
41 def test_email_is_only_from_choices(self):
42 user_emails = ['bob@example.com', 'anne@example.com']
43 form = ListSubscribe(user_emails, None, None,
44 dict(subscriber='alice@example.com',
45 display_name='Alice'))
46 self.assertFalse(form.is_valid())
47 self.assertTrue('subscriber' in form.errors)
48 self.assertTrue('Select a valid choice.' in
49 form.errors['subscriber'][0])
51 def test_subscribe_works(self):
52 user_emails = ['someone@example.com']
53 form = ListSubscribe(user_emails, None, None,
54 {'subscriber': 'someone@example.com',
55 'display_name': 'Someone'})
56 self.assertTrue(form.is_valid())
58 def test_subscribe_fails(self):
59 user_emails = ['someone@example.com']
60 form = ListSubscribe(user_emails, None, None,
61 {'subscriber': 'notaemail',
62 'display_name': 'Someone'})
63 self.assertFalse(form.is_valid())
64 self.assertTrue('subscriber' in form.errors.keys())
65 self.assertEqual(form.errors['subscriber'][0],
66 'Select a valid choice.'
67 ' notaemail is not one of the available choices.')
69 def test_subscribe_validates_email(self):
70 user_emails = ['something']
71 form = ListSubscribe(user_emails, None, None,
72 {'subscriber': 'something',
73 'display_name': 'Someone'})
74 self.assertFalse(form.is_valid())
75 self.assertTrue('subscriber' in form.errors.keys())
76 self.assertEqual(form.errors['subscriber'][0],
77 'Please enter a valid email address.')
79 def test_subscribe_with_user_uuid(self):
80 user_emails = ['aperson@example.com']
81 form = ListSubscribe(user_emails,
82 '00000000000000000000000000000004',
83 'aperson@example.com',
84 {'subscriber': '00000000000000000000000000000004'}
86 self.assertEqual(form.is_valid(), True)
88 def test_subscribe_with_invalid_uuid(self):
89 # Test that invalid UUID returns errors.
90 user_emails = ['aperson@example.com']
91 form = ListSubscribe(user_emails,
92 '4',
93 'aperson@example.com',
94 {'subscriber': '4'})
95 self.assertEqual(form.is_valid(), False)
96 self.assertEqual(form.errors['subscriber'][0],
97 'Please enter a valid email address.')
99 def test_list_subscribe_with_delivery_mode(self):
100 user_emails = ['aperson@example.com']
101 form = ListSubscribe(user_emails,
102 '00000000000000000000000000000004',
103 'aperson@example.com',
104 {'subscriber': '00000000000000000000000000000004',
105 'delivery_mode': 'plaintext_digests',
106 'delivery_status': 'by_user'})
107 self.assertTrue(form.is_valid())
109 def test_list_subscribe_with_invalid_delivery_status(self):
110 user_emails = ['aperson@example.com']
111 form = ListSubscribe(user_emails,
112 '00000000000000000000000000000004',
113 'aperson@example.com',
114 {'subscriber': '00000000000000000000000000000004',
115 'delivery_mode': 'plaintext_digest',
116 'delivery_status': 'by_bounces'})
117 self.assertFalse(form.is_valid())
118 self.assertTrue('delivery_status' in form.errors)
121 class TestChangeSubscription(TestCase):
123 def test_subscription_changes_only_to_user_addresses(self):
124 user_emails = ['one@example.com', 'two@example.com']
125 form = ChangeSubscriptionForm(user_emails, None, None,
126 {'subscriber': 'abcd@d.com',
127 'member_id': '1235sdfsd314'})
128 self.assertFalse(form.is_valid())
129 self.assertTrue('subscriber' in form.errors.keys())
130 self.assertEqual(form.errors['subscriber'][0],
131 'Select a valid choice. '
132 'abcd@d.com is not one of the available choices.')
134 def test_subscription_works(self):
135 user_emails = ['one@example.com', 'two@example.com']
136 form = ChangeSubscriptionForm(user_emails, None, None,
137 {'subscriber': 'two@example.com',
138 'member_id': '1235sdfsd314'})
139 self.assertTrue(form.is_valid())
141 def test_subscription_form_labels(self):
142 user_emails = ['one@example.com', 'two@example.com']
143 form = ChangeSubscriptionForm(user_emails, None, None,
144 {'member_id': '1235sdfsd314'})
145 self.assertTrue(form.is_valid())
146 self.assertEqual(form.fields['subscriber'].label, 'Select Email')
148 def test_form_validity(self):
149 form = ChangeSubscriptionForm(
150 ['email@example.com', 'john@example.com', 'doe@example.com'],
151 None, None,
152 {'subscriber': 'email@example.com',
153 'member_id': '1235sdfsd314'})
154 self.assertTrue(form.is_valid())
156 def test_required_fields(self):
157 # There is only one required fields.
158 form = ChangeSubscriptionForm(
159 ['email@example.com', 'john@example.com', 'doe@example.com'],
160 None, None,
161 {'member_id': '1235sdfsd314'})
162 self.assertTrue(form.is_valid())
164 def test_change_subscription_with_user_uuid(self):
165 user_emails = ['aperson@example.com']
166 form = ChangeSubscriptionForm(
167 user_emails,
168 '00000000000000000000000000000004',
169 'aperson@example.com',
170 {'subscriber': '00000000000000000000000000000004',
171 'member_id': '1235sdfsd314'})
172 self.assertEqual(form.is_valid(), True)
174 def test_subscribe_with_invalid_uuid(self):
175 # Test that invalid UUID returns errors.
176 user_emails = ['aperson@example.com']
177 # UUIDs are 32bit and 4 is not a valid email.
178 form = ChangeSubscriptionForm(user_emails,
179 '4',
180 'aperson@example.com',
181 {'subscriber': '4'})
182 self.assertEqual(form.is_valid(), False)
183 self.assertEqual(form.errors['subscriber'][0],
184 'Invalid: "4" should be either email or UUID')
187 class TestListNew(TestCase):
189 def test_form_fields_list(self):
190 domain_choices = [
191 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
192 style_choices = [
193 ("legacy-default", 'Ordinary discussion mailing list style.'),
194 ("legacy-announce", 'Announce only mailing list style.')]
195 form = ListNew(domain_choices, style_choices,
196 {'listname': 'xyz',
197 'mail_host': 'mailman.most-desirable.org',
198 'list_owner': 'contact@mailman.most-desirable.org',
199 'advertised': 'True',
200 'list_style': 'legacy-default',
201 'description': 'The Most Desirable organization'})
202 self.assertTrue(form.is_valid(), form.errors)
204 def test_form_fields_list_invalid(self):
205 domain_choices = [
206 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
207 style_choices = [
208 ("legacy-default", 'Ordinary discussion mailing list style.'),
209 ("legacy-announce", 'Announce only mailing list style.')]
210 form = ListNew(domain_choices, style_choices,
211 {'listname': 'xy#z',
212 'mail_host': 'mailman.most-desirable.org',
213 'list_owner': 'mailman.most-desirable.org',
214 'advertised': 'abcd',
215 'list_style': 'defg',
216 'description': 'The Most Desirable organization'})
217 self.assertFalse(form.is_valid())
218 # Test that all invalid fields are actually checked.
219 for field in ('list_owner', 'advertised'):
220 self.assertTrue(field in form.errors)
221 self.assertTrue('Enter a valid email address.' in
222 form.errors['list_owner'])
223 self.assertTrue(
224 'Select a valid choice. abcd is not one of the available choices.'
225 in form.errors['advertised'])
227 def test_form_without_domain_choices(self):
228 form = ListNew([],
229 {'listname': 'xyz',
230 'mail_host': 'mailman.most-desirable.org',
231 'list_owner': 'contact@mailman.most-desirable.org',
232 'advertised': 'True',
233 'description': 'The Most Desirable organization', })
234 # Without domain choices, the form is not going to be valid.
235 self.assertFalse(form.is_valid())
236 self.assertTrue(form.fields['mail_host'].help_text == # noqa: W504
237 'Site admin has not created any domains')
239 def test_listname_validation(self):
240 domain_choices = [
241 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
242 style_choices = [
243 ("legacy-default", 'Ordinary discussion mailing list style.'),
244 ("legacy-announce", 'Announce only mailing list style.')]
245 form = ListNew(domain_choices, style_choices,
246 {'listname': 'xy@z',
247 'mail_host': 'mailman.most-desirable.org',
248 'list_owner': 'mailman.most-desirable.org',
249 'advertised': 'abcd',
250 'description': 'The Most Desirable organization', })
251 self.assertFalse(form.is_valid())
252 self.assertTrue('listname' in form.errors)
253 self.assertTrue('Please enter a valid listname' in
254 form.errors['listname'])
256 @unittest.expectedFailure
257 def test_listname_validation_errors_sane(self):
258 # This test is going to fail right now, but needs to be fixed.
259 form = ListNew([
260 ("mailman.most-desirable.org", "mailman.most-desirable.org")],
261 {'listname': 'xy#z',
262 'mail_host': 'mailman.most-desirable.org',
263 'list_owner': 'mailman.most-desirable.org',
264 'advertised': 'abcd',
265 'description': 'The Most Desirable organization', })
266 self.assertFalse(form.is_valid())
267 self.assertTrue('listname' in form.errors)
268 self.assertEqual(
269 'Please enter a valid listname, "@" is not allowed in listname',
270 form.errors['listname'])
272 def test_form_fields_order(self):
273 domain_choices = [
274 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
275 style_choices = [
276 ("legacy-default", 'Ordinary discussion mailing list style.'),
277 ("legacy-announce", 'Announce only mailing list style.')]
278 form = ListNew(domain_choices, style_choices,
279 {'listname': 'xyz',
280 'mail_host': 'mailman.most-desirable.org',
281 'list_owner': 'mailman@most-desirable.org',
282 'list_style': 'legacy-default',
283 'advertised': 'True',
284 'description': 'The Most Desirable organization', })
285 self.assertTrue(form.is_valid())
286 # The order of the fields should remain exactly like this.
287 self.assertEqual(list(form.fields),
288 ['listname',
289 'mail_host',
290 'list_owner',
291 'advertised',
292 'list_style',
293 'description'])
296 class TestListIdentityForm(TestCase):
298 def test_not_required_fields(self):
299 # Only advertised is the required form field.
300 form = ListIdentityForm({
301 'advertised': 'True',
302 }, mlist=None)
303 self.assertTrue(form.is_valid(), form.errors)
305 def test_field_validations(self):
306 form = ListIdentityForm({
307 'advertised': 'abcd',
308 'description': 'This is the most desirable organization',
309 'info': 'This is a larger description of this mailing list.',
310 'display_name': 'Most Desirable Mailing List',
311 'subject_prefix': ' [Most Desirable] ',
312 'preferred_language': 'en',
313 'member_roster_visibility': 'public',
314 }, mlist=None)
315 self.assertFalse(form.is_valid())
316 self.assertTrue('advertised' in form.errors)
317 self.assertEqual(['Select a valid choice. abcd is not one of the available choices.'], # noqa
318 form.errors['advertised'])
319 # We shouldn't be removing trailing whitespaces, but we
320 # should remove the leading ones.
321 self.assertEqual(form.cleaned_data['subject_prefix'],
322 '[Most Desirable] ')
325 class TestListMassSubscription(TestCase):
327 def test_all_valid_email_formats(self):
328 form = ListMassSubscription({
329 'emails': '''
330 jdoe@example.com
331 <jdoe@example.com>
332 John Doe <jdoe@example.com>
333 "John Doe" <jdoe@example.com>
334 jdoe@example.com (John Doe)'''})
335 self.assertTrue(form.is_valid())
337 def test_required_fields(self):
338 form = ListMassSubscription({'email': ' '})
339 self.assertFalse(form.is_valid())
340 self.assertEqual(['This field is required.'],
341 form.errors['emails'])
342 form = ListMassSubscription({'email': '----'})
343 self.assertFalse(form.is_valid())
344 self.assertEqual(['This field is required.'],
345 form.errors['emails'])
348 class TestListMassRemoval(TestCase):
350 def test_all_valid_formats(self):
351 form = ListMassRemoval({
352 'emails': '''
353 jdoe@example.com
354 <jdoe@example.com>
355 John Doe <jdoe@example.com>
356 "John Doe" <jdoe@example.com>
357 jdoe@example.com (John Doe)'''})
358 self.assertTrue(form.is_valid())
361 class TestAddBanForm(TestCase):
363 def test_form_validity(self):
364 form = AddBanForm({'email': 'jdoe@example.com'})
365 self.assertTrue(form.is_valid())
367 def test_missing_fields_errors(self):
368 form = AddBanForm({})
369 self.assertFalse(form.is_valid())
370 self.assertTrue('email' in form.errors)
371 self.assertEqual(form.errors['email'],
372 ['Please enter an email address.'])
374 @unittest.expectedFailure
375 def test_invalid_fields_type(self):
376 # Valid values for email is either a regexp or an email address.
377 # However, this is currently not validated by the form.
378 form = AddBanForm({'email': 'invalid@'})
379 self.assertFalse(form.is_valid())
380 self.assertTrue('email' in form.errors)
381 self.assertEqual(form.errors['email'],
382 ['Please enter a valid email address.'])
385 class TestListHeaderMatchForm(TestCase):
387 def test_form_validity(self):
388 # Test by putting only required fields.
389 form = ListHeaderMatchForm({
390 'header': 'To',
391 'pattern': 'value@example.com'})
392 self.assertTrue(form.is_valid())
393 # Test all fileds.
394 form = ListHeaderMatchForm({
395 'header': 'To',
396 'pattern': 'value@example.com',
397 'action': 'accept'})
398 self.assertTrue(form.is_valid())
399 # Defer is not a valid action, so validation should fail here.
400 form = ListHeaderMatchForm({
401 'header': 'To',
402 'pattern': 'value@example.com',
403 'action': 'defer'})
404 self.assertFalse(form.is_valid())
405 self.assertTrue('action' in form.errors)
408 class TestMemberModeration(TestCase):
410 def test_moderation_action_validity(self):
411 form = MemberModeration({'moderation_action': 'moderation'})
412 self.assertFalse(form.is_valid())
413 self.assertTrue('moderation_action' in form.errors)
414 self.assertEqual(form.errors['moderation_action'],
415 ['Select a valid choice. moderation is not one of the available choices.']) # noqa
417 def test_required_fields(self):
418 form = MemberModeration({})
419 self.assertTrue(form.is_valid())
422 class TestListAutomaticResponsesForm(TestCase):
424 fields = ('autorespond_owner', 'autoresponse_owner_text',
425 'autorespond_postings', 'autoresponse_postings_text',
426 'autorespond_requests', 'autoresponse_request_text',
427 'autoresponse_grace_period', 'send_welcome_message',
428 'welcome_message_uri', 'send_goodbye_message',
429 'goodbye_message_uri', 'admin_immed_notify',
430 'admin_notify_mchanges')
432 def prepare_formdata(self, values):
433 return dict(((key, val) for key, val in zip(self.fields, values) if val is not None)) # noqa
435 def test_required_fields_only(self):
436 values = ('respond_and_continue', None,
437 'respond_and_continue', None,
438 'respond_and_continue', None,
439 '2', None,
440 None, None,
441 None, None)
442 formdata = self.prepare_formdata(values)
443 form = ListAutomaticResponsesForm(formdata, mlist=None)
444 self.assertTrue(form.is_valid())
446 def test_all_values(self):
447 values = ('respond_and_continue', 'Autorespond text',
448 'respond_and_continue', 'Autorespond text',
449 'respond_and_continue', 'Autorespond text',
450 '2', 'True',
451 'http://example.com/welcome_text',
452 'True',
453 'http://example.com/goodbye_message',
454 'True', 'False')
455 formdata = self.prepare_formdata(values)
456 form = ListAutomaticResponsesForm(formdata, mlist=None)
457 print(form.errors)
458 self.assertTrue(form.is_valid())
461 class TestAlterMessageForm(TestCase):
463 fields = ('filter_content', 'collapse_alternatives',
464 'convert_html_to_plaintext', 'anonymous_list',
465 'include_rfc2369_headers', 'allow_list_posts',
466 'reply_list_posts', 'reply_to_address',
467 'first_strip_reply_to', 'reply_goes_to_list',
468 'posting_pipeline')
470 def prepare_formdata(self, values):
471 return dict(((key, val) for key, val in zip(self.fields, values) if val is not None)) # noqa
474 class TestDMARCMitigationsForm(TestCase):
476 def test_required_fields(self):
477 # All fields in the form are optional, so an empty form should be
478 # valid.
479 form = DMARCMitigationsForm({}, mlist=None)
480 self.assertTrue(form.is_valid())
482 def test_all_fields(self):
483 formdata = dict(
484 dmarc_mitigate_action='munge_from',
485 dmarc_mitigate_unconditionally='True',
486 dmarc_moderation_notice='This is a moderation notice',
487 dmarc_wrapped_message_text='This is wrapped message text')
488 form = DMARCMitigationsForm(formdata, mlist=None)
489 self.assertTrue(form.is_valid())
492 class TestDigestSettingsForm(TestCase):
494 def test_required_fields(self):
495 form = DigestSettingsForm({}, mlist=None)
496 self.assertFalse(form.is_valid())
497 self.assertTrue('digest_size_threshold' in form.errors)
498 self.assertEqual(form.errors['digest_size_threshold'],
499 ['This field is required.'])
500 form = DigestSettingsForm({'digest_size_threshold': 40}, mlist=None)
501 self.assertTrue(form.is_valid())
503 def test_all_fields(self):
504 formdata = dict(
505 digests_enabled='True',
506 digests_send_periodic='True',
507 digests_volume_frequency='daily',
508 digest_size_threshold='10',
510 form = DigestSettingsForm(formdata, mlist=None)
511 self.assertTrue(form.is_valid())
514 class TestMessageAcceptanceForm(TestCase):
516 fields = ('acceptable_aliases', 'require_explicit_destination',
517 'administrivia', 'default_member_action',
518 'default_nonmember_action', 'max_message_size',
519 'max_num_recipients')
521 def prepare_formdata(self, values):
522 return dict(((key, val) for key, val in zip(self.fields, values) if val is not None)) # noqa
524 def test_required_fields(self):
525 # Without any fields, form should not be valid.
526 form = MessageAcceptanceForm({}, mlist=None)
527 self.assertFalse(form.is_valid())
528 # Now lets try with only required fields.
529 values = (None, None, None, 'hold', 'hold', 40, 100)
530 form = MessageAcceptanceForm(self.prepare_formdata(values), mlist=None)
531 print(form.errors)
532 self.assertTrue(form.is_valid())
534 def test_all_fields(self):
535 pass
538 class TestArchiveSettingsForm(TestCase):
540 def setUp(self):
541 self.mlist = create_mock_list()
542 archivers = {'pipermail': True, 'hyperkitty': True}
543 self.mlist.archivers.keys.side_effect = archivers.keys
544 self.mlist.archivers.__getitem__.side_effect = archivers.__getitem__
545 self.mlist.archivers.__iter__.side_effect = archivers.__iter__
546 self.mlist.archivers.__contains__.side_effect = archivers.__contains__
548 def test_required_fields(self):
549 # First try without any fields.
550 form = ArchiveSettingsForm({}, mlist=self.mlist)
551 self.assertFalse(form.is_valid())
552 self.assertTrue('archive_policy' in form.errors)
553 # Now, with only required fields, this should be a valid form.
554 form = ArchiveSettingsForm(dict(archive_policy='public'),
555 mlist=self.mlist)
556 self.assertTrue(form.is_valid())
558 def test_all_fields(self):
559 formdata = dict(archive_policy='public',
560 archivers=['pipermail', 'hyperkitty'])
561 form = ArchiveSettingsForm(formdata, mlist=self.mlist)
562 self.assertTrue(form.is_valid())
564 def test_setup_archivers_populated(self):
565 formdata = dict(archive_policy='public',
566 archivers=['pipermail', 'hyperkitty'])
567 form = ArchiveSettingsForm(formdata, mlist=self.mlist,
568 initial={'archivers': None})
569 self.assertTrue(form.is_valid())
570 self.assertEqual(form.initial['archivers'],
571 ['hyperkitty', 'pipermail'])
573 def test_optional_archive_rendering_field(self):
574 # Test that archive rendering mode is set only when Hyperkitty is
575 # installed.
576 formdata = dict(archive_policy='public',
577 archivers=['pipermail'])
578 form = ArchiveSettingsForm(formdata, mlist=self.mlist,
579 initial={'archivers': None})
580 self.assertTrue(form.is_valid())
581 self.assertFalse('archive_rendering_mode' in form.fields)
582 # With Hyperkitty installed, the field should show up.
583 with unittest.mock.patch(
584 'postorius.forms.list_forms.apps.is_installed',
585 return_value=True):
586 formdata = dict(archive_policy='public',
587 archivers=['pipermail'])
588 form = ArchiveSettingsForm(formdata, mlist=self.mlist,
589 initial={'archivers': None})
590 self.assertTrue(form.is_valid())
591 self.assertTrue('archive_rendering_mode' in form.fields)
594 class TestMemberPolicyForm(TestCase):
596 def test_required_fields(self):
597 form = MemberPolicyForm({}, mlist=None)
598 self.assertFalse(form.is_valid())
599 self.assertTrue('subscription_policy' in form.errors)
600 self.assertTrue('unsubscription_policy' in form.errors)
601 form = MemberPolicyForm(
602 dict(subscription_policy='confirm',
603 unsubscription_policy='confirm'),
604 mlist=None)
605 self.assertTrue(form.is_valid())
608 class TestListAnonymousSubscribe(TestCase):
610 def test_required_fields_only(self):
611 form = ListAnonymousSubscribe(dict(email='bob@exmaple.com'))
612 self.assertTrue(form.is_valid())
614 def test_email_is_validated(self):
615 form = ListAnonymousSubscribe(dict(email='invalid'))
616 self.assertFalse(form.is_valid())
617 self.assertTrue('email' in form.errors)
618 self.assertEqual(form.errors['email'],
619 ['Please enter a valid email address.'])
621 def test_all_fields(self):
622 form = ListAnonymousSubscribe(dict(email='bob@example.com',
623 display_name='Bob'))
624 self.assertTrue(form.is_valid())