class RabbitMQInstanceTestCase(TestCase): """ Test cases for RabbitMQInstanceMixin """ def setUp(self): super().setUp() with patch( 'instance.tests.models.factories.openedx_instance.OpenEdXInstance._write_metadata_to_consul', return_value=(1, True) ): self.instance = OpenEdXInstanceFactory() @responses.activate @ddt.data( ('GET', ['overview'], '/api/overview'), ('PUT', ['users', 'testuser'], '/api/users/testuser'), ('DELETE', ['permissions', '/some_vhost', 'testuser'], '/api/permissions/%2Fsome_vhost/testuser') ) @ddt.unpack def test_rabbitmq_request(self, method, url_parts, expected_url, mock_consul): """ Test to make sure the _rabbitmq_request parameters form the correct URLs """ url = '{service_url}{path}'.format( service_url=self.instance.rabbitmq_server.api_url, path=expected_url ) expected_body = {'info': 'This is a mocked request to URL {url}'.format(url=url)} with patch( 'instance.tests.models.factories.openedx_instance.OpenEdXInstance._write_metadata_to_consul', return_value=(1, True) ): # Mock the URL with a uniquely identifying body so that we can verify that the # correct URL is formed and called. responses.add(method, url, json=expected_body) self.instance = OpenEdXInstanceFactory() response = self.instance._rabbitmq_request(method.lower(), *url_parts) self.assertDictEqual( response.json(), expected_body ) @responses.activate def test_provision_rabbitmq(self, mock_consul): """ Record the calls to the RabbitMQ API and make sure a new vhost along with two new users are created during provision and deleted during deprovision. The use of `responses.RequestsMock` raises an exception during context deconstruction if any of the URLs added to the `responses` object aren't ever called. Also, if any RabbitMQ API URLs are called that haven't been mocked, a `RabbitMQAPIError` should be raised (given the default `.env.test` configuration). So, this test should pass if and only if all of the specifically mocked URLs are called during both provision and deprovision. """ rabbitmq_users = [self.instance.rabbitmq_provider_user, self.instance.rabbitmq_consumer_user] rabbitmq_vhost = urllib.parse.quote(self.instance.rabbitmq_vhost, safe='') vhosts_calls = ['vhosts/{}'.format(rabbitmq_vhost)] users_calls = ['users/{}'.format(user) for user in rabbitmq_users] permissions_calls = ['permissions/{}/{}'.format(rabbitmq_vhost, user) for user in rabbitmq_users] provision_calls = [ '{}/api/{}'.format(self.instance.rabbitmq_server.api_url, url) for url in vhosts_calls + users_calls + permissions_calls ] deprovision_calls = [ '{}/api/{}'.format(self.instance.rabbitmq_server.api_url, url) for url in vhosts_calls + users_calls ] # Spec the provisioning calls with responses.RequestsMock() as rsps: for url in provision_calls: rsps.add( responses.PUT, url, content_type='application/json', body='{}' ) self.instance.provision_rabbitmq() # Spec the deprovisioning calls with responses.RequestsMock() as rsps: for url in deprovision_calls: rsps.add( responses.DELETE, url, content_type='application/json', body='{}' ) self.instance.deprovision_rabbitmq() @responses.activate def test_rabbitmq_api_error(self, mock_consul): """ Test that RabbitMQAPIError is thrown during auth issues """ with responses.RequestsMock() as rsps: # Emulate 401 Unauthorized rsps.add( responses.GET, '{}/api/overview'.format(self.instance.rabbitmq_server.api_url), content_type='application/json', body='{}', status=401 ) with self.assertRaises(RabbitMQAPIError): self.instance._rabbitmq_request('get', 'overview') @ddt.data( ({'name': 'test'}, 'test'), ({'name': 'test', 'description': 'test description'}, 'test (test description)') ) @ddt.unpack def test_string_representation(self, fields, representation, mock_consul): """ Test that the str method returns the appropriate values. """ rabbitmq = self.instance.rabbitmq_server for name, value in fields.items(): setattr(rabbitmq, name, value) rabbitmq.save() self.assertEqual(str(rabbitmq), representation) @patch('instance.models.mixins.rabbitmq.RabbitMQInstanceMixin._rabbitmq_request') def test_deprovision_rabbitmq(self, mock_rabbitmq_request, mock_consul): """ Test deprovision_rabbitmq does correct calls. """ self.instance.rabbitmq_provisioned = True self.instance.deprovision_rabbitmq() mock_rabbitmq_request.assert_any_call('delete', 'vhosts', self.instance.rabbitmq_vhost) mock_rabbitmq_request.assert_any_call('delete', 'users', self.instance.rabbitmq_consumer_user.username) mock_rabbitmq_request.assert_any_call('delete', 'users', self.instance.rabbitmq_provider_user.username) @patch('instance.models.mixins.rabbitmq.RabbitMQInstanceMixin._rabbitmq_request', side_effect=RabbitMQAPIError()) def test_ignore_errors_deprovision_rabbitmq(self, mock_rabbitmq_request, mock_consul): """ Test rabbitmq is set as deprovision when ignoring errors. """ self.instance.rabbitmq_provisioned = True self.instance.deprovision_rabbitmq(ignore_errors=True) self.assertFalse(self.instance.rabbitmq_provisioned)
class RabbitMQInstanceTestCase(TestCase): """ Test cases for RabbitMQInstanceMixin """ def setUp(self): super().setUp() self.instance = None @responses.activate @ddt.data( ('GET', ['overview'], '/api/overview'), ('PUT', ['users', 'testuser'], '/api/users/testuser'), ('DELETE', ['permissions', '/some_vhost', 'testuser'], '/api/permissions/%2Fsome_vhost/testuser') ) @ddt.unpack def test_rabbitmq_request(self, method, url_parts, expected_url): """ Test to make sure the _rabbitmq_request parameters form the correct URLs """ url = '{service_url}{path}'.format( service_url=settings.RABBITMQ_API_URL, path=expected_url ) expected_body = {'info': 'This is a mocked request to URL {url}'.format(url=url)} # Mock the URL with a uniquely identifying body so that we can verify that the # correct URL is formed and called. responses.add(method, url, json=expected_body) self.instance = OpenEdXInstanceFactory(use_ephemeral_databases=False) response = self.instance._rabbitmq_request(method.lower(), *url_parts) self.assertDictEqual( response.json(), expected_body ) @responses.activate def test_provision_rabbitmq(self): """ Record the calls to the RabbitMQ API and make sure a new vhost along with two new users are created during provision and deleted during deprobision. The use of `responses.RequestsMock` raises an exception during context deconstruction if any of the URLs added to the `responses` object aren't ever called. Also, if any RabbitMQ API URLs are called that haven't been mocked, a `RabbitMQAPIError` should be raised (given the default `.env.test` configuration). So, this test should pass if and only if all of the specifically mocked URLs are called during both provision and deprovision. """ self.instance = OpenEdXInstanceFactory(use_ephemeral_databases=False) rabbitmq_users = [self.instance.rabbitmq_provider_user, self.instance.rabbitmq_consumer_user] rabbitmq_vhost = urllib.parse.quote(self.instance.rabbitmq_vhost, safe='') vhosts_calls = ['vhosts/{}'.format(rabbitmq_vhost)] users_calls = ['users/{}'.format(user) for user in rabbitmq_users] permissions_calls = ['permissions/{}/{}'.format(rabbitmq_vhost, user) for user in rabbitmq_users] provision_calls = [ '{}/api/{}'.format(settings.RABBITMQ_API_URL, url) for url in vhosts_calls + users_calls + permissions_calls ] deprovision_calls = [ '{}/api/{}'.format(settings.RABBITMQ_API_URL, url) for url in vhosts_calls + users_calls ] # Spec the provisioning calls with responses.RequestsMock() as rsps: for url in provision_calls: rsps.add( responses.PUT, url, content_type='application/json', body='{}' ) self.instance.provision_rabbitmq() # Spec the deprovisioning calls with responses.RequestsMock() as rsps: for url in deprovision_calls: rsps.add( responses.DELETE, url, content_type='application/json', body='{}' ) self.instance.deprovision_rabbitmq() @override_settings(RABBITMQ_ADMIN_PASSWORD='******') def test_rabbitmq_api_error(self): """ Test that RabbitMQAPIError is thrown during auth issues """ self.instance = OpenEdXInstanceFactory(use_ephemeral_databases=False) with self.assertRaises(RabbitMQAPIError): self.instance._rabbitmq_request('get', 'overview')