diff --git a/.travis.yml b/.travis.yml index 49b268c26..6051a2bd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,8 @@ install: - pip install --upgrade https://github.com/openwisp/django-netjsonconfig/tarball/master # temporary: remove when openwisp-notifications is released - pip install -U https://github.com/openwisp/openwisp-notifications/tarball/master + # To be removed after new release of openwisp-utils + - pip install --upgrade https://github.com/openwisp/openwisp-utils/tarball/master script: - ./run-qa-checks diff --git a/openwisp_monitoring/check/tests/__init__.py b/openwisp_monitoring/check/tests/__init__.py index e69de29bb..ad5feee83 100644 --- a/openwisp_monitoring/check/tests/__init__.py +++ b/openwisp_monitoring/check/tests/__init__.py @@ -0,0 +1,12 @@ +_FPING_REACHABLE = ( + '', + bytes( + '10.40.0.1 : xmt/rcv/%loss = 5/5/0%, min/avg/max = 0.04/0.08/0.15', + encoding='utf8', + ), +) + +_FPING_UNREACHABLE = ( + '', + bytes('192.168.255.255 : xmt/rcv/%loss = 3/0/100%', encoding='utf8'), +) diff --git a/openwisp_monitoring/check/tests/test_ping.py b/openwisp_monitoring/check/tests/test_ping.py index 11af8fb18..ba9b021a8 100644 --- a/openwisp_monitoring/check/tests/test_ping.py +++ b/openwisp_monitoring/check/tests/test_ping.py @@ -9,6 +9,7 @@ from .. import settings from ..classes import Ping from ..exceptions import OperationalError +from . import _FPING_REACHABLE, _FPING_UNREACHABLE Chart = load_model('monitoring', 'Chart') AlertSettings = load_model('monitoring', 'AlertSettings') @@ -24,15 +25,9 @@ class TestPing(TestDeviceMonitoringMixin, TransactionTestCase): '', bytes("fping: option requires an argument -- 'z'", encoding='utf8'), ) - _FPING_OUTPUT = ( - '', - bytes( - '10.40.0.1 : xmt/rcv/%loss = 5/5/0%, ' 'min/avg/max = 0.04/0.08/0.15', - 'utf8', - ), - ) - def test_check_ping_no_params(self): + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) + def test_check_ping_no_params(self, mocked_method): device = self._create_device(organization=self._create_org()) # will ping localhost device.management_ip = '127.0.0.1' @@ -65,9 +60,9 @@ def test_check_ping_params(self): for key in self._RTT_KEYS: self.assertTrue(result[key] < 1) - def test_check_ping_unreachable(self): + @patch.object(Ping, '_command', return_value=_FPING_UNREACHABLE) + def test_check_ping_unreachable(self, mocked_method): device = self._create_device(organization=self._create_org()) - # will hopefully ping an unexisting private address device.management_ip = '192.168.255.255' check = Check( name='Ping check', @@ -98,7 +93,8 @@ def test_operational_error(self, _command): else: self.fail('OperationalError not raised') - def _check_no_ip_case(self, status, management_ip_only=False): + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) + def _check_no_ip_case(self, status, mocked_method, management_ip_only=False): device = self._create_device( organization=self._create_org(), last_ip='127.0.0.1' ) @@ -124,24 +120,29 @@ def _check_no_ip_case(self, status, management_ip_only=False): self.assertEqual(device.monitoring.status, expected_status) self.assertEqual(Metric.objects.count(), expected_metrics_count) + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) @patch('openwisp_monitoring.check.settings.MANAGEMENT_IP_ONLY', True) - def test_device_without_ip_unknown_status(self): + def test_device_without_ip_unknown_status(self, mocked_method): self._check_no_ip_case('unknown') + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) @patch('openwisp_monitoring.check.settings.MANAGEMENT_IP_ONLY', True) - def test_device_without_ip_ok_status(self): + def test_device_without_ip_ok_status(self, mocked_method): self._check_no_ip_case('ok') + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) @patch('openwisp_monitoring.check.settings.MANAGEMENT_IP_ONLY', True) - def test_device_without_ip_problem_status(self): + def test_device_without_ip_problem_status(self, mocked_method): self._check_no_ip_case('problem') + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) @patch('openwisp_monitoring.check.settings.MANAGEMENT_IP_ONLY', True) - def test_device_without_ip_critical_status(self): + def test_device_without_ip_critical_status(self, mocked_method): self._check_no_ip_case('critical') + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) @patch('openwisp_monitoring.check.settings.MANAGEMENT_IP_ONLY', False) - def test_device_with_last_ip_unknown_status(self): + def test_device_with_last_ip_unknown_status(self, mocked_method): self._check_no_ip_case('unknown', management_ip_only=True) def test_content_object_none(self): @@ -193,7 +194,7 @@ def test_schema_violation(self): else: self.fail('ValidationError not raised') - @patch.object(Ping, '_command', return_value=_FPING_OUTPUT) + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) def test_store_result(self, mocked_method): self.assertEqual(Check.objects.count(), 0) device = self._create_device(organization=self._create_org()) @@ -220,7 +221,7 @@ def test_store_result(self, mocked_method): self.assertEqual(points[0]['rtt_avg'], result['rtt_avg']) self.assertEqual(points[0]['rtt_max'], result['rtt_max']) - @patch.object(Ping, '_command', return_value=_FPING_OUTPUT) + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) @patch.object(monitoring_settings, 'AUTO_CHARTS', return_value=[]) def test_auto_chart_disabled(self, *args): device = self._create_device(organization=self._create_org()) diff --git a/openwisp_monitoring/check/tests/test_utils.py b/openwisp_monitoring/check/tests/test_utils.py index af5aaf44b..41efcdfbe 100644 --- a/openwisp_monitoring/check/tests/test_utils.py +++ b/openwisp_monitoring/check/tests/test_utils.py @@ -7,17 +7,11 @@ from ..classes import Ping from ..settings import CHECK_CLASSES from ..utils import run_checks_async +from . import _FPING_REACHABLE class TestUtils(TestDeviceMonitoringMixin, TransactionTestCase): _PING = CHECK_CLASSES[0][0] - _FPING_OUTPUT = ( - '', - bytes( - '10.40.0.1 : xmt/rcv/%loss = 5/5/0%, ' 'min/avg/max = 0.04/0.08/0.15', - 'utf8', - ), - ) def _create_check(self): device = self._create_device(organization=self._create_org()) @@ -25,12 +19,12 @@ def _create_check(self): device.save() # check is automatically created via django signal - @patch.object(Ping, '_command', return_value=_FPING_OUTPUT) + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) def test_run_checks_async_success(self, mocked_method): self._create_check() run_checks_async() - @patch.object(Ping, '_command', return_value=_FPING_OUTPUT) + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) def test_management_command(self, mocked_method): self._create_check() management.call_command('run_checks') diff --git a/openwisp_monitoring/device/tests/__init__.py b/openwisp_monitoring/device/tests/__init__.py index defedafae..77d7ec670 100644 --- a/openwisp_monitoring/device/tests/__init__.py +++ b/openwisp_monitoring/device/tests/__init__.py @@ -1,4 +1,5 @@ import json +from copy import deepcopy from django.test import TestCase from django.urls import reverse @@ -37,16 +38,24 @@ def _post_data(self, id, key, data): netjson = json.dumps(data) return self.client.post(url, netjson, content_type='application/json') - def create_test_adata(self): + def create_test_adata(self, no_resources=False): o = self._create_org() d = self._create_device(organization=o) data = self._data() + # creation of resources metrics can be avoided in tests not involving them + # this speeds up those tests by reducing requests made + if no_resources: + del data['resources'] r = self._post_data(d.id, d.key, data) self.assertEqual(r.status_code, 200) dd = DeviceData(pk=d.pk) self.assertDictEqual(dd.data, data) - self.assertEqual(Metric.objects.count(), 9) - self.assertEqual(Chart.objects.count(), 7) + if no_resources: + metric_count, chart_count = 6, 4 + else: + metric_count, chart_count = 9, 7 + self.assertEqual(Metric.objects.count(), metric_count) + self.assertEqual(Chart.objects.count(), chart_count) if_dict = {'wlan0': data['interfaces'][0], 'wlan1': data['interfaces'][1]} for ifname in ['wlan0', 'wlan1']: iface = if_dict[ifname] @@ -62,32 +71,42 @@ def create_test_adata(self): m = Metric.objects.get(key=ifname, field_name='clients', object_id=d.pk) points = m.read(limit=10, order='time DESC') self.assertEqual(len(points), len(iface['wireless']['clients'])) + return dd - def _create_multiple_measurements(self, create=True): + def _create_multiple_measurements(self, create=True, no_resources=False, count=4): if create: - self.create_test_adata() + self.create_test_adata(no_resources=no_resources) self.assertEqual(self.device_model.objects.count(), 1) d = self.device_model.objects.first() - data2 = self._data() + dd = DeviceData(pk=d.pk) + data = self._data() + # creation of resources metrics can be avoided in tests not involving them + # this speeds up those tests by reducing requests made + if no_resources: + del data['resources'] + data2 = deepcopy(data) data2['interfaces'][0]['statistics']['rx_bytes'] = 400000000 data2['interfaces'][0]['statistics']['tx_bytes'] = 100000000 data2['interfaces'][1]['statistics']['rx_bytes'] = 2000000000 data2['interfaces'][1]['statistics']['tx_bytes'] = 1000000000 r = self._post_data(d.id, d.key, data2) - data3 = self._data() + if count == 2: + return dd + data3 = deepcopy(data) data3['interfaces'][0]['statistics']['rx_bytes'] = 500000000 data3['interfaces'][0]['statistics']['tx_bytes'] = 300000000 data3['interfaces'][1]['statistics']['rx_bytes'] = 0 data3['interfaces'][1]['statistics']['tx_bytes'] = 0 r = self._post_data(d.id, d.key, data3) - data4 = self._data() + if count == 3: + return dd + data4 = deepcopy(data) data4['interfaces'][0]['statistics']['rx_bytes'] = 1200000000 data4['interfaces'][0]['statistics']['tx_bytes'] = 600000000 data4['interfaces'][1]['statistics']['rx_bytes'] = 1000000000 data4['interfaces'][1]['statistics']['tx_bytes'] = 500000000 r = self._post_data(d.id, d.key, data4) self.assertEqual(r.status_code, 200) - dd = DeviceData(pk=d.pk) self.assertDictEqual(dd.data, data4) return dd diff --git a/openwisp_monitoring/device/tests/test_admin.py b/openwisp_monitoring/device/tests/test_admin.py index f17b8c671..306ce260b 100644 --- a/openwisp_monitoring/device/tests/test_admin.py +++ b/openwisp_monitoring/device/tests/test_admin.py @@ -20,7 +20,7 @@ def _login_admin(self): self.client.force_login(u) def test_device_admin(self): - dd = self._create_multiple_measurements() + dd = self.create_test_adata() url = reverse('admin:config_device_change', args=[dd.pk]) self._login_admin() r = self.client.get(url) @@ -51,17 +51,14 @@ def test_remove_invalid_interface(self): def test_wifi_clients_admin(self): self._login_admin() - d = self._create_device(organization=self._create_org()) - data = self._data() - r = self._post_data(d.id, d.key, data) - url = reverse('admin:config_device_change', args=[d.id]) + dd = self.create_test_adata(no_resources=True) + url = reverse('admin:config_device_change', args=[dd.id]) r1 = self.client.get(url, follow=True) - self.assertEqual(r.status_code, 200) self.assertEqual(r1.status_code, 200) self.assertContains(r1, '00:ee:ad:34:f5:3b') def test_uuid_bug(self): - dd = self._create_multiple_measurements() + dd = self.create_test_adata(no_resources=True) uuid = str(dd.pk).replace('-', '') url = reverse('admin:config_device_change', args=[uuid]) self._login_admin() diff --git a/openwisp_monitoring/device/tests/test_api.py b/openwisp_monitoring/device/tests/test_api.py index 0993d49ca..73c10e1dd 100644 --- a/openwisp_monitoring/device/tests/test_api.py +++ b/openwisp_monitoring/device/tests/test_api.py @@ -59,23 +59,24 @@ def test_200_none(self): self.assertEqual(Chart.objects.count(), 0) def test_200_create(self): - self.create_test_adata() + self.create_test_adata(no_resources=True) def test_200_traffic_counter_incremented(self): - self.create_test_adata() - self.assertEqual(self.device_model.objects.count(), 1) + dd = self.create_test_adata(no_resources=True) d = self.device_model.objects.first() data2 = self._data() + # creation of resources metrics can be avoided here as it is not involved + # this speeds up the test by reducing requests made + del data2['resources'] data2['interfaces'][0]['statistics']['rx_bytes'] = 983 data2['interfaces'][0]['statistics']['tx_bytes'] = 1567 data2['interfaces'][1]['statistics']['rx_bytes'] = 2983 data2['interfaces'][1]['statistics']['tx_bytes'] = 4567 r = self._post_data(d.id, d.key, data2) self.assertEqual(r.status_code, 200) - dd = DeviceData(pk=d.pk) self.assertDictEqual(dd.data, data2) - self.assertEqual(Metric.objects.count(), 9) - self.assertEqual(Chart.objects.count(), 7) + self.assertEqual(Metric.objects.count(), 6) + self.assertEqual(Chart.objects.count(), 4) if_dict = {'wlan0': data2['interfaces'][0], 'wlan1': data2['interfaces'][1]} for ifname in ['wlan0', 'wlan1']: iface = if_dict[ifname] @@ -92,20 +93,21 @@ def test_200_traffic_counter_incremented(self): self.assertEqual(len(points), len(iface['wireless']['clients']) * 2) def test_200_traffic_counter_reset(self): - self.create_test_adata() - self.assertEqual(self.device_model.objects.count(), 1) + dd = self.create_test_adata(no_resources=True) d = self.device_model.objects.first() data2 = self._data() + # creation of resources metrics can be avoided here as it is not involved + # this speeds up the test by reducing requests made + del data2['resources'] data2['interfaces'][0]['statistics']['rx_bytes'] = 50 data2['interfaces'][0]['statistics']['tx_bytes'] = 20 data2['interfaces'][1]['statistics']['rx_bytes'] = 80 data2['interfaces'][1]['statistics']['tx_bytes'] = 120 r = self._post_data(d.id, d.key, data2) self.assertEqual(r.status_code, 200) - dd = DeviceData(pk=d.pk) self.assertDictEqual(dd.data, data2) - self.assertEqual(Metric.objects.count(), 9) - self.assertEqual(Chart.objects.count(), 7) + self.assertEqual(Metric.objects.count(), 6) + self.assertEqual(Chart.objects.count(), 4) if_dict = {'wlan0': data2['interfaces'][0], 'wlan1': data2['interfaces'][1]} for ifname in ['wlan0', 'wlan1']: iface = if_dict[ifname] @@ -121,38 +123,10 @@ def test_200_traffic_counter_reset(self): points = m.read(limit=10, order='time DESC') self.assertEqual(len(points), len(iface['wireless']['clients']) * 2) - def _create_multiple_measurements(self, create=True): - if create: - self.create_test_adata() - self.assertEqual(self.device_model.objects.count(), 1) - d = self.device_model.objects.first() - data2 = self._data() - data2['interfaces'][0]['statistics']['rx_bytes'] = 400000000 - data2['interfaces'][0]['statistics']['tx_bytes'] = 100000000 - data2['interfaces'][1]['statistics']['rx_bytes'] = 2000000000 - data2['interfaces'][1]['statistics']['tx_bytes'] = 1000000000 - r = self._post_data(d.id, d.key, data2) - data3 = self._data() - data3['interfaces'][0]['statistics']['rx_bytes'] = 500000000 - data3['interfaces'][0]['statistics']['tx_bytes'] = 300000000 - data3['interfaces'][1]['statistics']['rx_bytes'] = 0 - data3['interfaces'][1]['statistics']['tx_bytes'] = 0 - r = self._post_data(d.id, d.key, data3) - data4 = self._data() - data4['interfaces'][0]['statistics']['rx_bytes'] = 1200000000 - data4['interfaces'][0]['statistics']['tx_bytes'] = 600000000 - data4['interfaces'][1]['statistics']['rx_bytes'] = 1000000000 - data4['interfaces'][1]['statistics']['tx_bytes'] = 500000000 - r = self._post_data(d.id, d.key, data4) - self.assertEqual(r.status_code, 200) - dd = DeviceData(pk=d.pk) - self.assertDictEqual(dd.data, data4) - return dd - def test_200_multiple_measurements(self): - dd = self._create_multiple_measurements() - self.assertEqual(Metric.objects.count(), 9) - self.assertEqual(Chart.objects.count(), 7) + dd = self._create_multiple_measurements(no_resources=True) + self.assertEqual(Metric.objects.count(), 6) + self.assertEqual(Chart.objects.count(), 4) expected = { 'wlan0': {'rx_bytes': 10000, 'tx_bytes': 6000}, 'wlan1': {'rx_bytes': 4587, 'tx_bytes': 2993}, @@ -228,7 +202,7 @@ def test_auto_chart_disabled(self, *args): self.assertEqual(Chart.objects.count(), 0) def test_get_device_metrics_200(self): - dd = self._create_multiple_measurements() + dd = self.create_test_adata() d = self.device_model.objects.get(pk=dd.pk) r = self.client.get(self._url(d.pk.hex, d.key)) self.assertEqual(r.status_code, 200) @@ -253,19 +227,22 @@ def test_get_device_metrics_200(self): self.assertEqual(charts[1]['title'], 'WiFi clients: wlan1') self.assertEqual(charts[2]['title'], 'Traffic: wlan0') self.assertEqual(charts[3]['title'], 'Traffic: wlan1') + self.assertEqual(charts[4]['title'], 'Memory Usage') + self.assertEqual(charts[5]['title'], 'CPU Load') + self.assertEqual(charts[6]['title'], 'Disk Usage') def test_get_device_metrics_histogram_ignore_x(self): o = self._create_org() d = self._create_device(organization=o) m = self._create_object_metric(content_object=d, name='applications') self._create_chart(metric=m, configuration='histogram') - self._create_multiple_measurements(create=False) + self._create_multiple_measurements(create=False, no_resources=True, count=2) r = self.client.get(self._url(d.pk.hex, d.key)) self.assertEqual(r.status_code, 200) self.assertTrue(len(r.data['x']) > 50) def test_get_device_metrics_1d(self): - dd = self._create_multiple_measurements() + dd = self.create_test_adata() d = self.device_model.objects.get(pk=dd.pk) r = self.client.get('{0}&time=1d'.format(self._url(d.pk, d.key))) self.assertEqual(r.status_code, 200) @@ -287,8 +264,8 @@ def test_get_device_metrics_400_bad_time_range(self): self.assertEqual(r.status_code, 400) def test_get_device_metrics_csv(self): - dd = self._create_multiple_measurements() - d = self.device_model.objects.get(pk=dd.pk) + d = self._create_device(organization=self._create_org()) + self._create_multiple_measurements(create=False, count=2) m = self._create_object_metric(content_object=d, name='applications') self._create_chart(metric=m, configuration='histogram') r = self.client.get('{0}&csv=1'.format(self._url(d.pk, d.key))) @@ -314,11 +291,11 @@ def test_get_device_metrics_csv(self): last_line = rows[-1].strip().split(',') self.assertEqual( last_line, - [last_line[0], '1', '2', '1.2', '0.6', '3', '1.5', '9.73', '0', '8.27'], + [last_line[0], '1', '2', '0.4', '0.1', '2', '1', '9.73', '0', '8.27'], ) def test_get_device_metrics_400_bad_timezone(self): - dd = self._create_multiple_measurements() + dd = self.create_test_adata(no_resources=True) d = self.device_model.objects.get(pk=dd.pk) wrong_timezone_values = ( 'wrong', @@ -350,11 +327,10 @@ def test_device_metrics_received_signal(self): def test_invalid_chart_config(self): # Tests if chart_config is invalid, then it is skipped and not failed - self.create_test_adata() - d = DeviceData.objects.first() - self.assertEqual(Chart.objects.count(), 7) + d = self._create_device(organization=self._create_org()) + m = self._create_object_metric(name='test_metric', content_object=d) + c = self._create_chart(metric=m, test_data=None) with redirect_stderr(StringIO()) as stderr: - c = Chart.objects.first() c.configuration = 'invalid' c.save() r = self.client.get(self._url(d.pk.hex, d.key)) @@ -391,7 +367,7 @@ def test_available_memory(self): self.assertEqual(r.status_code, 200) def test_get_device_status_200(self): - dd = self._create_multiple_measurements() + dd = self.create_test_adata(no_resources=True) d = self.device_model.objects.get(pk=dd.pk) url = self._url(d.pk.hex, d.key) # status not requested @@ -403,4 +379,4 @@ def test_get_device_status_200(self): self.assertEqual(r.status_code, 200) self.assertIn('data', r.data) self.assertIsInstance(r.data['data'], dict) - self.assertEqual(DeviceData(pk=d.pk).data, r.data['data']) + self.assertEqual(dd.data, r.data['data']) diff --git a/openwisp_monitoring/device/tests/test_device_notifcations.py b/openwisp_monitoring/device/tests/test_device_notifications.py similarity index 100% rename from openwisp_monitoring/device/tests/test_device_notifcations.py rename to openwisp_monitoring/device/tests/test_device_notifications.py diff --git a/openwisp_monitoring/device/tests/test_recovery.py b/openwisp_monitoring/device/tests/test_recovery.py index 8215a0dd4..07639308c 100644 --- a/openwisp_monitoring/device/tests/test_recovery.py +++ b/openwisp_monitoring/device/tests/test_recovery.py @@ -4,7 +4,9 @@ from django.urls import reverse from swapper import load_model +from ...check.classes import Ping from ...check.tasks import auto_create_ping +from ...check.tests import _FPING_REACHABLE from ..signals import health_status_changed from ..tasks import trigger_device_checks from ..utils import get_device_recovery_cache_key @@ -27,11 +29,16 @@ def _create_device_monitoring(self): dm.save() return dm - def test_trigger_device_recovery_task(self): + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) + def test_trigger_device_recovery_task(self, mocked_method): d = self._create_device(organization=self._create_org()) d.management_ip = '10.40.0.5' d.save() data = self._data() + # Creation of resources, clients and traffic metrics can be avoided here + # as they are not involved. This speeds up the test by reducing requests made. + del data['resources'] + del data['interfaces'] auto_create_ping.delay( model='device', app_label='config', object_id=str(d.pk), created=True, ) diff --git a/openwisp_monitoring/monitoring/tests/__init__.py b/openwisp_monitoring/monitoring/tests/__init__.py index 04c7e729e..35068dd8d 100644 --- a/openwisp_monitoring/monitoring/tests/__init__.py +++ b/openwisp_monitoring/monitoring/tests/__init__.py @@ -1,6 +1,5 @@ from datetime import timedelta -from django.contrib.auth import get_user_model from django.utils.timezone import now from swapper import load_model @@ -9,7 +8,6 @@ from .. import settings as app_settings from ..utils import create_database, get_db, query -User = get_user_model() start_time = now() ten_minutes_ago = start_time - timedelta(minutes=10) Chart = load_model('monitoring', 'Chart') diff --git a/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py b/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py index 5c1a882a6..83cf7162d 100644 --- a/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py +++ b/openwisp_monitoring/monitoring/tests/test_monitoring_notifications.py @@ -29,7 +29,7 @@ def test_general_check_threshold_crossed_immediate(self): m = self._create_general_metric(name='load') self._create_alert_settings(metric=m, operator='>', value=90, seconds=0) - with self.subTest("Test notification for metric exceeding alert settings"): + with self.subTest('Test notification for metric exceeding alert settings'): m.write(99) self.assertFalse(m.is_healthy) self.assertEqual(Notification.objects.count(), 1) @@ -39,12 +39,12 @@ def test_general_check_threshold_crossed_immediate(self): self.assertEqual(n.action_object, m.alertsettings) self.assertEqual(n.level, 'warning') - with self.subTest("Test no double alarm for metric exceeding alert settings"): + with self.subTest('Test no double alarm for metric exceeding alert settings'): m.write(95) self.assertFalse(m.is_healthy) self.assertEqual(Notification.objects.count(), 1) - with self.subTest("Test notification for metric falling behind alert settings"): + with self.subTest('Test notification for metric falling behind alert settings'): m.write(60) self.assertTrue(m.is_healthy) self.assertEqual(Notification.objects.count(), 2) @@ -55,7 +55,7 @@ def test_general_check_threshold_crossed_immediate(self): self.assertEqual(n.level, 'info') with self.subTest( - "Test no double alarm for metric falling behind alert settings" + 'Test no double alarm for metric falling behind alert settings' ): m.write(40) self.assertTrue(m.is_healthy) @@ -94,16 +94,16 @@ def test_general_check_threshold_crossed_for_long_time(self): m = self._create_general_metric(name='load') self._create_alert_settings(metric=m, operator='>', value=90, seconds=61) - with self.subTest("Test no notification is generated for healthy status"): + with self.subTest('Test no notification is generated for healthy status'): m.write(89, time=ten_minutes_ago) self.assertTrue(m.is_healthy) self.assertEqual(Notification.objects.count(), 0) - with self.subTest("Test no notification is generated when check=False"): + with self.subTest('Test no notification is generated when check=False'): m.write(91, time=ten_minutes_ago, check=False) self.assertEqual(Notification.objects.count(), 0) - with self.subTest("Test notification for metric with current timestamp"): + with self.subTest('Test notification for metric with current timestamp'): m.write(92) self.assertFalse(m.is_healthy) self.assertEqual(Notification.objects.count(), 1) @@ -222,7 +222,7 @@ def test_object_check_threshold_crossed_for_long_time(self): self.assertTrue(om.is_healthy) self.assertEqual(Notification.objects.count(), 2) - def test_general_metric_multiple_notifications(self): + def test_multiple_notifications(self): testorg = self._create_org() admin = self._create_admin() staff = self._create_user( @@ -238,94 +238,57 @@ def test_general_metric_multiple_notifications(self): OrganizationUser.objects.create(user=user, organization=testorg) OrganizationUser.objects.create(user=staff, organization=testorg) self.assertIsNotNone(staff.notificationuser) - m = self._create_general_metric(name='load') - alert_s = self._create_alert_settings( - metric=m, operator='>', value=90, seconds=61 - ) - m._notify_users(notification_type='default', alert_settings=alert_s) - self.assertEqual(Notification.objects.count(), 1) - n = notification_queryset.first() - self.assertEqual(n.recipient, admin) - self.assertEqual(n.actor, m) - self.assertEqual(n.target, None) - self.assertEqual(n.action_object, m.alertsettings) - self.assertEqual(n.level, 'info') - self.assertEqual(n.verb, 'default verb') - self.assertIn( - 'Default notification with default verb and level info', n.message - ) - def test_object_metric_multiple_notifications(self): - testorg = self._create_org() - admin = self._create_admin() - staff = self._create_user( - username='staff', email='staff@staff.com', password='staff', is_staff=True - ) - self._create_user( - username='staff-lone', - email='staff-lone@staff.com', - password='staff', - is_staff=True, - ) - user = self._create_user(is_staff=False) - OrganizationUser.objects.create(user=user, organization=testorg) - OrganizationUser.objects.create(user=staff, organization=testorg) - self.assertIsNotNone(staff.notificationuser) - d = self._create_device(organization=testorg) - om = self._create_object_metric(name='load', content_object=d) - alert_s = self._create_alert_settings( - metric=om, operator='>', value=90, seconds=61 - ) - om._notify_users(notification_type='default', alert_settings=alert_s) - self.assertEqual(Notification.objects.count(), 2) - n = notification_queryset.first() - self.assertEqual(n.recipient, admin) - self.assertEqual(n.actor, om) - self.assertEqual(n.target, d) - self.assertEqual(n.action_object, om.alertsettings) - self.assertEqual(n.level, 'info') - self.assertEqual(n.verb, 'default verb') - self.assertIn( - 'Default notification with default verb and level info', n.message, - ) - n = notification_queryset.last() - self.assertEqual(n.recipient, staff) - self.assertEqual(n.actor, om) - self.assertEqual(n.target, d) - self.assertEqual(n.action_object, om.alertsettings) - self.assertEqual(n.level, 'info') - self.assertEqual(n.verb, 'default verb') + with self.subTest('Test general metric multiple notifications'): + m = self._create_general_metric(name='load') + alert_s = self._create_alert_settings( + metric=m, operator='>', value=90, seconds=61 + ) + m._notify_users(notification_type='default', alert_settings=alert_s) + self.assertEqual(Notification.objects.count(), 1) + n = notification_queryset.first() + self._check_notification_parameters(n, admin, m, None) + self.assertIn( + 'Default notification with default verb and level info', n.message + ) + Notification.objects.all().delete() - def test_object_metric_multiple_notifications_no_org(self): - testorg = self._create_org() - admin = self._create_admin() - staff = self._create_user( - username='staff', email='staff@staff.com', password='staff', is_staff=True - ) - self._create_user( - username='staff-lone', - email='staff-lone@staff.com', - password='staff', - is_staff=True, - first_name="'staff-lone'", - ) - user = self._create_user(is_staff=False) - OrganizationUser.objects.create(user=user, organization=testorg) - OrganizationUser.objects.create(user=staff, organization=testorg) - self.assertIsNotNone(staff.notificationuser) - om = self._create_object_metric(name='logins', content_object=user) - alert_s = self._create_alert_settings( - metric=om, operator='>', value=90, seconds=0 - ) - om._notify_users(notification_type='default', alert_settings=alert_s) - self.assertEqual(Notification.objects.count(), 1) - n = notification_queryset.first() - self.assertEqual(n.recipient, admin) - self.assertEqual(n.actor, om) - self.assertEqual(n.target, user) - self.assertEqual(n.action_object, om.alertsettings) - self.assertEqual(n.level, 'info') - self.assertEqual(n.verb, 'default verb') + with self.subTest('Test object metric multiple notifications'): + d = self._create_device(organization=testorg) + om = self._create_object_metric(name='load', content_object=d) + alert_s = self._create_alert_settings( + metric=om, operator='>', value=90, seconds=61 + ) + self.assertEqual(Notification.objects.count(), 0) + om._notify_users(notification_type='default', alert_settings=alert_s) + self.assertEqual(Notification.objects.count(), 2) + n = notification_queryset.first() + self._check_notification_parameters(n, admin, om, d) + self.assertIn( + 'Default notification with default verb and level info', n.message, + ) + n = notification_queryset.last() + self._check_notification_parameters(n, staff, om, d) + Notification.objects.all().delete() + + with self.subTest('Test object metric multiple notifications no org'): + om = self._create_object_metric(name='logins', content_object=user) + alert_s = self._create_alert_settings( + metric=om, operator='>', value=90, seconds=0 + ) + self.assertEqual(Notification.objects.count(), 0) + om._notify_users(notification_type='default', alert_settings=alert_s) + self.assertEqual(Notification.objects.count(), 1) + n = notification_queryset.first() + self._check_notification_parameters(n, admin, om, user) + + def _check_notification_parameters(self, notification, recepient, metric, target): + self.assertEqual(notification.recipient, recepient) + self.assertEqual(notification.actor, metric) + self.assertEqual(notification.target, target) + self.assertEqual(notification.action_object, metric.alertsettings) + self.assertEqual(notification.level, 'info') + self.assertEqual(notification.verb, 'default verb') def test_email_notification(self): self._create_admin() @@ -335,7 +298,7 @@ def test_email_notification(self): exp_target_link = f'http://example.com/admin/config/device/{d.id}/change/' exp_email_body = '{message}' f'\n\nFor more information see {exp_target_link}.' - with self.subTest("Test notification email for metric crossed alert settings"): + with self.subTest('Test notification email for metric crossed alert settings'): m.write(99) n = notification_queryset.first() email = mail.outbox.pop() @@ -351,7 +314,7 @@ def test_email_notification(self): html_message, ) - with self.subTest("Test notification email for metric returned under threhold"): + with self.subTest('Test notification email for metric returned under threhold'): m.write(50) n = notification_queryset.last() email = mail.outbox.pop() diff --git a/runtests.py b/runtests.py index 15de37b28..58f81566f 100755 --- a/runtests.py +++ b/runtests.py @@ -16,5 +16,4 @@ args.insert(2, 'openwisp_monitoring') else: args.insert(2, 'openwisp2') - args.insert(3, 'openwisp_monitoring.notifications') execute_from_command_line(args) diff --git a/tests/openwisp2/sample_check/tests.py b/tests/openwisp2/sample_check/tests.py index e732c2ab4..f7c596070 100644 --- a/tests/openwisp2/sample_check/tests.py +++ b/tests/openwisp2/sample_check/tests.py @@ -1,5 +1,9 @@ +from unittest.mock import patch + from django.test import TransactionTestCase from django.utils.timezone import now +from openwisp_monitoring.check.classes import Ping +from openwisp_monitoring.check.tests import _FPING_REACHABLE from openwisp_monitoring.check.tests.test_models import TestModels as BaseTestModels from openwisp_monitoring.check.tests.test_ping import TestPing as BaseTestPing from openwisp_monitoring.check.tests.test_utils import TestUtils as BaseTestUtils @@ -18,7 +22,8 @@ class TestPing(BaseTestPing, TestDeviceMonitoringMixin, TransactionTestCase): class TestModels(BaseTestModels, TestDeviceMonitoringMixin, TransactionTestCase): - def test_last_called(self): + @patch.object(Ping, '_command', return_value=_FPING_REACHABLE) + def test_last_called(self, mocked_method): device = self._create_device(organization=self._create_org()) # will ping localhost device.management_ip = '127.0.0.1' diff --git a/tests/openwisp2/sample_device_monitoring/tests.py b/tests/openwisp2/sample_device_monitoring/tests.py index 8209760f2..e4aa66ccb 100644 --- a/tests/openwisp2/sample_device_monitoring/tests.py +++ b/tests/openwisp2/sample_device_monitoring/tests.py @@ -1,7 +1,11 @@ from openwisp_monitoring.device.tests import ( DeviceMonitoringTestCase as DeviceMonitoringTestCase, ) +from openwisp_monitoring.device.tests.test_admin import TestAdmin as BaseTestAdmin from openwisp_monitoring.device.tests.test_api import TestDeviceApi as BaseTestDeviceApi +from openwisp_monitoring.device.tests.test_device_notifications import ( + TestDeviceNotifications as BaseTestDeviceNotifications, +) from openwisp_monitoring.device.tests.test_models import ( TestDeviceData as BaseTestDeviceData, ) @@ -40,6 +44,14 @@ class TestDeviceApi(BaseTestDeviceApi): pass +class TestAdmin(BaseTestAdmin): + pass + + +class TestDeviceNotifications(BaseTestDeviceNotifications): + pass + + # this is necessary to avoid excuting the base test suites del BaseTestRecovery del DeviceMonitoringTestCase @@ -47,3 +59,5 @@ class TestDeviceApi(BaseTestDeviceApi): del BaseTestDeviceMonitoring del BaseTestSettings del BaseTestDeviceApi +del BaseTestAdmin +del BaseTestDeviceNotifications diff --git a/tests/openwisp2/sample_monitoring/tests.py b/tests/openwisp2/sample_monitoring/tests.py index fd9201f1f..390badbe6 100644 --- a/tests/openwisp2/sample_monitoring/tests.py +++ b/tests/openwisp2/sample_monitoring/tests.py @@ -7,6 +7,9 @@ from openwisp_monitoring.monitoring.tests.test_models import ( TestModels as BaseTestModels, ) +from openwisp_monitoring.monitoring.tests.test_monitoring_notifications import ( + TestMonitoringNotifications as BaseTestMonitoringNotifications, +) class TestDatabase(BaseTestDatabase): @@ -34,7 +37,12 @@ class TestCharts(BaseTestCharts): pass +class TestMonitoringNotifications(BaseTestMonitoringNotifications): + pass + + # this is necessary to avoid excuting the base test suites del BaseTestDatabase del BaseTestCharts del BaseTestModels +del BaseTestMonitoringNotifications diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index d579a5180..e9db2da16 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -274,6 +274,9 @@ if TESTING: OPENWISP_MONITORING_MAC_VENDOR_DETECTION = False +# Temporarily added to identify slow tests +TEST_RUNNER = 'openwisp_utils.tests.TimeLoggingTestRunner' + LOGGING = { 'version': 1, 'filters': {'require_debug_true': {'()': 'django.utils.log.RequireDebugTrue'}},