- تنفيذ مكون ProcedureSelection لتمكين المستخدمين من اختيار الإجراءات من قائمة، وإدارة الاختيارات، ومعالجة الإجراءات المرفوضة. - إنشاء مكون StepProgress لعرض تقدم معالج متعدد الخطوات بشكل مرئي. - تعريف أنواع مشتركة للإجراءات، وخطوات التدفق، ورسائل الدردشة في ملف types.ts. - إضافة اختبارات وحدة لخطافات useFileUpload و useTaskPolling لضمان الأداء السليم ومعالجة الأخطاء. - تنفيذ اختبارات واجهة برمجة التطبيقات (API) للتحقق من تنسيقات نقاط النهاية وضمان اتساق ربط الواجهة الأمامية بالخلفية.
108 lines
4.5 KiB
Python
108 lines
4.5 KiB
Python
"""Tests for file validation utility."""
|
|
import io
|
|
from unittest.mock import patch, MagicMock
|
|
from app.utils.file_validator import validate_file, FileValidationError
|
|
import pytest
|
|
|
|
|
|
class TestFileValidator:
|
|
def test_no_file_raises(self, app):
|
|
"""Should raise when no file provided."""
|
|
with app.app_context():
|
|
with pytest.raises(FileValidationError, match="No file provided"):
|
|
validate_file(None, allowed_types=["pdf"])
|
|
|
|
def test_empty_filename_raises(self, app):
|
|
"""Should raise when filename is empty."""
|
|
with app.app_context():
|
|
mock_file = MagicMock()
|
|
mock_file.filename = ''
|
|
with pytest.raises(FileValidationError, match="No file provided"):
|
|
validate_file(mock_file, allowed_types=["pdf"])
|
|
|
|
def test_wrong_extension_raises(self, app):
|
|
"""Should raise when file extension is not allowed."""
|
|
with app.app_context():
|
|
mock_file = MagicMock()
|
|
mock_file.filename = 'test.exe'
|
|
with pytest.raises(FileValidationError, match="not allowed"):
|
|
validate_file(mock_file, allowed_types=["pdf"])
|
|
|
|
def test_empty_file_raises(self, app):
|
|
"""Should raise when file is empty (0 bytes)."""
|
|
with app.app_context():
|
|
content = io.BytesIO(b'')
|
|
mock_file = MagicMock()
|
|
mock_file.filename = 'test.pdf'
|
|
mock_file.seek = content.seek
|
|
mock_file.tell = content.tell
|
|
mock_file.read = content.read
|
|
with pytest.raises(FileValidationError, match="empty"):
|
|
validate_file(mock_file, allowed_types=["pdf"])
|
|
|
|
def test_valid_pdf_passes(self, app):
|
|
"""Should accept valid PDF file with correct magic bytes."""
|
|
with app.app_context():
|
|
pdf_bytes = b'%PDF-1.4 test content' + b'\x00' * 8192
|
|
content = io.BytesIO(pdf_bytes)
|
|
|
|
mock_file = MagicMock()
|
|
mock_file.filename = 'document.pdf'
|
|
mock_file.seek = content.seek
|
|
mock_file.tell = content.tell
|
|
mock_file.read = content.read
|
|
|
|
with patch('app.utils.file_validator.magic') as mock_magic:
|
|
mock_magic.from_buffer.return_value = 'application/pdf'
|
|
filename, ext = validate_file(mock_file, allowed_types=["pdf"])
|
|
|
|
assert filename == 'document.pdf'
|
|
assert ext == 'pdf'
|
|
|
|
def test_mime_mismatch_raises(self, app):
|
|
"""Should raise when MIME type doesn't match extension."""
|
|
with app.app_context():
|
|
content = io.BytesIO(b'not a real pdf' + b'\x00' * 8192)
|
|
|
|
mock_file = MagicMock()
|
|
mock_file.filename = 'fake.pdf'
|
|
mock_file.seek = content.seek
|
|
mock_file.tell = content.tell
|
|
mock_file.read = content.read
|
|
|
|
with patch('app.utils.file_validator.magic') as mock_magic:
|
|
mock_magic.from_buffer.return_value = 'text/plain'
|
|
with pytest.raises(FileValidationError, match="does not match"):
|
|
validate_file(mock_file, allowed_types=["pdf"])
|
|
|
|
def test_file_too_large_raises(self, app):
|
|
"""Should raise when file exceeds size limit."""
|
|
with app.app_context():
|
|
# Create a file larger than the PDF size limit (20MB)
|
|
large_content = io.BytesIO(b'%PDF-1.4' + b'\x00' * (21 * 1024 * 1024))
|
|
|
|
mock_file = MagicMock()
|
|
mock_file.filename = 'large.pdf'
|
|
mock_file.seek = large_content.seek
|
|
mock_file.tell = large_content.tell
|
|
mock_file.read = large_content.read
|
|
|
|
with pytest.raises(FileValidationError, match="too large"):
|
|
validate_file(mock_file, allowed_types=["pdf"])
|
|
|
|
def test_dangerous_pdf_raises(self, app):
|
|
"""Should raise when PDF contains dangerous patterns."""
|
|
with app.app_context():
|
|
pdf_bytes = b'%PDF-1.4 /JavaScript evil_code' + b'\x00' * 8192
|
|
content = io.BytesIO(pdf_bytes)
|
|
|
|
mock_file = MagicMock()
|
|
mock_file.filename = 'evil.pdf'
|
|
mock_file.seek = content.seek
|
|
mock_file.tell = content.tell
|
|
mock_file.read = content.read
|
|
|
|
with patch('app.utils.file_validator.magic') as mock_magic:
|
|
mock_magic.from_buffer.return_value = 'application/pdf'
|
|
with pytest.raises(FileValidationError, match="unsafe"):
|
|
validate_file(mock_file, allowed_types=["pdf"]) |