fix: 确认生成兜底增强 — project_id 为空时通过 user_id 查找素材 #1338
@@ -27,18 +27,14 @@ from packages.domain.edit_plan import EditPlanStatus
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _auto_fallback_draft_to_editing(
|
||||
svc: EditPlanService, plan_id: str, plan_check
|
||||
) -> None:
|
||||
def _auto_fallback_draft_to_editing(svc: EditPlanService, plan_id: str, plan_check) -> None:
|
||||
"""自动兜底 1: draft → editing"""
|
||||
if plan_check.status == EditPlanStatus.DRAFT:
|
||||
logger.info("模板编辑器自动兜底: plan=%s draft→editing", plan_id)
|
||||
svc.transition_status(plan_id, EditPlanStatus.EDITING)
|
||||
|
||||
|
||||
def _auto_fallback_copy_template_clips(
|
||||
svc: EditPlanService, plan_id: str, plan_check, db: Session
|
||||
) -> None:
|
||||
def _auto_fallback_copy_template_clips(svc: EditPlanService, plan_id: str, plan_check, db: Session) -> None:
|
||||
"""自动兜底 2: 无片段 + 有 template_id → 从模板复制片段配置"""
|
||||
existing_clips = svc.count_clips(plan_id)
|
||||
if existing_clips == 0 and plan_check.template_id:
|
||||
@@ -53,15 +49,15 @@ def _auto_fallback_copy_template_clips(
|
||||
for cfg in configs:
|
||||
svc.create_clip(
|
||||
plan_id=plan_id,
|
||||
clip_type=cfg.clip_type.value
|
||||
if hasattr(cfg.clip_type, "value")
|
||||
else cfg.clip_type,
|
||||
clip_type=cfg.clip_type.value if hasattr(cfg.clip_type, "value") else cfg.clip_type,
|
||||
order=cfg.order,
|
||||
template_clip_config_id=cfg.id,
|
||||
duration=cfg.default_duration,
|
||||
transition_effect=cfg.transition_effect.value
|
||||
if hasattr(cfg.transition_effect, "value")
|
||||
else cfg.transition_effect,
|
||||
transition_effect=(
|
||||
cfg.transition_effect.value
|
||||
if hasattr(cfg.transition_effect, "value")
|
||||
else cfg.transition_effect
|
||||
),
|
||||
)
|
||||
logger.info(
|
||||
"模板编辑器自动兜底: plan=%s 从 template_clip_configs 复制了 %d 个片段",
|
||||
@@ -90,17 +86,14 @@ def _auto_fallback_copy_template_clips(
|
||||
)
|
||||
|
||||
|
||||
def _auto_fallback_assign_assets(
|
||||
svc: EditPlanService, plan_id: str, plan_check
|
||||
) -> list:
|
||||
def _auto_fallback_assign_assets(svc: EditPlanService, plan_id: str, plan_check) -> list:
|
||||
"""自动兜底 3: 为没有素材的片段分配素材。返回剩余无素材片段列表。"""
|
||||
all_clips = svc.list_clips(plan_id)
|
||||
clips_without_asset = [c for c in all_clips if not c.asset_id]
|
||||
config_asset_ids = (plan_check.config or {}).get("asset_ids", [])
|
||||
|
||||
logger.info(
|
||||
"模板编辑器自动兜底3 诊断: plan=%s total_clips=%d "
|
||||
"clips_without_asset=%d config_asset_ids=%r",
|
||||
"模板编辑器自动兜底3 诊断: plan=%s total_clips=%d " "clips_without_asset=%d config_asset_ids=%r",
|
||||
plan_id,
|
||||
len(all_clips),
|
||||
len(clips_without_asset),
|
||||
@@ -161,43 +154,74 @@ def _auto_fallback_auto_material_mode(
|
||||
clips_without_asset: list,
|
||||
asset_library_repo: Any,
|
||||
asset_repo: Any,
|
||||
user_id: str = "",
|
||||
) -> None:
|
||||
"""自动兜底 4: 项目有视频素材库时自动选素材"""
|
||||
"""自动兜底 4: 自动选素材分配给无素材片段
|
||||
|
||||
查找策略(按优先级):
|
||||
1. plan 有 project_id → 从项目素材库查找
|
||||
2. plan 无 project_id 但有 user_id → 从用户上传的素材中查找
|
||||
"""
|
||||
if not clips_without_asset:
|
||||
return
|
||||
if not plan_check.project_id:
|
||||
|
||||
ready_videos: list = []
|
||||
source_desc = ""
|
||||
|
||||
# 策略 1: 通过 project_id 查找项目素材库
|
||||
if plan_check.project_id:
|
||||
libs = asset_library_repo.find_by_project(plan_check.project_id)
|
||||
video_lib = None
|
||||
for lib in libs:
|
||||
lib_kind = lib.kind.value if hasattr(lib.kind, "value") else lib.kind
|
||||
if lib_kind == "video":
|
||||
video_lib = lib
|
||||
break
|
||||
if video_lib:
|
||||
assets = asset_repo.find_by_library(video_lib.id)
|
||||
ready_videos = [
|
||||
a
|
||||
for a in assets
|
||||
if (a.status.value if hasattr(a.status, "value") else a.status) == "ready"
|
||||
and a.mime_type
|
||||
and a.mime_type.startswith("video")
|
||||
]
|
||||
source_desc = f"素材库 {video_lib.name}"
|
||||
|
||||
# 策略 2: 通过 user_id 查找用户上传的素材
|
||||
if not ready_videos and user_id and hasattr(asset_repo, "find_ready_videos_by_user"):
|
||||
logger.info(
|
||||
"模板编辑器自动兜底4: plan=%s project_id 为空,尝试通过 user_id=%s 查找素材",
|
||||
plan_id,
|
||||
user_id,
|
||||
)
|
||||
ready_videos = asset_repo.find_ready_videos_by_user(user_id)
|
||||
source_desc = f"用户上传 (user_id={user_id[:8]}...)"
|
||||
|
||||
if not ready_videos:
|
||||
logger.warning(
|
||||
"模板编辑器自动兜底4: plan=%s 未找到可用素材 (project_id=%s, user_id=%s)",
|
||||
plan_id,
|
||||
plan_check.project_id or "(empty)",
|
||||
user_id[:8] + "..." if user_id else "(empty)",
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(
|
||||
"模板编辑器自动兜底4: plan=%s 自动选素材分配给 %d 个无素材片段",
|
||||
"模板编辑器自动兜底4: plan=%s 自动选素材分配给 %d 个无素材片段 (来源: %s, 共 %d 个)",
|
||||
plan_id,
|
||||
len(clips_without_asset),
|
||||
source_desc,
|
||||
len(ready_videos),
|
||||
)
|
||||
random.shuffle(ready_videos)
|
||||
for i, clip in enumerate(clips_without_asset):
|
||||
asset = ready_videos[i % len(ready_videos)]
|
||||
svc.assign_asset(clip.id, asset.id)
|
||||
logger.info(
|
||||
"模板编辑器自动兜底4: plan=%s 从 %s 分配了 %d 个素材给 %d 个片段",
|
||||
plan_id,
|
||||
source_desc,
|
||||
len(ready_videos),
|
||||
len(clips_without_asset),
|
||||
)
|
||||
libs = asset_library_repo.find_by_project(plan_check.project_id)
|
||||
video_lib = None
|
||||
for lib in libs:
|
||||
lib_kind = lib.kind.value if hasattr(lib.kind, "value") else lib.kind
|
||||
if lib_kind == "video":
|
||||
video_lib = lib
|
||||
break
|
||||
|
||||
if video_lib:
|
||||
assets = asset_repo.find_by_library(video_lib.id)
|
||||
ready_videos = [
|
||||
a
|
||||
for a in assets
|
||||
if (a.status.value if hasattr(a.status, "value") else a.status) == "ready"
|
||||
and a.mime_type
|
||||
and a.mime_type.startswith("video")
|
||||
]
|
||||
if ready_videos:
|
||||
random.shuffle(ready_videos)
|
||||
for i, clip in enumerate(clips_without_asset):
|
||||
asset = ready_videos[i % len(ready_videos)]
|
||||
svc.assign_asset(clip.id, asset.id)
|
||||
logger.info(
|
||||
"模板编辑器自动兜底4: plan=%s 从素材库 %s 分配了 %d 个素材",
|
||||
plan_id,
|
||||
video_lib.name,
|
||||
len(ready_videos),
|
||||
)
|
||||
|
||||
@@ -71,20 +71,22 @@ def generate_editor_draft(
|
||||
_auto_fallback_copy_template_clips(plan_svc, plan_id, plan_check, db)
|
||||
clips_without_asset = _auto_fallback_assign_assets(plan_svc, plan_id, plan_check)
|
||||
_auto_fallback_auto_material_mode(
|
||||
plan_svc, plan_id, plan_check, clips_without_asset, asset_library_repo, asset_repo
|
||||
plan_svc,
|
||||
plan_id,
|
||||
plan_check,
|
||||
clips_without_asset,
|
||||
asset_library_repo,
|
||||
asset_repo,
|
||||
user_id=str(current_user.user.id),
|
||||
)
|
||||
|
||||
# 检查是否可生成(含最后防线自动修复 + 诊断日志)
|
||||
try:
|
||||
can_gen, reason = plan_svc.can_generate(plan_id)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
|
||||
) from exc
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
if not can_gen:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail=reason
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=reason)
|
||||
|
||||
try:
|
||||
clip_count = plan_svc.mark_clips_ready(plan_id)
|
||||
@@ -123,9 +125,7 @@ def generate_editor_draft(
|
||||
|
||||
return EditPlanGenerateResponse(
|
||||
plan_id=plan_id,
|
||||
plan_status=updated_plan.status.value
|
||||
if hasattr(updated_plan.status, "value")
|
||||
else updated_plan.status,
|
||||
plan_status=updated_plan.status.value if hasattr(updated_plan.status, "value") else updated_plan.status,
|
||||
generation_task_id=gen_task.id,
|
||||
clip_count=clip_count,
|
||||
)
|
||||
@@ -160,9 +160,7 @@ def get_editor_generation_status(
|
||||
try:
|
||||
gen_status = plan_svc.get_generation_status(plan_id)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
|
||||
) from exc
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
|
||||
plan = gen_status["plan"]
|
||||
clips = gen_status["clips"]
|
||||
@@ -184,21 +182,15 @@ def get_editor_generation_status(
|
||||
video_url = ""
|
||||
if raw_video_url:
|
||||
try:
|
||||
video_url = storage_service.get_download_url(
|
||||
raw_video_url, expires_seconds=86400
|
||||
)
|
||||
video_url = storage_service.get_download_url(raw_video_url, expires_seconds=86400)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"生成视频签名URL失败: template_id=%s error=%s", template_id, e
|
||||
)
|
||||
logger.warning("生成视频签名URL失败: template_id=%s error=%s", template_id, e)
|
||||
video_url = raw_video_url
|
||||
|
||||
progress = gen_status.get("progress", 0.0)
|
||||
error_message = gen_status.get("error_message", "")
|
||||
gen_task_status = gen_status.get("generation_task_status")
|
||||
plan_status_val = (
|
||||
plan.status.value if hasattr(plan.status, "value") else plan.status
|
||||
)
|
||||
plan_status_val = plan.status.value if hasattr(plan.status, "value") else plan.status
|
||||
if plan_status_val == "completed" and progress < 100:
|
||||
progress = 100.0
|
||||
|
||||
|
||||
@@ -284,6 +284,24 @@ class SQLAlchemyAssetRepository:
|
||||
)
|
||||
return int(result or 0)
|
||||
|
||||
def find_ready_videos_by_user(
|
||||
self,
|
||||
user_id: str,
|
||||
*,
|
||||
limit: int = 50,
|
||||
) -> list[Asset]:
|
||||
"""查找用户上传的所有就绪视频素材。"""
|
||||
query = self.session.query(AssetModel).filter(
|
||||
AssetModel.uploaded_by_user_id == user_id,
|
||||
AssetModel.status == "ready",
|
||||
AssetModel.file_type == "video",
|
||||
)
|
||||
query = query.order_by(AssetModel.created_at.desc())
|
||||
if limit > 0:
|
||||
query = query.limit(limit)
|
||||
models = query.all()
|
||||
return [self._to_domain(m) for m in models]
|
||||
|
||||
def search_candidates(
|
||||
self,
|
||||
project_id: str,
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
"""Unit tests for PR #1338: 确认生成兜底增强 — user_id 查找素材.
|
||||
|
||||
覆盖:
|
||||
- SQLAlchemyAssetRepository.find_ready_videos_by_user
|
||||
- _auto_fallback_auto_material_mode 策略2 (user_id 兜底)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "packages"))
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from packages.adapters.sqlalchemy_impl.asset_repository import SQLAlchemyAssetRepository
|
||||
from packages.adapters.sqlalchemy_impl.models import Base
|
||||
from packages.domain import Asset, AssetStatus
|
||||
|
||||
|
||||
def _make_repo():
|
||||
engine = create_engine("sqlite:///:memory:")
|
||||
Base.metadata.create_all(engine)
|
||||
session = sessionmaker(bind=engine)()
|
||||
return SQLAlchemyAssetRepository(session)
|
||||
|
||||
|
||||
class TestFindReadyVideosByUser:
|
||||
"""SQLAlchemyAssetRepository.find_ready_videos_by_user 测试."""
|
||||
|
||||
def test_returns_ready_videos_for_user(self):
|
||||
repo = _make_repo()
|
||||
user_id = "user-abc-123"
|
||||
v1 = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="video1.mp4",
|
||||
storage_key="v/v1.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
v2 = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="video2.mp4",
|
||||
storage_key="v/v2.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
repo.create(v1)
|
||||
repo.create(v2)
|
||||
result = repo.find_ready_videos_by_user(user_id)
|
||||
assert len(result) == 2
|
||||
assert {a.id for a in result} == {v1.id, v2.id}
|
||||
|
||||
def test_excludes_non_video_assets(self):
|
||||
repo = _make_repo()
|
||||
user_id = "user-abc-123"
|
||||
video = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="clip.mp4",
|
||||
storage_key="v/clip.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
image = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="photo.jpg",
|
||||
storage_key="v/photo.jpg",
|
||||
mime_type="image/jpeg",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
repo.create(video)
|
||||
repo.create(image)
|
||||
result = repo.find_ready_videos_by_user(user_id)
|
||||
assert len(result) == 1
|
||||
assert result[0].id == video.id
|
||||
|
||||
def test_excludes_non_ready_assets(self):
|
||||
repo = _make_repo()
|
||||
user_id = "user-abc-123"
|
||||
ready = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="ready.mp4",
|
||||
storage_key="v/ready.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
uploading = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="uploading.mp4",
|
||||
storage_key="v/uploading.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.UPLOADING,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
repo.create(ready)
|
||||
repo.create(uploading)
|
||||
result = repo.find_ready_videos_by_user(user_id)
|
||||
assert len(result) == 1
|
||||
assert result[0].id == ready.id
|
||||
|
||||
def test_excludes_other_users_assets(self):
|
||||
repo = _make_repo()
|
||||
my_video = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="mine.mp4",
|
||||
storage_key="v/mine.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id="user-A",
|
||||
)
|
||||
other_video = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name="other.mp4",
|
||||
storage_key="v/other.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id="user-B",
|
||||
)
|
||||
repo.create(my_video)
|
||||
repo.create(other_video)
|
||||
result = repo.find_ready_videos_by_user("user-A")
|
||||
assert len(result) == 1
|
||||
assert result[0].id == my_video.id
|
||||
|
||||
def test_empty_result_for_unknown_user(self):
|
||||
repo = _make_repo()
|
||||
result = repo.find_ready_videos_by_user("nonexistent-user")
|
||||
assert result == []
|
||||
|
||||
def test_respects_limit(self):
|
||||
repo = _make_repo()
|
||||
user_id = "user-abc-123"
|
||||
for i in range(10):
|
||||
asset = Asset.create(
|
||||
project_id="",
|
||||
library_id="lib-1",
|
||||
name=f"video_{i}.mp4",
|
||||
storage_key=f"v/v{i}.mp4",
|
||||
mime_type="video/mp4",
|
||||
status=AssetStatus.READY,
|
||||
uploaded_by_user_id=user_id,
|
||||
)
|
||||
repo.create(asset)
|
||||
result = repo.find_ready_videos_by_user(user_id, limit=3)
|
||||
assert len(result) == 3
|
||||
|
||||
|
||||
class TestAutoFallbackAutoMaterialModeUserId:
|
||||
"""_auto_fallback_auto_material_mode user_id 兜底策略测试."""
|
||||
|
||||
def _make_plan_check(self, project_id="", template_id="tmpl-1"):
|
||||
plan = MagicMock()
|
||||
plan.project_id = project_id
|
||||
plan.template_id = template_id
|
||||
plan.config = {}
|
||||
return plan
|
||||
|
||||
def _make_clip(self, clip_id="clip-1"):
|
||||
clip = MagicMock()
|
||||
clip.id = clip_id
|
||||
clip.asset_id = ""
|
||||
return clip
|
||||
|
||||
def test_skips_when_no_clips_without_asset(self):
|
||||
from app.api.routes.templates_editor._fallback import (
|
||||
_auto_fallback_auto_material_mode,
|
||||
)
|
||||
|
||||
svc = MagicMock()
|
||||
plan_check = self._make_plan_check()
|
||||
_auto_fallback_auto_material_mode(
|
||||
svc,
|
||||
"plan-1",
|
||||
plan_check,
|
||||
[],
|
||||
MagicMock(),
|
||||
MagicMock(),
|
||||
user_id="user-1",
|
||||
)
|
||||
svc.assign_asset.assert_not_called()
|
||||
|
||||
def test_strategy2_user_id_fallback(self):
|
||||
from app.api.routes.templates_editor._fallback import (
|
||||
_auto_fallback_auto_material_mode,
|
||||
)
|
||||
|
||||
svc = MagicMock()
|
||||
plan_check = self._make_plan_check(project_id="")
|
||||
clip = self._make_clip("clip-1")
|
||||
mock_asset = MagicMock()
|
||||
mock_asset.id = "asset-from-user"
|
||||
mock_asset.status = AssetStatus.READY
|
||||
mock_asset.mime_type = "video/mp4"
|
||||
asset_repo = MagicMock()
|
||||
asset_repo.find_ready_videos_by_user.return_value = [mock_asset]
|
||||
asset_library_repo = MagicMock()
|
||||
_auto_fallback_auto_material_mode(
|
||||
svc,
|
||||
"plan-1",
|
||||
plan_check,
|
||||
[clip],
|
||||
asset_library_repo,
|
||||
asset_repo,
|
||||
user_id="user-123",
|
||||
)
|
||||
asset_repo.find_ready_videos_by_user.assert_called_once_with("user-123")
|
||||
svc.assign_asset.assert_called_once_with("clip-1", "asset-from-user")
|
||||
|
||||
def test_strategy1_takes_priority_over_strategy2(self):
|
||||
from app.api.routes.templates_editor._fallback import (
|
||||
_auto_fallback_auto_material_mode,
|
||||
)
|
||||
|
||||
svc = MagicMock()
|
||||
plan_check = self._make_plan_check(project_id="proj-1")
|
||||
clip = self._make_clip("clip-1")
|
||||
mock_lib = MagicMock()
|
||||
mock_lib.id = "lib-video"
|
||||
mock_lib.kind = MagicMock()
|
||||
mock_lib.kind.value = "video"
|
||||
mock_asset = MagicMock()
|
||||
mock_asset.id = "asset-from-project"
|
||||
mock_asset.status = "ready"
|
||||
mock_asset.mime_type = "video/mp4"
|
||||
asset_library_repo = MagicMock()
|
||||
asset_library_repo.find_by_project.return_value = [mock_lib]
|
||||
asset_repo = MagicMock()
|
||||
asset_repo.find_by_library.return_value = [mock_asset]
|
||||
_auto_fallback_auto_material_mode(
|
||||
svc,
|
||||
"plan-1",
|
||||
plan_check,
|
||||
[clip],
|
||||
asset_library_repo,
|
||||
asset_repo,
|
||||
user_id="user-123",
|
||||
)
|
||||
asset_library_repo.find_by_project.assert_called_once_with("proj-1")
|
||||
asset_repo.find_ready_videos_by_user.assert_not_called()
|
||||
svc.assign_asset.assert_called_once_with("clip-1", "asset-from-project")
|
||||
|
||||
def test_falls_back_when_project_has_no_videos(self):
|
||||
from app.api.routes.templates_editor._fallback import (
|
||||
_auto_fallback_auto_material_mode,
|
||||
)
|
||||
|
||||
svc = MagicMock()
|
||||
plan_check = self._make_plan_check(project_id="proj-1")
|
||||
clip = self._make_clip("clip-1")
|
||||
asset_library_repo = MagicMock()
|
||||
asset_library_repo.find_by_project.return_value = []
|
||||
mock_asset = MagicMock()
|
||||
mock_asset.id = "asset-from-user"
|
||||
mock_asset.status = AssetStatus.READY
|
||||
mock_asset.mime_type = "video/mp4"
|
||||
asset_repo = MagicMock()
|
||||
asset_repo.find_ready_videos_by_user.return_value = [mock_asset]
|
||||
_auto_fallback_auto_material_mode(
|
||||
svc,
|
||||
"plan-1",
|
||||
plan_check,
|
||||
[clip],
|
||||
asset_library_repo,
|
||||
asset_repo,
|
||||
user_id="user-123",
|
||||
)
|
||||
asset_repo.find_ready_videos_by_user.assert_called_once_with("user-123")
|
||||
svc.assign_asset.assert_called_once_with("clip-1", "asset-from-user")
|
||||
|
||||
def test_no_assets_found_does_nothing(self):
|
||||
from app.api.routes.templates_editor._fallback import (
|
||||
_auto_fallback_auto_material_mode,
|
||||
)
|
||||
|
||||
svc = MagicMock()
|
||||
plan_check = self._make_plan_check(project_id="")
|
||||
clip = self._make_clip("clip-1")
|
||||
asset_repo = MagicMock()
|
||||
asset_repo.find_ready_videos_by_user.return_value = []
|
||||
asset_library_repo = MagicMock()
|
||||
_auto_fallback_auto_material_mode(
|
||||
svc,
|
||||
"plan-1",
|
||||
plan_check,
|
||||
[clip],
|
||||
asset_library_repo,
|
||||
asset_repo,
|
||||
user_id="user-123",
|
||||
)
|
||||
svc.assign_asset.assert_not_called()
|
||||
|
||||
def test_no_user_id_skips_strategy2(self):
|
||||
from app.api.routes.templates_editor._fallback import (
|
||||
_auto_fallback_auto_material_mode,
|
||||
)
|
||||
|
||||
svc = MagicMock()
|
||||
plan_check = self._make_plan_check(project_id="")
|
||||
clip = self._make_clip("clip-1")
|
||||
asset_repo = MagicMock()
|
||||
asset_library_repo = MagicMock()
|
||||
_auto_fallback_auto_material_mode(
|
||||
svc,
|
||||
"plan-1",
|
||||
plan_check,
|
||||
[clip],
|
||||
asset_library_repo,
|
||||
asset_repo,
|
||||
user_id="",
|
||||
)
|
||||
asset_repo.find_ready_videos_by_user.assert_not_called()
|
||||
svc.assign_asset.assert_not_called()
|
||||
Reference in New Issue
Block a user