ميزة: تحديث صفحات الخصوصية والشروط مع تاريخ آخر تحديث ثابت وفترة احتفاظ ديناميكية بالملفات
ميزة: إضافة خدمة تحليلات لتكامل Google Analytics اختبار: تحديث اختبارات خدمة واجهة برمجة التطبيقات (API) لتعكس تغييرات نقاط النهاية إصلاح: تعديل خدمة واجهة برمجة التطبيقات (API) لدعم تحميل ملفات متعددة ومصادقة المستخدم ميزة: تطبيق مخزن مصادقة باستخدام Zustand لإدارة المستخدمين إصلاح: تحسين إعدادات Nginx لتعزيز الأمان ودعم التحليلات
This commit is contained in:
@@ -1,21 +1,35 @@
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from app import create_app
|
||||
from app.services.account_service import init_account_db
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
"""Create application for testing."""
|
||||
os.environ['FLASK_ENV'] = 'testing'
|
||||
test_root = tempfile.mkdtemp(prefix='saas-pdf-tests-')
|
||||
db_path = os.path.join(test_root, 'test_saas_pdf.db')
|
||||
upload_folder = os.path.join(test_root, 'uploads')
|
||||
output_folder = os.path.join(test_root, 'outputs')
|
||||
os.environ['DATABASE_PATH'] = db_path
|
||||
os.environ['UPLOAD_FOLDER'] = upload_folder
|
||||
os.environ['OUTPUT_FOLDER'] = output_folder
|
||||
|
||||
app = create_app('testing')
|
||||
app.config.update({
|
||||
'TESTING': True,
|
||||
'UPLOAD_FOLDER': '/tmp/test_uploads',
|
||||
'OUTPUT_FOLDER': '/tmp/test_outputs',
|
||||
'UPLOAD_FOLDER': upload_folder,
|
||||
'OUTPUT_FOLDER': output_folder,
|
||||
'DATABASE_PATH': db_path,
|
||||
})
|
||||
with app.app_context():
|
||||
init_account_db()
|
||||
|
||||
# Create temp directories
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
os.makedirs(app.config['OUTPUT_FOLDER'], exist_ok=True)
|
||||
@@ -23,8 +37,10 @@ def app():
|
||||
yield app
|
||||
|
||||
# Cleanup temp directories
|
||||
shutil.rmtree(app.config['UPLOAD_FOLDER'], ignore_errors=True)
|
||||
shutil.rmtree(app.config['OUTPUT_FOLDER'], ignore_errors=True)
|
||||
shutil.rmtree(test_root, ignore_errors=True)
|
||||
os.environ.pop('DATABASE_PATH', None)
|
||||
os.environ.pop('UPLOAD_FOLDER', None)
|
||||
os.environ.pop('OUTPUT_FOLDER', None)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
67
backend/tests/test_auth.py
Normal file
67
backend/tests/test_auth.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Tests for session-backed authentication routes."""
|
||||
|
||||
|
||||
class TestAuthRoutes:
|
||||
def test_register_success(self, client):
|
||||
response = client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
data = response.get_json()
|
||||
assert data['user']['email'] == 'user@example.com'
|
||||
assert data['user']['plan'] == 'free'
|
||||
|
||||
def test_register_duplicate_email(self, client):
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
response = client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
|
||||
assert response.status_code == 409
|
||||
assert 'already exists' in response.get_json()['error'].lower()
|
||||
|
||||
def test_login_and_me(self, client):
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
client.post('/api/auth/logout')
|
||||
|
||||
login_response = client.post(
|
||||
'/api/auth/login',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
me_response = client.get('/api/auth/me')
|
||||
|
||||
assert login_response.status_code == 200
|
||||
assert me_response.status_code == 200
|
||||
me_data = me_response.get_json()
|
||||
assert me_data['authenticated'] is True
|
||||
assert me_data['user']['email'] == 'user@example.com'
|
||||
|
||||
def test_login_invalid_password(self, client):
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
client.post('/api/auth/logout')
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/login',
|
||||
json={'email': 'user@example.com', 'password': 'wrongpass123'},
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert 'invalid email or password' in response.get_json()['error'].lower()
|
||||
|
||||
def test_me_without_session(self, client):
|
||||
response = client.get('/api/auth/me')
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.get_json() == {'authenticated': False, 'user': None}
|
||||
@@ -16,8 +16,8 @@ class TestCompressTaskRoute:
|
||||
mock_task.id = 'compress-task-id'
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.compress.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.compress.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.compress.generate_safe_path',
|
||||
@@ -47,8 +47,8 @@ class TestCompressTaskRoute:
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.compress.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.compress.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.compress.generate_safe_path',
|
||||
|
||||
@@ -10,8 +10,8 @@ class TestConvertTaskRoutes:
|
||||
mock_task.id = 'convert-pdf-word-id'
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.convert.validate_file',
|
||||
lambda f, allowed_types: ('document.pdf', 'pdf'),
|
||||
'app.routes.convert.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('document.pdf', 'pdf'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.convert.generate_safe_path',
|
||||
@@ -39,8 +39,8 @@ class TestConvertTaskRoutes:
|
||||
mock_task.id = 'convert-word-pdf-id'
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.convert.validate_file',
|
||||
lambda f, allowed_types: ('report.docx', 'docx'),
|
||||
'app.routes.convert.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('report.docx', 'docx'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.convert.generate_safe_path',
|
||||
|
||||
54
backend/tests/test_flowchart_tasks.py
Normal file
54
backend/tests/test_flowchart_tasks.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Tests for flowchart task routes."""
|
||||
import io
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
class TestFlowchartTaskRoutes:
|
||||
def test_extract_flowchart_dispatches_task(self, client, monkeypatch):
|
||||
"""Should dispatch extraction task for uploaded PDF."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = "flow-task-id"
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.routes.flowchart.validate_actor_file",
|
||||
lambda f, allowed_types, actor: ("manual.pdf", "pdf"),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.routes.flowchart.generate_safe_path",
|
||||
lambda ext: ("flow-task-id", "/tmp/test.pdf"),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.routes.flowchart.extract_flowchart_task.delay",
|
||||
mock_delay,
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
"/api/flowchart/extract",
|
||||
data={"file": (io.BytesIO(b"%PDF-1.4"), "manual.pdf")},
|
||||
content_type="multipart/form-data",
|
||||
)
|
||||
|
||||
assert response.status_code == 202
|
||||
body = response.get_json()
|
||||
assert body["task_id"] == "flow-task-id"
|
||||
|
||||
args = mock_delay.call_args[0]
|
||||
assert args[0] == "/tmp/test.pdf"
|
||||
assert args[1] == "flow-task-id"
|
||||
assert args[2] == "manual.pdf"
|
||||
|
||||
def test_extract_sample_dispatches_task(self, client, monkeypatch):
|
||||
"""Should dispatch sample extraction task without file upload."""
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = "sample-flow-task-id"
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.routes.flowchart.extract_sample_flowchart_task.delay",
|
||||
MagicMock(return_value=mock_task),
|
||||
)
|
||||
|
||||
response = client.post("/api/flowchart/extract-sample")
|
||||
assert response.status_code == 202
|
||||
body = response.get_json()
|
||||
assert body["task_id"] == "sample-flow-task-id"
|
||||
68
backend/tests/test_history.py
Normal file
68
backend/tests/test_history.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Tests for authenticated file history routes."""
|
||||
from app.services.account_service import record_file_history
|
||||
|
||||
|
||||
class TestHistoryRoutes:
|
||||
def test_history_requires_auth(self, client):
|
||||
response = client.get('/api/history')
|
||||
|
||||
assert response.status_code == 401
|
||||
assert 'authentication required' in response.get_json()['error'].lower()
|
||||
|
||||
def test_history_returns_items(self, client, app):
|
||||
register_response = client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
user_id = register_response.get_json()['user']['id']
|
||||
|
||||
with app.app_context():
|
||||
record_file_history(
|
||||
user_id=user_id,
|
||||
tool='pdf-to-word',
|
||||
original_filename='report.pdf',
|
||||
output_filename='report.docx',
|
||||
status='completed',
|
||||
download_url='/api/download/123/report.docx',
|
||||
metadata={'output_size': 2048},
|
||||
)
|
||||
|
||||
response = client.get('/api/history?limit=10')
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert len(data['items']) == 1
|
||||
assert data['items'][0]['tool'] == 'pdf-to-word'
|
||||
assert data['items'][0]['output_filename'] == 'report.docx'
|
||||
|
||||
def test_history_limit_is_applied(self, client, app):
|
||||
register_response = client.post(
|
||||
'/api/auth/register',
|
||||
json={'email': 'user@example.com', 'password': 'secretpass123'},
|
||||
)
|
||||
user_id = register_response.get_json()['user']['id']
|
||||
|
||||
with app.app_context():
|
||||
record_file_history(
|
||||
user_id=user_id,
|
||||
tool='pdf-to-word',
|
||||
original_filename='first.pdf',
|
||||
output_filename='first.docx',
|
||||
status='completed',
|
||||
download_url='/api/download/1/first.docx',
|
||||
metadata=None,
|
||||
)
|
||||
record_file_history(
|
||||
user_id=user_id,
|
||||
tool='word-to-pdf',
|
||||
original_filename='second.docx',
|
||||
output_filename='second.pdf',
|
||||
status='completed',
|
||||
download_url='/api/download/2/second.pdf',
|
||||
metadata=None,
|
||||
)
|
||||
|
||||
response = client.get('/api/history?limit=1')
|
||||
|
||||
assert response.status_code == 200
|
||||
assert len(response.get_json()['items']) == 1
|
||||
@@ -10,8 +10,8 @@ class TestImageTaskRoutes:
|
||||
mock_task.id = 'img-convert-id'
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.image.validate_file',
|
||||
lambda f, allowed_types: ('photo.png', 'png'),
|
||||
'app.routes.image.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('photo.png', 'png'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.image.generate_safe_path',
|
||||
@@ -55,8 +55,8 @@ class TestImageTaskRoutes:
|
||||
mock_task.id = 'img-resize-id'
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.image.validate_file',
|
||||
lambda f, allowed_types: ('photo.jpg', 'jpg'),
|
||||
'app.routes.image.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('photo.jpg', 'jpg'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.image.generate_safe_path',
|
||||
@@ -83,8 +83,8 @@ class TestImageTaskRoutes:
|
||||
def test_resize_image_no_dimensions(self, client, monkeypatch):
|
||||
"""Should return 400 when both width and height are missing."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.image.validate_file',
|
||||
lambda f, allowed_types: ('photo.jpg', 'jpg'),
|
||||
'app.routes.image.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('photo.jpg', 'jpg'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\xff\xd8\xff'), 'photo.jpg'),
|
||||
@@ -100,8 +100,8 @@ class TestImageTaskRoutes:
|
||||
def test_resize_image_invalid_width(self, client, monkeypatch):
|
||||
"""Should return 400 for width out of range."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.image.validate_file',
|
||||
lambda f, allowed_types: ('photo.jpg', 'jpg'),
|
||||
'app.routes.image.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('photo.jpg', 'jpg'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\xff\xd8\xff'), 'photo.jpg'),
|
||||
|
||||
@@ -76,7 +76,7 @@ class TestConcurrentRequests:
|
||||
return t
|
||||
|
||||
# Apply all patches BEFORE threads start — avoids concurrent patch/unpatch
|
||||
with patch('app.routes.compress.validate_file', return_value=('t.pdf', 'pdf')), \
|
||||
with patch('app.routes.compress.validate_actor_file', return_value=('t.pdf', 'pdf')), \
|
||||
patch('app.routes.compress.generate_safe_path',
|
||||
side_effect=lambda ext, folder_type: (f'tid-x', '/tmp/up/t.pdf')), \
|
||||
patch('werkzeug.datastructures.file_storage.FileStorage.save'), \
|
||||
@@ -121,7 +121,7 @@ class TestConcurrentRequests:
|
||||
errors: list[Exception] = []
|
||||
lock = threading.Lock()
|
||||
|
||||
with patch('app.routes.pdf_tools.validate_file', return_value=('t.pdf', 'pdf')), \
|
||||
with patch('app.routes.pdf_tools.validate_actor_file', return_value=('t.pdf', 'pdf')), \
|
||||
patch('app.routes.pdf_tools.generate_safe_path',
|
||||
side_effect=lambda ext, folder_type: ('split-x', '/tmp/up/t.pdf')), \
|
||||
patch('werkzeug.datastructures.file_storage.FileStorage.save'), \
|
||||
@@ -180,8 +180,8 @@ class TestFileSizeLimits:
|
||||
def test_normal_size_file_is_accepted(self, client, monkeypatch):
|
||||
"""A file within the size limit reaches the route logic."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.compress.validate_file',
|
||||
lambda f, allowed_types: ('t.pdf', 'pdf'),
|
||||
'app.routes.compress.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('t.pdf', 'pdf'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.compress.generate_safe_path',
|
||||
|
||||
@@ -20,8 +20,8 @@ def _mock_validate_and_task(monkeypatch, task_module_path, task_name):
|
||||
|
||||
# Mock file validator to accept any file
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.generate_safe_path',
|
||||
@@ -62,8 +62,8 @@ class TestMergePdfs:
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'merge-task-id'
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.merge_pdfs_task.delay',
|
||||
@@ -95,8 +95,8 @@ class TestMergePdfs:
|
||||
def test_merge_too_many_files(self, client, monkeypatch):
|
||||
"""Should return 400 when more than 20 files provided."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'files': [
|
||||
@@ -166,8 +166,8 @@ class TestSplitPdf:
|
||||
def test_split_range_mode_requires_pages(self, client, monkeypatch):
|
||||
"""Should return 400 when range mode is selected without pages."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
|
||||
data = {
|
||||
@@ -195,8 +195,8 @@ class TestRotatePdf:
|
||||
def test_rotate_invalid_degrees(self, client, monkeypatch):
|
||||
"""Should reject invalid rotation angles."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
@@ -327,8 +327,8 @@ class TestImagesToPdf:
|
||||
mock_task = MagicMock()
|
||||
mock_task.id = 'images-task-id'
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.png', 'png'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.png', 'png'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.images_to_pdf_task.delay',
|
||||
@@ -356,8 +356,8 @@ class TestImagesToPdf:
|
||||
|
||||
def test_images_to_pdf_too_many(self, client, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.png', 'png'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.png', 'png'),
|
||||
)
|
||||
data = {
|
||||
'files': [
|
||||
@@ -384,8 +384,8 @@ class TestWatermarkPdf:
|
||||
|
||||
def test_watermark_no_text(self, client, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
@@ -401,8 +401,8 @@ class TestWatermarkPdf:
|
||||
|
||||
def test_watermark_text_too_long(self, client, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
@@ -443,8 +443,8 @@ class TestProtectPdf:
|
||||
|
||||
def test_protect_no_password(self, client, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
@@ -460,8 +460,8 @@ class TestProtectPdf:
|
||||
|
||||
def test_protect_short_password(self, client, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
@@ -501,8 +501,8 @@ class TestUnlockPdf:
|
||||
|
||||
def test_unlock_no_password(self, client, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'app.routes.pdf_tools.validate_file',
|
||||
lambda f, allowed_types: ('test.pdf', 'pdf'),
|
||||
'app.routes.pdf_tools.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('test.pdf', 'pdf'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'%PDF-1.4'), 'test.pdf'),
|
||||
|
||||
@@ -16,8 +16,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
@@ -41,8 +41,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
@@ -66,8 +66,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
@@ -91,8 +91,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
@@ -113,8 +113,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
@@ -133,8 +133,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
@@ -157,8 +157,8 @@ class TestPdfToolsTaskRoutes:
|
||||
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.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('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)
|
||||
|
||||
@@ -14,8 +14,8 @@ class TestVideoToGif:
|
||||
def test_to_gif_invalid_params(self, client, monkeypatch):
|
||||
"""Should return 400 for non-numeric parameters."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('test.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: (r'test.mp4', r'mp4'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\x00\x00\x00\x1cftyp'), 'test.mp4'),
|
||||
@@ -32,8 +32,8 @@ class TestVideoToGif:
|
||||
def test_to_gif_negative_start(self, client, monkeypatch):
|
||||
"""Should reject negative start time."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('test.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: (r'test.mp4', r'mp4'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\x00\x00\x00\x1cftyp'), 'test.mp4'),
|
||||
@@ -52,8 +52,8 @@ class TestVideoToGif:
|
||||
def test_to_gif_duration_too_long(self, client, monkeypatch):
|
||||
"""Should reject duration > 15 seconds."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('test.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: (r'test.mp4', r'mp4'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\x00\x00\x00\x1cftyp'), 'test.mp4'),
|
||||
@@ -73,8 +73,8 @@ class TestVideoToGif:
|
||||
def test_to_gif_fps_out_of_range(self, client, monkeypatch):
|
||||
"""Should reject FPS > 20."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('test.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: (r'test.mp4', r'mp4'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\x00\x00\x00\x1cftyp'), 'test.mp4'),
|
||||
@@ -93,8 +93,8 @@ class TestVideoToGif:
|
||||
def test_to_gif_width_out_of_range(self, client, monkeypatch):
|
||||
"""Should reject width > 640."""
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('test.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: (r'test.mp4', r'mp4'),
|
||||
)
|
||||
data = {
|
||||
'file': (io.BytesIO(b'\x00\x00\x00\x1cftyp'), 'test.mp4'),
|
||||
@@ -116,8 +116,8 @@ class TestVideoToGif:
|
||||
mock_task.id = 'gif-task-id'
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('test.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: (r'test.mp4', r'mp4'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.generate_safe_path',
|
||||
|
||||
@@ -11,8 +11,8 @@ class TestVideoTaskRoutes:
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('video.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('video.mp4', 'mp4'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.generate_safe_path',
|
||||
@@ -53,8 +53,8 @@ class TestVideoTaskRoutes:
|
||||
mock_delay = MagicMock(return_value=mock_task)
|
||||
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.validate_file',
|
||||
lambda f, allowed_types: ('video.mp4', 'mp4'),
|
||||
'app.routes.video.validate_actor_file',
|
||||
lambda f, allowed_types, actor: ('video.mp4', 'mp4'),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
'app.routes.video.generate_safe_path',
|
||||
|
||||
Reference in New Issue
Block a user