Compare commits

..

6 Commits

Author SHA1 Message Date
CI Bot 1db2ee0808 fix: preserve insertion order in asset_id dedup to fix test ordering
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 38s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m37s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m38s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m47s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m25s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m41s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m28s
AI Code Review / AI Code Review (pull_request) Failing after 4m43s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m2s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m29s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m44s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m33s
CI/CD Pipeline / CI Gate (pull_request) Successful in 9s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 27s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 46s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 717h12m19s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 717h12m21s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 717h12m20s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 717h12m22s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 717h17m24s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 717h17m24s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 717h18m5s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 717h17m25s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 717h18m6s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 717h18m4s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 717h18m4s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 717h18m6s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 717h18m6s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 717h45m58s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 717h46m0s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 717h51m42s
Changed set-based dedup to order-preserving dedup in _build_asset_url_map.
Set dedup caused non-deterministic iteration order which broke
side_effect-based mock assertions in tests.
2026-08-17 18:08:26 +08:00
CI Bot ac416493e0 fix: update test mocks for batch query + add asset_id dedup
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m50s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m51s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m7s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m59s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m7s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m48s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m29s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m0s
AI Code Review / AI Code Review (pull_request) Successful in 7m1s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m34s
CI/CD Pipeline / CI Gate (pull_request) Failing after 8s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 718h42m49s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 718h42m52s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 718h42m52s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 718h48m15s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 718h48m17s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 718h42m50s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 718h48m49s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 718h48m49s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 718h48m53s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 718h48m50s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 718h48m52s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 718h48m53s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 719h16m28s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 719h16m29s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 719h21m54s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 719h22m30s
- Replace find_by_id mocks with find_by_ids (returns list) in all 28 tests
- Add .id attributes to mock assets for asset_map key matching
- Dedup asset_ids in _build_asset_url_map to avoid redundant signing
- Fixes AI Code Review blocking issues #1 and #3
2026-08-17 16:37:39 +08:00
CI Bot 9e97473eec fix: batch query assets to resolve N+1 in _build_asset_url_map
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 58s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 0s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 0s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m18s
AI Code Review / AI Code Review (pull_request) Failing after 2m19s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m26s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m52s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m45s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m50s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 6m39s
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 718h50m45s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 718h50m46s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 718h50m47s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 718h50m47s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 718h50m47s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 718h56m25s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 718h56m21s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 718h56m27s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 718h57m23s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 718h57m23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 718h57m25s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 718h57m27s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 718h57m27s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 719h24m23s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 719h31m0s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 719h31m4s
- Add find_by_ids() to SQLAlchemyAssetRepository (single SQL IN query)
- Replace per-id find_by_id loop with single batch call
- Fixes AI Code Review blocking performance issue in PR #1404
2026-08-17 16:28:49 +08:00
CI Bot 34bd9372ce style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 37s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m49s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m50s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m0s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m5s
AI Code Review / AI Code Review (pull_request) Failing after 2m36s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m26s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m24s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m59s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m18s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 7m34s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 5m23s
CI/CD Pipeline / CI Gate (pull_request) Successful in 7s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 719h38m19s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 719h38m23s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 719h38m21s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 719h38m23s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 719h38m23s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 719h45m19s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 719h45m55s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 719h45m20s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 719h45m57s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 719h45m20s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 719h45m59s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 719h45m54s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 719h45m59s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 720h11m58s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 720h19m32s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 720h19m35s
2026-08-17 07:40:34 +00:00
xiaoxia 2e53c59cd7 fix: remove unused typing.Any import + add clips API response structure tests
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 47s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m45s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m49s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m58s
AI Code Review / AI Code Review (pull_request) Successful in 3m36s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m37s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m7s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m15s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m41s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 719h49m52s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 719h49m53s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 719h50m37s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 719h50m39s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 719h50m39s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 719h50m41s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 719h50m43s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 720h23m30s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 720h24m14s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 720h24m18s
- Remove unused 'from typing import Any' (Ruff F401)
- Add 28 unit/integration tests covering:
  - _clip_to_response: enum conversion, datetime formatting, asset_url param
  - _build_asset_url_map: empty list, storage failure, asset not found,
    no storage_key, success path, exception handling, mixed scenarios
  - Route integration: list/detail/split/merge with asset URL resolution
