diff --git a/apps/api/app/api/routes/feature_flags.py b/apps/api/app/api/routes/feature_flags.py index 287ebf745..fada08be9 100755 --- a/apps/api/app/api/routes/feature_flags.py +++ b/apps/api/app/api/routes/feature_flags.py @@ -23,7 +23,6 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status from pydantic import BaseModel, Field from packages.adapters.redis.feature_flag_store import ( - FEATURE_FLAG_REDIS_PREFIX, FeatureFlagConfig, RedisFeatureFlagStore, ) diff --git a/apps/api/app/api/routes/generation_tasks.py b/apps/api/app/api/routes/generation_tasks.py index f3f94208d..04d509b02 100755 --- a/apps/api/app/api/routes/generation_tasks.py +++ b/apps/api/app/api/routes/generation_tasks.py @@ -10,7 +10,6 @@ from app.core.task_enqueue import ( USER_PENDING_LIMIT, GlobalQueueFull, UserPendingLimitExceeded, - check_queue_limits, safe_enqueue_generation_task, ) from app.dependencies import ( diff --git a/apps/worker/video_processing/dedup.py b/apps/worker/video_processing/dedup.py index e71d96cb8..efbbd7b4d 100755 --- a/apps/worker/video_processing/dedup.py +++ b/apps/worker/video_processing/dedup.py @@ -1,10 +1,8 @@ """Video deduplication module - compute fingerprints and detect duplicates.""" import hashlib -import json import logging import os -import subprocess import tempfile from dataclasses import dataclass from typing import Optional @@ -327,7 +325,6 @@ def check_duplicate_task(self: Task, generated_video_id: str) -> dict: raise ValueError(f"Generated video {generated_video_id} not found") local_path = os.path.join(temp_dir, f"{generated_video_id}.mp4") - storage_key = video.file_url.split("/")[-1] storage_service.download_file( f"projects/{video.project_id}/generated/{generated_video_id}/{generated_video_id}.mp4", local_path ) diff --git a/apps/worker/video_processing/ffmpeg_utils.py b/apps/worker/video_processing/ffmpeg_utils.py index 379353910..02c1a099e 100755 --- a/apps/worker/video_processing/ffmpeg_utils.py +++ b/apps/worker/video_processing/ffmpeg_utils.py @@ -81,7 +81,7 @@ def run_ffmpeg( timeout=timeout, ) return (result.stdout or "", result.stderr or "") - except subprocess.TimeoutExpired as e: + except subprocess.TimeoutExpired: logger.error( "FFmpeg 命令超时 (%ds): command=%s", timeout or -1, diff --git a/apps/worker/video_processing/oss_helpers.py b/apps/worker/video_processing/oss_helpers.py index 8638e4567..78944886d 100755 --- a/apps/worker/video_processing/oss_helpers.py +++ b/apps/worker/video_processing/oss_helpers.py @@ -11,7 +11,6 @@ import logging import os import threading from pathlib import Path -from typing import Optional from urllib.parse import urlparse import oss2 diff --git a/apps/worker/video_processing/processor.py b/apps/worker/video_processing/processor.py index 236b602c5..6e9329d3a 100644 --- a/apps/worker/video_processing/processor.py +++ b/apps/worker/video_processing/processor.py @@ -5,7 +5,6 @@ import os import tempfile from dataclasses import dataclass -from pathlib import Path from typing import List import ffmpeg diff --git a/apps/worker/video_processing/render_adapter.py b/apps/worker/video_processing/render_adapter.py index 2b09bc3db..4fd5f76c0 100755 --- a/apps/worker/video_processing/render_adapter.py +++ b/apps/worker/video_processing/render_adapter.py @@ -17,15 +17,15 @@ import logging import tempfile from dataclasses import dataclass from pathlib import Path -from typing import Any, Callable +from typing import Callable from sqlalchemy.orm import Session from video_processing.oss_helpers import download_asset, upload_to_oss -from video_processing.unified_render_service import RenderResult, UnifiedRenderService +from video_processing.unified_render_service import UnifiedRenderService from packages.adapters.sqlalchemy_impl.edit_plan_clip_repository import SQLAlchemyEditPlanClipRepository from packages.adapters.sqlalchemy_impl.edit_plan_repository import SQLAlchemyEditPlanRepository -from packages.domain.edit_plan import EditPlan, EditPlanStatus +from packages.domain.edit_plan import EditPlanStatus from packages.domain.edit_plan_clip import EditPlanClip, EditPlanClipStatus logger = logging.getLogger(__name__) diff --git a/apps/worker/worker_app/tasks/asset_analyzer.py b/apps/worker/worker_app/tasks/asset_analyzer.py index 218990289..c38a26304 100755 --- a/apps/worker/worker_app/tasks/asset_analyzer.py +++ b/apps/worker/worker_app/tasks/asset_analyzer.py @@ -10,12 +10,10 @@ from __future__ import annotations import json import logging -import math import os import subprocess import tempfile from dataclasses import dataclass, field -from typing import Any import numpy as np from PIL import Image diff --git a/apps/worker/worker_app/tasks/classification.py b/apps/worker/worker_app/tasks/classification.py index 65ffff9ee..cb0fd5a1e 100755 --- a/apps/worker/worker_app/tasks/classification.py +++ b/apps/worker/worker_app/tasks/classification.py @@ -1,4 +1,3 @@ -from celery import Task from celery.utils.log import get_task_logger from worker_app.celery_app import celery_app @@ -9,7 +8,6 @@ from packages.adapters.sqlalchemy_impl.classification_job_repository import ( SQLAlchemyClassificationJobRepository, ) from packages.domain import ( - ClassificationJob, ClassificationJobStatus, ClassificationStatus, ) diff --git a/apps/worker/worker_app/tasks/compose_video.py b/apps/worker/worker_app/tasks/compose_video.py index b02d4f686..4150e0fc2 100755 --- a/apps/worker/worker_app/tasks/compose_video.py +++ b/apps/worker/worker_app/tasks/compose_video.py @@ -5,12 +5,9 @@ from __future__ import annotations -import logging import os -import shutil import subprocess import tempfile -from datetime import datetime, timezone from pathlib import Path from celery.utils.log import get_task_logger diff --git a/apps/worker/worker_app/tasks/edit_plan_generation.py b/apps/worker/worker_app/tasks/edit_plan_generation.py index a0011daf2..5c789d99b 100755 --- a/apps/worker/worker_app/tasks/edit_plan_generation.py +++ b/apps/worker/worker_app/tasks/edit_plan_generation.py @@ -21,7 +21,6 @@ import logging import tempfile from datetime import datetime, timezone from pathlib import Path -from typing import Optional from worker_app.celery_app import celery_app from worker_app.db import SessionLocal diff --git a/apps/worker/worker_app/tasks/generation.py b/apps/worker/worker_app/tasks/generation.py index edb22eb8d..2e145eeed 100644 --- a/apps/worker/worker_app/tasks/generation.py +++ b/apps/worker/worker_app/tasks/generation.py @@ -13,15 +13,13 @@ from __future__ import annotations -import json import logging import os import tempfile import time from dataclasses import dataclass, field -from datetime import datetime, timezone from pathlib import Path -from typing import Any, Optional +from typing import Any from worker_app.celery_app import celery_app from worker_app.db import SessionLocal @@ -174,7 +172,6 @@ def _build_plan_and_clips_from_task( path_duration[p] = probe_duration(p) clips: list[_VirtualClip] = [] - n = len(downloaded_paths) if mode == "pip": # 1 main + N-1 overlay @@ -912,7 +909,6 @@ def generate_video(self, task_id: str) -> dict: render_result = render_service.render() render_output_path = render_result.output_path render_duration = render_result.duration - render_file_size = render_result.file_size render_elapsed = time.monotonic() - render_start logger.info( "[task_id=%s] [渲染] unified 引擎完成: 耗时=%.1fs", diff --git a/apps/worker/worker_app/tasks/ingest.py b/apps/worker/worker_app/tasks/ingest.py index e131cec5c..0261c6e8c 100755 --- a/apps/worker/worker_app/tasks/ingest.py +++ b/apps/worker/worker_app/tasks/ingest.py @@ -1,9 +1,6 @@ import subprocess from datetime import datetime, timezone -from typing import Optional -from celery import Celery -from celery.app.task import Task from celery.utils.log import get_task_logger from worker_app.celery_app import celery_app from worker_app.core.asset_types import infer_mime_type_from_storage_key diff --git a/apps/worker/worker_app/tasks/voice_extraction.py b/apps/worker/worker_app/tasks/voice_extraction.py index 994954596..b08335809 100644 --- a/apps/worker/worker_app/tasks/voice_extraction.py +++ b/apps/worker/worker_app/tasks/voice_extraction.py @@ -1,14 +1,11 @@ """Voice extraction tasks - extract voice tracks and background music from videos.""" -import json import logging import os import subprocess import tempfile -from typing import Optional from celery import Task -from sqlalchemy.orm import Session from worker_app.celery_app import celery_app from worker_app.db import SessionLocal diff --git a/requirements-base.txt b/requirements-base.txt index e1b3606da..f01d9a400 100755 --- a/requirements-base.txt +++ b/requirements-base.txt @@ -24,9 +24,6 @@ celery==5.4.0 # 对象存储 oss2==2.18.4 -cryptography==46.0.5 -# 覆盖系统预装的旧版pyOpenSSL,与cryptography 46.0.5兼容 -pyOpenSSL==26.2.0 # HTTP 客户端 httpx==0.27.2 diff --git a/tests/conftest.py b/tests/conftest.py index 22b851da5..e2155d134 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,18 +17,13 @@ os.environ.setdefault("USE_IN_MEMORY_DB", "True") # CI 环境没有 Redis,所有 Celery 异步任务都 mock 掉,避免连接超时报错 # 集成测试只测 API 层逻辑(参数校验、权限、DB 操作),异步任务由 worker 单测覆盖 -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock def _mock_celery_task(): """全局 mock Celery 任务的 delay/apply_async/send_task 方法。""" from celery import Celery, Task - # 保存原始方法 - _orig_delay = Task.delay - _orig_apply_async = Task.apply_async - _orig_send_task = Celery.send_task - def _mock_delay(self, *args, **kwargs): mock_result = MagicMock() mock_result.id = "mock-task-id" diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index b6aa3765e..7a9247719 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -113,7 +113,6 @@ class PerfAssert: raise ValueError(f"未知的阈值级别: {threshold_level},可选: {list(PERF_THRESHOLDS.keys())}") threshold_ms = PERF_THRESHOLDS[threshold_level] - num_samples = samples or self.sample_count result = PerfResult(name=name or threshold_level, threshold_ms=threshold_ms) # 预热(第一次请求可能有冷启动开销) diff --git a/tests/integration/fixtures/duplication_routes_fixed.py b/tests/integration/fixtures/duplication_routes_fixed.py deleted file mode 100755 index 39c94649f..000000000 --- a/tests/integration/fixtures/duplication_routes_fixed.py +++ /dev/null @@ -1,282 +0,0 @@ -"""查重 API 路由。""" - -from __future__ import annotations - -import logging -from typing import Any -from uuid import uuid4 - -from app.auth import AuthenticatedUser, get_current_user -from app.core.storage import OSSStorageService, get_storage_service -from app.dependencies import get_duplication_repository -from app.schemas.duplication import ( - DuplicateSegmentResponse, - DuplicationDetailResponse, - DuplicationRecordResponse, - DuplicationUploadResponse, -) -from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFile, status - -from packages.application import ( - DeleteDuplicationRecordUseCase, - GetDuplicationDetailUseCase, - ListDuplicationRecordsUseCase, - RetryDuplicationUseCase, - UploadForDuplicationCommand, - UploadForDuplicationUseCase, -) -from packages.domain.duplication import DuplicationRecord - -logger = logging.getLogger(__name__) - -router = APIRouter() - -# 查重功能只接受视频文件 -ALLOWED_VIDEO_MIME_TYPES = frozenset( - { - "video/mp4", - "video/mpeg", - "video/quicktime", - "video/x-msvideo", - "video/webm", - "video/x-matroska", - "video/3gpp", - } -) - - -def _validate_video_mime_type(content_type: str | None) -> str: - """验证视频文件的 MIME 类型,如果无效则抛出异常。""" - if not content_type: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Content-Type header is required", - ) - - # 处理带参数的类型,如 "video/mp4; charset=utf-8" - base_type = content_type.split(";")[0].strip().lower() - - if base_type not in ALLOWED_VIDEO_MIME_TYPES: - raise HTTPException( - status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE, - detail="只支持视频文件。支持的类型: mp4, mpeg, mov, avi, webm, mkv, 3gp", - ) - - return base_type - - -def _to_record_response(record: DuplicationRecord) -> DuplicationRecordResponse: - return DuplicationRecordResponse( - id=record.id, - filename=record.filename, - file_size=record.file_size, - duration_seconds=record.duration_seconds, - status=record.status, - duplicate_rate=record.duplicate_rate, - duplicate_count=record.duplicate_count, - created_at=record.created_at.isoformat(), - updated_at=record.updated_at.isoformat(), - ) - - -def _to_detail_response(record: DuplicationRecord) -> DuplicationDetailResponse: - return DuplicationDetailResponse( - id=record.id, - filename=record.filename, - file_size=record.file_size, - duration_seconds=record.duration_seconds, - status=record.status, - duplicate_rate=record.duplicate_rate, - duplicate_count=record.duplicate_count, - created_at=record.created_at.isoformat(), - updated_at=record.updated_at.isoformat(), - segments=[ - DuplicateSegmentResponse( - id=seg.id, - source_start=seg.source_start, - source_end=seg.source_end, - matched_video_id=seg.matched_video_id, - matched_video_name=seg.matched_video_name, - matched_start=seg.matched_start, - matched_end=seg.matched_end, - similarity=seg.similarity, - ) - for seg in record.segments - ], - ) - - -@router.post("/upload", response_model=DuplicationUploadResponse) -async def upload_for_duplication( - file: UploadFile = File(..., description="要查重的视频文件"), - authenticated_user: AuthenticatedUser = Depends(get_current_user), - duplication_repository: Any = Depends(get_duplication_repository), - storage_service: OSSStorageService = Depends(get_storage_service), -) -> DuplicationUploadResponse: - """上传视频进行查重。""" - if file.filename is None: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="文件名不能为空", - ) - - # P0-1: 验证 MIME 类型(只接受视频文件) - validated_content_type = _validate_video_mime_type(file.content_type) - - # P0-2: 验证文件大小(参考 OSS_DIRECT_UPLOAD_MAX_MB) - from app.config import get_settings - - settings = get_settings() - max_size_bytes = settings.OSS_DIRECT_UPLOAD_MAX_MB * 1024 * 1024 - - # 先检查 Content-Length header(如果可用) - if file.size is not None and file.size > max_size_bytes: - raise HTTPException( - status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, - detail=f"文件超过上传限制 ({settings.OSS_DIRECT_UPLOAD_MAX_MB}MB)", - ) - - # 读取文件内容并上传到 OSS - file_id = uuid4().hex[:8] - safe_filename = file.filename.replace("/", "_").replace("\\", "_") - storage_key = f"duplication/{file_id}/{safe_filename}" - - try: - content = await file.read() - file_size = len(content) - - # 再次检查实际文件大小 - if file_size > max_size_bytes: - raise HTTPException( - status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, - detail=f"文件超过上传限制 ({settings.OSS_DIRECT_UPLOAD_MAX_MB}MB)", - ) - except HTTPException: - raise - except Exception as exc: - logger.error("读取查重文件失败: %s", exc, exc_info=True) - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="文件读取失败,请稍后重试", - ) from exc - - try: - storage_service.upload_file( - content, - storage_key, - content_type=validated_content_type, - ) - except Exception as exc: - logger.error("查重文件上传 OSS 失败: %s", exc, exc_info=True) - raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - detail="文件上传失败,请稍后重试", - ) from exc - - use_case = UploadForDuplicationUseCase(duplication_repository) - record = use_case.execute( - UploadForDuplicationCommand( - user_id=authenticated_user.user.id, - filename=file.filename, - file_size=file_size, - storage_key=storage_key, - ) - ) - - logger.info( - "Duplication upload: record=%s file=%s user=%s", - record.id, - file.filename, - authenticated_user.user.id, - ) - - return DuplicationUploadResponse( - id=record.id, - status=record.status, - message=f'文件 "{file.filename}" 已上传,正在查重中...', - ) - - -@router.get("/records", response_model=list[DuplicationRecordResponse]) -def list_duplication_records( - authenticated_user: AuthenticatedUser = Depends(get_current_user), - duplication_repository: Any = Depends(get_duplication_repository), -) -> list[DuplicationRecordResponse]: - """获取当前用户的查重记录列表。""" - use_case = ListDuplicationRecordsUseCase(duplication_repository) - records = use_case.execute(authenticated_user.user.id) - return [_to_record_response(r) for r in records] - - -@router.get("/records/{record_id}", response_model=DuplicationDetailResponse) -def get_duplication_detail( - record_id: str, - authenticated_user: AuthenticatedUser = Depends(get_current_user), - duplication_repository: Any = Depends(get_duplication_repository), -) -> DuplicationDetailResponse: - """获取查重记录详情(含重复片段)。""" - use_case = GetDuplicationDetailUseCase(duplication_repository) - record = use_case.execute(record_id) - if record is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"查重记录 {record_id} 不存在", - ) - if record.user_id != authenticated_user.user.id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"查重记录 {record_id} 不存在", - ) - return _to_detail_response(record) - - -@router.delete("/records/{record_id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response) -def delete_duplication_record( - record_id: str, - authenticated_user: AuthenticatedUser = Depends(get_current_user), - duplication_repository: Any = Depends(get_duplication_repository), -) -> Response: - """删除查重记录。""" - # 检查记录是否存在且属于当前用户 - detail_uc = GetDuplicationDetailUseCase(duplication_repository) - record = detail_uc.execute(record_id) - if record is None or record.user_id != authenticated_user.user.id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"查重记录 {record_id} 不存在", - ) - - use_case = DeleteDuplicationRecordUseCase(duplication_repository) - use_case.execute(record_id) - return Response(status_code=204) - - -@router.post("/records/{record_id}/retry", response_model=DuplicationUploadResponse) -def retry_duplication( - record_id: str, - authenticated_user: AuthenticatedUser = Depends(get_current_user), - duplication_repository: Any = Depends(get_duplication_repository), -) -> DuplicationUploadResponse: - """重新提交查重。""" - # 检查记录存在且属于当前用户 - detail_uc = GetDuplicationDetailUseCase(duplication_repository) - record = detail_uc.execute(record_id) - if record is None or record.user_id != authenticated_user.user.id: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"查重记录 {record_id} 不存在", - ) - - use_case = RetryDuplicationUseCase(duplication_repository) - updated = use_case.execute(record_id) - if updated is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"查重记录 {record_id} 不存在", - ) - - return DuplicationUploadResponse( - id=updated.id, - status=updated.status, - message="已重新提交查重", - ) diff --git a/tests/integration/test_api_performance.py b/tests/integration/test_api_performance.py index d7535cf32..2b9b30435 100644 --- a/tests/integration/test_api_performance.py +++ b/tests/integration/test_api_performance.py @@ -18,7 +18,6 @@ from __future__ import annotations import os import uuid -from typing import Optional import pytest from fastapi.testclient import TestClient diff --git a/tests/integration/test_duplication_api.py b/tests/integration/test_duplication_api.py index 520eef14f..bdb57dd8d 100644 --- a/tests/integration/test_duplication_api.py +++ b/tests/integration/test_duplication_api.py @@ -14,7 +14,6 @@ from __future__ import annotations import os import sys -from dataclasses import replace from datetime import datetime, timezone from typing import Any from unittest.mock import MagicMock @@ -33,7 +32,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "apps", " from app.api.routes.duplication import router from app.auth import AuthenticatedUser, get_current_user from app.core.storage import get_storage_service -from app.dependencies import get_db_session, get_duplication_repository +from app.dependencies import get_duplication_repository from packages.domain.duplication import DuplicateSegment, DuplicationRecord diff --git a/tests/integration/test_duplication_upload_error_handling.py b/tests/integration/test_duplication_upload_error_handling.py index 358acbecd..9930e401d 100644 --- a/tests/integration/test_duplication_upload_error_handling.py +++ b/tests/integration/test_duplication_upload_error_handling.py @@ -32,11 +32,9 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "apps", " from app.api.routes.duplication import _validate_video_mime_type, router from app.auth import AuthenticatedUser, get_current_user -from app.core.storage import OSSStorageService, get_storage_service +from app.core.storage import get_storage_service from app.dependencies import get_duplication_repository -from packages.domain.duplication import DuplicationRecord - # ── 导入真实模块(不创建 fake module) ──────────────────────────────────────── from packages.domain.entities import User diff --git a/tests/integration/test_error_scenarios.py b/tests/integration/test_error_scenarios.py index 4f8bc06cf..f11e36504 100755 --- a/tests/integration/test_error_scenarios.py +++ b/tests/integration/test_error_scenarios.py @@ -14,7 +14,6 @@ from __future__ import annotations -import json import os import sys import uuid diff --git a/tests/integration/test_full_pipeline.py b/tests/integration/test_full_pipeline.py index 36059c7e9..92224ef53 100644 --- a/tests/integration/test_full_pipeline.py +++ b/tests/integration/test_full_pipeline.py @@ -9,16 +9,12 @@ import shutil import subprocess import tempfile from pathlib import Path -from unittest.mock import MagicMock, patch import pytest from video_processing.unified_render_service import ( - RenderResult, UnifiedRenderService, ) from worker_app.tasks.generation import ( - OUTPUT_HEIGHT, - OUTPUT_WIDTH, _build_plan_and_clips_from_task, _create_fallback_clip, _mux_audio_track, diff --git a/tests/integration/test_generation_api.py b/tests/integration/test_generation_api.py index 5a609d7af..17256c7ef 100755 --- a/tests/integration/test_generation_api.py +++ b/tests/integration/test_generation_api.py @@ -515,7 +515,6 @@ class TestRetryGenerationTask: task_id = resp.json()["items"][0]["id"] # 直接修改 repository 中的任务状态为 failed - from app.dependencies import get_generation_task_repository # 由于是 stub,我们需要通过另一种方式设置状态 # 让我们直接通过 retry 测试来验证 diff --git a/tests/integration/test_ingest_pipeline.py b/tests/integration/test_ingest_pipeline.py index c424975e5..7c630d330 100644 --- a/tests/integration/test_ingest_pipeline.py +++ b/tests/integration/test_ingest_pipeline.py @@ -3,7 +3,7 @@ from packages.adapters.in_memory import ( InMemoryIngestJobRepository, ) from packages.application import SubmitIngestJobCommand, SubmitIngestJobUseCase -from packages.domain import Asset, IngestJob, IngestJobStatus +from packages.domain import Asset, IngestJobStatus def simulate_ingest_asset( diff --git a/tests/integration/test_project_management.py b/tests/integration/test_project_management.py deleted file mode 100644 index c164101c1..000000000 --- a/tests/integration/test_project_management.py +++ /dev/null @@ -1,263 +0,0 @@ -"""项目管理功能集成测试""" - -import pytest - -# 项目管理功能尚未实现,相关模块不存在,跳过整个文件 -pytest.skip( - "项目管理功能尚未实现(project_management_repositories / " - "project_management_use_cases / TaskPriority / TaskStatus 均不存在)", - allow_module_level=True, -) - -from packages.adapters.in_memory.project_management_repositories import ( - InMemoryMilestoneRepository, - InMemoryTaskIssueRepository, - InMemoryTaskRepository, -) -from packages.application.project_management_use_cases import ( - CreateMilestoneUseCase, - CreateTaskIssueUseCase, - CreateTaskUseCase, - ListProjectTasksUseCase, - ListTaskIssuesUseCase, - ResolveTaskIssueUseCase, - UpdateTaskProgressUseCase, - UpdateTaskStatusUseCase, -) -from packages.domain import TaskPriority, TaskStatus - - -def test_create_task(): - """测试创建任务""" - repo = InMemoryTaskRepository() - use_case = CreateTaskUseCase(repo) - - task = use_case.execute( - project_id="proj_1", - name="开发登录功能", - description="实现用户登录功能", - priority=TaskPriority.HIGH, - ) - - assert task.id is not None - assert task.name == "开发登录功能" - assert task.status == TaskStatus.PENDING - assert task.priority == TaskPriority.HIGH - assert task.progress == 0.0 - - -def test_list_tasks(): - """测试获取任务列表""" - repo = InMemoryTaskRepository() - create_use_case = CreateTaskUseCase(repo) - - # 创建两个任务 - create_use_case.execute( - project_id="proj_1", - name="任务1", - ) - create_use_case.execute( - project_id="proj_1", - name="任务2", - ) - - # 查询任务列表 - list_use_case = ListProjectTasksUseCase(repo) - tasks = list_use_case.execute("proj_1") - - assert len(tasks) == 2 - assert tasks[0].name == "任务1" - assert tasks[1].name == "任务2" - - -def test_update_task_status(): - """测试更新任务状态""" - repo = InMemoryTaskRepository() - create_use_case = CreateTaskUseCase(repo) - update_use_case = UpdateTaskStatusUseCase(repo) - - # 创建任务 - task = create_use_case.execute( - project_id="proj_1", - name="测试任务", - ) - - # 更新状态为进行中 - updated_task = update_use_case.execute(task.id, TaskStatus.IN_PROGRESS) - - assert updated_task.status == TaskStatus.IN_PROGRESS - assert updated_task.actual_start_date is not None - - -def test_update_task_progress(): - """测试更新任务进度""" - repo = InMemoryTaskRepository() - create_use_case = CreateTaskUseCase(repo) - progress_use_case = UpdateTaskProgressUseCase(repo) - - # 创建任务 - task = create_use_case.execute( - project_id="proj_1", - name="测试任务", - ) - - # 更新进度到 50% - updated_task = progress_use_case.execute(task.id, 50.0) - - assert updated_task.progress == 50.0 - assert updated_task.status == TaskStatus.IN_PROGRESS - - # 更新进度到 100% - completed_task = progress_use_case.execute(task.id, 100.0) - - assert completed_task.progress == 100.0 - assert completed_task.status == TaskStatus.COMPLETED - assert completed_task.actual_end_date is not None - - -def test_create_milestone(): - """测试创建里程碑""" - repo = InMemoryMilestoneRepository() - use_case = CreateMilestoneUseCase(repo) - - milestone = use_case.execute( - project_id="proj_1", - name="V1.0 发布", - description="第一个正式版本", - ) - - assert milestone.id is not None - assert milestone.name == "V1.0 发布" - assert milestone.completed is False - - -def test_create_and_resolve_issue(): - """测试创建和解决任务问题""" - repo = InMemoryTaskIssueRepository() - create_use_case = CreateTaskIssueUseCase(repo) - resolve_use_case = ResolveTaskIssueUseCase(repo) - list_use_case = ListTaskIssuesUseCase(repo) - - # 创建问题 - issue = create_use_case.execute( - task_id="task_1", - project_id="proj_1", - title="接口报错", - description="调用登录接口返回 500", - ) - - assert issue.id is not None - assert issue.title == "接口报错" - assert issue.resolved is False - - # 解决问题 - resolved_issue = resolve_use_case.execute(issue.id) - - assert resolved_issue.resolved is True - assert resolved_issue.resolved_at is not None - - # 查询任务问题列表 - issues = list_use_case.execute("task_1") - assert len(issues) == 1 - assert issues[0].resolved is True - - -def test_task_hierarchy(): - """测试任务层级关系""" - repo = InMemoryTaskRepository() - create_use_case = CreateTaskUseCase(repo) - - # 创建父任务 - parent_task = create_use_case.execute( - project_id="proj_1", - name="开发用户模块", - ) - - # 创建子任务 - child_task_1 = create_use_case.execute( - project_id="proj_1", - name="登录功能", - parent_task_id=parent_task.id, - ) - - child_task_2 = create_use_case.execute( - project_id="proj_1", - name="注册功能", - parent_task_id=parent_task.id, - ) - - # 查询子任务 - children = repo.list_by_parent(parent_task.id) - - assert len(children) == 2 - assert children[0].parent_task_id == parent_task.id - assert children[1].parent_task_id == parent_task.id - - -def test_get_task_detail(): - """测试获取任务详情""" - from packages.application.get_task_detail_use_case import GetTaskDetailUseCase - - repo = InMemoryTaskRepository() - create_use_case = CreateTaskUseCase(repo) - get_use_case = GetTaskDetailUseCase(repo) - - # 创建任务 - task = create_use_case.execute( - project_id="proj_1", - name="测试任务", - description="这是一个测试任务", - ) - - # 获取详情 - retrieved_task = get_use_case.execute(task.id) - - assert retrieved_task.id == task.id - assert retrieved_task.name == "测试任务" - assert retrieved_task.description == "这是一个测试任务" - - # 测试不存在的任务 - try: - get_use_case.execute("nonexistent_id") - assert False, "应该抛出异常" - except ValueError as e: - assert "not found" in str(e) - - -def test_update_task(): - """测试任务基本信息更新""" - from packages.application.update_task_use_case import UpdateTaskUseCase - - repo = InMemoryTaskRepository() - create_use_case = CreateTaskUseCase(repo) - update_use_case = UpdateTaskUseCase(repo) - - # 创建任务 - task = create_use_case.execute( - project_id="proj_1", - name="原始任务", - description="原始描述", - priority="low", - ) - - # 更新任务 - updated_task = update_use_case.execute( - task_id=task.id, - name="更新后的任务", - description="更新后的描述", - priority="high", - ) - - assert updated_task.name == "更新后的任务" - assert updated_task.description == "更新后的描述" - assert updated_task.priority == "high" - - # 部分更新 - partial_updated = update_use_case.execute( - task_id=task.id, - name="又更新了", - ) - - assert partial_updated.name == "又更新了" - assert partial_updated.description == "更新后的描述" # 保持不变 - assert partial_updated.priority == "high" # 保持不变 diff --git a/tests/integration/test_subscription_api.py b/tests/integration/test_subscription_api.py index 1beb29f7e..d462d5c59 100644 --- a/tests/integration/test_subscription_api.py +++ b/tests/integration/test_subscription_api.py @@ -16,7 +16,6 @@ from __future__ import annotations import importlib.util import os import sys -from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Optional @@ -30,12 +29,10 @@ from fastapi.testclient import TestClient sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "apps", "api")) -from app.auth import AuthenticatedUser, get_current_user -from app.dependencies import get_user_repository +from app.auth import AuthenticatedUser # ── 导入真实模块(不创建 fake module) ──────────────────────────────────────── from packages.domain.entities import User -from packages.ports.user_repository import UserRepository # ── 导入被测路由模块(从 fixtures 加载简化版路由) ───────────────────────────── _fixture_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "subscription_routes.py") diff --git a/tests/integration/test_task_center_api.py b/tests/integration/test_task_center_api.py index bec726186..a55f5b2ee 100755 --- a/tests/integration/test_task_center_api.py +++ b/tests/integration/test_task_center_api.py @@ -629,7 +629,6 @@ class TestTaskCenterCrossEndpoint: # 2. 重试失败任务 retry_resp = tc.post("/tasks/gen-fail-cross/retry") assert retry_resp.status_code == 200 - new_task_id = retry_resp.json()["source_id"] # 3. 再次列出,应有2个任务(旧的failed + 新的pending) list_resp2 = tc.get("/tasks") diff --git a/tests/integration/test_tts_api.py b/tests/integration/test_tts_api.py index 69d6e5587..63e065b0c 100644 --- a/tests/integration/test_tts_api.py +++ b/tests/integration/test_tts_api.py @@ -18,7 +18,6 @@ from __future__ import annotations import os import sys from datetime import datetime, timezone -from unittest.mock import MagicMock # ── 环境变量 & sys.path(必须在导入 app.* 之前设置) ────────────────────────── os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") diff --git a/tests/integration/test_voice_clone_api.py b/tests/integration/test_voice_clone_api.py index 86f9a219a..a63d402c1 100755 --- a/tests/integration/test_voice_clone_api.py +++ b/tests/integration/test_voice_clone_api.py @@ -18,7 +18,6 @@ from __future__ import annotations import os import sys from datetime import datetime, timezone -from unittest.mock import MagicMock # ── 环境变量 & sys.path(必须在导入 app.* 之前设置) ────────────────────────── os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") diff --git a/tests/render_compare/audio_diff.py b/tests/render_compare/audio_diff.py index d9e5013da..e0d069255 100644 --- a/tests/render_compare/audio_diff.py +++ b/tests/render_compare/audio_diff.py @@ -181,7 +181,7 @@ def compute_audio_diff( timeout=120, ) stderr = result.stderr or "" - except subprocess.CalledProcessError as e: + except subprocess.CalledProcessError: # 如果音频格式不兼容,返回失败 return AudioDiffResult( audio_a=str(audio_a), diff --git a/tests/render_compare/runner.py b/tests/render_compare/runner.py index 393908382..433984ae7 100644 --- a/tests/render_compare/runner.py +++ b/tests/render_compare/runner.py @@ -30,7 +30,7 @@ import json import os import sys import time -from dataclasses import dataclass, field +from dataclasses import dataclass from datetime import datetime from pathlib import Path from typing import Any @@ -403,9 +403,6 @@ def generate_html_report(summary: dict[str, Any], output_path: Path): scenarios = summary["scenarios"] # 按通过/失败分组 - passed_list = [s for s in scenarios if s["passed"]] - failed_list = [s for s in scenarios if not s["passed"]] - # 构建场景卡片 scenario_cards = "" for s in scenarios: diff --git a/tests/unit/test_asset_diagnosis.py b/tests/unit/test_asset_diagnosis.py index 270e84be8..3e0085742 100644 --- a/tests/unit/test_asset_diagnosis.py +++ b/tests/unit/test_asset_diagnosis.py @@ -1,7 +1,6 @@ import os import sys from pathlib import Path -from uuid import uuid4 # 设置必要环境变量(必须在导入 app 模块之前) os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") @@ -9,7 +8,6 @@ os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) -import pytest from app.api.routes.asset_diagnosis import _build_diagnosis from fastapi import FastAPI from fastapi.testclient import TestClient diff --git a/tests/unit/test_asset_library_delete.py b/tests/unit/test_asset_library_delete.py index 648dcafa4..9c4178be6 100644 --- a/tests/unit/test_asset_library_delete.py +++ b/tests/unit/test_asset_library_delete.py @@ -13,7 +13,6 @@ from __future__ import annotations import os import sys from pathlib import Path -from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") diff --git a/tests/unit/test_asset_tagging.py b/tests/unit/test_asset_tagging.py index 2b36b40a4..436c1df98 100644 --- a/tests/unit/test_asset_tagging.py +++ b/tests/unit/test_asset_tagging.py @@ -86,7 +86,7 @@ def test_find_by_tag_ids(asset_repo, tag_repo): a2.add_tag(tag1.id) asset_repo.update(a2) - a3 = _create_asset(asset_repo, name="c.mp4") + _create_asset(asset_repo, name="c.mp4") # 无标签 # 按 tag1 筛选 → a1, a2 diff --git a/tests/unit/test_audio_url_presign.py b/tests/unit/test_audio_url_presign.py index df5035876..75f8f08f2 100755 --- a/tests/unit/test_audio_url_presign.py +++ b/tests/unit/test_audio_url_presign.py @@ -8,8 +8,6 @@ from __future__ import annotations from unittest.mock import MagicMock, patch -import pytest - class TestAudioUrlSigner: """测试音频URL签名函数的行为。""" diff --git a/tests/unit/test_auto_clip_service.py b/tests/unit/test_auto_clip_service.py index 25b8b1ebc..2d39ca880 100644 --- a/tests/unit/test_auto_clip_service.py +++ b/tests/unit/test_auto_clip_service.py @@ -10,10 +10,9 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) from dataclasses import dataclass, field from enum import Enum from typing import Any -from unittest.mock import MagicMock import pytest -from app.services.auto_clip_service import AutoClipService, ClipAssignDetail +from app.services.auto_clip_service import AutoClipService # ── Stub 实体 ───────────────────────────────────────────────────────────────── diff --git a/tests/unit/test_chunked_upload_routes.py b/tests/unit/test_chunked_upload_routes.py index 4c48bcd25..438168db6 100644 --- a/tests/unit/test_chunked_upload_routes.py +++ b/tests/unit/test_chunked_upload_routes.py @@ -20,7 +20,6 @@ from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") -import pytest # 确保 app 模块可导入 sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) diff --git a/tests/unit/test_config_oss.py b/tests/unit/test_config_oss.py index c1679d6e9..0ddcad23e 100644 --- a/tests/unit/test_config_oss.py +++ b/tests/unit/test_config_oss.py @@ -12,12 +12,9 @@ from __future__ import annotations import importlib.util import os -import sys from pathlib import Path from unittest.mock import patch -import pytest - def _load_settings_class(): """ diff --git a/tests/unit/test_config_schemas_and_ai_endpoints.py b/tests/unit/test_config_schemas_and_ai_endpoints.py index bbb5825ad..b2f2ee2dd 100644 --- a/tests/unit/test_config_schemas_and_ai_endpoints.py +++ b/tests/unit/test_config_schemas_and_ai_endpoints.py @@ -15,9 +15,8 @@ from __future__ import annotations import os import sys -from datetime import datetime, timezone from pathlib import Path -from typing import Any, Optional +from typing import Any from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") @@ -41,7 +40,7 @@ class TestNormalizePlanConfig: assert result == DEFAULT_EDIT_PLAN_CONFIG.copy() def test_empty_dict_returns_full_defaults(self): - from packages.domain.config_schemas import DEFAULT_EDIT_PLAN_CONFIG, normalize_plan_config + from packages.domain.config_schemas import normalize_plan_config result = normalize_plan_config({}) assert result["cover"]["type"] == "ai_frame" @@ -363,7 +362,7 @@ def _create_ai_test_app(): from fastapi import FastAPI from fastapi.testclient import TestClient -from packages.domain.edit_plan import EditPlan, EditPlanStatus +from packages.domain.edit_plan import EditPlan @pytest.fixture diff --git a/tests/unit/test_cosyvoice_service.py b/tests/unit/test_cosyvoice_service.py index 764033338..9cc9a43fb 100755 --- a/tests/unit/test_cosyvoice_service.py +++ b/tests/unit/test_cosyvoice_service.py @@ -3,7 +3,7 @@ from __future__ import annotations import json -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock import httpx import pytest diff --git a/tests/unit/test_dedup_engine.py b/tests/unit/test_dedup_engine.py index 5b7e17633..2b8b2a656 100644 --- a/tests/unit/test_dedup_engine.py +++ b/tests/unit/test_dedup_engine.py @@ -37,13 +37,10 @@ if "worker_app.celery_app" in sys.modules and isinstance(sys.modules["worker_app if "worker_app.db" in sys.modules and isinstance(sys.modules["worker_app.db"], MagicMock): sys.modules["worker_app.db"].SessionLocal = MagicMock() -# Mock celery.Task base class — 仅在 celery 不可用时注入 mock,避免污染真实包 -try: - import celery as _real_celery # noqa: F401 -except ImportError: - _mock_if_absent("celery", MagicMock()) - if "celery" in sys.modules and isinstance(sys.modules["celery"], MagicMock): - sys.modules["celery"].Task = object +# Mock celery.Task base class +_mock_if_absent("celery", MagicMock()) +if "celery" in sys.modules and isinstance(sys.modules["celery"], MagicMock): + sys.modules["celery"].Task = object # Mock packages.shared.storage _mock_if_absent("packages.shared") diff --git a/tests/unit/test_duplication_use_cases.py b/tests/unit/test_duplication_use_cases.py index 2d474a32c..66393a4ec 100644 --- a/tests/unit/test_duplication_use_cases.py +++ b/tests/unit/test_duplication_use_cases.py @@ -22,7 +22,7 @@ from packages.application.duplication import ( UploadForDuplicationCommand, UploadForDuplicationUseCase, ) -from packages.domain.duplication import DuplicateSegment, DuplicationRecord +from packages.domain.duplication import DuplicationRecord def _make_record(status="pending", **kwargs): diff --git a/tests/unit/test_edit_plan_generation_api.py b/tests/unit/test_edit_plan_generation_api.py index b7ee3c545..8f05cd720 100755 --- a/tests/unit/test_edit_plan_generation_api.py +++ b/tests/unit/test_edit_plan_generation_api.py @@ -12,7 +12,6 @@ from __future__ import annotations import os import sys from dataclasses import dataclass, field -from datetime import datetime, timezone from typing import Any, Optional from unittest.mock import MagicMock, patch diff --git a/tests/unit/test_edit_plan_worker_failure.py b/tests/unit/test_edit_plan_worker_failure.py index aad74f916..00c33b163 100755 --- a/tests/unit/test_edit_plan_worker_failure.py +++ b/tests/unit/test_edit_plan_worker_failure.py @@ -13,7 +13,6 @@ from __future__ import annotations import os import sys from dataclasses import dataclass, field -from datetime import datetime, timezone from types import ModuleType from typing import Any, Optional from unittest.mock import MagicMock, patch @@ -176,7 +175,6 @@ class TestRenderEditPlanFailureUpdatesGenTask: gen_task = StubGenerationTask(id="gen-task-001", status=_StubStatus("running")) plan_repo = StubPlanRepo(plan) - clip_repo = StubClipRepo([]) gen_task_repo = StubGenTaskRepo(gen_task) # 让 clip_repo 抛异常以触发 except 路径 diff --git a/tests/unit/test_edit_plans_api.py b/tests/unit/test_edit_plans_api.py index c8ba3a78b..997236010 100644 --- a/tests/unit/test_edit_plans_api.py +++ b/tests/unit/test_edit_plans_api.py @@ -13,7 +13,6 @@ from __future__ import annotations import os import sys -from datetime import datetime, timezone from pathlib import Path from typing import Optional from unittest.mock import MagicMock diff --git a/tests/unit/test_edit_template_service.py b/tests/unit/test_edit_template_service.py index f467ba5a7..b2cbbbd0c 100644 --- a/tests/unit/test_edit_template_service.py +++ b/tests/unit/test_edit_template_service.py @@ -16,8 +16,8 @@ import os import sys from datetime import datetime, timezone from pathlib import Path -from typing import Any, List, Optional -from unittest.mock import MagicMock, patch +from typing import List, Optional +from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") @@ -27,7 +27,7 @@ import pytest sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) from packages.domain.edit_template import EditTemplate, EditTemplateStatus -from packages.domain.template_clip_config import ClipType, TemplateClipConfig, TransitionEffect +from packages.domain.template_clip_config import ClipType, TemplateClipConfig # --------------------------------------------------------------------------- # Stub Repositories @@ -273,7 +273,7 @@ class TestEditTemplateServiceCRUD: def test_list_templates_active_only(self): svc = _make_service() - t1 = svc.create_template(name="活跃") + svc.create_template(name="活跃") t2 = svc.create_template(name="停用") svc.deactivate_template(t2.id) result = svc.list_templates(active_only=True) diff --git a/tests/unit/test_email_service.py b/tests/unit/test_email_service.py index 08b97b4f8..228d1c3ff 100644 --- a/tests/unit/test_email_service.py +++ b/tests/unit/test_email_service.py @@ -2,7 +2,7 @@ 邮件服务测试 """ -from unittest.mock import MagicMock, Mock, patch +from unittest.mock import MagicMock, patch import pytest diff --git a/tests/unit/test_feature_flag.py b/tests/unit/test_feature_flag.py index 81cddd67a..a349cc3b2 100755 --- a/tests/unit/test_feature_flag.py +++ b/tests/unit/test_feature_flag.py @@ -5,10 +5,8 @@ from __future__ import annotations -import time -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock -import pytest from packages.adapters.redis.feature_flag_store import ( FeatureFlagConfig, @@ -337,7 +335,6 @@ class TestRedisStoreDegradation: def test_get_returns_default_when_redis_unavailable(self): """Redis 连接失败时返回默认关闭配置,不抛异常。""" - import importlib from packages.adapters.redis import feature_flag_store as ff_module @@ -358,7 +355,6 @@ class TestRedisStoreDegradation: def test_list_all_returns_empty_on_redis_error(self): """Redis 错误时 list_all 返回空字典。""" - import importlib from packages.adapters.redis import feature_flag_store as ff_module diff --git a/tests/unit/test_form_upload_routes.py b/tests/unit/test_form_upload_routes.py index e3b8c6161..cc4c2239f 100644 --- a/tests/unit/test_form_upload_routes.py +++ b/tests/unit/test_form_upload_routes.py @@ -21,7 +21,6 @@ from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") -import pytest # 确保 app 模块可导入 sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) diff --git a/tests/unit/test_generation_feature_flag.py b/tests/unit/test_generation_feature_flag.py index e3f92c4fb..5c743a598 100644 --- a/tests/unit/test_generation_feature_flag.py +++ b/tests/unit/test_generation_feature_flag.py @@ -11,18 +11,14 @@ from __future__ import annotations import os import sys -from datetime import datetime, timezone from types import ModuleType -from typing import Any -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") from pathlib import Path -import pytest - # ── Mock worker 模块以避免数据库连接 ────────────────────────────────────────── sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "apps", "worker")) @@ -226,8 +222,6 @@ def test_legacy_engine_two_clips_concat_duration(): import subprocess import tempfile - from video_processing.ffmpeg_utils import probe_duration - from apps.worker.worker_app.tasks.generation import _render_with_legacy_engine with tempfile.TemporaryDirectory() as tmpdir: diff --git a/tests/unit/test_generation_logging.py b/tests/unit/test_generation_logging.py index 13bdd5497..7d4d1b373 100644 --- a/tests/unit/test_generation_logging.py +++ b/tests/unit/test_generation_logging.py @@ -17,7 +17,7 @@ from __future__ import annotations import json import sys from pathlib import Path -from unittest.mock import MagicMock, patch +from unittest.mock import patch sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "worker")) diff --git a/tests/unit/test_generation_presigned_url.py b/tests/unit/test_generation_presigned_url.py index 8a93983d9..d22088206 100755 --- a/tests/unit/test_generation_presigned_url.py +++ b/tests/unit/test_generation_presigned_url.py @@ -12,7 +12,7 @@ from __future__ import annotations import os import sys from pathlib import Path -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") diff --git a/tests/unit/test_job_service.py b/tests/unit/test_job_service.py index 74c51967b..ea2fa9b99 100755 --- a/tests/unit/test_job_service.py +++ b/tests/unit/test_job_service.py @@ -429,7 +429,7 @@ class TestListJobsUseCase: def test_list_by_project_with_status_filter(self, repo): create_uc = CreateJobUseCase(repo) - j1 = create_uc.execute(CreateJobCommand(project_id="proj-1", job_type=JobType.VIDEO_COMPOSE)) + create_uc.execute(CreateJobCommand(project_id="proj-1", job_type=JobType.VIDEO_COMPOSE)) j2 = create_uc.execute(CreateJobCommand(project_id="proj-1", job_type=JobType.VIDEO_COMPOSE)) j2.mark_running() repo.update(j2) @@ -460,7 +460,7 @@ class TestListJobsUseCase: class TestGetJobStatisticsUseCase: def test_statistics(self, repo): create_uc = CreateJobUseCase(repo) - j1 = create_uc.execute(CreateJobCommand(project_id="proj-1", job_type=JobType.VIDEO_COMPOSE)) + create_uc.execute(CreateJobCommand(project_id="proj-1", job_type=JobType.VIDEO_COMPOSE)) j2 = create_uc.execute(CreateJobCommand(project_id="proj-1", job_type=JobType.VIDEO_COMPOSE)) j2.mark_running() repo.update(j2) diff --git a/tests/unit/test_oss_upload_crash_fix.py b/tests/unit/test_oss_upload_crash_fix.py index cb47cc8cd..806e9b4cf 100755 --- a/tests/unit/test_oss_upload_crash_fix.py +++ b/tests/unit/test_oss_upload_crash_fix.py @@ -15,8 +15,6 @@ import time from pathlib import Path from unittest.mock import MagicMock, patch -import pytest - # ── oss_bucket connect_timeout 测试 ─────────────────────────────────────────── diff --git a/tests/unit/test_p02_p03_fixes.py b/tests/unit/test_p02_p03_fixes.py index dddb6b097..5f96080b8 100644 --- a/tests/unit/test_p02_p03_fixes.py +++ b/tests/unit/test_p02_p03_fixes.py @@ -9,7 +9,6 @@ from __future__ import annotations import logging from unittest.mock import MagicMock, patch -import pytest from video_processing.ffmpeg_utils import build_xfade_filter_chain # ── P0-3: build_xfade_filter_chain 安全钳制 ────────────────────────────────── diff --git a/tests/unit/test_p02_worker_oss_fix.py b/tests/unit/test_p02_worker_oss_fix.py index f76d3679c..58f7d37fd 100755 --- a/tests/unit/test_p02_worker_oss_fix.py +++ b/tests/unit/test_p02_worker_oss_fix.py @@ -11,8 +11,6 @@ from __future__ import annotations import os from unittest.mock import MagicMock, patch -import pytest - # ── oss_bucket endpoint scheme 修复 ────────────────────────────────────────── @@ -34,11 +32,10 @@ class TestOSSBucketEndpointScheme: "OSS_BUCKET_NAME": "test-bucket", }, ), - patch("video_processing.oss_helpers.oss2.Auth") as mock_auth, + patch("video_processing.oss_helpers.oss2.Auth"), patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket_instance) as mock_bucket_cls, ): # 清除缓存,确保重新创建 - import video_processing.oss_helpers as oss_mod bucket = oss_bucket() @@ -67,9 +64,8 @@ class TestOSSBucketEndpointScheme: patch("video_processing.oss_helpers.oss2.Auth"), patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket_instance) as mock_bucket_cls, ): - import video_processing.oss_helpers as oss_mod - bucket = oss_bucket() + oss_bucket() call_args = mock_bucket_cls.call_args endpoint_arg = call_args[0][1] @@ -95,9 +91,8 @@ class TestOSSBucketEndpointScheme: patch("video_processing.oss_helpers.oss2.Auth"), patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket_instance) as mock_bucket_cls, ): - import video_processing.oss_helpers as oss_mod - bucket = oss_bucket() + oss_bucket() call_args = mock_bucket_cls.call_args endpoint_arg = call_args[0][1] @@ -117,7 +112,6 @@ class TestOSSBucketEndpointScheme: }, clear=True, ): - import video_processing.oss_helpers as oss_mod bucket = oss_bucket() assert bucket is None @@ -177,7 +171,7 @@ class TestGetSignedDownloadUrl: patch("video_processing.oss_helpers.oss2.Auth"), patch("video_processing.oss_helpers.oss2.Bucket", return_value=mock_bucket), ): - result = get_signed_download_url("https://test-bucket.oss-cn-hangzhou.aliyuncs.com/generated/test.mp4") + get_signed_download_url("https://test-bucket.oss-cn-hangzhou.aliyuncs.com/generated/test.mp4") mock_bucket.sign_url.assert_called_once() # 验证传给 sign_url 的是纯 storage key,不是完整 URL diff --git a/tests/unit/test_phase8_clip_models.py b/tests/unit/test_phase8_clip_models.py index 236955e55..6ef807281 100644 --- a/tests/unit/test_phase8_clip_models.py +++ b/tests/unit/test_phase8_clip_models.py @@ -300,7 +300,7 @@ from sqlalchemy.orm import sessionmaker from packages.adapters.sqlalchemy_impl.edit_plan_clip_repository import ( SQLAlchemyEditPlanClipRepository, ) -from packages.adapters.sqlalchemy_impl.models import Base, EditPlanClipModel, TemplateClipConfigModel +from packages.adapters.sqlalchemy_impl.models import Base from packages.adapters.sqlalchemy_impl.template_clip_config_repository import ( SQLAlchemyTemplateClipConfigRepository, ) diff --git a/tests/unit/test_phase8_edit_models.py b/tests/unit/test_phase8_edit_models.py index 838170e7f..6e5b1e0e1 100644 --- a/tests/unit/test_phase8_edit_models.py +++ b/tests/unit/test_phase8_edit_models.py @@ -8,7 +8,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) import logging from sqlalchemy import create_engine -from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy.orm import sessionmaker from packages.adapters.sqlalchemy_impl.edit_plan_repository import ( SQLAlchemyEditPlanRepository, diff --git a/tests/unit/test_plan_generator.py b/tests/unit/test_plan_generator.py index d4838ba5f..6599df932 100644 --- a/tests/unit/test_plan_generator.py +++ b/tests/unit/test_plan_generator.py @@ -16,13 +16,12 @@ import os import sys from datetime import datetime, timezone from pathlib import Path -from typing import Any, List, Optional +from typing import List, Optional from unittest.mock import MagicMock, patch os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") -import pytest sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) diff --git a/tests/unit/test_render_adapter.py b/tests/unit/test_render_adapter.py index 6f1e05a8e..7d6ff9736 100755 --- a/tests/unit/test_render_adapter.py +++ b/tests/unit/test_render_adapter.py @@ -6,12 +6,10 @@ from __future__ import annotations from dataclasses import dataclass, field -from pathlib import Path from typing import Any from unittest.mock import MagicMock, patch -import pytest -from video_processing.render_adapter import RenderAdapter, RenderAdapterResult +from video_processing.render_adapter import RenderAdapter # ── Fixtures ────────────────────────────────────────────────────────────────── @@ -318,7 +316,7 @@ class TestRenderPlan: def progress_cb(progress: float, stage: str) -> None: progress_values.append((progress, stage)) - result = adapter.render_plan( + adapter.render_plan( "plan_001", work_dir=tmp_path / "work", progress_cb=progress_cb, diff --git a/tests/unit/test_session_store.py b/tests/unit/test_session_store.py index 44c8392a2..389aaedd2 100644 --- a/tests/unit/test_session_store.py +++ b/tests/unit/test_session_store.py @@ -3,8 +3,7 @@ Redis Session 存储测试 """ import json -from datetime import datetime -from unittest.mock import MagicMock, Mock +from unittest.mock import Mock import pytest diff --git a/tests/unit/test_strenum_deserialization.py b/tests/unit/test_strenum_deserialization.py index d02e35123..8f3275560 100644 --- a/tests/unit/test_strenum_deserialization.py +++ b/tests/unit/test_strenum_deserialization.py @@ -14,7 +14,6 @@ from unittest.mock import MagicMock import pytest -from packages.adapters.sqlalchemy_impl.generation_task_repository import SQLAlchemyGenerationTaskRepository from packages.adapters.sqlalchemy_impl.tts_job_repository import SQLAlchemyTTSJobRepository from packages.adapters.sqlalchemy_impl.voice_clone_profile_repository import SQLAlchemyVoiceCloneProfileRepository from packages.domain.generation_task import GenerationTaskStatus diff --git a/tests/unit/test_template_use_cases.py b/tests/unit/test_template_use_cases.py index aff04d284..df6cab792 100644 --- a/tests/unit/test_template_use_cases.py +++ b/tests/unit/test_template_use_cases.py @@ -2,7 +2,7 @@ Template Use Cases 单元测试 — 剪辑计划模板 CRUD + 业务规则校验 """ -from unittest.mock import MagicMock, Mock +from unittest.mock import Mock import pytest diff --git a/tests/unit/test_tts_oss_transfer.py b/tests/unit/test_tts_oss_transfer.py index 0fa0ed7ac..67271ad44 100644 --- a/tests/unit/test_tts_oss_transfer.py +++ b/tests/unit/test_tts_oss_transfer.py @@ -9,7 +9,6 @@ from __future__ import annotations from datetime import datetime, timezone from unittest.mock import MagicMock, patch -import pytest from packages.application.cosyvoice_service import CosyVoiceService from packages.application.tts_job.workflow import TTSWorkflowService diff --git a/tests/unit/test_tts_save_to_library.py b/tests/unit/test_tts_save_to_library.py index fc62b0e5c..b40711839 100644 --- a/tests/unit/test_tts_save_to_library.py +++ b/tests/unit/test_tts_save_to_library.py @@ -12,7 +12,7 @@ from __future__ import annotations from datetime import datetime, timezone -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock import pytest @@ -287,7 +287,6 @@ class TestSaveToLibraryUseCase: repo = MagicMock() repo.count_by_user.return_value = 0 # 配额未满 - expected_item = _make_voice_library_item() repo.create.side_effect = lambda item: item use_case = CreateVoiceLibraryUseCase(repo) diff --git a/tests/unit/test_tts_segment_synthesis.py b/tests/unit/test_tts_segment_synthesis.py index 3624f0ced..583b3a2ba 100644 --- a/tests/unit/test_tts_segment_synthesis.py +++ b/tests/unit/test_tts_segment_synthesis.py @@ -11,7 +11,7 @@ from __future__ import annotations import os import tempfile from datetime import datetime, timezone -from unittest.mock import MagicMock, call, patch +from unittest.mock import MagicMock, patch import pytest diff --git a/tests/unit/test_tts_streaming.py b/tests/unit/test_tts_streaming.py index 39a20ff36..8b2bd29ca 100644 --- a/tests/unit/test_tts_streaming.py +++ b/tests/unit/test_tts_streaming.py @@ -9,14 +9,12 @@ from __future__ import annotations -import asyncio from unittest.mock import AsyncMock, MagicMock, patch import pytest from packages.application.cosyvoice_service import CosyVoiceError, CosyVoiceService from packages.application.tts_job.streaming_service import ( - TTSStreamingError, TTSStreamingService, ) diff --git a/tests/unit/test_tts_synthesis_failure.py b/tests/unit/test_tts_synthesis_failure.py index a21153558..84094e6e7 100644 --- a/tests/unit/test_tts_synthesis_failure.py +++ b/tests/unit/test_tts_synthesis_failure.py @@ -6,9 +6,8 @@ from __future__ import annotations -import logging from datetime import datetime, timezone -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock import pytest diff --git a/tests/unit/test_unified_render_service.py b/tests/unit/test_unified_render_service.py index cef802d5d..b3223fa13 100755 --- a/tests/unit/test_unified_render_service.py +++ b/tests/unit/test_unified_render_service.py @@ -12,7 +12,6 @@ from unittest.mock import MagicMock, patch import pytest from video_processing.unified_render_service import ( - RenderLayer, RenderResult, ResolvedClip, UnifiedRenderService, @@ -683,7 +682,7 @@ class TestAssSubtitles: def test_generate_ass_with_subtitle_only(self, tmp_path): """只有字幕时生成 ASS 文件。""" ass_path = tmp_path / "test.ass" - result = generate_ass_subtitles( + generate_ass_subtitles( ass_path, video_width=1280, video_height=720, diff --git a/tests/unit/test_upload_routes.py b/tests/unit/test_upload_routes.py index b3afc7d4f..f4d35469b 100644 --- a/tests/unit/test_upload_routes.py +++ b/tests/unit/test_upload_routes.py @@ -20,7 +20,6 @@ from unittest.mock import MagicMock os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") -import pytest # 确保 app 模块可导入 sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) diff --git a/tests/unit/test_uuid_field_length.py b/tests/unit/test_uuid_field_length.py index 79e76d12b..2e8a1919d 100644 --- a/tests/unit/test_uuid_field_length.py +++ b/tests/unit/test_uuid_field_length.py @@ -13,7 +13,6 @@ import uuid import pytest from packages.adapters.sqlalchemy_impl.models import ( - Base, ClassificationJobModel, EditPlanClipModel, EditPlanModel, diff --git a/tests/unit/test_video_compose_service.py b/tests/unit/test_video_compose_service.py index 501ffa665..f7202ae6c 100644 --- a/tests/unit/test_video_compose_service.py +++ b/tests/unit/test_video_compose_service.py @@ -14,15 +14,14 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) from app.services.video_compose_service import ( ComposeCommand, - ComposeValidation, VideoComposeService, _build_concat_filter, _build_xfade_filter, _chain_filters, ) -from packages.domain.edit_plan import EditPlan, EditPlanStatus -from packages.domain.edit_plan_clip import EditPlanClip, EditPlanClipStatus +from packages.domain.edit_plan import EditPlanStatus +from packages.domain.edit_plan_clip import EditPlanClipStatus # ── Stub 实体 ───────────────────────────────────────────────────────────────── diff --git a/tests/unit/test_voice_clone_workflow.py b/tests/unit/test_voice_clone_workflow.py index a20d0cfbd..cd9b67833 100644 --- a/tests/unit/test_voice_clone_workflow.py +++ b/tests/unit/test_voice_clone_workflow.py @@ -2,7 +2,7 @@ from __future__ import annotations -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock import pytest diff --git a/tests/unit/test_voice_library_use_cases.py b/tests/unit/test_voice_library_use_cases.py index 3fa913525..87cf3a6a4 100644 --- a/tests/unit/test_voice_library_use_cases.py +++ b/tests/unit/test_voice_library_use_cases.py @@ -8,7 +8,7 @@ 4. 边界条件与异常场景 """ -from unittest.mock import MagicMock, Mock, call +from unittest.mock import Mock import pytest