import { expect, test } from "bun:test"; import { defaultConfig } from "../src/stores/use-config-store"; import { submitBackendGenerationTask, type GenerationTaskDependencies } from "../src/services/api/generation-task"; import type { GenerationTask } from "../src/services/api/task-center"; import type { ProjectDetail } from "../src/services/api/projects"; import { buildShotAssetReferenceContext, resolveShotAssetMentionPrompt } from "../src/pages/projects/detail/workflow-shot-references"; test("shot asset prompt keeps character names and maps character image, voice and scene assets", () => { const detail = { assets: [ { id: "character-1", title: "张天昊", category: "character", mediaType: "image", primaryVersionId: "character-version-1", character: { versionId: "character-version-1", definition: { voiceLanguage: "普通话", voiceAge: "青年男性", voiceTimbre: "略带疲惫和震惊" }, representations: [{ id: "representation-1", resourceId: "character-image-1", mediaType: "image/png", role: "primary" }], voice: { profile: { sampleResourceId: "character-audio-1", language: "普通话", timbre: "沉稳" }, instructions: "内心独白语气" }, }, }, { id: "scene-1", title: "坑底场景", category: "environment", mediaType: "image", primaryVersionId: "scene-version-1", storageKey: "resource:scene-image-1", }, ], shotReferences: [ { shotId: "shot-1", assetVersionId: "character-version-1", status: "linked" }, { shotId: "shot-1", assetVersionId: "scene-version-1", status: "linked" }, ], } as ProjectDetail; const context = buildShotAssetReferenceContext(detail, "shot-1"); const prompt = resolveShotAssetMentionPrompt("张天昊在 @[asset:scene-1] 睁开眼睛", context); expect(context.referenceImages).toHaveLength(2); expect(context.referenceAudios).toHaveLength(1); expect(context.referenceAudios[0]?.storageKey).toBe("resource:character-audio-1"); expect(context.resolvedCharacterVersions).toEqual([{ assetId: "character-1", versionId: "character-version-1" }]); expect(prompt).toContain("张天昊在 图片2 睁开眼睛"); expect(prompt).toContain("- 张天昊:人物参考:图片1;声音参考:音频1"); expect(prompt).toContain("声音说明:普通话;青年男性;略带疲惫和震惊;内心独白语气"); expect(prompt).toContain("- 坑底场景:场景参考:图片2"); }); test("background generation submission returns after task creation without waiting", async () => { let waitCalls = 0; const task = { id: "task-1", type: "canvas_video", status: "queued", prompt: "角色表演", attempts: 0, createdAt: "2026-08-30T00:00:00.000Z", updatedAt: "2026-08-30T00:00:00.000Z", } satisfies GenerationTask; const dependencies: GenerationTaskDependencies = { createTask: async () => task, waitTask: async () => { waitCalls += 1; throw new Error("should not wait"); }, runLocal: async () => ({ mode: "video" }), createId: () => "id-1", now: () => "2026-08-30T00:00:00.000Z", }; const submitted = await submitBackendGenerationTask({ projectId: "project-1", mode: "video", prompt: "角色表演", config: { ...defaultConfig, model: "MiniMax-H3", videoModel: "MiniMax-H3" }, metadata: { shotId: "shot-1", videoEditOperation: "reference_to_video" }, }, dependencies); expect(submitted).toBe(task); expect(waitCalls).toBe(0); });