- Incremental coverage: 100% (95/95 added lines covered)
2026-08-17 15:35:46 +08:00
CI Bot 9aa0c69b26 fix(clips): 全面修复 Clips API 响应结构
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 49s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 0s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m51s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m20s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m22s
AI Code Review / AI Code Review (pull_request) Failing after 2m26s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m56s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 3m36s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m51s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m53s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m33s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m35s
CI/CD Pipeline / CI Gate (pull_request) Failing after 30s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 720h38m50s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 720h38m52s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 720h38m51s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 720h38m52s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 720h38m51s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 720h42m26s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 720h42m24s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 720h43m12s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 720h43m14s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 720h43m12s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 720h43m16s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 720h43m16s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 720h43m16s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 721h12m29s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 721h16m3s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 721h16m49s
P0: EditorClipResponse 补全所有数据库字段
- 新增 start_time, transition_duration, asset_id, status,
  template_clip_config_id, created_at, updated_at
- 新增 asset_url (OSS签名URL, 1小时有效)

P1: split/merge 端点统一使用 EditorClipResponse
- split 返回 {left_clip, right_clip} 均为 EditorClipResponse
- merge 返回 {merged_clip, deleted_clip_ids}
- 移除 dict[str, Any] 响应类型

P1: asset_url 签名URL解析
- 批量查询 Asset 表获取 storage_key
- 使用 StorageService.get_download_url() 签名
- 列表/详情/split/merge 端点均支持

