1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2017-2021 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)
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
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/>.
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
,
93 'aperson@example.com',
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'],
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'],
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(
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
,
180 'aperson@example.com',
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
):
191 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
193 ("legacy-default", 'Ordinary discussion mailing list style.'),
194 ("legacy-announce", 'Announce only mailing list style.')]
195 form
= ListNew(domain_choices
, style_choices
,
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
):
206 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
208 ("legacy-default", 'Ordinary discussion mailing list style.'),
209 ("legacy-announce", 'Announce only mailing list style.')]
210 form
= ListNew(domain_choices
, style_choices
,
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'])
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
):
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
):
241 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
243 ("legacy-default", 'Ordinary discussion mailing list style.'),
244 ("legacy-announce", 'Announce only mailing list style.')]
245 form
= ListNew(domain_choices
, style_choices
,
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.
260 ("mailman.most-desirable.org", "mailman.most-desirable.org")],
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
)
269 'Please enter a valid listname, "@" is not allowed in listname',
270 form
.errors
['listname'])
272 def test_form_fields_order(self
):
274 ("mailman.most-desirable.org", "mailman.most-desirable.org")]
276 ("legacy-default", 'Ordinary discussion mailing list style.'),
277 ("legacy-announce", 'Announce only mailing list style.')]
278 form
= ListNew(domain_choices
, style_choices
,
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
),
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',
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',
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'],
325 class TestListMassSubscription(TestCase
):
327 def test_all_valid_email_formats(self
):
328 form
= ListMassSubscription({
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({
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({
391 'pattern': 'value@example.com'})
392 self
.assertTrue(form
.is_valid())
394 form
= ListHeaderMatchForm({
396 'pattern': 'value@example.com',
398 self
.assertTrue(form
.is_valid())
399 # Defer is not a valid action, so validation should fail here.
400 form
= ListHeaderMatchForm({
402 'pattern': 'value@example.com',
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,
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',
451 'http://example.com/welcome_text',
453 'http://example.com/goodbye_message',
455 formdata
= self
.prepare_formdata(values
)
456 form
= ListAutomaticResponsesForm(formdata
, mlist
=None)
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',
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
479 form
= DMARCMitigationsForm({}, mlist
=None)
480 self
.assertTrue(form
.is_valid())
482 def test_all_fields(self
):
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
):
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)
532 self
.assertTrue(form
.is_valid())
534 def test_all_fields(self
):
538 class TestArchiveSettingsForm(TestCase
):
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'),
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
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',
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'),
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',
624 self
.assertTrue(form
.is_valid())