import os from unittest import mock import pytest from django.core.files.uploadedfile import SimpleUploadedFile from django.urls import reverse from rest_framework import status from devices.models.device import Device from devices.models.sensor import ArmaSensor from devices.services.sensor.rabbitmq import SensorResponseException BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SENSOR_DATA = { "name": "SENSOR_TEST", "ip": "192.168.56.103", "port": 5000, "type": 'firewall' } VECTOR_URL_NAMES = ['status', 'start', 'stop', 'restart', 'reload', 'service_info'] ZEEK_URL_NAMES = ['status', 'start', 'stop', 'restart'] ZEEK_URL_NAMES_POST = ['protocols_disable', 'settings_update'] def mock_sensor_management(response=None): class MockSensorManagement: def _get_connection_data(self): return 1, 2 def send_message(*args, **kwargs): if response: return response raise SensorResponseException({'status': 'error'}) return MockSensorManagement def mock_sensor_redis(): class MockSensorRedis: def __init__(self, *args, **kwargs): pass def get_status(self): return {'status': 'online'} return MockSensorRedis @pytest.mark.django_db class TestSensorAPI: @pytest.fixture(autouse=True) def setup_tests(self, api_client, django_user_model, add_user_with_permissions): self.user = add_user_with_permissions(username='test_admin', password='test_admin_pass', is_superuser=True) @pytest.mark.unit @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response='ok')) @mock.patch('devices.serializers.sensor_serializers.SensorService', mock_sensor_redis()) def test_getting_list_of_sensors(self, api_client): api_client.force_authenticate(self.user) sensor = ArmaSensor.objects.create(**SENSOR_DATA) url = reverse('sensor-list') response = api_client.get(url) assert response.json()['count'] == 1 assert response.json()['results'][0]['id'] == sensor.id @pytest.mark.unit @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response='ok')) @mock.patch('devices.serializers.sensor_serializers.SensorService', mock_sensor_redis()) def test_getting_sensor(self, api_client): api_client.force_authenticate(self.user) sensor = ArmaSensor.objects.create(**SENSOR_DATA) url = reverse('sensor-detail', args=[sensor.pk]) response = api_client.get(url) assert response.status_code == status.HTTP_200_OK assert response.json()['id'] == sensor.id @pytest.mark.unit @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response='ok')) @mock.patch('devices.views.sensor.SensorService', mock_sensor_redis()) def test_getting_sensor_with_invalid_id(self, api_client): api_client.force_authenticate(self.user) url = reverse('sensor-detail', args=[56789]) response = api_client.get(url) assert response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.unit @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response='ok')) @mock.patch('devices.views.sensor.SensorService', mock_sensor_redis()) @mock.patch('devices.serializers.sensor_serializers.SensorService', mock_sensor_redis()) def test_updating_sensor_with_valid_data(self, api_client): api_client.force_authenticate(self.user) sensor = ArmaSensor.objects.create(**SENSOR_DATA) url = reverse('sensor-detail', args=[sensor.id]) new_data = SENSOR_DATA.copy() new_data['port'] = 4545 new_data['name'] = 'new_sensor' response = api_client.patch(url, data=new_data) assert response.status_code == status.HTTP_200_OK assert response.json()['id'] == sensor.id assert response.json()['port'] == new_data['port'] assert response.json()['name'] == new_data['name'] @pytest.mark.unit def test_updating_sensor_with_invalid_port(self, api_client): api_client.force_authenticate(self.user) sensor = ArmaSensor.objects.create(**SENSOR_DATA) url = reverse('sensor-detail', args=[sensor.id]) new_data = { 'name': 'new_sensor', 'port': 99999 } response = api_client.patch(url, data=new_data) assert response.status_code == status.HTTP_400_BAD_REQUEST assert 'port' in response.json() assert response.json()['port'] == ['Ensure this value is less than or equal to 65535.'] @pytest.mark.unit def test_creating_sensor_without_ip(self, api_client): api_client.force_authenticate(self.user) url = reverse('sensor-list') data = SENSOR_DATA.copy() del data['ip'] response = api_client.post(url, data=data) assert response.status_code == status.HTTP_400_BAD_REQUEST assert 'ip' in response.json() assert response.json()['ip'] == ['This field is required.'] @pytest.mark.unit def test_delete_sensor(self, api_client): firewall = ArmaSensor.objects.create(**SENSOR_DATA) assert ArmaSensor.objects.filter(id=firewall.id).exists() assert Device.objects.filter(id=firewall.id).exists() api_client.force_authenticate(self.user) url = reverse('sensor-detail', args=[firewall.id]) response = api_client.delete(url) assert response.status_code == status.HTTP_204_NO_CONTENT assert not ArmaSensor.objects.filter(id=firewall.id).exists() assert not Device.objects.filter(id=firewall.id).exists() @pytest.mark.unit @pytest.mark.django_db class TestSensorServicesAPI: @pytest.fixture(autouse=True) def setup_tests(self, api_client, django_user_model, add_user_with_permissions): self.user = add_user_with_permissions(username='test_admin', password='test_admin_pass', is_superuser=True) self.sensor = ArmaSensor.objects.create(**SENSOR_DATA) @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response={'status': 'ok'})) def test_sensor_interfaces(self, api_client): api_client.force_authenticate(self.user) url = reverse(f'sensor-system-message', args=[self.sensor.id, 'interfaces']) response = api_client.get(url) assert response.status_code == status.HTTP_200_OK assert response.json() == {'status': 'ok'} @pytest.mark.parametrize('url_name', VECTOR_URL_NAMES) @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response={'status': 'ok'})) def test_sensor_vector(self, url_name, api_client): api_client.force_authenticate(self.user) url = reverse(f'sensor-vector-message', args=[self.sensor.id, url_name]) print(url) response = api_client.get(url) assert response.status_code == status.HTTP_200_OK assert response.json() == {'status': 'ok'} @pytest.mark.parametrize('url_name', VECTOR_URL_NAMES) @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management()) def test_sensor_vector_with_error(self, url_name, api_client): api_client.force_authenticate(self.user) url = reverse(f'sensor-vector-message', args=[self.sensor.id, url_name]) response = api_client.get(url) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {'status': 'error'} @pytest.mark.parametrize('url_name', ZEEK_URL_NAMES) @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response={'status': 'ok'})) def test_sensor_zeek(self, url_name, api_client): api_client.force_authenticate(self.user) url = reverse(f'sensor-zeek-message', args=[self.sensor.id, url_name]) response = api_client.get(url) assert response.status_code == status.HTTP_200_OK assert response.json() == {'status': 'ok'} @pytest.mark.parametrize('url_name', ZEEK_URL_NAMES) @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management()) def test_sensor_zeek_with_error(self, url_name, api_client): api_client.force_authenticate(self.user) url = reverse(f'sensor-zeek-message', args=[self.sensor.id, url_name]) response = api_client.get(url) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {'status': 'error'} @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response={'status': 'ok'})) def test_sensor_zeek_update_settings(self, api_client): test_file = os.path.join(BASE_DIR, 'tests/test_files/good_local.zeek') file = SimpleUploadedFile(name='good_local.zeek', content=open(test_file, 'rb').read()) api_client.force_authenticate(self.user) url = reverse(f'sensor-zeek-settings-update', args=[self.sensor.id]) data = {'file': file} response = api_client.post(url, data=data) assert response.status_code == status.HTTP_200_OK assert response.json() == {'status': 'ok'} @mock.patch('devices.views.sensor.SensorManagement', mock_sensor_management(response={'status': 'ok'})) def test_sensor_zeek_protocols_disable(self, api_client): api_client.force_authenticate(self.user) url = reverse(f'sensor-zeek-protocols-disable', args=[self.sensor.id]) data = { 'disable_protocols': ['test', 'aaa', '10101001'] } response = api_client.post(url, data=data) assert response.status_code == status.HTTP_200_OK assert response.json() == {'status': 'ok'}