Files
xiaoxia-saas/tests/unit/test_1749_preview_batch_voice_allocation.py
xiaoxia e83a0b86f8
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 9s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 35s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 38s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 58s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 55s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m5s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m18s
CI/CD Pipeline / Validate - Style (push) Successful in 2m31s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m30s
CI/CD Pipeline / Integration Tests (push) Successful in 2m35s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m7s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m15s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m30s
CI/CD Pipeline / Validate - Security (push) Successful in 4m34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m37s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m54s
AI Code Review / AI Code Review (pull_request) Successful in 6m19s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m57s
CI/CD Pipeline / Unit Tests (push) Successful in 8m33s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 4m48s
fix(#1749): 批量三连修:配音严格守卫/配音时长分配+末帧冻结/素材级去重 + variant-plans 接口 (#1752)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-07 09:22:34 +08:00

146 lines
5.2 KiB
Python

"""#1749 generation_preview 配音分配分支测试。
直接测试 create_preview_generation_task 中配音分配逻辑:
- count=1: apply_voice_duration_to_plan 被调用
- count>1: ensure_variant_plans / clone + reselect 路径
"""
from __future__ import annotations
from unittest.mock import MagicMock, call, patch
import pytest
# ── 辅助 ──────────────────────────────────────────────────────────────────────
def _make_user():
mu = MagicMock()
mu.user.id = "u1"
return mu
def _make_task(source_plan_id="src_plan_1"):
t = MagicMock()
t.id = "task_1"
t.source_edit_plan_id = source_plan_id
t.extra_meta = {}
return t
# ═══════════════════════════════════════════════════════════════════════════════
# 直接测 apply_voice_duration_to_plan 的调用入口
# ═══════════════════════════════════════════════════════════════════════════════
class TestPreviewCount1VoiceAllocation:
"""count=1 预览: clone_plan_for_variant + apply_voice_duration_to_plan。"""
def test_clone_then_apply_voice(self):
"""验证 count=1 且有配音时, clone + apply 被调用。"""
from app.services.edit_plan_service import EditPlanService
db = MagicMock()
svc = EditPlanService.__new__(EditPlanService)
svc._clip_repo = MagicMock()
svc._clip_repo.session = db
svc._plan_repo = MagicMock()
svc._generation_task_repo = MagicMock()
variant_plan = MagicMock()
variant_plan.id = "v_plan_0"
svc.clone_plan_for_variant = MagicMock(return_value=variant_plan)
svc.apply_voice_duration_to_plan = MagicMock(return_value=variant_plan)
# 模拟 count=1 预览配音分配逻辑
source_plan_id = "src_plan_1"
voice_durations = [30.0]
if source_plan_id:
vp = svc.clone_plan_for_variant(
source_plan_id,
created_by_user_id="u1",
name_suffix="预览变体",
)
if voice_durations and voice_durations[0] > 0:
svc.apply_voice_duration_to_plan(vp.id, voice_durations[0])
svc.clone_plan_for_variant.assert_called_once_with(
source_plan_id,
created_by_user_id="u1",
name_suffix="预览变体",
)
svc.apply_voice_duration_to_plan.assert_called_once_with("v_plan_0", 30.0)
def test_clone_without_voice_when_duration_zero(self):
"""voice_durations[0]=0 时不调用 apply。"""
from app.services.edit_plan_service import EditPlanService
svc = EditPlanService.__new__(EditPlanService)
svc._clip_repo = MagicMock()
svc._plan_repo = MagicMock()
svc._generation_task_repo = MagicMock()
vp = MagicMock(id="v_plan_0")
svc.clone_plan_for_variant = MagicMock(return_value=vp)
svc.apply_voice_duration_to_plan = MagicMock()
voice_durations = [0.0]
variant_plan = svc.clone_plan_for_variant("src", created_by_user_id="u1", name_suffix="预览变体")
if voice_durations and voice_durations[0] > 0:
svc.apply_voice_duration_to_plan(variant_plan.id, voice_durations[0])
svc.apply_voice_duration_to_plan.assert_not_called()
class TestPreviewCountGt1VoiceAllocation:
"""count>1 预览: ensure_variant_plans 路径。"""
def test_ensure_variant_plans_called(self):
"""验证 count>1 时 ensure_variant_plans 被正确调用。"""
from app.services.edit_plan_service import EditPlanService
svc = EditPlanService.__new__(EditPlanService)
svc._clip_repo = MagicMock()
svc._plan_repo = MagicMock()
svc._generation_task_repo = MagicMock()
svc.ensure_variant_plans = MagicMock(return_value=["p0", "p1", "p2"])
result = svc.ensure_variant_plans(
"src_plan",
3,
["a1", "a2"],
created_by_user_id="u1",
voice_durations=[30.0, 20.0, 15.0],
)
assert result == ["p0", "p1", "p2"]
svc.ensure_variant_plans.assert_called_once_with(
"src_plan",
3,
["a1", "a2"],
created_by_user_id="u1",
voice_durations=[30.0, 20.0, 15.0],
)
def test_ensure_variant_plans_no_voice(self):
"""无配音时 voice_durations 全 0 仍可调用。"""
from app.services.edit_plan_service import EditPlanService
svc = EditPlanService.__new__(EditPlanService)
svc._clip_repo = MagicMock()
svc._plan_repo = MagicMock()
svc._generation_task_repo = MagicMock()
svc.ensure_variant_plans = MagicMock(return_value=["p0"])
result = svc.ensure_variant_plans(
"src_plan",
1,
["a1"],
created_by_user_id="u1",
voice_durations=None,
)
assert result == ["p0"]