def test_70af5a4e5790_digests(self): IDS_TO_DIGESTABLE = [ (1, True), (2, False), (3, False), (4, True), ] mlist_table = sa.sql.table( 'mailinglist', sa.sql.column('id', sa.Integer), sa.sql.column('digests_enabled', sa.Boolean) ) # Downgrading. with transaction(): for table_id, enabled in IDS_TO_DIGESTABLE: config.db.store.execute(mlist_table.insert().values( id=table_id, digests_enabled=enabled)) with transaction(): alembic.command.downgrade(alembic_cfg, '47294d3a604') results = config.db.store.execute( 'SELECT id, digestable FROM mailinglist').fetchall() self.assertEqual(results, IDS_TO_DIGESTABLE) # Upgrading. with transaction(): alembic.command.upgrade(alembic_cfg, '70af5a4e5790') results = config.db.store.execute( 'SELECT id, digests_enabled FROM mailinglist').fetchall() self.assertEqual(results, IDS_TO_DIGESTABLE)
def test_delete_orphans(self): # When users are deleted, their UIDs are generally not deleted. We # never delete rows from that table in order to guarantee no # duplicates. However, some external testing frameworks want to be # able to reset the UID table, so they can use this interface to do # so. See LP: #1420083. # # Create some users. manager = getUtility(IUserManager) users_by_uid = {} with transaction(): for i in range(10): user = manager.create_user() users_by_uid[user.user_id] = user # The testing infrastructure does not record the UIDs for new # user options, so do that now to mimic the real system. UID.record(user.user_id) # We now have 10 unique uids. self.assertEqual(len(users_by_uid), 10) # Now delete all the users. with transaction(): for user in list(users_by_uid.values()): manager.delete_user(user) # There are still 10 unique uids in the database. self.assertEqual(UID.get_total_uid_count(), 10) # Cull the orphan UIDs. content, response = call_api( 'http://localhost:9001/3.0/reserved/uids/orphans', method='DELETE') self.assertEqual(response.status, 204) # Now there are no uids in the table. config.db.abort() self.assertEqual(UID.get_total_uid_count(), 0)
def test_member_changes_preferred_address(self): with transaction(): anne = self._usermanager.create_user('*****@*****.**') preferred = list(anne.addresses)[0] preferred.verified_on = now() anne.preferred_address = preferred self._mlist.subscribe(anne) # Take a look at Anne's current membership. content, response = call_api('http://*****:*****@example.com') self.assertEqual( entry_0['address'], 'http://*****:*****@example.com') # Anne registers a new address and makes it her preferred address. # There are no changes to her membership. with transaction(): new_preferred = anne.register('*****@*****.**') new_preferred.verified_on = now() anne.preferred_address = new_preferred # Take another look at Anne's current membership. content, response = call_api('http://*****:*****@example.com') self.assertEqual( entry_0['address'], 'http://*****:*****@example.com')
def setUp(self): # Create a fake mailing list and message object self._msg = mfs("""\ To: [email protected] From: [email protected] Subject: Testing the test list Message-ID: <ant> X-Message-ID-Hash: MS6QLWERIJLGCRF44J7USBFDELMNT2BW Tests are better than no tests but the water deserves to be swum. """) with transaction(): self._mlist = create_list('*****@*****.**') # Set up a temporary directory for the prototype archiver so that it's # easier to clean up. self._tempdir = tempfile.mkdtemp() config.push('prototype', """ [paths.testing] archive_dir: {0} """.format(self._tempdir)) # Capture the structure of a maildir. self._expected_dir_structure = set( (os.path.join(config.ARCHIVE_DIR, path) for path in ( 'prototype', os.path.join('prototype', self._mlist.fqdn_listname), os.path.join('prototype', self._mlist.fqdn_listname, 'cur'), os.path.join('prototype', self._mlist.fqdn_listname, 'new'), os.path.join('prototype', self._mlist.fqdn_listname, 'tmp'), ))) self._expected_dir_structure.add(config.ARCHIVE_DIR)
def test_absorb_addresses(self): # Absorbing the user absorbs all of the users addresses. I.e. they # are relinked to the absorbing user. anne_preferred = self._anne.preferred_address with transaction(): # This has to happen in a transaction so that both the user and # the preferences objects get valid ids. bart = self._manager.create_user('*****@*****.**', 'Bart Person') bart_secondary = self._manager.create_address( '*****@*****.**') bart.link(bart_secondary) # Absorb the Bart user into Anne. self._anne.absorb(bart) # Bart's primary and secondary addresses are now linked to Anne. anne_addresses = list( address.email for address in self._anne.addresses) self.assertIn('*****@*****.**', anne_addresses) self.assertIn('*****@*****.**', anne_addresses) # Anne's preferred address shouldn't change. self.assertEqual(self._anne.preferred_address, anne_preferred) # Check the reverse linkages by getting Bart's addresses from the user # manager. They should both point back to the Anne user. self.assertEqual( self._manager.get_user('*****@*****.**'), self._anne) self.assertEqual( self._manager.get_user('*****@*****.**'), self._anne) # The Bart user has been deleted. self.assertIsNone(self._manager.get_user_by_id(bart.user_id))
def test_clear(self): header_matches = IHeaderMatchList(self._mlist) header_matches.append('Header', 'pattern') self.assertEqual(len(self._mlist.header_matches), 1) with transaction(): header_matches.clear() self.assertEqual(len(self._mlist.header_matches), 0)
def test_transaction_abort_after_failing_subcommand(self): with transaction(): mlist = create_list('*****@*****.**') mlist.volume = 5 mlist.next_digest_number = 3 mlist.digest_last_sent_at = now() - timedelta(days=60) testargs = ['mailman', 'digests', '-b', '-l', '*****@*****.**', '--send'] output = StringIO() with ExitStack() as resources: enter = resources.enter_context enter(patch('sys.argv', testargs)) enter(patch('sys.stdout', output)) # Force an exception in the subcommand. enter(patch('mailman.commands.cli_digests.maybe_send_digest_now', side_effect=RuntimeError)) # Everything is already initialized. enter(patch('mailman.bin.mailman.initialize')) with self.assertRaises(RuntimeError): main() # Clear the current transaction to force a database reload. config.db.abort() # The volume and number haven't changed. self.assertEqual(mlist.volume, 5) self.assertEqual(mlist.next_digest_number, 3)
def test_successful_login_updates_password(self): # Passlib supports updating the hash when the hash algorithm changes. # When a user logs in successfully, the password will be updated if # necessary. # # Start by hashing Anne's password with a different hashing algorithm # than the one that the REST runner uses by default during testing. config_file = os.path.join(config.VAR_DIR, 'passlib-tmp.config') with open(config_file, 'w') as fp: print("""\ [passlib] schemes = hex_md5 """, file=fp) with configuration('passwords', configuration=config_file): with transaction(): self.anne.password = config.password_context.encrypt('abc123') # Just ensure Anne's password is hashed correctly. self.assertEqual(self.anne.password, 'e99a18c428cb38d5f260853678922e03') # Now, Anne logs in with a successful password. This should change it # back to the plaintext hash. call_api('http://localhost:9001/3.0/users/1/login', { 'cleartext_password': '******', }) self.assertEqual(self.anne.password, '{plaintext}abc123')
def test_lists_are_deleted_when_domain_is_deleted(self): # /domains/<domain> DELETE removes all associated mailing lists. with transaction(): create_list("*****@*****.**") content, response = call_api("http://*****:*****@example.com"))
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_linked_nonmember_findable_after_posting(self): # Like above, a nonmember posts a message to the mailing list. In # this case though, the nonmember already has a user record. They are # findable through the /members/find API using a role of nonmember. with transaction(): self._usermanager.create_user('*****@*****.**') self._go("""\ From: [email protected] To: [email protected] Subject: Nonmember post Message-ID: <alpha> Some text. """) # Now use the REST API to try to find the nonmember. response, content = call_api( 'http://*****:*****@example.com') self.assertEqual( nonmember['address'], 'http://*****:*****@example.com') # There is a user key in the JSON data because the address had # previously been linked to a user record. self.assertEqual(nonmember['user'], 'http://localhost:9001/3.0/users/1')
def test_absorb_duplicates(self): # Duplicate memberships, where the list-id and role match, are # ignored. Here we subscribe Anne to the test list as a member, and # Bart as both a member and an owner. Anne's member membership # remains unchanged, but she gains an owner membership. with transaction(): bart = self._manager.create_user('*****@*****.**') set_preferred(bart) self._mlist.subscribe(self._anne, MemberRole.member) self._mlist.subscribe(bart, MemberRole.member) self._mlist.subscribe(bart, MemberRole.owner) # There are now three memberships. all_members = list(self._manager.members) self.assertEqual(len(all_members), 3, all_members) # Do the absorption. self._anne.absorb(bart) # There are now only 2 memberships, both owned by Anne. all_members = list(self._manager.members) self.assertEqual(len(all_members), 2, all_members) memberships = set([ (member.list_id, member.role, member.address.email) for member in all_members ]) self.assertEqual(memberships, set([ ('test.example.com', MemberRole.member, '*****@*****.**'), ('test.example.com', MemberRole.owner, '*****@*****.**'), ]))
def test_accept_by_moderator_clears_request_queue(self): # After accepting a message held for moderator approval, there are no # more requests to handle. # # We start with nothing in the queue. content, response = call_api( 'http://*****:*****@example.com/requests') self.assertEqual(content['total_size'], 0) # Anne tries to subscribe to a list that only requests moderator # approval. with transaction(): self._mlist.subscription_policy = SubscriptionPolicy.moderate token, token_owner, member = self._registrar.register( self._anne, pre_verified=True, pre_confirmed=True) # There's now one request in the queue, and it's waiting on moderator # approval. content, response = call_api( 'http://*****:*****@example.com/requests') self.assertEqual(content['total_size'], 1) json = content['entries'][0] self.assertEqual(json['token_owner'], 'moderator') self.assertEqual(json['email'], '*****@*****.**') # The moderator approves the request. url = 'http://*****:*****@example.com/requests/{}' content, response = call_api(url.format(token), {'action': 'accept'}) self.assertEqual(response.status, 204) # And now the request queue is empty. content, response = call_api( 'http://*****:*****@example.com/requests') self.assertEqual(content['total_size'], 0)
def test_reject(self): # POST to the request to reject it. This leaves a bounce message in # the virgin queue. with transaction(): token, token_owner, member = self._registrar.register(self._anne) # Anne's subscription request got held. self.assertIsNone(member) # Clear out the virgin queue, which currently contains the # confirmation message sent to Anne. get_queue_messages('virgin') url = 'http://*****:*****@example.com/requests/{}' content, response = call_api(url.format(token), dict( action='reject', )) self.assertEqual(response.status, 204) # Anne is not a member. self.assertIsNone(self._mlist.members.get_member('*****@*****.**')) # The request URL no longer exists. with self.assertRaises(HTTPError) as cm: call_api(url.format(token), dict( action='reject', )) self.assertEqual(cm.exception.code, 404) # And the rejection message to Anne is now in the virgin queue. items = get_queue_messages('virgin') self.assertEqual(len(items), 1) message = items[0].msg self.assertEqual(message['From'], '*****@*****.**') self.assertEqual(message['To'], '*****@*****.**') self.assertEqual(message['Subject'], 'Request to mailing list "Ant" rejected')
def reset_the_world(): """Reset everything: * Clear out the database * Remove all residual queue and digest files * Clear the message store * Reset the global style manager This should be as thorough a reset of the system as necessary to keep tests isolated. """ # Reset the database between tests. config.db._reset() # Remove any digest files. for dirpath, dirnames, filenames in os.walk(config.LIST_DATA_DIR): for filename in filenames: if filename.endswith('.mmdf'): os.remove(os.path.join(dirpath, filename)) # Remove all residual queue files. for dirpath, dirnames, filenames in os.walk(config.QUEUE_DIR): for filename in filenames: os.remove(os.path.join(dirpath, filename)) # Clear out messages in the message store. message_store = getUtility(IMessageStore) with transaction(): for message in message_store.messages: message_store.delete_message(message['message-id']) # Reset the global style manager. getUtility(IStyleManager).populate() # Remove all dynamic header-match rules. config.chains['header-match'].flush()
def test_defer(self): # Defer the decision for some other moderator. with transaction(): token, token_owner, member = self._registrar.register(self._anne) # Anne's subscription request got held. self.assertIsNone(member) url = 'http://*****:*****@example.com/requests/{}' content, response = call_api(url.format(token), dict( action='defer', )) self.assertEqual(response.status, 204) # Anne is not a member. self.assertIsNone(self._mlist.members.get_member('*****@*****.**')) # The request URL still exists. content, response = call_api(url.format(token), dict( action='defer', )) self.assertEqual(response.status, 204) # And now we can accept it. content, response = call_api(url.format(token), dict( action='accept', )) self.assertEqual(response.status, 204) # Anne is a member. self.assertEqual( self._mlist.members.get_member('*****@*****.**').address, self._anne) # The request URL no longer exists. with self.assertRaises(HTTPError) as cm: call_api(url.format(token), dict( action='accept', )) self.assertEqual(cm.exception.code, 404)
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_get_digest_volume_frequency(self): with transaction(): self._mlist.digest_volume_frequency = DigestFrequency.yearly resource, response = call_api( 'http://localhost:9001/3.0/lists/ant.example.com/config' '/digest_volume_frequency') self.assertEqual(resource['digest_volume_frequency'], 'yearly')
def test_get_digests_enabled(self): with transaction(): self._mlist.digests_enabled = False resource, response = call_api( 'http://localhost:9001/3.0/lists/ant.example.com/config' '/digests_enabled') self.assertFalse(resource['digests_enabled'])
def setUp(self): # Create a fake mailing list and message object. self._msg = mfs("""\ To: [email protected] From: [email protected] Subject: Testing the test list Message-ID: <ant> Message-ID-Hash: MS6QLWERIJLGCRF44J7USBFDELMNT2BW Tests are better than no tests but the water deserves to be swum. """) with transaction(): self._mlist = create_list('*****@*****.**') tempdir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, tempdir) # Here's the command to execute our fake MHonArc process. shutil.copy( resource_filename('mailman.archiving.tests', 'fake_mhonarc.py'), tempdir) self._output_file = os.path.join(tempdir, 'output.txt') command = '{} {} {}'.format( sys.executable, os.path.join(tempdir, 'fake_mhonarc.py'), self._output_file) # Write an external configuration file which points the command at our # fake MHonArc process. self._cfg = os.path.join(tempdir, 'mhonarc.cfg') with open(self._cfg, 'w', encoding='utf-8') as fp: print("""\ [general] base_url: http://$hostname/archives/$fqdn_listname command: {command} """.format(command=command), file=fp)
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 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_request_is_not_held_message(self): requests = IListRequests(self._mlist) with transaction(): request_id = requests.hold_request(RequestType.subscription, 'foo') with self.assertRaises(HTTPError) as cm: call_api('http://localhost:9001/3.0/lists/ant.example.com' '/held/{}'.format(request_id)) self.assertEqual(cm.exception.code, 404)
def test_get_goodbye_message_uri(self): with transaction(): self._mlist.goodbye_message_uri = 'mailman:///goodbye.txt' resource, response = call_api( 'http://localhost:9001/3.0/lists/ant.example.com/config' '/goodbye_message_uri') self.assertEqual( resource['goodbye_message_uri'], 'mailman:///goodbye.txt')
def test_unverify_bad_request(self): # Too many segments after /verify. with transaction(): anne = getUtility(IUserManager).create_address('*****@*****.**') self.assertEqual(anne.verified_on, None) with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com/unverify/foo', {}) self.assertEqual(cm.exception.code, 400)
def test_address_user_id_is_hex(self): user_manager = getUtility(IUserManager) with transaction(): user_manager.create_user('*****@*****.**', 'Anne') response, headers = call_api( 'http://*****:*****@example.com') self.assertEqual( response['user'], 'http://localhost:9001/3.1/users/00000000000000000000000000000001')
def setUp(self): # Create a user and link 10 addresses to that user. self.manager = getUtility(IUserManager) with transaction(): anne = self.manager.create_user('*****@*****.**', 'Anne Person') for i in range(10): email = 'a{:02d}@example.com'.format(i) address = self.manager.create_address(email) anne.link(address)
def test_address_with_user(self): # An address which is already linked to a user has a 'user' key in the # JSON representation. with transaction(): getUtility(IUserManager).create_user('*****@*****.**') json, headers = call_api( 'http://*****:*****@example.com') self.assertEqual(headers['status'], '200') self.assertEqual(json['user'], 'http://localhost:9001/3.0/users/1')
def test_user_subresource_on_unlinked_address(self): # Trying to access the 'user' subresource on an address that is not # linked to a user will return a 404 error. with transaction(): getUtility(IUserManager).create_address('*****@*****.**') with self.assertRaises(HTTPError) as cm: call_api( 'http://*****:*****@example.com/user') self.assertEqual(cm.exception.code, 404)
def test_address_without_user(self): # The 'user' key is missing from the JSON representation of an address # with no linked user. with transaction(): getUtility(IUserManager).create_address('*****@*****.**') json, headers = call_api( 'http://*****:*****@example.com') self.assertEqual(headers['status'], '200') self.assertNotIn('user', json)
def test_get_a_url(self): with transaction(): getUtility(ITemplateManager).set('list:user:notice:welcome', 'ant.example.com', 'http://example.com/welcome') json, response = call_api( 'http://localhost:9001/3.1/lists/ant.example.com/uris' '/list:user:notice:welcome') self.assertEqual(response.status_code, 200) self.assertEqual( json, { 'http_etag': '"36f8bef800cfd278f097c61c5892a34c0650f4aa"', 'self_link': ('http://localhost:9001/3.1/lists/ant.example.com' '/uris/list:user:notice:welcome'), 'uri': 'http://example.com/welcome', })
def test_accept(self): # POST to the request to accept it. with transaction(): token, token_owner, member = self._registrar.register(self._anne) # Anne's subscription request got held. self.assertIsNone(member) url = 'http://*****:*****@example.com/requests/{}' content, response = call_api(url.format(token), dict(action='accept', )) self.assertEqual(response.status, 204) # Anne is a member. self.assertEqual( self._mlist.members.get_member('*****@*****.**').address, self._anne) # The request URL no longer exists. with self.assertRaises(HTTPError) as cm: call_api(url.format(token), dict(action='accept', )) self.assertEqual(cm.exception.code, 404)
def test_delete_user_twice(self): # You cannot DELETE a user twice, either by address or user id. with transaction(): anne = getUtility(IUserManager).create_user( '*****@*****.**', 'Anne Person') user_id = anne.user_id content, response = call_api( 'http://*****:*****@example.com', method='DELETE') self.assertEqual(response.status, 204) with self.assertRaises(HTTPError) as cm: call_api('http://*****:*****@example.com', method='DELETE') self.assertEqual(cm.exception.code, 404) with self.assertRaises(HTTPError) as cm: call_api('http://localhost:9001/3.0/users/{}'.format(user_id), method='DELETE') self.assertEqual(cm.exception.code, 404)
def setUp(self): self._mlist = create_list('*****@*****.**') # Add some owners, moderators, and members manager = getUtility(IUserManager) with transaction(): anne = manager.create_address('*****@*****.**') bart = manager.create_address('*****@*****.**') cris = manager.create_address('*****@*****.**') dave = manager.create_address('*****@*****.**') self._mlist.subscribe(anne, MemberRole.member) self._mlist.subscribe(anne, MemberRole.owner) self._mlist.subscribe(bart, MemberRole.moderator) self._mlist.subscribe(bart, MemberRole.owner) self._mlist.subscribe(cris, MemberRole.moderator) self._mlist.subscribe(dave, MemberRole.member) self._inq = make_testable_runner(IncomingRunner, 'in') self._pipelineq = make_testable_runner(PipelineRunner, 'pipeline') self._outq = make_testable_runner(OutgoingRunner, 'out')
def test_address_added_to_user(self): # An address is added to a user record. user_manager = getUtility(IUserManager) with transaction(): anne = user_manager.create_user('*****@*****.**') json, response = call_api( 'http://*****:*****@example.com/addresses', { 'email': '*****@*****.**', }) self.assertIn('*****@*****.**', [addr.email for addr in anne.addresses]) self.assertEqual(response.status_code, 201) self.assertEqual( response.headers['location'], 'http://*****:*****@example.org') # The address has no display name. anne_person = user_manager.get_address('*****@*****.**') self.assertEqual(anne_person.display_name, '')
def test_join_as_user_with_preferred_address(self): with transaction(): anne = self._usermanager.create_user('*****@*****.**') _set_preferred(anne) self._mlist.subscribe(anne) content, response = call_api('http://*****:*****@example.com') self.assertEqual( entry_0['address'], 'http://*****:*****@example.com') self.assertEqual(entry_0['list_id'], 'test.example.com')
def test_user_subresource_put(self): # By PUTing to the 'user' resource, you can change the user that an # address is linked to. user_manager = getUtility(IUserManager) with transaction(): anne = user_manager.create_user('*****@*****.**', 'Anne') bart = user_manager.create_user(display_name='Bart') json, response = call_api( 'http://*****:*****@example.com/user', { 'user_id': bart.user_id.hex, }, method='PUT') self.assertEqual(response.status_code, 200) self.assertEqual(anne.addresses, []) self.assertEqual([address.email for address in bart.addresses], ['*****@*****.**']) self.assertEqual(bart, user_manager.get_address('*****@*****.**').user)
def test_list_held_requests(self): # We can view all the held requests. with transaction(): token_1, token_owner, member = self._registrar.register(self._anne) # Anne's subscription request got held. self.assertIsNotNone(token_1) self.assertIsNone(member) token_2, token_owner, member = self._registrar.register(self._bart) self.assertIsNotNone(token_2) self.assertIsNone(member) json, response = call_api( 'http://*****:*****@example.com/requests') self.assertEqual(response.status_code, 200) self.assertEqual(json['total_size'], 2) tokens = set(entry['token'] for entry in json['entries']) self.assertEqual(tokens, {token_1, token_2}) emails = set(entry['email'] for entry in json['entries']) self.assertEqual(emails, {'*****@*****.**', '*****@*****.**'})
def test_user_subresource_cannot_put_int(self): # If the address is not yet linked to a user, POSTing a user id to the # 'user' subresource links the address to the given user. In # API 3.1, the user id must be the hex representation. user_manager = getUtility(IUserManager) with transaction(): anne = user_manager.create_user('*****@*****.**', 'Anne') user_manager.create_address('*****@*****.**') with self.assertRaises(HTTPError) as cm: call_api( 'http://*****:*****@example.com/user', { 'user_id': anne.user_id.int, }, method='PUT') self.assertEqual(cm.exception.code, 400) self.assertEqual( cm.exception.reason, 'Invalid Parameter "user_id":' ' badly formed hexadecimal UUID string.')
def test_subject_encoding_error(self): # GL#383: messages with badly encoded Subject headers crash the REST # server. self._msg = mfs("""\ From: [email protected] To: [email protected] Subject: =?GB2312?B?saa9o7fmtNPEpbVaQ2h1o6zDt7uoz+PX1L/guq7AtKGj?= Message-ID: <alpha> Something else. """) with transaction(): held_id = hold_message(self._mlist, self._msg) json, response = call_api( 'http://*****:*****@example.com/held') self.assertEqual(response.status_code, 200) self.assertEqual(json['total_size'], 1) self.assertEqual(json['entries'][0]['request_id'], held_id)
def test_existing_address_absorb(self): # Trying to add an existing address causes a merge if the # 'absorb_existing' flag is present. user_manager = getUtility(IUserManager) with transaction(): anne = user_manager.create_user('*****@*****.**') user_manager.create_user('*****@*****.**') json, response = call_api( 'http://*****:*****@example.com/addresses', { 'email': '*****@*****.**', 'absorb_existing': True, }) self.assertIn('*****@*****.**', [address.email for address in anne.addresses]) self.assertEqual(response.status_code, 201) self.assertEqual( response.headers['location'], 'http://*****:*****@example.com')
def test_mailing_list_with_different_address_and_list_id(self): # A mailing list can be renamed, in which case the list_name # will be different but the list_id will remain the same. # https://gitlab.com/mailman/mailman/issues/428 with transaction(): self._mlist.list_name = 'renamed' self.assertEqual(self._mlist.posting_address, '*****@*****.**') self._lmtp.sendmail( '*****@*****.**', ['*****@*****.**'], """\ From: [email protected] To: [email protected] Message-ID: <ant> Subject: This should be accepted. """) # The message is in the incoming queue but not the command queue. items = get_queue_messages('in', expected_count=1) self.assertEqual(items[0].msgdata['listid'], 'test.example.com')
def test_add_member_with_lower_case_email(self): # LP: #1425359 - Mailman is case-perserving, case-insensitive. This # test subscribes the mixed case address and ensures the lower cased # address can't be added. with transaction(): anne = self._usermanager.create_address('*****@*****.**') self._mlist.subscribe(anne) with self.assertRaises(HTTPError) as cm: call_api( 'http://*****:*****@example.com', 'pre_verified': True, 'pre_confirmed': True, 'pre_approved': True, }) self.assertEqual(cm.exception.code, 409) self.assertEqual(cm.exception.reason, 'Member already subscribed')
def test_create_new_membership_by_hex(self): with transaction(): user = getUtility(IUserManager).create_user('*****@*****.**') set_preferred(user) # Subscribe the user to the mailing list by hex UUID. json, response = call_api( 'http://localhost:9001/3.1/members', { 'list_id': 'ant.example.com', 'subscriber': '00000000000000000000000000000001', 'pre_verified': True, 'pre_confirmed': True, 'pre_approved': True, }) self.assertEqual(response.status_code, 201) self.assertEqual( response.headers['location'], 'http://localhost:9001/3.1/members/00000000000000000000000000000001' )
def test_get_a_url(self): with transaction(): getUtility(ITemplateManager).set('list:user:notice:welcome', 'example.com', 'http://example.com/welcome') resource, response = call_api( 'http://localhost:9001/3.1/domains/example.com/uris' '/list:user:notice:welcome') self.assertEqual(response.status_code, 200) self.assertEqual( resource, { 'http_etag': '"8884a0b3d675b4cb9899a7825daac9db88b70bed"', 'self_link': ('http://localhost:9001/3.1/domains/example.com' '/uris/list:user:notice:welcome'), 'uri': 'http://example.com/welcome', })
def test_user_subresource(self): # For an address which is linked to a user, accessing the user # subresource of the address path returns the user JSON representation. user_manager = getUtility(IUserManager) with transaction(): user_manager.create_user('*****@*****.**', 'Anne') json, response = call_api( 'http://*****:*****@example.com/user') self.assertEqual(response.status_code, 200) self.assertEqual(json['user_id'], 1) self.assertEqual(json['display_name'], 'Anne') user_resource = json['self_link'] self.assertEqual(user_resource, 'http://localhost:9001/3.0/users/1') # The self_link points to the correct user. json, response = call_api(user_resource) self.assertEqual(json['user_id'], 1) self.assertEqual(json['display_name'], 'Anne') self.assertEqual(json['self_link'], user_resource)
def _subscribe_and_add_bounce_event(self, addr, subscribe=True, create=True, context=None): user_mgr = getUtility(IUserManager) with transaction(): if create: anne = user_mgr.create_address(addr) else: anne = user_mgr.get_address(addr) if subscribe: self._mlist.subscribe(anne) self._processor.register(self._mlist, addr, self._msg, where=context) return self._mlist.members.get_member(addr)
def test_get_all_users(self): user_manager = getUtility(IUserManager) with transaction(): user_manager.create_user('*****@*****.**') user_manager.create_user('*****@*****.**') content, response = call_api('http://localhost:9001/3.1/users') entries = content['entries'] self.assertEqual(len(entries), 2) self.assertEqual(entries[0]['user_id'], '00000000000000000000000000000001') self.assertEqual( entries[0]['self_link'], 'http://localhost:9001/3.1/users/00000000000000000000000000000001') self.assertEqual(entries[1]['user_id'], '00000000000000000000000000000002') self.assertEqual( entries[1]['self_link'], 'http://localhost:9001/3.1/users/00000000000000000000000000000002')
def test_existing_address_link_with_arguments(self): # Creating a user with an existing address links them, and the # addition arguments get honored. user_manager = getUtility(IUserManager) with transaction(): user_manager.create_address('*****@*****.**') call_api('http://*****:*****@example.com', display_name='Anne Person', password='******', is_server_owner=True, )) anne = user_manager.get_user('*****@*****.**') self.assertEqual(anne.display_name, 'Anne Person') self.assertTrue(anne.is_server_owner) self.assertEqual(anne.password, '{plaintext}123') self.assertIn('*****@*****.**', [address.email for address in anne.addresses])
def subscribe(mlist, first_name, role=MemberRole.member): """Helper for subscribing a sample person to a mailing list.""" user_manager = getUtility(IUserManager) email = '{0}[email protected]'.format(first_name[0].lower()) full_name = '{0} Person'.format(first_name) with transaction(): person = user_manager.get_user(email) if person is None: address = user_manager.get_address(email) if address is None: person = user_manager.create_user(email, full_name) preferred_address = list(person.addresses)[0] mlist.subscribe(preferred_address, role) else: mlist.subscribe(address, role) else: preferred_address = list(person.addresses)[0] mlist.subscribe(preferred_address, role)
def test_cannot_create_new_membership_by_int(self): with transaction(): user = getUtility(IUserManager).create_user('*****@*****.**') set_preferred(user) # We can't use the int representation of the UUID with API 3.1. with self.assertRaises(HTTPError) as cm: call_api( 'http://localhost:9001/3.1/members', { 'list_id': 'ant.example.com', 'subscriber': '1', 'pre_verified': True, 'pre_confirmed': True, 'pre_approved': True, }) # This is a bad request because the `subscriber` value isn't something # that's known to the system, in API 3.1. It's not technically a 404 # because that's reserved for URL lookups. self.assertEqual(cm.exception.code, 400)
def test_get_member_id_by_hex(self): with transaction(): subscribe(self._mlist, 'Anne') response, headers = call_api( 'http://*****:*****@example.com')
def test_get_list_member_id_by_email(self): with transaction(): subscribe(self._mlist, 'Anne', email="*****@*****.**") json, response = call_api( 'http://*****:*****@example.com') self.assertEqual( json['member_id'], '00000000000000000000000000000001') self.assertEqual( json['self_link'], 'http://*****:*****@example.com')
def test_get_all_uris(self): manager = getUtility(ITemplateManager) with transaction(): manager.set('list:user:notice:welcome', 'ant.example.com', 'http://example.com/welcome') manager.set( 'list:user:notice:goodbye', 'ant.example.com', 'http://example.com/goodbye', 'a user', 'the password', ) json, response = call_api( 'http://localhost:9001/3.1/lists/ant.example.com/uris') self.assertEqual(response.status_code, 200) self.assertEqual(json['start'], 0) self.assertEqual(json['total_size'], 2) self.assertEqual( json['self_link'], 'http://localhost:9001/3.1/lists/ant.example.com/uris') self.assertEqual(json['entries'], [{ 'http_etag': '"6612187ed6604ce54a57405fd66742557391ed4a"', 'name': 'list:user:notice:goodbye', 'password': '******', 'self_link': ('http://localhost:9001/3.1/lists/ant.example.com' '/uris/list:user:notice:goodbye'), 'uri': 'http://example.com/goodbye', 'username': '******', }, { 'http_etag': '"cb1ab5eee2242143d2984edd0487532915ad3a8e"', 'name': 'list:user:notice:welcome', 'self_link': ('http://localhost:9001/3.1/lists/ant.example.com' '/uris/list:user:notice:welcome'), 'uri': 'http://example.com/welcome', }])
def test_delete_all_uris(self): manager = getUtility(ITemplateManager) with transaction(): manager.set( 'list:user:notice:welcome', 'ant.example.com', 'http://example.com/welcome') manager.set( 'list:user:notice:goodbye', 'ant.example.com', 'http://example.com/goodbye', 'a user', 'the password', ) json, response = call_api( 'http://localhost:9001/3.1/lists/ant.example.com/uris', method='DELETE') self.assertEqual(response.status_code, 204) self.assertIsNone( manager.raw('list:user:notice:welcome', 'ant.example.com')) self.assertIsNone( manager.raw('list:user:notice:goodbye', 'ant.example.com'))
def _dispose(self, mlist, msg, msgdata): """See `IRunner`.""" if msgdata.get('envsender') is None: msgdata['envsender'] = mlist.no_reply_address # Ensure that the email addresses of the message's senders are known # to Mailman. This will be used in nonmember posting dispositions. user_manager = getUtility(IUserManager) with transaction(): for sender in msg.senders: try: user_manager.create_address(sender) except ExistingAddressError: pass # Process the message through the mailing list's start chain. start_chain = (mlist.owner_chain if msgdata.get('to_owner', False) else mlist.posting_chain) process(mlist, msg, msgdata, start_chain) # Do not keep this message queued. return False
def test_patch_bad_regexp(self): header_matches = IHeaderMatchList(self._mlist) with transaction(): header_matches.append('header', 'pattern') with self.assertRaises(HTTPError) as cm: call_api( 'http://localhost:9001/3.0/lists/ant.example.com' '/header-matches/0', { 'header': 'header', 'pattern': '+invalid', }, method='PATCH') self.assertEqual(cm.exception.code, 400) self.assertEqual( cm.exception.reason, 'Invalid Parameter "pattern":' ' Expected a valid regexp, got +invalid.') self.assertEqual( cm.exception.reason, 'Invalid Parameter "pattern": ' 'Expected a valid regexp, got +invalid.')
def test_address_and_display_name_added_to_user(self): # Address with a display name is added to the user record. user_manager = getUtility(IUserManager) with transaction(): anne = user_manager.create_user('*****@*****.**') response, content = call_api( 'http://*****:*****@example.com/addresses', { 'email': '*****@*****.**', 'display_name': 'Ann E Person', }) self.assertIn('*****@*****.**', [addr.email for addr in anne.addresses]) self.assertEqual(content['status'], '201') self.assertEqual( content['location'], 'http://*****:*****@example.org') # The address has no display name. anne_person = user_manager.get_address('*****@*****.**') self.assertEqual(anne_person.display_name, 'Ann E Person')
def test_user_subresource_post_new_user(self): # If the address is not yet linked to a user, POSTing to the 'user' # subresources creates a new user object and links it to the address. user_manager = getUtility(IUserManager) with transaction(): anne_addr = user_manager.create_address('*****@*****.**') response, headers = call_api( 'http://*****:*****@example.com/user', { 'display_name': 'Anne', }) self.assertEqual(headers['status'], '201') anne = user_manager.get_user('*****@*****.**') self.assertIsNotNone(anne) self.assertEqual(anne.display_name, 'Anne') self.assertEqual([a.email for a in anne.addresses], ['*****@*****.**']) self.assertEqual(anne_addr.user, anne) self.assertEqual(headers['location'], 'http://localhost:9001/3.0/users/1')
def test_add_unlinked_address_to_user(self): user_manager = getUtility(IUserManager) with transaction(): anne = user_manager.create_user('*****@*****.**') user_manager.create_address('*****@*****.**') response, content = call_api( 'http://*****:*****@example.com/addresses', { 'email': '*****@*****.**', }) self.assertIn('*****@*****.**', [address.email for address in anne.addresses]) self.assertEqual(content['status'], '201') self.assertEqual( content['location'], 'http://*****:*****@example.com') # The address has no display name. anne_person = user_manager.get_address('*****@*****.**') self.assertEqual(anne_person.display_name, '')