class UlearnhubSyncaclFunctionalTests(UlearnHUBBaseTestCase): def setUp(self): conf_dir = os.path.dirname(__file__) self.app = loadapp('config:tests.ini', relative_to=conf_dir) self.testapp = UlearnhubTestApp(self) self.rabbit = RabbitClient(TEST_VHOST_URL) self.rabbit.management.cleanup(delete_all=True) self.rabbit.declare() httpretty.enable() http_mock_info() http_mock_checktoken() create_defaults(self.testapp.testapp.app.registry, BASE_DOMAIN, quiet=True) self.initialize_test_deployment() self.initialize_test_domain() self.patches = [] self.clients = {} def tearDown(self): # Make sure httpretty is disabled httpretty.disable() httpretty.reset() for testpatch in self.patches: testpatch.stop() self.rabbit.get_all('syncacl') self.rabbit.disconnect() for user_clients in self.clients.values(): for client in user_clients: client.disconnect() def assertMessagesInQueue(self, queue, retries=0, expected=None): messages = self.rabbit.get_all(queue) expected_message_count = expected if expected is not None else len( messages) for retry in xrange(retries): if len(messages) < expected_message_count: messages += self.rabbit.get_all(queue) else: break time.sleep(1) if len(messages) < expected_message_count: raise AssertionError( 'Missing messages on queue, expected {}, received {}, retryied {}' .format(expected_message_count, len(messages), retries)) return {item[0]['u']['u']: item[0] for item in messages} def test_domain_syncacl_bad_subscription_permissions(self): """ Given I'm a user without enough subscription permissions on max When I try to execute the service I get a Forbidden exception """ from .mockers.syncacl import batch_subscribe_request from .mockers.syncacl import context as context from .mockers.syncacl import initial_subscriptions as subscriptions aclless_context = context.copy() aclless_context['acls'] = [] http_mock_get_context(aclless_context) http_mock_get_context_subscriptions(subscriptions) self.testapp.post('/api/domains/test/services/syncacl'.format(), json.dumps(batch_subscribe_request), headers=oauth2Header(test_user), status=403) def test_domain_syncacl_bad_context_permissions(self): """ Given I'm a user without enough context permissions on max When I try to execute the service I get a Forbidden exception """ from .mockers.syncacl import batch_subscribe_request from .mockers.syncacl import context as context from .mockers.syncacl import initial_subscriptions as subscriptions http_mock_get_context(context, status=403) http_mock_get_context_subscriptions(subscriptions) self.testapp.post('/api/domains/test/services/syncacl'.format(), json.dumps(batch_subscribe_request), headers=oauth2Header(test_user), status=403) def test_domain_syncacl_initial_subscriptions(self): """ Given a newly created context When a bunch of users and groups acls are synced Then a set of actions is generated to generate needed subscriptions grants and revokes for new subscriptors """ from .mockers.syncacl import batch_subscribe_request from .mockers.syncacl import context as context from .mockers.syncacl import initial_subscriptions as subscriptions from .mockers.syncacl import ldap_test_group, ldap_test_group2, ldap_test_group3 http_mock_get_context(context) http_mock_get_context_subscriptions(subscriptions) self.add_patch(ldap_patch_connect()) self.add_patch(ldap_patch_disconnect()) self.add_patch( ldap_patch_group_search({ 'TestGroup': ldap_test_group, 'TestGroup2': ldap_test_group2, 'TestGroup3': ldap_test_group3 })) self.testapp.post('/api/domains/test/services/syncacl'.format(), json.dumps(batch_subscribe_request), headers=oauth2Header(test_user), status=200) # Index by username to be able to make asserts # This is mandatory, as we cannot assume the order of the queue messages = self.assertMessagesInQueue('syncacl', retries=3, expected=9) # Test group users new subscription without grants self.assertItemsEqual(messages['groupuser1']['d']['tasks'], ['subscribe']) self.assertIn('context', messages['groupuser1']['d']) self.assertItemsEqual(messages['groupuser2']['d']['tasks'], ['subscribe']) self.assertIn('context', messages['groupuser2']['d']) # Test group users new subscription with single grant self.assertItemsEqual(messages['groupuser3']['d']['tasks'], ['subscribe', 'grant']) self.assertItemsEqual(messages['groupuser3']['d']['tasks']['grant'], ['write']) self.assertIn('context', messages['groupuser3']['d']) self.assertItemsEqual(messages['groupuser4']['d']['tasks'], ['subscribe', 'grant']) self.assertItemsEqual(messages['groupuser4']['d']['tasks']['grant'], ['write']) self.assertIn('context', messages['groupuser4']['d']) # Test group users new subscription with single revoke self.assertItemsEqual(messages['groupuser5']['d']['tasks'], ['subscribe', 'revoke']) self.assertItemsEqual(messages['groupuser5']['d']['tasks']['revoke'], ['unsubscribe']) self.assertIn('context', messages['groupuser5']['d']) self.assertItemsEqual(messages['groupuser6']['d']['tasks'], ['subscribe', 'revoke']) self.assertItemsEqual(messages['groupuser6']['d']['tasks']['revoke'], ['unsubscribe']) self.assertIn('context', messages['groupuser6']['d']) # Test single user new subscription with single grant self.assertItemsEqual(messages['testuser1']['d']['tasks'], ['subscribe', 'grant']) self.assertItemsEqual(messages['testuser1']['d']['tasks']['grant'], ['write']) self.assertIn('context', messages['testuser1']['d']) # Test single user new subscription with multiple grant self.assertItemsEqual(messages['testowner']['d']['tasks'], ['subscribe', 'grant']) self.assertItemsEqual(messages['testowner']['d']['tasks']['grant'], ['write', 'flag']) self.assertIn('context', messages['testowner']['d']) # Test cretor grant self.assertItemsEqual(messages['testuser.creator']['d']['tasks'], ['grant']) self.assertItemsEqual( messages['testuser.creator']['d']['tasks']['grant'], ['flag']) self.assertIn('context', messages['testuser1']['d']) def test_domain_syncacl_change_acls(self): """ Given a existing context with subcriptions When a bunch of users and groups acls are synced Then a set of actions is generated to update thouse users subscriptions And the users that have been removed from acl are unsubscribed """ from .mockers.syncacl import batch_subscribe_request2 from .mockers.syncacl import context as context from .mockers.syncacl import existing_subscriptions as subscriptions from .mockers.syncacl import ldap_test_group, ldap_test_group2, ldap_test_group3 http_mock_get_context(context) http_mock_get_context_subscriptions(subscriptions) self.add_patch(ldap_patch_connect()) self.add_patch(ldap_patch_disconnect()) self.add_patch( ldap_patch_group_search({ 'TestGroup': ldap_test_group, 'TestGroup2': ldap_test_group2, 'TestGroup3': ldap_test_group3 })) self.testapp.post('/api/domains/test/services/syncacl'.format(), json.dumps(batch_subscribe_request2), headers=oauth2Header(test_user), status=200) # Index by username to be able to make asserts # This is mandatory, as we cannot assume the order of the queue messages = self.assertMessagesInQueue('syncacl', retries=3, expected=6) # Testuser1 remains untouched self.assertNotIn('testuser1', messages) # Users from gropu 2 remains untouched self.assertNotIn('groupuser3', messages) self.assertNotIn('groupuser4', messages) # Test subscribed group users revoke permission self.assertItemsEqual(messages['groupuser1']['d']['tasks'], ['revoke']) self.assertItemsEqual(messages['groupuser1']['d']['tasks']['revoke'], ['write']) self.assertIn('context', messages['groupuser1']['d']) self.assertItemsEqual(messages['groupuser2']['d']['tasks'], ['revoke']) self.assertItemsEqual(messages['groupuser2']['d']['tasks']['revoke'], ['write']) self.assertIn('context', messages['groupuser2']['d']) # Test subscribed group users unsubscribe self.assertItemsEqual(messages['groupuser5']['d']['tasks'], ['unsubscribe']) self.assertIn('context', messages['groupuser5']['d']) self.assertItemsEqual(messages['groupuser6']['d']['tasks'], ['unsubscribe']) self.assertIn('context', messages['groupuser6']['d']) # Test subscribed single user unsubscribe self.assertItemsEqual(messages['testowner']['d']['tasks'], ['unsubscribe']) self.assertIn('context', messages['testowner']['d']) # Test subscribed user revoke permission self.assertItemsEqual(messages['testuser.creator']['d']['tasks'], ['revoke']) self.assertItemsEqual( messages['testuser.creator']['d']['tasks']['revoke'], ['flag']) self.assertIn('context', messages['testuser.creator']['d']) def test_domain_syncacl_user_overwrites_group_permissions(self): """ Given a existing context with subcriptions When a bunch of users and groups acls are synced And a user from a group acl is also in users acl And both group and user has the same role Then the same and only action is generated """ from .mockers.syncacl import batch_subscribe_request3 from .mockers.syncacl import context as context from .mockers.syncacl import initial_subscriptions as subscriptions from .mockers.syncacl import ldap_test_group4 http_mock_get_context(context) http_mock_get_context_subscriptions(subscriptions) self.add_patch(ldap_patch_connect()) self.add_patch(ldap_patch_disconnect()) self.add_patch( ldap_patch_group_search({'TestGroup4': ldap_test_group4})) self.testapp.post('/api/domains/test/services/syncacl'.format(), json.dumps(batch_subscribe_request3), headers=oauth2Header(test_user), status=200) # Index by username to be able to make asserts # This is mandatory, as we cannot assume the order of the queue messages = self.assertMessagesInQueue('syncacl', retries=3, expected=2) self.assertItemsEqual(messages['groupuser1']['d']['tasks'], ['grant', 'subscribe']) self.assertItemsEqual(messages['groupuser1']['d']['tasks']['grant'], ['write', 'flag']) self.assertIn('context', messages['groupuser1']['d']) # Test subscribed user revoke permission self.assertItemsEqual(messages['testuser.creator']['d']['tasks'], ['grant']) self.assertItemsEqual( messages['testuser.creator']['d']['tasks']['grant'], ['flag']) self.assertIn('context', messages['testuser.creator']['d']) def test_domain_syncacl_user_overwrites_user_permissions(self): """ Given a existing context with subcriptions When a bunch of users and groups acls are synced And a user from a group acl is also in users acl And both group and user has the same role Then the action with more permissions is preserved """ from .mockers.syncacl import batch_subscribe_request4 from .mockers.syncacl import context as context from .mockers.syncacl import initial_subscriptions as subscriptions http_mock_get_context(context) http_mock_get_context_subscriptions(subscriptions) self.testapp.post('/api/domains/test/services/syncacl'.format(), json.dumps(batch_subscribe_request4), headers=oauth2Header(test_user), status=200) # Index by username to be able to make asserts # This is mandatory, as we cannot assume the order of the queue messages = self.assertMessagesInQueue('syncacl', retries=3, expected=1) # Test subscribed user revoke permission, preserves most important role self.assertItemsEqual(messages['testuser.creator']['d']['tasks'], ['grant']) self.assertItemsEqual( messages['testuser.creator']['d']['tasks']['grant'], ['flag']) self.assertIn('context', messages['testuser.creator']['d'])
class UlearnhubSyncLDAPGroupFunctionalTests(UlearnHUBBaseTestCase): def setUp(self): conf_dir = os.path.dirname(__file__) self.app = loadapp('config:tests.ini', relative_to=conf_dir) self.testapp = UlearnhubTestApp(self) self.rabbit = RabbitClient(TEST_VHOST_URL) self.rabbit.management.cleanup(delete_all=True) self.rabbit.declare() httpretty.enable() http_mock_info() http_mock_checktoken() create_defaults(self.testapp.testapp.app.registry, BASE_DOMAIN, quiet=True) self.initialize_test_deployment() self.initialize_test_domain() self.patches = [] self.clients = {} def tearDown(self): # Make sure httpretty is disabled httpretty.disable() httpretty.reset() for testpatch in self.patches: testpatch.stop() self.rabbit.get_all('syncacl') self.rabbit.disconnect() for user_clients in self.clients.values(): for client in user_clients: client.disconnect() def assertMessagesInQueue(self, queue, retries=0, expected=None): messages = self.rabbit.get_all(queue) expected_message_count = expected if expected is not None else len( messages) for retry in xrange(retries): if len(messages) < expected_message_count: messages += self.rabbit.get_all(queue) else: break time.sleep(1) if len(messages) < expected_message_count: raise AssertionError( 'Missing messages on queue, expected {}, received {}, retryied {}' .format(expected_message_count, len(messages), retries)) return {item[0]['u']['u']: item[0] for item in messages} def test_syncldapgroup(self): """ """ from .mockers.syncgroup import update_group_request from .mockers.syncacl import context from .mockers.syncacl import initial_subscriptions as subscriptions from .mockers.syncacl import ldap_test_group4 http_mock_group_communities([{ 'url': context['url'], 'groups': [], 'users': ['testuser1.creator'] }]) http_mock_get_context(context) http_mock_get_context_subscriptions(subscriptions) self.add_patch(ldap_patch_connect()) self.add_patch(ldap_patch_disconnect()) self.add_patch(ldap_patch_group_search({ 'group4': ldap_test_group4, })) self.testapp.post( '/api/deployments/{deployment}/components/{component}/services/{service}' .format(deployment='test', component='testldap', service='syncldapgroup'), json.dumps(update_group_request), headers=oauth2Header(test_user), status=200) # Index by username to be able to make asserts # This is mandatory, as we cannot assume the order of the queue messages = self.assertMessagesInQueue('syncacl', retries=3, expected=1) # Test subscribed user revoke permission, preserves most important role self.assertItemsEqual(messages['groupuser1']['d']['tasks'], ['subscribe']) self.assertIn('context', messages['groupuser1']['d'])
class FunctionalTests(unittest.TestCase, MaxTestBase): def setUp(self): conf_dir = os.path.dirname(__file__) self.app = loadapp('config:rabbitmq.ini', relative_to=conf_dir) self.reset_database(self.app) self.app.registry.max_store.security.insert(test_default_security) self.patched_post = patch('requests.post', new=partial(mock_post, self)) self.patched_post.start() self.testapp = MaxTestApp(self) self.create_user(test_manager) # Rabbitmq test client initialization rabbitmq_url = self.app.registry.settings['max.rabbitmq'] self.server = RabbitClient(rabbitmq_url) self.server.management.cleanup(delete_all=True) self.server.declare() def tearDown(self): import pyramid.testing pyramid.testing.tearDown() self.server.disconnect() def run_test(self, test_module_name, test_name): """ Runs a test method from another module in a dirty (but awesome) way """ method_dotted_name = '{}.FunctionalTests.{}'.format(test_module_name, test_name) test_method = import_object('max.tests', method_dotted_name) # Create a new function sharing code, name and current globals # plus other imports needed by other modules current_globals = globals() current_globals.update({ 'json': get_module('json'), 'oauth2Header': import_object('max.tests', 'base.oauth2Header'), 'test_manager': import_object('max.tests', 'test_manager') }) wrapped_test_method = new.function(test_method.func_code, current_globals, test_method.func_name) # execute the new method and return result (if any) return wrapped_test_method(self) # All tests within this module executes code from tests on other modules # As this module has rabbitmq activated in the ini, each test should produce # rabbitmq associated actions, coded with each tested codebase, so we just # execute the code, and check for the existence of the desired structures. # # NOTE that we may expect return values from the test, to know which values # the test produced. Please modify used tests to return those values if needed. @skipRabbitTest() def test_create_user_bindings(self): username = self.run_test('test_people', 'test_create_user') self.server.management.load_exchanges() self.assertIn('{}.publish'.format(username), self.server.management.exchanges_by_name) self.assertIn('{}.subscribe'.format(username), self.server.management.exchanges_by_name) @skipRabbitTest() def test_create_user_without_bindings(self): username = '******' self.create_user(username, qs_params={"notifications": False}) self.server.management.load_exchanges() self.assertNotIn('{}.publish'.format(username), self.server.management.exchanges_by_name) self.assertNotIn('{}.subscribe'.format(username), self.server.management.exchanges_by_name) @skipRabbitTest() def test_create_existing_user_dont_create_bindings(self): """ Given a created user without rabbitmq exchanges When i try to create the user again The exchanges won't be created """ username = '******' self.create_user(username, qs_params={"notifications": False}) self.create_user(username, expect=200) self.server.management.load_exchanges() self.assertNotIn('{}.publish'.format(username), self.server.management.exchanges_by_name) self.assertNotIn('{}.subscribe'.format(username), self.server.management.exchanges_by_name) @skipRabbitTest() def test_create_existing_user_create_bindings(self): """ Given a created user without rabbitmq exchanges When i try to create the user again And i explicitly request to create exchanges for notifications The exchanges will be created """ username = '******' self.create_user(username, qs_params={"notifications": False}) self.create_user(username, qs_params={"notifications": True}, expect=200) self.server.management.load_exchanges() self.assertIn('{}.publish'.format(username), self.server.management.exchanges_by_name) self.assertIn('{}.subscribe'.format(username), self.server.management.exchanges_by_name) @skipRabbitTest() def test_self_create_existing_user_dont_create_bindings(self): """ Given a created user without rabbitmq exchanges When i try to create the user again The exchanges won't be created """ username = '******' self.create_user(username, qs_params={"notifications": False}, creator=username) self.create_user(username, expect=200, creator=username) self.server.management.load_exchanges() self.assertNotIn('{}.publish'.format(username), self.server.management.exchanges_by_name) self.assertNotIn('{}.subscribe'.format(username), self.server.management.exchanges_by_name) @skipRabbitTest() def test_self_create_existing_user_create_bindings(self): """ Given a created user without rabbitmq exchanges When i try to create the user again And i explicitly request to create exchanges for notifications The exchanges will be created """ username = '******' self.create_user(username, qs_params={"notifications": False}, creator=username) self.create_user(username, qs_params={"notifications": True}, expect=200, creator=username) self.server.management.load_exchanges() self.assertIn('{}.publish'.format(username), self.server.management.exchanges_by_name) self.assertIn('{}.subscribe'.format(username), self.server.management.exchanges_by_name) @skipRabbitTest() def test_create_conversation_bindings(self): cid, creator = self.run_test('test_conversations', 'test_post_message_to_conversation_check_conversation') # Find defined bindings for this conversation bindings = self.server.management.load_exchange_bindings('conversations') bindings = [bind for bind in bindings if ".*".format(cid) in bind['routing_key']] self.assertEqual(len(bindings), 4) @skipRabbitTest() def test_create_conversation_check_notification(self): cid, creator = self.run_test('test_conversations', 'test_post_message_to_conversation_check_conversation') sleep(0.1) messages_to_push_queue = self.server.get_all('push') self.assertEqual(len(messages_to_push_queue), 1) carrot_message, haigha_message = messages_to_push_queue[0] self.assertEqual(haigha_message.delivery_info['routing_key'], '{}.notifications'.format(cid)) self.assertEqual(carrot_message['a'], 'a') self.assertEqual(carrot_message['o'], 'c') self.assertEqual(carrot_message['u']['u'], creator) self.assertEqual(carrot_message['u']['d'], creator) @skipRabbitTest() def test_delete_conversation_bindings(self): cid = self.run_test('test_conversations', 'test_conversation_owner_deletes_conversation') # Find defined bindings for this conversation bindings = self.server.management.load_exchange_bindings('conversations') bindings = [bind for bind in bindings if ".*".format(cid) in bind['routing_key']] self.assertEqual(len(bindings), 0) @skipRabbitTest() def test_remove_user_from_conversation_bindings(self): cid, userin, userout = self.run_test('test_conversations', 'test_user_leaves_two_people_conversation') # Find defined bindings for this conversation bindings = self.server.management.load_exchange_bindings('conversations') bindings = [bind for bind in bindings if ".*".format(cid) in bind['routing_key']] # Search for the bindings of the user still on the conversation userin_bindings = [ bind for bind in bindings if "{}.publish".format(userin) == bind['source'] or "{}.subscribe".format(userin) == bind['destination'] ] # search for the bindings of the user that left the conversation userout_bindings = [ bind for bind in bindings if "{}.publish".format(userout) == bind['source'] or "{}.subscribe".format(userout) == bind['destination'] ] self.assertEqual(len(bindings), 2) self.assertEqual(len(userin_bindings), 2) self.assertEqual(len(userout_bindings), 0) @skipRabbitTest() def test_add_new_user_to_conversation_bindings(self): cid, newuser = self.run_test('test_conversations', 'test_add_participant_to_conversation') # Find defined bindings for this conversation bindings = self.server.management.load_exchange_bindings('conversations') bindings = [bind for bind in bindings if ".*".format(cid) in bind['routing_key']] # Search for the bindings of the user still on the conversation newuser_bindings = [ bind for bind in bindings if "{}.publish".format(newuser) == bind['source'] or "{}.subscribe".format(newuser) == bind['destination'] ] self.assertEqual(len(bindings), 8) self.assertEqual(len(newuser_bindings), 2) @skipRabbitTest() def test_delete_context_bindings(self): context_hash = self.run_test('test_contexts_notifications', 'test_delete_context_with_notifications_removes_subscriptions') # Find defined bindings for this context bindings = self.server.management.load_exchange_bindings('activity') bindings = [bind for bind in bindings if ".*".format(context_hash) in bind['routing_key']] self.assertEqual(len(bindings), 0) @skipRabbitTest() def test_add_user_subscription_bindings(self): context_hash, subscribed_user = self.run_test('test_contexts_notifications', 'test_subscribe_user_to_context_with_notifications') # Find defined bindings for this context bindings = self.server.management.load_exchange_bindings('activity') bindings = [bind for bind in bindings if context_hash in bind['routing_key']] # Search for the bindings of the user still on the conversation subscribed_bindings = [ bind for bind in bindings if "{}.subscribe".format(subscribed_user) == bind['destination'] ] self.assertEqual(len(bindings), 1) self.assertEqual(len(subscribed_bindings), 1) @skipRabbitTest() def test_remove_user_subscription_bindings(self): context_hash, unsubscribed_user = self.run_test('test_contexts_notifications', 'test_unsubscribe_user_from_context_with_notifications') # Find defined bindings for this context bindings = self.server.management.load_exchange_bindings('activity') bindings = [bind for bind in bindings if context_hash in bind['routing_key']] # Search for the bindings of the user still on the conversation unsubscribed_bindings = [ bind for bind in bindings if "{}.subscribe".format(unsubscribed_user) == bind['destination'] ] self.assertEqual(len(bindings), 0) self.assertEqual(len(unsubscribed_bindings), 0) @skipRabbitTest() def test_post_message_check_notification(self): cid, creator, activity = self.run_test('test_contexts_notifications', 'test_post_activity_on_context_with_notifications') sleep(0.1) messages_to_push_queue = self.server.get_all('push') carrot_message, haigha_message = messages_to_push_queue[0] self.assertEqual(len(messages_to_push_queue), 1) self.assertEqual(haigha_message.delivery_info['routing_key'], '{}'.format(cid)) self.assertEqual(carrot_message['a'], 'a') self.assertEqual(carrot_message['o'], 'a') self.assertEqual(carrot_message['u']['u'], creator) self.assertEqual(carrot_message['u']['d'], creator) self.assertEqual(carrot_message['d']['text'].encode('utf-8'), activity['object']['content']) @skipRabbitTest() def test_post_message_check_no_notification(self): cid, creator, activity = self.run_test('test_contexts', 'test_post_activity_with_private_read_write_context') messages_to_push_queue = self.server.get_all('push') self.assertEqual(len(messages_to_push_queue), 0) @skipRabbitTest() def test_post_comment_check_notification(self): cid, creator, activity, comment = self.run_test('test_contexts_notifications', 'test_post_comment_with_comments_notification') sleep(0.1) messages_to_push_queue = self.server.get_all('push') self.assertEqual(len(messages_to_push_queue), 2) carrot_message, haigha_message = messages_to_push_queue[0] self.assertEqual(haigha_message.delivery_info['routing_key'], '{}'.format(cid)) self.assertEqual(carrot_message['a'], 'a') self.assertEqual(carrot_message['o'], 'a') self.assertEqual(carrot_message['u']['u'], creator) self.assertEqual(carrot_message['u']['d'], creator) self.assertEqual(carrot_message['d']['text'], activity['object']['content']) carrot_message, haigha_message = messages_to_push_queue[1] self.assertEqual(haigha_message.delivery_info['routing_key'], '{}'.format(cid)) self.assertEqual(carrot_message['a'], 'a') self.assertEqual(carrot_message['o'], 't') self.assertEqual(carrot_message['u']['u'], creator) self.assertEqual(carrot_message['u']['d'], creator) self.assertEqual(carrot_message['d']['text'], comment['object']['content'])
class ConversationTests(MaxBunnyTestCase): def setUp(self): self.log_patch = patch('maxbunny.consumer.BunnyConsumer.configure_logger', new=get_storing_logger) self.log_patch.start() def tearDown(self): self.log_patch.stop() self.server.delete_user('testuser1') self.server.get_all('push') self.server.disconnect() # Make sure httpretty is disabled httpretty.disable() httpretty.reset() def set_server(self, message, mid): self.server = RabbitClient(TEST_VHOST_URL) self.server.management.cleanup(delete_all=True) self.server.declare() self.server.create_users(CONVERSATION_0.users) self.server.conversations.create(CONVERSATION_0.id, users=CONVERSATION_0.users) # =========================== # TESTS FOR FAILING SCENARIOS # =========================== def test_invalid_message_empty_message(self): """ Given a message with missing routing_key When the message is processed Then an exception is raised And the push message is not queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers import BAD_MESSAGE as message self.set_server({}, None) httpretty.enable() http_mock_info() runner = MockRunner('conversations', 'maxbunny.ini', 'instances.ini') consumer = __consumer__(runner) self.assertRaisesWithMessage( BunnyMessageCancel, 'Conversation id missing on routing_key ""', consumer.process, message ) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 0) def test_invalid_message_missing_username(self): """ Given a message with missing username When the message is processed Then an exception is raised And the push message is not queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers.conversations import MISSING_USERNAME_MESSAGE as message self.set_server({}, None) httpretty.enable() http_mock_info() runner = MockRunner('conversations', 'maxbunny.ini', 'instances2.ini') consumer = __consumer__(runner) self.assertRaisesWithMessage( BunnyMessageCancel, 'Missing username in message', consumer.process, message ) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 0) def test_invalid_message_unknown_domain(self): """ Given a message with a domain specified And that domain doesn't match any of the known domains When the message is processed Then an exception is raised And the push message is not queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers.conversations import UNKNOWN_DOMAIN_MESSAGE as message self.set_server({}, None) httpretty.enable() http_mock_info() runner = MockRunner('conversations', 'maxbunny.ini', 'instances2.ini') consumer = __consumer__(runner) self.assertRaisesWithMessage( BunnyMessageCancel, 'Unknown domain "unknown"', consumer.process, message ) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 0) def test_missing_domain_missing_default(self): """ Given a message with no domain specified And there is no default domain specified When the message is processed Then an exception is raised And the push message is not queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers.conversations import MISSING_DOMAIN_MESSAGE as message self.set_server({}, None) httpretty.enable() http_mock_info() runner = MockRunner('conversations', 'maxbunny.ini', 'instances.ini') consumer = __consumer__(runner) self.assertRaisesWithMessage( BunnyMessageCancel, 'Missing domain, and default could not be loaded', consumer.process, message ) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 0) def test_message_with_domain_not_found_from_max(self): """ Given a message with a domain specified And that domain exists in the list of known domains When the message is processed And the conversation or user was not found on max Then the message is not posted And the push message is not queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers.conversations import CONVERSATION_MESSAGE as message message_id = '00000000001' self.set_server(message, message_id) httpretty.enable() http_mock_info() http_mock_post_user_message(uri='tests.local', message_id=message_id, status=404) runner = MockRunner('conversations', 'maxbunny.ini', 'instances2.ini') consumer = __consumer__(runner) self.assertRaisesWithMessage( BunnyMessageCancel, "User or conversation not found", consumer.process, message ) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 0) # ============================== # TESTS FOR SUCCESFULL SCENARIOS # ============================== def test_message_without_domain_to_default(self): """ Given a message with no domain specified And there is a default domain specified When the message is processed Then the message is posted And the push message is queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers.conversations import MISSING_DOMAIN_MESSAGE as message message_id = '00000000001' self.set_server(message, message_id) httpretty.enable() http_mock_info() http_mock_post_user_message(uri='tests.default', message_id=message_id) runner = MockRunner('conversations', 'maxbunny.ini', 'instances2.ini') consumer = __consumer__(runner) consumer.process(message) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 1) self.assertEqual(messages[0][0]['a'], 'k') self.assertEqual(messages[0][0]['o'], 'm') self.assertEqual(messages[0][0]['s'], 'b') self.assertEqual(messages[0][0]['d']['id'], '00000000001') def test_message_with_domain(self): """ Given a message with a domain specified And that domain exists in the list of known domains When the message is processed Then the message is posted And the push message is queued """ from maxbunny.consumers.conversations import __consumer__ from maxbunny.tests.mockers.conversations import CONVERSATION_MESSAGE as message message_id = '00000000001' self.set_server(message, message_id) httpretty.enable() http_mock_info() http_mock_post_user_message(uri='tests.local', message_id=message_id) runner = MockRunner('conversations', 'maxbunny.ini', 'instances2.ini') consumer = __consumer__(runner) consumer.process(message) httpretty.disable() httpretty.reset() sleep(0.1) # Leave a minimum time to message to reach rabbitmq messages = self.server.get_all('push') self.assertEqual(len(messages), 1) self.assertEqual(messages[0][0]['a'], 'k') self.assertEqual(messages[0][0]['o'], 'm') self.assertEqual(messages[0][0]['s'], 'b') self.assertEqual(messages[0][0]['d']['id'], '00000000001')
class ConsumerTests(MaxBunnyTestCase): def setUp(self): # Resets the global that holds the mocked stmp sent messages import maxbunny.tests maxbunny.tests.sent = [] self.log_patch = patch('maxbunny.consumer.BunnyConsumer.configure_logger', new=get_storing_logger) self.log_patch.start() self.smtp_patch = patch('smtplib.SMTP', new=MockSMTP) self.smtp_patch.start() self.server = RabbitClient(TEST_VHOST_URL) self.server.management.cleanup(delete_all=True) self.server.declare() self.server.ch.queue.declare( queue='tests', durable=True, auto_delete=False ) self.process = None def tearDown(self): self.log_patch.stop() self.smtp_patch.stop() self.server.get_all('tests') self.server.disconnect() try: self.process.terminate() except: pass # pragma: no cover def test_consumer_drop_no_uuid(self): """ Given a invalid non-json message When the consumer loop processes the message And the message triggers a Cancel exception Then the message is dropped And a warning is logged And the channel remains Open """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageCancel('Testing message drop')) self.process = ConsumerThread(consumer) self.server.send('', '', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped (NO_UUID), reason: Testing message drop') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_drops_on_requeue_exception_without_uuid(self): """ Given a message without UUID field When the consumer loop processes the message And the message triggers a Requeue exception Then the message is requeued And a warning is logged And the channel remains Open And a mail notification is sent """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageRequeue('Test requeueing')) self.process = ConsumerThread(consumer) self.server.send('', '{}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped (NO_UUID), reason: Test requeueing') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_drop_with_uuid(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers a Cancel exception Then the message is dropped And a warning is logged And the channel remains Open And no mail notification is sent """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageCancel('Testing message drop', notify=False)) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 0) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped, reason: Testing message drop') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_drop_with_notification(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers a Cancel exception Then the message is dropped And a warning is logged And the channel remains Open And a mail notification is sent """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageCancel('Testing message drop', notify=True)) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped, reason: Testing message drop') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_drop_no_recipient(self): """ Given a message with UUID field And a missing recipients smtp setting When the consumer loop processes the message And the message triggers a Cancel exception Then the message is dropped And a warning is logged And the channel remains Open And a mail notification is not sent """ runner = MockRunner('tests', 'maxbunny-norecipients.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageCancel('Testing message drop', notify=True)) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.6) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 0) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped, reason: Testing message drop') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_drop_on_max_non_5xx_error(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers a RequestError with status code different from 5xx Then the message is dropped And a warning is logged And the channel remains Open And a mail notification is sent """ from maxclient.client import RequestError runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, RequestError(401, 'Unauthorized')) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped, reason: Max server error: Unauthorized') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_drop_on_maxcarrot_exception(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers a MaxCarrotParsingError Then the message is dropped And a warning is logged And the channel remains Open And a mail notification is sent """ from maxcarrot.message import MaxCarrotParsingError runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, MaxCarrotParsingError()) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message dropped, reason: MaxCarrot Parsing error') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_requeues_on_max_5xx_error(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers a RequestError with status code different from 5xx Then the message is requeued And a warning is logged And the channel remains Open And a mail notification is sent """ from maxclient.client import RequestError runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, RequestError(500, 'Internal Server Error')) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message 0123456789 reueued, reason: Max server error: Internal Server Error') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 1) def test_consumer_requeues_on_requeue_exception(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers a Requeue exception Then the message is requeued And a warning is logged And the channel remains Open And a mail notification is sent And the id of the queued message is stored """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageRequeue('Test requeueing')) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message 0123456789 reueued, reason: Test requeueing') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 1) self.assertEqual(len(consumer.requeued), 1) self.assertIn('0123456789', consumer.requeued) def test_consumer_requeues_on_requeue_exception_unqueues_after(self): """ Given a message with UUID field When the consumer loop processes the message And the first time the message triggers a Requeue exception And the second time the message is succesfully processed Then the message is unqueued the second time And a warning is logged And the channel remains Open And a mail notification is sent And the id is removed from queued ones """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageRequeue('Test requeueing'), after=None) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message 0123456789 reueued, reason: Test requeueing') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) self.assertEqual(len(consumer.requeued), 0) def test_consumer_requeues_on_requeue_exception_drops_after(self): """ Given a message with UUID field When the consumer loop processes the message And the first time the message triggers a Requeue exception And the second time the message is triggers a Cancel exception Then the message is unqueued the second time And a warning is logged And the channel remains Open And a mail notification is sent And the id is removed from queued ones """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, BunnyMessageRequeue('Test requeueing'), after=BunnyMessageCancel('Testing message drop')) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 2) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 2) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message 0123456789 reueued, reason: Test requeueing') self.assertEqual(consumer.logger.warnings[1], 'Message dropped, reason: Testing message drop') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) self.assertEqual(len(consumer.requeued), 0) def test_consumer_requeues_on_unknown_exception(self): """ Given a message with UUID field When the consumer loop processes the message And the message triggers an unknown Exception Then the message is requeued And a warning is logged And the channel remains Open And a mail notification is sent """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner, Exception('Unknown exception')) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 1) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 1) self.assertTrue(self.process.isAlive()) self.assertEqual(consumer.logger.warnings[0], 'Message 0123456789 reueued, reason: Consumer failure: Unknown exception') self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 1) def test_consumer_stops_on_force_stop_connection(self): """ Given a running consumer When the rabbitmq connection is closed remotely Then the channel closes And a warning is logged """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner) self.process = ConsumerThread(consumer) self.process.start() sleep(0.2) # Leave a minimum life time to consumer self.server.management.force_close(consumer.remote(), 'Closed via ') sleep(1) # Leave a minimum time to consumer to stop self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.errors), 1) self.assertFalse(self.process.isAlive()) self.assertEqual(consumer.logger.errors[0], 'CONNECTION_FORCED - Closed via management plugin') sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0) def test_consumer_success(self): """ Given a message with UUID field When the consumer loop processes the message And the message suceeds Then the message is acks And nothing is logged And the channel remains Open And a no mail notification is sent """ runner = MockRunner('tests', 'maxbunny.ini', 'instances.ini') consumer = TestConsumer(runner) self.process = ConsumerThread(consumer) self.server.send('', '{"g": "0123456789"}', routing_key='tests') self.process.start() sleep(0.3) # give a minum time to mail to be sent from maxbunny.tests import sent # MUST import sent here to get current sent mails, self.assertEqual(len(sent), 0) self.assertEqual(len(consumer.logger.infos), 1) self.assertEqual(len(consumer.logger.warnings), 0) self.assertTrue(self.process.isAlive()) self.server.management.force_close(consumer.remote()) sleep(0.2) # Leave a minimum time to rabbitmq to release messages queued = self.server.get_all('tests') self.assertEqual(len(queued), 0)