/
marsela13
/
practic
Обзор
Документация
Войти
/
marsela13
/
practic
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_unit_export.py
569 строк
22 KB
marsela13
Upload files to "tests"
25 дек 2025, 15:34
25 дек 2025, 15:34
00b2246
Код
Авторство
О чём код?
""" Модульные тесты для экспорта данных. Тестирует функции экспорта результатов сканирования в форматы JSON и CSV. """ import sys import os import json import csv import tempfile from pathlib import Path import pytest # Добавление пути к проекту sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from network_scanner import NetworkScanner class TestJSONExport: """Тесты экспорта в JSON формат.""" def test_export_to_json_basic(self, sample_scan_results, temp_dir): """Тест базового экспорта в JSON.""" scanner = NetworkScanner() scanner.results = sample_scan_results # Создаем временный файл json_file = Path(temp_dir) / 'test_export.json' # Выполняем экспорт result = scanner.export_to_json(str(json_file)) # Проверяем результат assert result is True assert json_file.exists() # Читаем и проверяем содержимое with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) assert len(data) == 3 assert data[0]['ip'] == '192.168.1.1' assert data[0]['hostname'] == 'router.local' assert data[0]['status'] == 'Online' assert data[0]['open_ports'] == [80, 443, 22] def test_export_to_json_empty_results(self, temp_dir): """Тест экспорта пустых результатов в JSON.""" scanner = NetworkScanner() scanner.results = [] json_file = Path(temp_dir) / 'test_empty.json' result = scanner.export_to_json(str(json_file)) assert result is True assert json_file.exists() with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) assert data == [] def test_export_to_json_single_result(self, temp_dir): """Тест экспорта одного результата в JSON.""" scanner = NetworkScanner() scanner.results = [{ 'ip': '192.168.1.1', 'hostname': 'test.local', 'status': 'Online', 'open_ports': [80], 'timestamp': '2024-01-15T10:00:00.000000' }] json_file = Path(temp_dir) / 'test_single.json' result = scanner.export_to_json(str(json_file)) assert result is True with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) assert len(data) == 1 assert data[0]['ip'] == '192.168.1.1' def test_export_to_json_with_special_characters(self, temp_dir): """Тест экспорта в JSON со специальными символами.""" scanner = NetworkScanner() scanner.results = [{ 'ip': '192.168.1.1', 'hostname': 'тест-сервер.local', # Кириллица и дефис 'status': 'Online', 'open_ports': [80, 443], 'timestamp': '2024-01-15T10:00:00.000000' }] json_file = Path(temp_dir) / 'test_unicode.json' result = scanner.export_to_json(str(json_file)) assert result is True with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) assert data[0]['hostname'] == 'тест-сервер.local' def test_export_to_json_pretty_print(self, temp_dir): """Тест красивого форматирования JSON.""" scanner = NetworkScanner() scanner.results = sample_scan_results json_file = Path(temp_dir) / 'test_pretty.json' result = scanner.export_to_json(str(json_file)) assert result is True # Проверяем, что файл содержит отступы with open(json_file, 'r', encoding='utf-8') as f: content = f.read() # JSON с отступами должен содержать пробелы после { assert ' ' in content def test_export_to_json_permission_error(self): """Тест ошибки прав доступа при экспорте в JSON.""" scanner = NetworkScanner() scanner.results = sample_scan_results # Пытаемся записать в системную директорию (должна быть ошибка прав) json_file = '/root/test_export.json' # Пропускаем тест, если мы root if os.geteuid() == 0: pytest.skip("Тест пропущен: запущено от root") result = scanner.export_to_json(json_file) assert result is False def test_export_to_json_invalid_path(self): """Тест экспорта в JSON по невалидному пути.""" scanner = NetworkScanner() scanner.results = sample_scan_results # Невалидный путь json_file = '/invalid/path/test.json' result = scanner.export_to_json(json_file) assert result is False def test_export_to_json_encoding(self, temp_dir): """Тест кодировки при экспорте в JSON.""" scanner = NetworkScanner() scanner.results = [{ 'ip': '192.168.1.1', 'hostname': 'сервер', # Кириллица 'status': 'Online', 'open_ports': [80], 'timestamp': '2024-01-15T10:00:00.000000' }] json_file = Path(temp_dir) / 'test_encoding.json' result = scanner.export_to_json(str(json_file)) assert result is True # Проверяем, что файл можно прочитать с правильной кодировкой with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) assert data[0]['hostname'] == 'сервер' class TestCSVExport: """Тесты экспорта в CSV формат.""" def test_export_to_csv_basic(self, sample_scan_results, temp_dir): """Тест базового экспорта в CSV.""" scanner = NetworkScanner() scanner.results = sample_scan_results csv_file = Path(temp_dir) / 'test_export.csv' result = scanner.export_to_csv(str(csv_file)) assert result is True assert csv_file.exists() # Читаем и проверяем CSV with open(csv_file, 'r', encoding='utf-8', newline='') as f: reader = csv.DictReader(f) rows = list(reader) assert len(rows) == 3 # Проверяем заголовки expected_headers = ['IP', 'Hostname', 'Status', 'Timestamp', 'Port_22', 'Port_80', 'Port_443'] assert reader.fieldnames == expected_headers # Проверяем данные assert rows[0]['IP'] == '192.168.1.1' assert rows[0]['Hostname'] == 'router.local' assert rows[0]['Status'] == 'Online' assert rows[0]['Port_22'] == 'Open' assert rows[0]['Port_80'] == 'Open' assert rows[0]['Port_443'] == 'Open' # Второй хост не имеет открытого порта 22 assert rows[1]['IP'] == '192.168.1.100' assert rows[1]['Port_22'] == 'Closed' assert rows[1]['Port_80'] == 'Open' assert rows[1]['Port_443'] == 'Open' # Третий хост не имеет открытых портов assert rows[2]['IP'] == '192.168.1.150' assert rows[2]['Port_22'] == 'Closed' assert rows[2]['Port_80'] == 'Closed' assert rows[2]['Port_443'] == 'Closed' def test_export_to_csv_empty_results(self, temp_dir): """Тест экспорта пустых результатов в CSV.""" scanner = NetworkScanner() scanner.results = [] csv_file = Path(temp_dir) / 'test_empty.csv' result = scanner.export_to_csv(str(csv_file)) assert result is False # Должен вернуть False для пустых результатов assert not csv_file.exists() # Файл не должен быть создан def test_export_to_csv_single_result(self, temp_dir): """Тест экспорта одного результата в CSV.""" scanner = NetworkScanner() scanner.results = [{ 'ip': '192.168.1.1', 'hostname': 'test.local', 'status': 'Online', 'open_ports': [80, 443], 'timestamp': '2024-01-15T10:00:00.000000' }] csv_file = Path(temp_dir) / 'test_single.csv' result = scanner.export_to_csv(str(csv_file)) assert result is True with open(csv_file, 'r', encoding='utf-8', newline='') as f: reader = csv.DictReader(f) rows = list(reader) assert len(rows) == 1 assert reader.fieldnames == ['IP', 'Hostname', 'Status', 'Timestamp', 'Port_80', 'Port_443'] assert rows[0]['IP'] == '192.168.1.1' assert rows[0]['Port_80'] == 'Open' assert rows[0]['Port_443'] == 'Open' def test_export_to_csv_no_open_ports(self, temp_dir): """Тест экспорта результатов без открытых портов.""" scanner = NetworkScanner() scanner.results = [ { 'ip': '192.168.1.1', 'hostname': 'host1.local', 'status': 'Online', 'open_ports': [], 'timestamp': '2024-01-15T10:00:00.000000' }, { 'ip': '192.168.1.2', 'hostname': 'host2.local', 'status': 'Online', 'open_ports': [], 'timestamp': '2024-01-15T10:01:00.000000' } ] csv_file = Path(temp_dir) / 'test_no_ports.csv' result = scanner.export_to_csv(str(csv_file)) assert result is True with open(csv_file, 'r', encoding='utf-8', newline='') as f: reader = csv.DictReader(f) rows = list(reader) # Должны быть только базовые колонки assert reader.fieldnames == ['IP', 'Hostname', 'Status', 'Timestamp'] assert len(rows) == 2 def test_export_to_csv_mixed_ports(self, temp_dir): """Тест экспорта с разными наборами портов.""" scanner = NetworkScanner() scanner.results = [ { 'ip': '192.168.1.1', 'hostname': 'host1', 'status': 'Online', 'open_ports': [22, 80], # Только 22 и 80 'timestamp': '2024-01-15T10:00:00.000000' }, { 'ip': '192.168.1.2', 'hostname': 'host2', 'status': 'Online', 'open_ports': [443, 3389], # Только 443 и 3389 'timestamp': '2024-01-15T10:01:00.000000' }, { 'ip': '192.168.1.3', 'hostname': 'host3', 'status': 'Online', 'open_ports': [22, 80, 443, 3389], # Все порты 'timestamp': '2024-01-15T10:02:00.000000' } ] csv_file = Path(temp_dir) / 'test_mixed.csv' result = scanner.export_to_csv(str(csv_file)) assert result is True with open(csv_file, 'r', encoding='utf-8', newline='') as f: reader = csv.DictReader(f) rows = list(reader) # Должны быть колонки для всех портов: 22, 80, 443, 3389 expected_headers = ['IP', 'Hostname', 'Status', 'Timestamp', 'Port_22', 'Port_80', 'Port_443', 'Port_3389'] assert reader.fieldnames == expected_headers # Проверяем данные assert rows[0]['Port_22'] == 'Open' assert rows[0]['Port_80'] == 'Open' assert rows[0]['Port_443'] == 'Closed' assert rows[0]['Port_3389'] == 'Closed' assert rows[1]['Port_22'] == 'Closed' assert rows[1]['Port_80'] == 'Closed' assert rows[1]['Port_443'] == 'Open' assert rows[1]['Port_3389'] == 'Open' assert rows[2]['Port_22'] == 'Open' assert rows[2]['Port_80'] == 'Open' assert rows[2]['Port_443'] == 'Open' assert rows[2]['Port_3389'] == 'Open' def test_export_to_csv_special_characters(self, temp_dir): """Тест экспорта в CSV со специальными символами.""" scanner = NetworkScanner() scanner.results = [{ 'ip': '192.168.1.1', 'hostname': 'тест,сервер;"quotes"', # Запятая, кавычки, кириллица 'status': 'Online', 'open_ports': [80], 'timestamp': '2024-01-15T10:00:00.000000' }] csv_file = Path(temp_dir) / 'test_special.csv' result = scanner.export_to_csv(str(csv_file)) assert result is True # CSV должен корректно обрабатывать специальные символы with open(csv_file, 'r', encoding='utf-8', newline='') as f: content = f.read() # Проверяем, что содержимое можно прочитать with open(csv_file, 'r', encoding='utf-8', newline='') as f: reader = csv.reader(f) rows = list(reader) assert len(rows) == 2 # Заголовок + данные def test_export_to_csv_permission_error(self): """Тест ошибки прав доступа при экспорте в CSV.""" scanner = NetworkScanner() scanner.results = sample_scan_results # Пытаемся записать в системную директорию csv_file = '/root/test_export.csv' # Пропускаем тест, если мы root if os.geteuid() == 0: pytest.skip("Тест пропущен: запущено от root") result = scanner.export_to_csv(csv_file) assert result is False def test_export_to_csv_invalid_path(self): """Тест экспорта в CSV по невалидному пути.""" scanner = NetworkScanner() scanner.results = sample_scan_results csv_file = '/invalid/path/test.csv' result = scanner.export_to_csv(csv_file) assert result is False class TestExportComparison: """Тесты сравнения форматов экспорта.""" def test_json_vs_csv_size(self, sample_scan_results, temp_dir): """Тест сравнения размера JSON и CSV файлов.""" scanner = NetworkScanner() scanner.results = sample_scan_results json_file = Path(temp_dir) / 'test_comparison.json' csv_file = Path(temp_dir) / 'test_comparison.csv' # Экспортируем в оба формата json_result = scanner.export_to_json(str(json_file)) csv_result = scanner.export_to_csv(str(csv_file)) assert json_result is True assert csv_result is True # Получаем размеры файлов json_size = json_file.stat().st_size csv_size = csv_file.stat().st_size # Обычно CSV меньше JSON для табличных данных print(f"JSON size: {json_size} bytes") print(f"CSV size: {csv_size} bytes") # Для данного набора данных CSV должен быть меньше # Но это не всегда гарантировано if len(sample_scan_results) > 0: assert json_size > 0 assert csv_size > 0 def test_json_vs_csv_readability(self, sample_scan_results, temp_dir): """Тест читаемости JSON и CSV файлов.""" scanner = NetworkScanner() scanner.results = sample_scan_results json_file = Path(temp_dir) / 'test_readability.json' csv_file = Path(temp_dir) / 'test_readability.csv' scanner.export_to_json(str(json_file)) scanner.export_to_csv(str(csv_file)) # Проверяем, что оба файла можно прочитать with open(json_file, 'r', encoding='utf-8') as f: json_data = json.load(f) with open(csv_file, 'r', encoding='utf-8', newline='') as f: csv_reader = csv.DictReader(f) csv_data = list(csv_reader) # Проверяем, что данные совпадают assert len(json_data) == len(csv_data) # JSON содержит оригинальную структуру assert 'open_ports' in json_data[0] # CSV содержит плоскую структуру с колонками портов assert 'Port_80' in csv_data[0] def test_export_formats_completeness(self, sample_scan_results, temp_dir): """Тест полноты данных в разных форматах экспорта.""" scanner = NetworkScanner() scanner.results = sample_scan_results json_file = Path(temp_dir) / 'test_completeness.json' csv_file = Path(temp_dir) / 'test_completeness.csv' scanner.export_to_json(str(json_file)) scanner.export_to_csv(str(csv_file)) # Читаем JSON with open(json_file, 'r', encoding='utf-8') as f: json_data = json.load(f) # Читаем CSV with open(csv_file, 'r', encoding='utf-8', newline='') as f: csv_reader = csv.DictReader(f) csv_data = list(csv_reader) # Проверяем, что все хосты экспортированы в обоих форматах json_ips = {item['ip'] for item in json_data} csv_ips = {row['IP'] for row in csv_data} assert json_ips == csv_ips # Проверяем, что все порты присутствуют в CSV all_ports = set() for host in json_data: all_ports.update(host['open_ports']) # В CSV должны быть колонки для всех портов csv_headers = csv_reader.fieldnames csv_port_columns = [h for h in csv_headers if h.startswith('Port_')] csv_ports = {int(h.split('_')[1]) for h in csv_port_columns} assert all_ports == csv_ports class TestExportErrorHandling: """Тесты обработки ошибок при экспорте.""" def test_export_json_io_error(self, monkeypatch): """Тест ошибки ввода-вывода при экспорте JSON.""" scanner = NetworkScanner() scanner.results = sample_scan_results # Мокаем open чтобы вызвать ошибку def mock_open(*args, **kwargs): raise IOError("Mock IO error") monkeypatch.setattr('builtins.open', mock_open) result = scanner.export_to_json('test.json') assert result is False def test_export_csv_csv_error(self, monkeypatch): """Тест ошибки CSV модуля при экспорте.""" scanner = NetworkScanner() scanner.results = sample_scan_results # Мокаем csv.DictWriter чтобы вызвать ошибку def mock_dictwriter(*args, **kwargs): raise csv.Error("Mock CSV error") monkeypatch.setattr('csv.DictWriter', mock_dictwriter) result = scanner.export_to_csv('test.csv') assert result is False def test_export_json_encoding_error(self, sample_scan_results): """Тест ошибки кодировки при экспорте JSON.""" scanner = NetworkScanner() # Создаем данные с несериализуемым объектом scanner.results = [{ 'ip': '192.168.1.1', 'hostname': 'test', 'status': 'Online', 'open_ports': [80], 'timestamp': '2024-01-15T10:00:00.000000', 'invalid_field': object() # Несериализуемый объект }] result = scanner.export_to_json('test.json') assert result is False if __name__ == '__main__': pytest.main([__file__, '-v'])