feat: 同步精简接口并统一表单校验
This commit is contained in:
@@ -10,8 +10,8 @@ import {
|
||||
qualityKey,
|
||||
qualitySession,
|
||||
qualityTargets,
|
||||
savedVideoValidation,
|
||||
videoRepairInfo,
|
||||
sessionVideoValidation,
|
||||
isVisualValidation,
|
||||
validQualityInput
|
||||
} from '@/features/production/quality'
|
||||
import type { KeyframeReadiness } from '@/features/production/types'
|
||||
@@ -125,8 +125,7 @@ function server() {
|
||||
id: 'video-repair-1',
|
||||
status: 'queued',
|
||||
isPrimary: false,
|
||||
videoUrl: null,
|
||||
rawJson: JSON.stringify({ repair: { sourceVideoId: 'video-1', attempt: 1 } })
|
||||
videoUrl: null
|
||||
})
|
||||
}
|
||||
else if (path.endsWith('/repair')) data = repair
|
||||
@@ -136,13 +135,14 @@ function server() {
|
||||
shotId: 'shot-1',
|
||||
videoId: 'video-1',
|
||||
validatedAt: '2026-09-03',
|
||||
isPrimary: validation.passed && !!videoRepairInfo(video.rawJson),
|
||||
isPrimary: validation.passed,
|
||||
videoUrl: video.videoUrl,
|
||||
sampleFrames: [{ label: '中间', timeSeconds: 2 }],
|
||||
allowedTexts: []
|
||||
}
|
||||
else data = keyframeResult
|
||||
} else if (path.endsWith(`/projects/${projectId}`)) data = project
|
||||
else if (path.endsWith('/checkpoints')) data = []
|
||||
else if (path.includes('/readiness')) data = readiness
|
||||
else if (path.endsWith('/keyframes')) data = [keyframe]
|
||||
else if (path.endsWith('/videos')) data = [video]
|
||||
@@ -437,9 +437,10 @@ describe('质量面板渐进展示', () => {
|
||||
|
||||
it('视频历史读取不触发视觉模型,畸形历史不会伪造通过', () => {
|
||||
const data = server()
|
||||
expect(savedVideoValidation(JSON.stringify({ videoValidation: data.validation }))?.passed).toBe(true)
|
||||
expect(savedVideoValidation('{broken')).toBeNull()
|
||||
expect(savedVideoValidation('{"videoValidation":{"passed":true}}')).toBeNull()
|
||||
expect(sessionVideoValidation(data.projectId, 'shot-1', 'video-1')).toBeNull()
|
||||
expect(isVisualValidation(data.validation)).toBe(true)
|
||||
expect(isVisualValidation('{broken')).toBe(false)
|
||||
expect(isVisualValidation({ passed: true })).toBe(false)
|
||||
expect(allowedTextLines(' 招牌\n\n招牌\n编号 ')).toEqual(['招牌', '编号'])
|
||||
expect(validQualityInput(input({ maxRepairAttempts: 0 }))).toBe(true)
|
||||
expect(qualityKey('a', { kind: 'batch', episodeNo: 1, title: '' })).not.toBe(
|
||||
@@ -454,7 +455,9 @@ describe('视频修复候选与复检晋升', () => {
|
||||
const data = setup({ kind: 'video', shotId: 'shot-1', assetId: 'video-1', title: '视频' })
|
||||
await data.service.run('repair', input({ maxRepairAttempts: 2 }))
|
||||
expect(data.posts()).toHaveLength(0)
|
||||
data.video.rawJson = JSON.stringify({ videoValidation: { ...data.validation, passed: false } })
|
||||
data.validation.passed = false
|
||||
await data.service.run('validate', input())
|
||||
data.fetcher.mockClear()
|
||||
await data.service.run('repair', input({ maxRepairAttempts: 2 }))
|
||||
expect(data.posts()).toHaveLength(1)
|
||||
expect(data.posts()[0]?.[0]).toBe('/api/storyboard-shots/shot-1/videos/video-1/repair')
|
||||
@@ -465,21 +468,23 @@ describe('视频修复候选与复检晋升', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('链路次数上限不能作为零次修复,已达上限不创建任务', async () => {
|
||||
it('修复不接受零次上限,后端次数限制错误原样保留且不重试', async () => {
|
||||
const data = setup({ kind: 'video', shotId: 'shot-1', assetId: 'video-1', title: '视频' })
|
||||
data.video.rawJson = JSON.stringify({
|
||||
videoValidation: { ...data.validation, passed: false },
|
||||
repair: { attempt: 2 }
|
||||
})
|
||||
data.validation.passed = false
|
||||
await data.service.run('validate', input())
|
||||
data.fetcher.mockClear()
|
||||
await data.service.run('repair', input({ maxRepairAttempts: 0 }))
|
||||
await data.service.run('repair', input({ maxRepairAttempts: 2 }))
|
||||
expect(data.posts()).toHaveLength(0)
|
||||
vi.spyOn(qualityApi, 'repairVideo').mockRejectedValueOnce(new Error('修复次数上限已达到'))
|
||||
await data.service.run('repair', input({ maxRepairAttempts: 2 }))
|
||||
expect(qualityApi.repairVideo).toHaveBeenCalledTimes(1)
|
||||
expect(data.service.session.value.error).toContain('次数上限')
|
||||
expect(sessionVideoValidation(data.projectId, 'shot-1', 'video-1')?.passed).toBe(false)
|
||||
})
|
||||
|
||||
it('复检通过的修复候选准确显示自动晋升,前端不再另发主视频 PUT', async () => {
|
||||
const data = setup({ kind: 'video', shotId: 'shot-1', assetId: 'video-1', title: '修复候选' })
|
||||
data.video.rawJson = JSON.stringify({ repair: { attempt: 1, sourceVideoId: 'source' } })
|
||||
|
||||
data.video.isPrimary = false
|
||||
await data.service.run('validate', input())
|
||||
const receipt = data.service.session.value.receipt!
|
||||
@@ -499,8 +504,7 @@ describe('视频修复候选与复检晋升', () => {
|
||||
projectId: data.projectId,
|
||||
target: { kind: 'video', shotId: 'shot-1', assetId: 'video-1', title: '视频' },
|
||||
open: true,
|
||||
disabled: false,
|
||||
savedRawJson: JSON.stringify({ videoValidation: { ...data.validation, passed: false } })
|
||||
disabled: false
|
||||
}
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
Reference in New Issue
Block a user