ميزة: إضافة مكوني ProcedureSelection و StepProgress لأداة مخططات التدفق بصيغة PDF
- تنفيذ مكون ProcedureSelection لتمكين المستخدمين من اختيار الإجراءات من قائمة، وإدارة الاختيارات، ومعالجة الإجراءات المرفوضة. - إنشاء مكون StepProgress لعرض تقدم معالج متعدد الخطوات بشكل مرئي. - تعريف أنواع مشتركة للإجراءات، وخطوات التدفق، ورسائل الدردشة في ملف types.ts. - إضافة اختبارات وحدة لخطافات useFileUpload و useTaskPolling لضمان الأداء السليم ومعالجة الأخطاء. - تنفيذ اختبارات واجهة برمجة التطبيقات (API) للتحقق من تنسيقات نقاط النهاية وضمان اتساق ربط الواجهة الأمامية بالخلفية.
This commit is contained in:
176
backend/tests/test_pdf_tools_tasks.py
Normal file
176
backend/tests/test_pdf_tools_tasks.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""Tests for PDF tools Celery task routes — ensures frontend→backend request formats work."""
|
||||
import io
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
class TestPdfToolsTaskRoutes:
|
||||
"""
|
||||
These tests verify that the backend route accepts the exact request format
|
||||
the frontend sends, processes parameters correctly, and dispatches the
|
||||
appropriate Celery task.
|
||||
"""
|
||||
|
||||
def test_split_dispatches_task(self, client, monkeypatch):
|
||||
"""Split route should dispatch split_pdf_task with correct params."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'split-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('split-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.split_pdf_task.delay', mock_delay)
|
||||
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'mode': 'range',
|
||||
'pages': '1-5',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/split', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
# Verify task was called with (input_path, task_id, filename, mode, pages)
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[3] == 'range'
|
||||
assert args[4] == '1-5'
|
||||
|
||||
def test_rotate_dispatches_task(self, client, monkeypatch):
|
||||
"""Rotate route should dispatch with rotation and pages params."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'rotate-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('rotate-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.rotate_pdf_task.delay', mock_delay)
|
||||
|
||||
# Frontend sends: rotation=90, pages=all
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'rotation': '180',
|
||||
'pages': 'all',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/rotate', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[3] == 180 # rotation as int
|
||||
assert args[4] == 'all'
|
||||
|
||||
def test_watermark_dispatches_task(self, client, monkeypatch):
|
||||
"""Watermark route should dispatch with text and opacity."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'wm-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('wm-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.watermark_pdf_task.delay', mock_delay)
|
||||
|
||||
# Frontend sends: text and opacity (as decimal string)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'text': 'CONFIDENTIAL',
|
||||
'opacity': '0.3',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/watermark', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[3] == 'CONFIDENTIAL'
|
||||
assert args[4] == 0.3
|
||||
|
||||
def test_protect_dispatches_task(self, client, monkeypatch):
|
||||
"""Protect route should dispatch with password."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'protect-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('protect-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.protect_pdf_task.delay', mock_delay)
|
||||
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'password': 'mySecret123',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/protect', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[3] == 'mySecret123'
|
||||
|
||||
def test_unlock_dispatches_task(self, client, monkeypatch):
|
||||
"""Unlock route should dispatch with password."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'unlock-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('unlock-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.unlock_pdf_task.delay', mock_delay)
|
||||
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'password': 'oldPassword',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/unlock', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
|
||||
def test_page_numbers_dispatches_task(self, client, monkeypatch):
|
||||
"""Page numbers route should dispatch with position and start_number."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'pn-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('pn-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.add_page_numbers_task.delay', mock_delay)
|
||||
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'position': 'top-right',
|
||||
'start_number': '5',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/page-numbers', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[3] == 'top-right'
|
||||
assert args[4] == 5
|
||||
|
||||
def test_pdf_to_images_dispatches_task(self, client, monkeypatch):
|
||||
"""PDF to images route should dispatch with format and dpi."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'p2i-id'
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr('app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.generate_safe_path',
|
||||
lambda ext, folder_type: ('p2i-id', '/tmp/test.pdf'))
|
||||
monkeypatch.setattr('app.routes.pdf_tools.pdf_to_images_task.delay', mock_delay)
|
||||
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
'format': 'jpg',
|
||||
'dpi': '300',
|
||||
}
|
||||
response = client.post('/api/pdf-tools/pdf-to-images', data=data,
|
||||
content_type='multipart/form-data')
|
||||
assert response.status_code == 202
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[3] == 'jpg'
|
||||
assert args[4] == 300
|
||||
Reference in New Issue
Block a user