import datetime import json from secrets import choice from unittest.mock import patch import fakeredis from unittest import mock import pytest from django.contrib.auth import get_user_model from django.urls import reverse from assets.models.assets import OperatingSystem, Asset # noinspection PyUnresolvedReferences from console.tests.test_utils import add_user_with_permissions, get_url, test_server from core.utils import dtnow from dashboard.models import DashboardLayout # TODO: enable when correlator is ready # from dashboard.tasks import update_incs_by_time_statistics, update_events_by_time_statistics from dashboard.tasks import update_incs_by_time_statistics from dashboard.utils import RedisInstances from dashboard.views import check_user_widgets from incident.models import Incident from networkmap.api import DANGER_STATUSES from perms.models import Perm from users.models import UserInfo # from dashboard.serializers import DashboardSerializer User = get_user_model() real_datetime_class = datetime.datetime USERNAME = 'test_not_superuser' PASSWORD = 'test_password' def mock_datetime_now(target, dt): class DatetimeSubclassMeta(type): @classmethod def __instancecheck__(mcs, obj): return isinstance(obj, real_datetime_class) class BaseMockedDatetime(real_datetime_class): @classmethod def now(cls, tz=None): return target.replace(tzinfo=tz) @classmethod def utcnow(cls): return target # Python2 & Python3 compatible metaclass MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {}) return mock.patch.object(dt, 'datetime', MockedDatetime) @pytest.mark.django_db class TestApi(object): @pytest.fixture(autouse=True) def testing_redis(self, django_user_model): server = fakeredis.FakeServer() test_redis = fakeredis.FakeStrictRedis(server=server) test_redis.set(RedisInstances.WIDGET_INCS_BY_TIME_DAY, json.dumps([0] * 24)) test_redis.set(RedisInstances.WIDGET_INCS_BY_TIME_WEEK, json.dumps([0] * 7)) test_redis.set(RedisInstances.WIDGET_INCS_BY_TIME_MONTH, json.dumps([0] * 30)) test_redis.set(RedisInstances.WIDGET_INCS_BY_TIME_YEAR, json.dumps([0] * 12)) test_redis.set(RedisInstances.WIDGET_EVENTS_BY_TIME_DAY, json.dumps([0] * 24)) test_redis.set(RedisInstances.WIDGET_EVENTS_BY_TIME_WEEK, json.dumps([0] * 7)) test_redis.set(RedisInstances.WIDGET_EVENTS_BY_TIME_MONTH, json.dumps([0] * 30)) test_redis.set(RedisInstances.WIDGET_EVENTS_BY_TIME_YEAR, json.dumps([0] * 12)) redis_patcher = patch('dashboard.tasks.redis_instance', test_redis) self.redis = redis_patcher.start() user1 = django_user_model.objects.create_superuser(username='admin400', password='nimda') user2 = django_user_model.objects.create_user(username=USERNAME, password=PASSWORD) UserInfo(user=user1).save() UserInfo(user=user2).save() return test_redis @pytest.mark.unit def test_inccidents_by_time_hour_task(self, testing_redis): """ Test for checking the celery task for incidents by time widget past hour incident sum Steps of the test: 1. Get REDIS data before invoking task 2. Create 2 incidents 3. Run task again 4. Check if value of day stats in REDIS changed :param testing_redis: fixture for initializing testing REDIS instance """ day_data_before = json.loads(testing_redis.get(RedisInstances.WIDGET_INCS_BY_TIME_DAY)) Incident.objects.create(timestamp=dtnow(), title='test_inc_1', importance=1, status=2, event_count=1, events='') update_incs_by_time_statistics() day_data_after = json.loads(testing_redis.get(RedisInstances.WIDGET_INCS_BY_TIME_DAY)) assert day_data_before != day_data_after @pytest.mark.unit def test_incidents_by_time_week_update(self, testing_redis): """ Test for checking the data for incidents by time widget (week data), when day changes Steps of the test: 1. Create 3 incidents with the last of which occurs the day after the first two 2. Every time incidents created, call update widget data function 3. Compare last value of week data with 3 (sum of incidents) :param testing_redis: """ for i in range(3): incident_datetime = datetime.datetime(2020, 1, 1, 21, 59, 0, 0, None) + datetime.timedelta(hours=i) def dtnow_for_widgets(minutes=0): hours_from_dtnow_call = minutes return incident_datetime + datetime.timedelta(minutes=1, hours=hours_from_dtnow_call) with patch('dashboard.tasks.dtnow', dtnow_for_widgets): created_inc = Incident.objects.create(timestamp=incident_datetime, title=f"test_inc{i}", importance=1, status=2, event_count=1, events='') update_incs_by_time_statistics() week_stats = json.loads(testing_redis.get(RedisInstances.WIDGET_INCS_BY_TIME_WEEK)) assert week_stats[-1] == 1 @pytest.mark.unit def test_incidents_by_time_month_update(self, testing_redis): """ Test for checking the data for incidents by time widget (month data), when day changes Steps of the test: 1. Create 3 incidents with the last of which occurs the day after the first two 2. Every time incidents created, call update widget data function 3. Compare last value of week data with 3 (sum of incidents) :param testing_redis: """ for i in range(3): incident_datetime = datetime.datetime(2020, 1, 1, 21, 59, 0, 0, None) + datetime.timedelta(hours=i) def dtnow_for_widgets(minutes=0): hours_from_dtnow_call = minutes return incident_datetime + datetime.timedelta(minutes=1, hours=hours_from_dtnow_call) with patch('dashboard.tasks.dtnow', dtnow_for_widgets): created_inc = Incident.objects.create(timestamp=incident_datetime, title=f"test_inc{i}", importance=1, status=2, event_count=1, events='') update_incs_by_time_statistics() month_stats = json.loads(testing_redis.get(RedisInstances.WIDGET_INCS_BY_TIME_MONTH)) assert month_stats[-1] == 1 @pytest.mark.unit def test_incidents_by_time_year_update(self, testing_redis): """ Test for checking the data for incidents by time widget (month data), when day changes Steps of the test: 1. Create 3 incidents with the last of which occurs the day after the first two and on next month 2. Every time incidents created, call update widget data function 3. Compare last value of week data with 3 (sum of incidents) :param testing_redis: fixture for initializing testing REDIS instance """ for i in range(3): incident_datetime = datetime.datetime(2020, 1, 31, 21, 59, 0, 0, None) + datetime.timedelta(hours=i) def dtnow_for_widgets(minutes=0): hours_from_dtnow_call = minutes return incident_datetime + datetime.timedelta(minutes=1, hours=hours_from_dtnow_call) with patch('dashboard.tasks.dtnow', dtnow_for_widgets): created_inc = Incident.objects.create(timestamp=incident_datetime, title=f"test_inc{i}", importance=1, status=2, event_count=1, events='') update_incs_by_time_statistics() year_stats = json.loads(testing_redis.get(RedisInstances.WIDGET_INCS_BY_TIME_YEAR)) assert year_stats[-1] == 1 @pytest.mark.unit def test_assets_by_incs_widget_counting_right_incidents(self, get_url, client): """ Test for checking if widget backend correctly counts right incidents Steps of the test: 1. Create asset for testing purposes 2. Create 3 incidents with statuses within DANGER_STATUSES list and assign them to the asset from step 1 3. Run widget backend 4. Change status of one created incident to RESOLVED 5. Run widget backend again 6. Compare results of step 2 and 4 :return: PASSED if values are not equal FAILED if not """ test_os = OperatingSystem.objects.create(name='test_OS') test_asset = Asset.objects.create(name='test_asset', ip='1.1.1.1', os=test_os) for i in range(3): test_inc = Incident.objects.create(timestamp=dtnow(), title=f"test_inc{i}", importance=1, status=choice(DANGER_STATUSES), event_count=1, events='') test_asset.incidents.add(test_inc) url = reverse('assets_by_incs-list') client.force_login(User.objects.get(username='admin400')) response = client.get(url) response_result_before = json.loads(response.content.decode('utf-8')) inc_count_before = response_result_before[0].get('inc_count') changing_inc = Incident.objects.get(title='test_inc1') changing_inc.status = Incident.Status.RESOLVED changing_inc.save() response = client.get(url) response_result_after = json.loads(response.content.decode('utf-8')) inc_count_after = response_result_after[0].get('inc_count') assert inc_count_after != inc_count_before @pytest.mark.unit def test_opened_incs_widget_counter(self, client): """ Test for checking if widget backend correctly counts opened widgets Steps of the test: 1. Create incidents with statuses within DANGER_STATUSES list 2. Run widget backend 3. Change status of 1 incident from step 1 to RESOLVED 4. Run backend again 5. Compare results of step 2 and step 4 :param client: django testing client :return: PASSED if values are not equal FAILED othervise """ for i in range(3): Incident.objects.create(timestamp=dtnow(), title=f"test_inc{i}", importance=1, status=choice(DANGER_STATUSES), event_count=1, events='') url = reverse('opened_incs') client.force_login(User.objects.get(username='admin400')) response = client.get(url) response_result_before = json.loads(response.content.decode('utf-8')) count_before = response_result_before.get('inc_count') changing_inc = Incident.objects.get(title='test_inc1') changing_inc.status = Incident.Status.RESOLVED changing_inc.save() response = client.get(url) response_result_after = json.loads(response.content.decode('utf-8')) count_after = response_result_after.get('inc_count') assert count_before != count_after @pytest.mark.unit @pytest.mark.parametrize('user_perms, result_count, widget_name', [ ([], 0, ''), ([Perm.can_view_correlation_rules_list], 1, 'correlator-info'), ([[Perm.can_view_sys_info], 2, 'sys-info']), ]) def test_user_allowed_widgets(self, client, user_perms: list, result_count: int, widget_name: str): """ Test for checking the filtration of user system information and services widget, depending on his permissions """ # Getting testing user test_user = User.objects.get(username=USERNAME) # Getting user dashboard instance widgets_before = DashboardLayout.objects.get(user=test_user) # Adding testing widgets to users dashboard widgets_before.widgets = [ { "x": "0", "y": "0", "id": "sys-info", "width": "2", "height": "5" }, { "x": "2", "y": "5", "id": "services", "width": "2", "height": "5" }, { "x": "4", "y": "10", "id": "correlator-info", "width": "2", "height": "5" }, { "x": "6", "y": "15", "id": "incs-by-category", "width": "2", "height": "5" }, ] widgets_before.save() [test_user.user_permissions.add(Perm.get_rights(perm)) for perm in user_perms] check_user_widgets(test_user) widgets_after = DashboardLayout.objects.get(user=test_user) assert len(widgets_after.widgets) == result_count if result_count: assert widgets_after.widgets[0]['id'] == widget_name