c81ebb7053
- 根因:polling-backoff-recovery 测试断言失败后跳过 loop.dispose(),残留自续期定时器导致 node --test 子进程永不退出 - dead-code-cleanup:移除套娃用例 test_full_vitest_green(再跑一遍全量套件,放大问题),execSync 加 300s 超时;其余静态断言保留 - package.json:node --test --test-force-exit --test-timeout=180000 - CI workflow 前端测试步骤改为 npm test(与 package.json 单一来源) - 修正 3 处过时期望值(轮询退避/品牌 files+taskType/巡店 delete_conditions) - 验证:本地 666 用例全过,19-24s,无残留进程
218 lines
8.7 KiB
TypeScript
218 lines
8.7 KiB
TypeScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { http } from '../src/shared/api/http.ts'
|
|
import {
|
|
getImageVideoSecretStatus,
|
|
saveImageVideoSecrets,
|
|
runImageVideoDouyinCopy,
|
|
runImageVideoWorkflow,
|
|
getImageVideoWorkflowResult,
|
|
getImageVideoAsyncTask,
|
|
uploadImageVideoMedia,
|
|
listImageVideoVoices,
|
|
deleteImageVideoVoice,
|
|
cloneImageVideoVoice,
|
|
synthesizeImageVideoVoice,
|
|
} from '../src/shared/api/types/modules/image-video.ts'
|
|
import {
|
|
expandBrandFolderRecursive,
|
|
getBrandTasks,
|
|
createBrandTask,
|
|
getBrandTaskEventsUrl,
|
|
} from '../src/shared/api/types/modules/brand.ts'
|
|
import {
|
|
getCurrentUserAppColumnKeys,
|
|
type PermissionMenuItem,
|
|
} from '../src/shared/api/types/modules/permission.ts'
|
|
import {
|
|
getDigitalHumanVersions,
|
|
releaseDigitalHumanVersion,
|
|
getDigitalHumanVersionDownloadUrl,
|
|
} from '../src/shared/api/types/modules/digital-human.ts'
|
|
|
|
function setupWindow() {
|
|
const store = new Map<string, string>([['uid', '42']])
|
|
;(globalThis as Record<string, unknown>).window = {
|
|
localStorage: {
|
|
getItem: (key: string) => store.get(key) ?? null,
|
|
setItem: (key: string, value: string) => void store.set(key, value),
|
|
},
|
|
location: { origin: 'http://localhost' },
|
|
}
|
|
}
|
|
|
|
function mockRequest(
|
|
t: Parameters<typeof test>[1] extends (t: infer T) => unknown ? T : never,
|
|
impl: (config: { url?: string; method?: string; params?: Record<string, unknown>; data?: unknown; timeout?: number }) => Promise<unknown>,
|
|
) {
|
|
t.mock.method(http, 'request', impl as never)
|
|
}
|
|
|
|
const okResponse = (data: unknown) => Promise.resolve({ data: { success: true, message: 'ok', data } })
|
|
|
|
test('test_image_video_secrets', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; params?: Record<string, unknown>; data?: unknown }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return okResponse({ userId: 42, configured: true })
|
|
})
|
|
await getImageVideoSecretStatus()
|
|
await saveImageVideoSecrets({ expireDays: 30 })
|
|
assert.equal(calls[0].url, '/newApi/api/image-video/secrets')
|
|
assert.deepEqual(calls[0].params, { user_id: 42 })
|
|
assert.equal(calls[1].url, '/newApi/api/image-video/secrets')
|
|
assert.equal(calls[1].method, 'PUT')
|
|
assert.deepEqual(calls[1].data, { expireDays: 30, userId: 42 })
|
|
})
|
|
|
|
test('test_image_video_workflow', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; data?: unknown }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return okResponse({ taskId: 1, taskType: 'workflow', status: 'RUNNING' })
|
|
})
|
|
const params = { api_key_info: { t8star_key: 'a', t8_video_key: 'b', ai_conductor_key: 'c' } }
|
|
await runImageVideoWorkflow(params as never)
|
|
await getImageVideoWorkflowResult('exec-1')
|
|
assert.equal(calls[0].url, '/newApi/api/image-video/workflow/run')
|
|
assert.equal(calls[0].method, 'POST')
|
|
assert.deepEqual(calls[0].data, { userId: 42, parameters: params })
|
|
assert.equal(calls[1].url, '/newApi/api/image-video/workflow/result')
|
|
assert.deepEqual(calls[1].data, { userId: 42, executeId: 'exec-1' })
|
|
})
|
|
|
|
test('test_image_video_douyin_async', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; data?: unknown; params?: Record<string, unknown> }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return okResponse({ taskId: 2, taskType: 'copy', status: 'PENDING' })
|
|
})
|
|
await runImageVideoDouyinCopy({ url: 'https://x.com/v' })
|
|
await getImageVideoAsyncTask(7)
|
|
assert.equal(calls[0].url, '/newApi/api/image-video/douyin-copy')
|
|
assert.equal(calls[0].method, 'POST')
|
|
assert.deepEqual(calls[0].data, { userId: 42, url: 'https://x.com/v' })
|
|
assert.equal(calls[1].url, '/newApi/api/image-video/tasks/7')
|
|
assert.deepEqual(calls[1].params, { user_id: 42 })
|
|
})
|
|
|
|
test('test_image_video_voice', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; data?: unknown }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return okResponse({ taskId: 3, taskType: 'voice', status: 'SUCCESS' })
|
|
})
|
|
await listImageVideoVoices('v1')
|
|
await deleteImageVideoVoice('vid-1')
|
|
await cloneImageVideoVoice({ name: 'clone' })
|
|
await synthesizeImageVideoVoice({ text: '你好', voiceId: 'vid-1' })
|
|
assert.equal(calls[0].url, '/newApi/api/image-video/voice/list')
|
|
assert.deepEqual(calls[0].data, { userId: 42, name: 'v1' })
|
|
assert.equal(calls[1].url, '/newApi/api/image-video/voice/delete')
|
|
assert.deepEqual(calls[1].data, { userId: 42, voiceId: 'vid-1' })
|
|
assert.equal(calls[2].url, '/newApi/api/image-video/voice/clone')
|
|
assert.deepEqual(calls[2].data, { userId: 42, name: 'clone' })
|
|
assert.equal(calls[3].url, '/newApi/api/image-video/voice/synthesis')
|
|
assert.deepEqual(calls[3].data, { userId: 42, text: '你好', voiceId: 'vid-1' })
|
|
})
|
|
|
|
test('test_image_video_upload_media', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; data?: unknown }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return okResponse({ url: 'https://oss/x', objectKey: 'k1', originalFilename: 'a.png', mediaType: 'image' })
|
|
})
|
|
const file = new File(['x'], 'a.png', { type: 'image/png' })
|
|
const result = await uploadImageVideoMedia(file)
|
|
assert.equal(calls[0].url, '/newApi/api/files/upload')
|
|
assert.equal(calls[0].method, 'POST')
|
|
assert.ok(calls[0].data instanceof FormData)
|
|
assert.deepEqual(result, { url: 'https://oss/x', objectKey: 'k1', originalFilename: 'a.png', mediaType: 'image' })
|
|
})
|
|
|
|
test('test_brand_tasks', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; data?: unknown }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return Promise.resolve({ data: { success: true, items: [{ id: 1 }] } })
|
|
})
|
|
await getBrandTasks()
|
|
await createBrandTask(['/tmp/a'], 'STRATEGY_A')
|
|
await expandBrandFolderRecursive('/tmp/a')
|
|
assert.equal(calls[0].url, '/api/brand/tasks?userId=42')
|
|
assert.equal(calls[0].method, 'GET')
|
|
assert.equal(calls[1].url, '/api/brand/tasks?userId=42')
|
|
assert.equal(calls[1].method, 'POST')
|
|
assert.deepEqual(calls[1].data, { files: ['/tmp/a'], strategy: 'STRATEGY_A', taskType: 2 })
|
|
assert.equal(calls[2].url, '/api/brand/expand-folder-recursive')
|
|
assert.equal(calls[2].method, 'POST')
|
|
assert.deepEqual(calls[2].data, { folder: '/tmp/a' })
|
|
assert.equal(
|
|
getBrandTaskEventsUrl(7),
|
|
'/api/brand/tasks/7/events?user_id=42',
|
|
)
|
|
})
|
|
|
|
test('test_permission_column_keys', async (t) => {
|
|
setupWindow()
|
|
mockRequest(t, () =>
|
|
Promise.resolve({
|
|
data: { success: true, data: [{ column_key: 'collect-data' }, { columnKey: 'Shop-Match' }] },
|
|
}),
|
|
)
|
|
const keys = await getCurrentUserAppColumnKeys()
|
|
assert.deepEqual(keys, ['collect-data', 'shop-match'])
|
|
})
|
|
|
|
test('test_digital_human_versions', async (t) => {
|
|
setupWindow()
|
|
const calls: { url?: string; method?: string; params?: Record<string, unknown> }[] = []
|
|
mockRequest(t, (config) => {
|
|
calls.push(config)
|
|
return okResponse({ records: [{ id: 1, version: '1.0.0' }], total: 1 })
|
|
})
|
|
await getDigitalHumanVersions({ page: 1, pageSize: 10 })
|
|
await releaseDigitalHumanVersion('1.0.0')
|
|
await getDigitalHumanVersionDownloadUrl('1.0.0')
|
|
assert.equal(calls[0].url, '/newApi/api/digital-human/versions')
|
|
assert.deepEqual(calls[0].params, { page: 1, pageSize: 10 })
|
|
assert.equal(calls[1].url, '/newApi/api/digital-human/versions/1.0.0/release')
|
|
assert.equal(calls[1].method, 'POST')
|
|
assert.equal(calls[2].url, '/newApi/api/digital-human/versions/1.0.0/download-url')
|
|
})
|
|
|
|
test('test_image_video_export_compat', async (t) => {
|
|
setupWindow()
|
|
const fromJavaModules = await import('../src/shared/api/java-modules.ts')
|
|
const fromImageVideo = await import('../src/shared/api/types/modules/image-video.ts')
|
|
const imageVideoNames = [
|
|
'getImageVideoSecretStatus', 'saveImageVideoSecrets', 'runImageVideoDouyinCopy',
|
|
'runImageVideoWorkflow', 'getImageVideoWorkflowResult', 'getImageVideoAsyncTask',
|
|
'uploadImageVideoMedia', 'listImageVideoVoices', 'deleteImageVideoVoice',
|
|
'cloneImageVideoVoice', 'synthesizeImageVideoVoice',
|
|
]
|
|
for (const name of imageVideoNames) {
|
|
assert.equal(fromJavaModules[name], fromImageVideo[name], `${name} 应为同一引用`)
|
|
}
|
|
const fromBrand = await import('../src/shared/api/types/modules/brand.ts')
|
|
assert.equal(typeof fromBrand.expandBrandFolderRecursive, 'function')
|
|
const item: PermissionMenuItem = { id: 1, column_key: 'x' }
|
|
assert.equal(item.column_key, 'x')
|
|
})
|
|
|
|
test('test_image_video_unwrap_and_error', async (t) => {
|
|
setupWindow()
|
|
mockRequest(t, () => okResponse({ taskId: 1, taskType: 't', status: 'RUNNING' }))
|
|
const result = await getImageVideoAsyncTask(1)
|
|
assert.equal(result.taskId, 1)
|
|
|
|
mockRequest(t, () => Promise.resolve({ data: { success: false, message: '密钥未配置' } }))
|
|
await assert.rejects(getImageVideoSecretStatus(), /密钥未配置/)
|
|
})
|