P2: ClipsFromAssetsResponse 补上 plan_id 字段
2026-08-17 14:42:58 +08:00
6 changed files with 148 additions and 110 deletions
@@ -207,10 +207,13 @@ const GeneratePage: React.FC = () => {
onRetry={handleRetryGenerate}
onDismissError={handleDismissError}
presetVoices={presetVoices}
videoRatio={videoRatio}
/* Step5 多预览 */
previewCount={previewCount}
onPreviewCountChange={setPreviewCount}
previewItems={step5Preview.items}
previewSelectedIndex={step5Preview.selectedIndex}
onSelectPreview={step5Preview.setSelectedIndex}
previewOverallStatus={step5Preview.previewStatus}
previewOverallError={step5Preview.previewError}
previewOverallProgress={step5Preview.progress}
@@ -63,10 +63,13 @@ export interface GenerateStepContentProps {
onDismissError: () => void
/* 其他 */
presetVoices: PresetVoiceItem[]
videoRatio: string
/* Step4 预览(多预览) */
previewCount: number
onPreviewCountChange: (count: number) => void
previewItems: PreviewItem[]
previewSelectedIndex: number
onSelectPreview: (index: number) => void
previewOverallStatus: PreviewStatus
previewOverallError: string
previewOverallProgress: number
@@ -107,9 +110,12 @@ export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) =
onRetry,
onDismissError,
presetVoices,
videoRatio,
previewCount,
onPreviewCountChange,
previewItems,
previewSelectedIndex,
onSelectPreview,
previewOverallStatus,
previewOverallError,
previewOverallProgress,
@@ -155,9 +161,12 @@ export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) =
case 5:
return (
<Step5GeneratePreview
videoRatio={videoRatio}
previewCount={previewCount}
onPreviewCountChange={onPreviewCountChange}
items={previewItems}
selectedIndex={previewSelectedIndex}
onSelectPreview={onSelectPreview}
overallStatus={previewOverallStatus}
overallError={previewOverallError}
overallProgress={previewOverallProgress}
@@ -1,11 +1,11 @@
/**
* Step 5 生成预览组件(支持多预览)
* 调用后端预览生成接口,展示预览生成状态
* 预览视频由右侧 PreviewVideoPanel 展示,本组件不重复显示预览网格
* 调用后端预览生成接口,展示多个真实视频预览(网格布局)
*/
import React from "react"
import {
CheckCircleFilled,
LoadingOutlined,
ReloadOutlined,
PlayCircleOutlined,
ExclamationCircleFilled,
@@ -15,9 +15,12 @@ import { InputNumber } from "antd"
import type { PreviewItem, PreviewStatus } from "../hooks/useStep5Preview"
interface Step5GeneratePreviewProps {
videoRatio: string
previewCount: number
onPreviewCountChange: (count: number) => void
items: PreviewItem[]
selectedIndex: number
onSelectPreview: (index: number) => void
overallStatus: PreviewStatus
overallError: string
overallProgress: number
@@ -34,9 +37,12 @@ const PREVIEW_COUNT_OPTIONS = [
]
const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
videoRatio,
previewCount,
onPreviewCountChange,
items,
selectedIndex,
onSelectPreview,
overallStatus,
overallError,
overallProgress,
@@ -44,6 +50,7 @@ const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
onGeneratePreview,
onRegeneratePreview,
}) => {
const aspectRatio = (videoRatio || "16:9").replace(":", "/") // "9:16" → "9/16", "16:9" → "16/9"
const isIdle = overallStatus === "idle"
const isError = overallStatus === "error" && !items.some((it) => it.status === "ready")
@@ -120,7 +127,109 @@ const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
</div>
)}
{/* 预览网格已移除 — 右侧 PreviewVideoPanel 已展示实际预览视频,左侧不再重复显示 */}
{/* 多预览网格(生成中/完成/部分完成) */}
{(anyGenerating || overallStatus === "ready") && items.length > 0 && (
<div
className="xx-preview-grid"
style={{
display: "grid",
gridTemplateColumns: `repeat(${Math.min(items.length, 3)}, 1fr)`,
gap: 12,
maxWidth: `${Math.min(items.length, 3) * 280 + (Math.min(items.length, 3) - 1) * 12}px`,
margin: "0 auto 16px",
}}
>
{items.map((item) => {
const isSelected = item.index === selectedIndex
return (
<div
key={item.index}
onClick={() => {
if (item.status === "ready") onSelectPreview(item.index)
}}
style={{
borderRadius: 8,
border: isSelected ? "2px solid #1677ff" : "1px solid #e8e8e8",
overflow: "hidden",
cursor: item.status === "ready" ? "pointer" : "default",
opacity: item.status === "error" ? 0.6 : 1,
transition: "all 0.2s",
}}
>
{/* 轻量卡片:深色背景 + 状态指示 */}
<div
style={{
aspectRatio,
background: "#1a1a2e",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
position: "relative",
gap: 8,
}}
>
{/* 中心:预览编号 */}
<span
style={{
fontSize: 24,
fontWeight: 700,
color: "#fff",
opacity: 0.9,
}}
>
预览 #{item.index + 1}
</span>
{/* 状态指示 */}
{item.status === "generating" && (
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<LoadingOutlined style={{ fontSize: 14, color: "#fff" }} spin />
<span style={{ color: "rgba(255,255,255,0.8)", fontSize: 12 }}>
生成中 {item.progress}%
</span>
</div>
)}
{item.status === "pending" && (
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<ClockCircleOutlined
style={{ fontSize: 14, color: "rgba(255,255,255,0.6)" }}
/>
<span style={{ color: "rgba(255,255,255,0.6)", fontSize: 12 }}>
排队中...
</span>
</div>
)}
{item.status === "ready" && (
<CheckCircleFilled style={{ fontSize: 18, color: "#52c41a" }} />
)}
{item.status === "error" && (
<ExclamationCircleFilled style={{ fontSize: 18, color: "#ef4444" }} />
)}
{/* 选中角标 */}
{isSelected && item.status === "ready" && (
<div
style={{
position: "absolute",
top: 4,
right: 4,
background: "#1677ff",
color: "#fff",
fontSize: 10,
padding: "2px 6px",
borderRadius: 4,
}}
>
预览 #{item.index + 1}
</div>
)}
</div>
</div>
)
})}
</div>
)}
{/* 整体进度条(多预览生成中) */}
{anyGenerating && (
@@ -135,7 +244,7 @@ const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
<CheckCircleFilled style={{ color: "#52c41a", marginRight: 8 }} />
<span>
{items.filter((it) => it.status === "ready").length} 个预览生成成功
",请在右侧预览面板查看效果"
{items.length > 1 ? ",点击选择要查看的版本" : ",确认效果后进入下一步"}
</span>
<button
className="xx-preview-regenerate-btn"
+8 -21
View File
@@ -31,27 +31,14 @@ vi.mock("react-router-dom", async () => {
})
vi.mock("@/store/authStore", () => ({
useAuthStore: Object.assign(
(selector: any) =>
selector({
user: { id: "1", username: "testuser" },
token: "mock-token",
refreshToken: "mock-refresh-token",
isAuthenticated: true,
setAuth: mockSetAuth,
clearAuth: mockClearAuth,
}),
{
getState: () => ({
user: { id: "1", username: "testuser" },
token: "mock-token",
refreshToken: "mock-refresh-token",
isAuthenticated: true,
setAuth: mockSetAuth,
clearAuth: mockClearAuth,
}),
},
),
useAuthStore: (selector: any) =>
selector({
user: { id: "1", username: "testuser" },
token: "mock-token",
isAuthenticated: true,
setAuth: mockSetAuth,
clearAuth: mockClearAuth,
}),
}))
vi.mock("@tanstack/react-query", () => ({
+14 -80
View File
@@ -108,30 +108,21 @@ def _run_with_fakes(
session = MagicMock()
session_maker = MagicMock(return_value=session)
captured: dict = {"upload_calls": []}
captured = {"upload_calls": [], "session": session_maker()}
def _tracking_upload(local_path, storage_key):
captured["upload_calls"].append((local_path, storage_key))
return upload_fn(local_path, storage_key)
# Wrap session_maker to capture the session INSIDE the patch context.
# This avoids stale PromiseProxy cache issues in full-suite runs.
_created_sessions: list = []
_orig_sm = session_maker
def _tracking_sm(*a, **kw):
s = _orig_sm(*a, **kw)
_created_sessions.append(s)
return s
bound_task = _make_bound_task()
import worker_app.db as _db_mod
with patch.object(_db_mod, "SessionLocal", _tracking_sm):
with patch(
"packages.adapters.sqlalchemy_impl.generated_video_repository.SQLAlchemyGeneratedVideoRepository",
return_value=repo,
):
with patch(
"packages.adapters.sqlalchemy_impl.generated_video_repository.SQLAlchemyGeneratedVideoRepository",
return_value=repo,
"worker_app.db.SessionLocal",
session_maker,
):
with patch(
"video_processing.oss_helpers.upload_to_oss",
@@ -143,7 +134,6 @@ def _run_with_fakes(
):
result = _call_task(batch_download_videos, bound_task, [v.id for v in videos], user_id)
captured["session"] = _created_sessions[0] if _created_sessions else None
captured["result"] = result
return captured
@@ -254,67 +244,19 @@ def test_batch_download_single_video():
def test_batch_download_session_closed():
"""DB session is always closed (via finally block).
Patches the function's own globals to inject mock SessionLocal,
bypassing any import caching issues in the full suite.
"""
import sys
import worker_app.db as _db_mod
from apps.worker.worker_app.tasks.batch_download import batch_download_videos
"""DB session is always closed (via finally block)."""
videos = [_FakeVideo("v1", "v.mp4")]
repo = _FakeGeneratedVideoRepository(videos)
session = MagicMock()
mock_session_factory = MagicMock(return_value=session)
session_maker = MagicMock(return_value=session)
def _noop_download(url, dest):
Path(dest).parent.mkdir(parents=True, exist_ok=True)
Path(dest).write_bytes(b"fake video data")
_run_with_fakes(videos, session_maker=session_maker)
bound_task = _make_bound_task()
# Get the raw function to patch its globals
raw_fn = _get_raw_task_fn(batch_download_videos)
# Patch SessionLocal in ALL possible module locations
_db_mod.SessionLocal = mock_session_factory
if "worker_app.db" in sys.modules:
sys.modules["worker_app.db"].SessionLocal = mock_session_factory
# Also patch in the function's own globals if it has a reference there
if "SessionLocal" in raw_fn.__globals__:
raw_fn.__globals__["SessionLocal"] = mock_session_factory
try:
with patch(
"packages.adapters.sqlalchemy_impl.generated_video_repository.SQLAlchemyGeneratedVideoRepository",
return_value=repo,
):
with patch("video_processing.oss_helpers.upload_to_oss", return_value="https://oss.example.com/zip"):
with patch(
"apps.worker.worker_app.tasks.batch_download._download_video_to_file",
_noop_download,
):
raw_fn(bound_task, ["v1"], "user_1")
finally:
pass # Don't restore - other tests handle their own patches
# Diagnostic: check if our mock factory was actually called
assert mock_session_factory.called, (
"SessionLocal mock was never called! "
f"raw_fn={raw_fn}, type={type(raw_fn)}"
)
session.close.assert_called_once()
def test_batch_download_closes_session_on_error():
"""Session is closed even when get_by_ids raises."""
import sys
import worker_app.db as _db_mod
from apps.worker.worker_app.tasks.batch_download import batch_download_videos
class _ExplodingRepo:
@@ -322,25 +264,17 @@ def test_batch_download_closes_session_on_error():
raise RuntimeError("db down")
session = MagicMock()
mock_session_factory = MagicMock(return_value=session)
session_maker = MagicMock(return_value=session)
bound_task = _make_bound_task()
raw_fn = _get_raw_task_fn(batch_download_videos)
_db_mod.SessionLocal = mock_session_factory
if "worker_app.db" in sys.modules:
sys.modules["worker_app.db"].SessionLocal = mock_session_factory
if "SessionLocal" in raw_fn.__globals__:
raw_fn.__globals__["SessionLocal"] = mock_session_factory
with patch(
"packages.adapters.sqlalchemy_impl.generated_video_repository.SQLAlchemyGeneratedVideoRepository",
return_value=_ExplodingRepo(),
):
with pytest.raises(RuntimeError, match="db down"):
raw_fn(bound_task, ["v1"], "u")
with patch("worker_app.db.SessionLocal", session_maker):
with pytest.raises(RuntimeError, match="db down"):
_call_task(batch_download_videos, bound_task, ["v1"], "u")
assert mock_session_factory.called, "SessionLocal mock was never called!"
session.close.assert_called_once()
+1 -5
View File
@@ -61,9 +61,6 @@ def _make_mock_clip(clip_id="clip-001", order=0, duration=10.0, clip_type="video
clip.config = {}
clip.asset_id = "asset-001"
clip.status = "ready"
clip.template_clip_config_id = ""
clip.created_at = None
clip.updated_at = None
return clip
@@ -330,8 +327,7 @@ class TestClipSplitMerge:
resp = c.post(BASE + "/clips/merge", json={"clip_ids": ["clip-001", "clip-002"]})
assert resp.status_code == 200
data = resp.json()
assert "merged_clip" in data
assert "id" in data["merged_clip"]
assert "id" in data
mock_plan_svc.merge_clips.assert_called_once_with(["clip-001", "clip-002"])
def test_merge_clips_single_422(self, client):