def test_email_only(self): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') result = self._command.invoke(members, ( '--email-only', 'ant.example.com')) self.assertEqual( result.output, '[email protected]\[email protected]\n')
def test_no_welcome_message_to_moderators(self): # Welcome messages go only to mailing list members, not to moderators. subscribe(self._mlist, 'Anne', MemberRole.moderator, '*****@*****.**') # There is no welcome message in the virgin queue. messages = get_queue_messages('virgin') self.assertEqual(len(messages), 0)
def test_bad_preferences_url(self): with transaction(): subscribe(self._mlist, 'Anne') with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com/preferences/bogus') self.assertEqual(cm.exception.code, 404)
def test_member_ids_are_hex(self): with transaction(): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') json, response = call_api('http://localhost:9001/3.1/members') entries = json['entries'] self.assertEqual(len(entries), 2) self.assertEqual( entries[0]['self_link'], 'http://localhost:9001/3.1/members/00000000000000000000000000000001' ) self.assertEqual(entries[0]['member_id'], '00000000000000000000000000000001') self.assertEqual( entries[0]['user'], 'http://localhost:9001/3.1/users/00000000000000000000000000000001') self.assertEqual( entries[1]['self_link'], 'http://localhost:9001/3.1/members/00000000000000000000000000000002' ) self.assertEqual(entries[1]['member_id'], '00000000000000000000000000000002') self.assertEqual( entries[1]['user'], 'http://localhost:9001/3.1/users/00000000000000000000000000000002')
def test_member_ids_are_hex(self): with transaction(): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') response, headers = call_api('http://localhost:9001/3.1/members') entries = response['entries'] self.assertEqual(len(entries), 2) self.assertEqual( entries[0]['self_link'], 'http://localhost:9001/3.1/members/00000000000000000000000000000001') self.assertEqual( entries[0]['member_id'], '00000000000000000000000000000001') self.assertEqual( entries[0]['user'], 'http://localhost:9001/3.1/users/00000000000000000000000000000001') self.assertEqual( entries[1]['self_link'], 'http://localhost:9001/3.1/members/00000000000000000000000000000002') self.assertEqual( entries[1]['member_id'], '00000000000000000000000000000002') self.assertEqual( entries[1]['user'], 'http://localhost:9001/3.1/users/00000000000000000000000000000002')
def test_delete_other_role(self): with transaction(): subscribe(self._mlist, 'Anne', MemberRole.moderator) json, response = call_api('http://localhost:9001/3.0/members/1', method='DELETE') self.assertEqual(response.status_code, 204) self.assertEqual(len(list(self._mlist.moderators.members)), 0)
def test_delete_other_role(self): with transaction(): subscribe(self._mlist, 'Anne', MemberRole.moderator) response, headers = call_api( 'http://localhost:9001/3.0/members/1', method='DELETE') self.assertEqual(headers.status, 204) self.assertEqual(len(list(self._mlist.moderators.members)), 0)
def test_simple_message(self): # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests make_digest_messages(self._mlist) self._check_virgin_queue()
def test_mime_digest_format(self): # Make sure that the format of the MIME digest is as expected. self._mlist.digest_size_threshold = 0.6 self._mlist.volume = 1 self._mlist.next_digest_number = 1 self._mlist.send_welcome_message = False # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests # Fill the digest. process = config.handlers['to-digest'].process size = 0 for i in range(1, 5): text = Template("""\ From: [email protected] To: [email protected] Subject: Test message $i List-Post: <*****@*****.**> Here is message $i """).substitute(i=i) msg = message_from_string(text) process(self._mlist, msg, {}) size += len(text) if size >= self._mlist.digest_size_threshold * 1024: break # Run the digest runner to create the MIME and RFC 1153 digests. runner = make_testable_runner(DigestRunner) runner.run() items = get_queue_messages('virgin', expected_count=2) # Find the MIME one. mime_digest = None for item in items: if item.msg.is_multipart(): assert mime_digest is None, 'We got two MIME digests' mime_digest = item.msg fp = StringIO() # Verify the structure is what we expect. structure(mime_digest, fp) self.assertMultiLineEqual( fp.getvalue(), """\ multipart/mixed text/plain text/plain multipart/digest message/rfc822 text/plain message/rfc822 text/plain message/rfc822 text/plain message/rfc822 text/plain text/plain """)
def test_mime_digest_format(self): # Make sure that the format of the MIME digest is as expected. self._mlist.digest_size_threshold = 0.6 self._mlist.volume = 1 self._mlist.next_digest_number = 1 self._mlist.send_welcome_message = False # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests # Fill the digest. process = config.handlers['to-digest'].process size = 0 for i in range(1, 5): text = Template("""\ From: [email protected] To: [email protected] Subject: Test message $i List-Post: <*****@*****.**> Here is message $i """).substitute(i=i) msg = message_from_string(text) process(self._mlist, msg, {}) size += len(text) if size >= self._mlist.digest_size_threshold * 1024: break # Run the digest runner to create the MIME and RFC 1153 digests. runner = make_testable_runner(DigestRunner) runner.run() items = get_queue_messages('virgin') self.assertEqual(len(items), 2) # Find the MIME one. mime_digest = None for item in items: if item.msg.is_multipart(): assert mime_digest is None, 'We got two MIME digests' mime_digest = item.msg fp = StringIO() # Verify the structure is what we expect. structure(mime_digest, fp) self.assertMultiLineEqual(fp.getvalue(), """\ multipart/mixed text/plain text/plain message/rfc822 text/plain message/rfc822 text/plain message/rfc822 text/plain message/rfc822 text/plain text/plain """)
def test_patch_membership_with_bogus_address(self): # Try to change a subscription address to one that does not yet exist. with transaction(): subscribe(self._mlist, 'Anne') with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, 'Address not registered')
def test_sync_no_change(self): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) result = self._command.invoke(syncmembers, ( '--no-change', infp.name, 'ant.example.com')) self.assertEqual(result.output, '[DEL] Bart Person' ' <*****@*****.**>\n') self.assertEqual(len(list(self._mlist.members.members)), 2)
def test_no_role(self): # Test for no matching roles. subscribe(self._mlist, 'Anne') result = self._command.invoke(findmember, ( '--role', 'owner', '.', )) self.assertEqual(result.exit_code, 0) self.assertEqual(result.output, '')
def test_patch_membership_with_bogus_address(self): # Try to change a subscription address to one that does not yet exist. with transaction(): subscribe(self._mlist, 'Anne') with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, b'Address not registered')
def test_find_member_error(self): # .find_member() can only return zero or one memberships. Anything # else is an error. subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Anne', MemberRole.owner) with self.assertRaises(TooManyMembersError) as cm: self._service.find_member('*****@*****.**') self.assertEqual(cm.exception.subscriber, '*****@*****.**') self.assertEqual(cm.exception.list_id, None) self.assertEqual(cm.exception.role, None)
def test_already_subscribed_with_display_name(self): subscribe(self._mlist, 'Anne') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) result = self._command.invoke( members, ('--add', infp.name, 'ant.example.com')) self.assertEqual( result.output, 'Already subscribed (skipping): Anne Person <*****@*****.**>\n' )
def test_case_insensitive_pattern(self): # Test that patterns are case insensitive. subscribe(self._mlist, 'Anne') result = self._command.invoke(findmember, ('APerson', )) self.assertEqual(result.exit_code, 0) self.assertEqual( result.output, """\ Email: [email protected] List: ant.example.com MemberRole.member """)
def test_patch_membership_with_unverified_address(self): # Try to change a subscription address to one that is not yet verified. with transaction(): subscribe(self._mlist, 'Anne') self._usermanager.create_address('*****@*****.**') with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, b'Unverified address')
def test_basic_find(self): # Test a simple find of one membership. subscribe(self._mlist, 'Anne') result = self._command.invoke(findmember, ('.', )) self.assertEqual(result.exit_code, 0) self.assertEqual( result.output, """\ Email: [email protected] List: ant.example.com MemberRole.member """)
def test_patch_membership_with_unverified_address(self): # Try to change a subscription address to one that is not yet verified. with transaction(): subscribe(self._mlist, 'Anne') self._usermanager.create_address('*****@*****.**') with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, 'Unverified address')
def test_simple_message(self): # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests make_digest_messages(self._mlist) self._check_virgin_queue() # The digest mbox and all intermediary mboxes must have been removed # (GL #259). self.assertEqual(os.listdir(self._mlist.data_path), [])
def test_sync_no_change(self): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) result = self._command.invoke(members, ( '--no-change', '--sync', infp.name, 'ant.example.com')) self.assertEqual( result.output, 'Warning: The --sync option is deprecated. ' 'Use `mailman syncmembers` instead.\n' '[DEL] Bart Person <*****@*****.**>\n')
def test_only_role(self): # Test only finding requested role. subscribe(self._mlist, 'Anne') subscribe(self._mlistb, 'Bart', role=MemberRole.owner) result = self._command.invoke(findmember, ('--role', 'owner', '.')) self.assertEqual(result.exit_code, 0) self.assertEqual( result.output, """\ Email: [email protected] List: bee.example.com MemberRole.owner """)
def test_override_yes_admin_notify(self): subscribe(self._mlist, 'Anne') self._mlist.admin_notify_mchanges = True with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('', file=infp) result = self._command.invoke(syncmembers, ( '-A', infp.name, 'ant.example.com')) self.assertEqual(result.output, '[DEL] Anne Person <*****@*****.**>\n') self.assertEqual(result.exit_code, 0) members = list(self._mlist.members.members) self.assertEqual(len(members), 0) get_queue_messages('virgin', expected_count=0)
def test_already_subscribed_with_display_name(self): subscribe(self._mlist, 'Anne') outfp = StringIO() with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) self.args.list = ['ant.example.com'] self.args.input_filename = infp.name with patch('builtins.print', partial(print, file=outfp)): self.command.process(self.args) self.assertEqual( outfp.getvalue(), 'Already subscribed (skipping): Anne Person <*****@*****.**>\n' )
def test_sync_empty_tuple(self): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) print('""', file=infp) result = self._command.invoke(syncmembers, ( '--no-change', infp.name, 'ant.example.com')) self.assertEqual(result.output, "Cannot parse as valid email " "address (skipping): \"\"\n" "[DEL] Bart Person " "<*****@*****.**>\n") self.assertEqual(len(list(self._mlist.members.members)), 2)
def test_sync_del_upper_case_email(self): subscribe(self._mlist, 'Bart', email='*****@*****.**') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('<*****@*****.**>', file=infp) result = self._command.invoke(syncmembers, ( infp.name, 'ant.example.com')) self.assertEqual( result.output, '[ADD] [email protected]\n' '[DEL] Bart Person <*****@*****.**>\n') members = list(self._mlist.members.members) self.assertEqual(len(members), 1) self.assertEqual(members[0].address.email, '*****@*****.**')
def test_sync_commented_lines(self): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) print('#Bart Person <*****@*****.**>', file=infp) result = self._command.invoke(syncmembers, ( infp.name, 'ant.example.com')) self.assertEqual(result.output, '[DEL] Bart Person' ' <*****@*****.**>\n') members = list(self._mlist.members.members) self.assertEqual(len(members), 1) self.assertEqual(members[0].address.email, '*****@*****.**')
def test_patch_membership_of_preferred_address(self): # Try to change a subscription to an address when the user is # subscribed via their preferred address. with transaction(): subscribe(self._mlist, 'Anne') anne = self._usermanager.create_address('*****@*****.**') anne.verified_on = now() with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, 'Address is not controlled by user')
def test_sync_nothing_to_do(self): subscribe(self._mlist, 'Anne') subscribe(self._mlist, 'Bart') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('Anne Person <*****@*****.**>', file=infp) print('Bart Person <*****@*****.**>', file=infp) result = self._command.invoke(syncmembers, ( infp.name, 'ant.example.com')) self.assertEqual(result.output, "Nothing to do\n") members = list(self._mlist.members.members) self.assertEqual(len(members), 2) addresses = [member.address.original_email for member in members] self.assertIn('*****@*****.**', addresses) self.assertIn('*****@*****.**', addresses)
def test_sync_no_display_name(self): subscribe(self._mlist, 'Bart') subscribe(self._mlist, 'Cate', role=MemberRole.nonmember) with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('<*****@*****.**>', file=infp) result = self._command.invoke(syncmembers, ( infp.name, 'ant.example.com')) self.assertEqual( result.output, '[ADD] [email protected]\n' '[DEL] Bart Person <*****@*****.**>\n') members = list(self._mlist.members.members) self.assertEqual(len(members), 1) self.assertEqual(members[0].address.email, '*****@*****.**')
def test_process_digest(self): # MIME digests messages are multiparts. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests make_digest_messages(self._mlist) items = get_queue_messages('virgin', expected_count=2) for item in items: try: cook_headers.process(self._mlist, item.msg, {}) except AttributeError as error: # LP: #1130696 would raise an AttributeError on .sender self.fail(error)
def test_patch_membership_of_preferred_address(self): # Try to change a subscription to an address when the user is # subscribed via their preferred address. with transaction(): subscribe(self._mlist, 'Anne') anne = self._usermanager.create_address('*****@*****.**') anne.verified_on = now() with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, b'Address is not controlled by user')
def test_no_override_goodbye(self): self._mlist.send_goodbye_message = True subscribe(self._mlist, 'Anne') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('', file=infp) result = self._command.invoke(syncmembers, ( infp.name, 'ant.example.com')) self.assertEqual(result.output, '[DEL] Anne Person <*****@*****.**>\n') self.assertEqual(result.exit_code, 0) members = list(self._mlist.members.members) self.assertEqual(len(members), 0) items = get_queue_messages('virgin', expected_count=1) self.assertIn('You have been unsubscribed', str(items[0].msg['subject']))
def test_sync_no_del_upper_case_email(self): subscribe(self._mlist, 'Bart', email='*****@*****.**') with NamedTemporaryFile('w', buffering=1, encoding='utf-8') as infp: print('<*****@*****.**>', file=infp) print('*****@*****.**', file=infp) result = self._command.invoke(syncmembers, ( infp.name, 'ant.example.com')) self.assertEqual( result.output, '[ADD] [email protected]\n') members = list(self._mlist.members.members) self.assertEqual(len(members), 2) addresses = [member.address.original_email for member in members] self.assertIn('*****@*****.**', addresses) self.assertIn('*****@*****.**', addresses)
def test_welcome_message(self): subscribe(self._mlist, 'Anne', email='*****@*****.**') # Now there's one message in the virgin queue. items = get_queue_messages('virgin', expected_count=1) message = items[0].msg self.assertEqual(str(message['subject']), 'Welcome to the "Test List" mailing list') self.assertMultiLineEqual(message.get_payload(), """\ Welcome to the Test List mailing list. Posting address: [email protected] Help and other requests: [email protected] Your name: Anne Person Your address: [email protected] """)
def test_process_digest(self): # MIME digests messages are multiparts. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests make_digest_messages(self._mlist) messages = [bag.msg for bag in get_queue_messages('virgin')] self.assertEqual(len(messages), 2) for msg in messages: try: cook_headers.process(self._mlist, msg, {}) except AttributeError as error: # LP: #1130696 would raise an AttributeError on .sender self.fail(error)
def setUp(self): self._mlist = create_list('*****@*****.**') self._mlist.personalize = Personalization.individual # Make Anne a member of this mailing list. self._anne = subscribe(self._mlist, 'Anne', email='*****@*****.**') # Clear out any results from the previous test. del _deliveries[:] self._msg = mfs("""\ From: [email protected] To: [email protected] Subject: test """) # Set up a personalized footer for decoration. self._template_dir = tempfile.mkdtemp() path = os.path.join(self._template_dir, 'site', 'en', 'member-footer.txt') os.makedirs(os.path.dirname(path)) with open(path, 'w') as fp: print("""\ address : $user_address delivered: $user_delivered_to language : $user_language name : $user_name options : $user_optionsurl """, file=fp) config.push('templates', """ [paths.testing] template_dir: {0} """.format(self._template_dir)) self._mlist.footer_uri = 'mailman:///member-footer.txt' # Let assertMultiLineEqual work without bounds. self.maxDiff = None
def setUp(self): self._mlist = create_list('*****@*****.**') self._member = subscribe(self._mlist, 'Anne', email='*****@*****.**') self._msg = mfs("""\ From: [email protected] To: [email protected] Subject: You bounced Message-ID: <first> """) # Set up the translation context. self._var_dir = tempfile.mkdtemp() xx_template_path = os.path.join( self._var_dir, 'templates', 'site', 'xx', 'probe.txt') os.makedirs(os.path.dirname(xx_template_path)) config.push('xx template dir', """\ [paths.testing] var_dir: {0} """.format(self._var_dir)) language_manager = getUtility(ILanguageManager) language_manager.add('xx', 'utf-8', 'Freedonia') self._member.preferences.preferred_language = 'xx' with open(xx_template_path, 'w') as fp: print("""\ blah blah blah $listname $address $optionsurl $owneraddr """, file=fp) # Let assertMultiLineEqual work without bounds. self.maxDiff = None
def test_issue141_one_last_digest(self): # Currently DigestMode.summary_digests are equivalent to mime_digests. self._mlist.send_welcome_message = False bart = subscribe(self._mlist, 'Bart') self._mlist.send_one_last_digest_to( bart.address, DeliveryMode.summary_digests) make_digest_messages(self._mlist) # There should be one message in the outgoing queue, destined for # Bart, formatted as a MIME digest. items = get_queue_messages('virgin') self.assertEqual(len(items), 1) # Bart is the only recipient. self.assertEqual(items[0].msgdata['recipients'], set(['*****@*****.**'])) # The message is a MIME digest, with the structure we expect. fp = StringIO() structure(items[0].msg, fp) self.assertMultiLineEqual(fp.getvalue(), """\ multipart/mixed text/plain text/plain message/rfc822 text/plain text/plain """)
def test_welcome_message(self): subscribe(self._mlist, 'Anne', email='*****@*****.**') # Now there's one message in the virgin queue. messages = get_queue_messages('virgin') self.assertEqual(len(messages), 1) message = messages[0].msg self.assertEqual(str(message['subject']), 'Welcome to the "Test List" mailing list') self.assertMultiLineEqual(message.get_payload(), """\ Welcome to the Test List mailing list. Posting address: [email protected] Help and other requests: [email protected] Your name: Anne Person Your address: [email protected] Your options: http://example.com/[email protected] """)
def test_get_list_member_id_by_email(self): with transaction(): subscribe(self._mlist, 'Anne', email="*****@*****.**") response, headers = call_api( 'http://*****:*****@example.com') self.assertEqual(response['member_id'], '00000000000000000000000000000001') self.assertEqual( response['self_link'], 'http://*****:*****@example.com')
def test_all_preferences(self): with transaction(): member = subscribe(self._mlist, 'Anne') member.preferences.delivery_mode = DeliveryMode.summary_digests response, headers = call_api( 'http://localhost:9001/3.1/members' '/00000000000000000000000000000001/all/preferences') self.assertEqual(response['delivery_mode'], 'summary_digests')
def setUp(self): self._mlist = create_list('*****@*****.**') self._mlist.send_welcome_message = False self._mailbox_path = os.path.join(self._mlist.data_path, 'digest.mmdf') # The mailing list needs at least one digest recipient. member = subscribe(self._mlist, 'Anne') member.preferences.delivery_mode = DeliveryMode.plaintext_digests self._subject_number = 1 self._runner = make_testable_runner(DigestRunner, 'digest')
def test_welcome_message(self): subscribe(self._mlist, "Anne", email="*****@*****.**") # Now there's one message in the virgin queue. items = get_queue_messages("virgin", expected_count=1) message = items[0].msg self.assertEqual(str(message["subject"]), 'Welcome to the "Test List" mailing list') self.assertMultiLineEqual( message.get_payload(), """\ Welcome to the Test List mailing list. Posting address: [email protected] Help and other requests: [email protected] Your name: Anne Person Your address: [email protected] Your options: http://example.com/[email protected] """, )
def setUp(self): self._mlist = create_list('*****@*****.**') self._mlist.digests_enabled = True self._mlist.digest_size_threshold = 100000 self._mlist.send_welcome_message = False self._command = Digests() self._handler = config.handlers['to-digest'] self._runner = make_testable_runner(DigestRunner, 'digest') # The mailing list needs at least one digest recipient. member = subscribe(self._mlist, 'Anne') member.preferences.delivery_mode = DeliveryMode.plaintext_digests
def setUp(self): self._mlist = create_list('*****@*****.**') self._mlist.send_welcome_message = False self._member = subscribe(self._mlist, 'Anne', email='*****@*****.**') self._msg = mfs("""\ From: [email protected] To: [email protected] Subject: You bounced Message-ID: <first> """)
def test_non_ascii_message(self): # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests msg = Message() msg['From'] = '*****@*****.**' msg['To'] = '*****@*****.**' msg['Content-Type'] = 'multipart/mixed' msg.attach(MIMEText('message with non-ascii chars: \xc3\xa9', 'plain', 'utf-8')) mbox = digest_mbox(self._mlist) mbox.add(msg.as_string()) # Use any error logs as the error message if the test fails. error_log = LogFileMark('mailman.error') make_digest_messages(self._mlist, msg) # The runner will send the file to the shunt queue on exception. self.assertEqual(len(self._shuntq.files), 0, error_log.read()) self._check_virgin_queue()
def test_role_moderator(self): subscribe(self._mlist, 'Anne', role=MemberRole.owner) subscribe(self._mlist, 'Bart', role=MemberRole.moderator) subscribe(self._mlist, 'Cate', role=MemberRole.nonmember) subscribe(self._mlist, 'Dave', role=MemberRole.member) self.args.list = ['ant.example.com'] self.args.role = 'moderator' with NamedTemporaryFile('w', encoding='utf-8') as outfp: self.args.output_filename = outfp.name self.command.process(self.args) with open(outfp.name, 'r', encoding='utf-8') as infp: lines = infp.readlines() self.assertEqual(len(lines), 1) self.assertEqual(lines[0], 'Bart Person <*****@*****.**>\n')
def test_digest_messages(self): # In LP: #1130697, the digest runner creates MIME digests using the # stdlib MIMEMutlipart class, however this class does not have the # extended attributes we require (e.g. .sender). The fix is to use a # subclass of MIMEMultipart and our own Message subclass; this adds # back the required attributes. (LP: #1130696) self._mlist.send_welcome_message = False # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests # Start by creating the raw ingredients for the digests. This also # runs the digest runner, thus producing the digest messages into the # virgin queue. make_digest_messages(self._mlist) # Run the virgin queue processor, which runs the cook-headers and # to-outgoing handlers. This should produce no error. error_log = LogFileMark('mailman.error') runner = make_testable_runner(VirginRunner, 'virgin') runner.run() error_text = error_log.read() self.assertEqual(len(error_text), 0, error_text) self.assertEqual(len(get_queue_messages('shunt')), 0) messages = get_queue_messages('out') self.assertEqual(len(messages), 2) # Which one is the MIME digest? mime_digest = None for bag in messages: if bag.msg.get_content_type() == 'multipart/mixed': assert mime_digest is None, 'Found two MIME digests' mime_digest = bag.msg # The cook-headers handler ran. self.assertIn('x-mailman-version', mime_digest) self.assertEqual(mime_digest['precedence'], 'list') # The list's -request address is the original sender. self.assertEqual(bag.msgdata['original_sender'], '*****@*****.**')
def test_no_welcome_message_to_moderators(self): # Welcome messages go only to mailing list members, not to moderators. subscribe(self._mlist, "Anne", MemberRole.moderator, "*****@*****.**") # There is no welcome message in the virgin queue. get_queue_messages("virgin", expected_count=0)
def test_multilingual_digest(self): # When messages come in with a content-type character set different # than that of the list's preferred language, recipients will get an # internationalized digest. # # Subscribe some users receiving digests. anne = subscribe(self._mlist, 'Anne') anne.preferences.delivery_mode = DeliveryMode.mime_digests bart = subscribe(self._mlist, 'Bart') bart.preferences.delivery_mode = DeliveryMode.plaintext_digests msg = mfs("""\ From: [email protected] To: [email protected] Subject: =?iso-2022-jp?b?GyRCMGxIVhsoQg==?= MIME-Version: 1.0 Content-Type: text/plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit \x1b$B0lHV\x1b(B """) self._process(self._mlist, msg, {}) self._runner.run() # There are two digests in the virgin queue; one is the MIME digest # and the other is the RFC 1153 digest. messages = get_queue_messages('virgin') self.assertEqual(len(messages), 2) if messages[0].msg.is_multipart(): mime, rfc1153 = messages[0].msg, messages[1].msg else: rfc1153, mime = messages[0].msg, messages[1].msg # The MIME version contains a mix of French and Japanese. The digest # chrome added by Mailman is in French. self.assertEqual(mime['subject'].encode(), '=?iso-8859-1?q?Groupe_Test=2C_Vol_1=2C_Parution_1?=') self.assertEqual(str(mime['subject']), 'Groupe Test, Vol 1, Parution 1') # The first subpart contains the iso-8859-1 masthead. masthead = mime.get_payload(0).get_payload(decode=True).decode( 'iso-8859-1') self.assertMultiLineEqual(masthead.splitlines()[0], 'Envoyez vos messages pour la liste Test à') # The second subpart contains the utf-8 table of contents. self.assertEqual(mime.get_payload(1)['content-description'], "Today's Topics (1 messages)") toc = mime.get_payload(1).get_payload(decode=True).decode('utf-8') self.assertMultiLineEqual(toc.splitlines()[0], 'Thèmes du jour :') # The third subpart contains the posted message in Japanese. self.assertEqual(mime.get_payload(2).get_content_type(), 'message/rfc822') post = mime.get_payload(2).get_payload(0) self.assertEqual(post['subject'], '=?iso-2022-jp?b?GyRCMGxIVhsoQg==?=') # Compare the bytes so that this module doesn't contain string # literals in multiple incompatible character sets. self.assertEqual(post.get_payload(decode=True), b'\x1b$B0lHV\x1b(B\n') # The RFC 1153 digest will have the same subject, but its payload will # be recast into utf-8. self.assertEqual(str(rfc1153['subject']), 'Groupe Test, Vol 1, Parution 1') self.assertEqual(rfc1153.get_charset(), 'utf-8') lines = rfc1153.get_payload(decode=True).decode('utf-8').splitlines() self.assertEqual(lines[0], 'Envoyez vos messages pour la liste Test à')
def test_send_digests_for_all_lists(self): # Populate ant's digest. msg = mfs("""\ To: [email protected] From: [email protected] Subject: message 1 """) self._handler.process(self._mlist, msg, {}) del msg['subject'] msg['subject'] = 'message 2' self._handler.process(self._mlist, msg, {}) # Create the second list. bee = create_list('*****@*****.**') bee.digests_enabled = True bee.digest_size_threshold = 100000 bee.send_welcome_message = False member = subscribe(bee, 'Bart') member.preferences.delivery_mode = DeliveryMode.plaintext_digests # Populate bee's digest. msg = mfs("""\ To: [email protected] From: [email protected] Subject: message 3 """) self._handler.process(bee, msg, {}) del msg['subject'] msg['subject'] = 'message 4' self._handler.process(bee, msg, {}) # There are no digests for either list already being sent, but the # mailing lists do have a digest mbox collecting messages. ant_mailbox_path = os.path.join(self._mlist.data_path, 'digest.mmdf') self.assertGreater(os.path.getsize(ant_mailbox_path), 0) # Check bee's digest. bee_mailbox_path = os.path.join(bee.data_path, 'digest.mmdf') self.assertGreater(os.path.getsize(bee_mailbox_path), 0) # Both. get_queue_messages('digest', expected_count=0) # Process all mailing list digests by not setting any arguments. args = FakeArgs() args.send = True self._command.process(args) self._runner.run() # Now, neither list has a digest mbox and but there are plaintext # digest in the outgoing queue for both. self.assertFalse(os.path.exists(ant_mailbox_path)) self.assertFalse(os.path.exists(bee_mailbox_path)) items = get_queue_messages('virgin', expected_count=2) # Figure out which digest is going to ant and which to bee. if items[0].msg['to'] == '*****@*****.**': ant = items[0].msg bee = items[1].msg else: assert items[0].msg['to'] == '*****@*****.**' ant = items[1].msg bee = items[0].msg # Check ant's digest. digest_contents = str(ant) self.assertIn('Subject: message 1', digest_contents) self.assertIn('Subject: message 2', digest_contents) # Check bee's digest. digest_contents = str(bee) self.assertIn('Subject: message 3', digest_contents) self.assertIn('Subject: message 4', digest_contents)
def test_cannot_get_member_id_by_int(self): with transaction(): subscribe(self._mlist, 'Anne') with self.assertRaises(HTTPError) as cm: call_api('http://localhost:9001/3.1/members/1') self.assertEqual(cm.exception.code, 404)