Files
edx-platform/lms/djangoapps/monitoring/tests/test_middleware.py
Robert Raposa 59e0f6efcf ARCHBOM-1244: Add code_owner custom metric (#24084)
* includes ADR for Monitoring by Code Owner
* add monitoring middleware to add the following custom metrics:
- code_owner: The owning team mapped to the current view.
- code_owner_mapping_error: If there are any errors when trying to
        perform the mapping.
- view_func_module: The __module__ of the view_func, which can
        be used to find missing mappings.
* add script to generate `settings.CODE_OWNER_MAPPINGS` from
     a csv file.

ARCHBOM-1244
2020-06-01 12:27:38 -04:00

136 lines
5.9 KiB
Python

"""
Tests for the LMS monitoring middleware
"""
import ddt
import timeit
from django.test import override_settings
from mock import call, patch, Mock
from unittest import TestCase
from unittest.mock import ANY
from lms.djangoapps.monitoring.middleware import _process_code_owner_mappings, CodeOwnerMetricMiddleware
def _mock_get_view_func_module(view_func):
"""
Enables mocking/overriding a private function that normally gets the view_func.__module__
because it was too difficult to mock the __module__ method.
"""
return view_func.mock_module
@ddt.ddt
class CodeOwnerMetricMiddlewareTests(TestCase):
"""
Tests for the LMS monitoring utility functions
"""
def setUp(self):
super().setUp()
self.mock_get_response = Mock()
self.middleware = CodeOwnerMetricMiddleware(self.mock_get_response)
def test_init(self):
self.assertEqual(self.middleware.get_response, self.mock_get_response)
def test_request_call(self):
self.mock_get_response.return_value = 'test-response'
request = Mock()
self.assertEqual(self.middleware(request), 'test-response')
@override_settings(CODE_OWNER_MAPPINGS={
'team-red': [
'openedx.core.djangoapps.xblock',
'lms.djangoapps.grades',
],
'team-blue': [
'common.djangoapps.xblock_django',
],
})
@patch('lms.djangoapps.monitoring.middleware.set_custom_metric')
@patch('lms.djangoapps.monitoring.middleware._get_view_func_module', side_effect=_mock_get_view_func_module)
@ddt.data(
('xbl', None),
('xblock_2', None),
('xblock', 'team-red'),
('openedx.core.djangoapps', None),
('openedx.core.djangoapps.xblock', 'team-red'),
('openedx.core.djangoapps.xblock.views', 'team-red'),
('grades', 'team-red'),
('lms.djangoapps.grades', 'team-red'),
('xblock_django', 'team-blue'),
('common.djangoapps.xblock_django', 'team-blue'),
)
@ddt.unpack
def test_code_owner_mapping_hits_and_misses(
self, view_func_module, expected_owner, mock_get_view_func_module, mock_set_custom_metric
):
with patch('lms.djangoapps.monitoring.middleware._PATH_TO_CODE_OWNER_MAPPINGS', _process_code_owner_mappings()):
mock_view_func = self._create_view_func_mock(view_func_module)
self.middleware.process_view(None, mock_view_func, None, None)
self._assert_code_owner_custom_metrics(
view_func_module, mock_set_custom_metric, expected_code_owner=expected_owner
)
@patch('lms.djangoapps.monitoring.middleware.set_custom_metric')
def test_code_owner_no_mappings(self, mock_set_custom_metric):
mock_view_func = self._create_view_func_mock('xblock')
self.middleware.process_view(None, mock_view_func, None, None)
mock_set_custom_metric.assert_not_called()
@override_settings(CODE_OWNER_MAPPINGS=['invalid_setting_as_list'])
@patch('lms.djangoapps.monitoring.middleware.set_custom_metric')
@patch('lms.djangoapps.monitoring.middleware._get_view_func_module', side_effect=_mock_get_view_func_module)
def test_load_config_with_invalid_dict(self, mock_get_view_func_module, mock_set_custom_metric):
with patch('lms.djangoapps.monitoring.middleware._PATH_TO_CODE_OWNER_MAPPINGS', _process_code_owner_mappings()):
mock_view_func = self._create_view_func_mock('xblock')
self.middleware.process_view(None, mock_view_func, None, None)
self._assert_code_owner_custom_metrics(
'xblock', mock_set_custom_metric, has_error=True
)
@patch('lms.djangoapps.monitoring.middleware.set_custom_metric')
@patch('lms.djangoapps.monitoring.middleware._get_view_func_module', side_effect=_mock_get_view_func_module)
def test_mapping_performance(self, mock_get_view_func_module, mock_set_custom_metric):
code_owner_mappings = {
'team-red': []
}
# create a long list of mappings that are nearly identical
for n in range(1, 200):
path = 'openedx.core.djangoapps.{}'.format(n)
code_owner_mappings['team-red'].append(path)
with override_settings(CODE_OWNER_MAPPINGS=code_owner_mappings):
with patch(
'lms.djangoapps.monitoring.middleware._PATH_TO_CODE_OWNER_MAPPINGS', _process_code_owner_mappings()
):
# test a module that matches nearly to the end, but doesn't actually match
mock_view_func = self._create_view_func_mock('openedx.core.djangoapps.XXX.views')
call_iterations = 100
time = timeit.timeit(
lambda: self.middleware.process_view(None, mock_view_func, None, None), number=call_iterations
)
average_time = time / call_iterations
self.assertTrue(average_time < 0.0005, 'Mapping takes {}s which is too slow.'.format(average_time))
expected_calls = []
for n in range(1, call_iterations):
expected_calls.append(call('view_func_module', 'openedx.core.djangoapps.XXX.views'))
mock_set_custom_metric.assert_has_calls(expected_calls)
def _assert_code_owner_custom_metrics(
self, view_func_module, mock_set_custom_metric, expected_code_owner=None, has_error=False,
):
call_list = []
call_list.append(call('view_func_module', view_func_module))
if expected_code_owner:
call_list.append(call('code_owner', expected_code_owner))
if has_error:
call_list.append(call('code_owner_mapping_error', ANY))
mock_set_custom_metric.assert_has_calls(call_list)
def _create_view_func_mock(self, module):
"""
Mock view function where __module__ returns 'test_middleware'
"""
view_func = Mock()
view_func.mock_module = module
return view_func