import { describe, expect, it } from 'vitest'; import { canonicalProjectedResourceMediaType, resolveProjectCharacterAnimationCapability, resolveProjectResourceEditCapability, resourceEditPromptMaxLength, } from '../src/view/project-development/resourceEditModel'; import type { ProjectResource } from '../src/view/project-development/resourceProjectionModel'; function resource( input: Partial & Pick, ): ProjectResource { return { subtype: 'task-artifact', label: input.path, mediaType: '', sourceLabel: '测试来源', taskTitle: null, manifestAssetId: null, producerTaskId: 'task-1', externalResourceId: null, referenceResourceIds: [], dependencies: [], dependencyDepth: 0, ...input, }; } describe('全类型资源编辑能力模型', () => { it('把现役资源稳定分流到图片画布、媒体派生、文本派生和版本分支', () => { const cases: Array<{ resource: ProjectResource; route: 'image-canvas' | 'derive'; editKind?: string; normalizeSource?: boolean; }> = [ { resource: resource({ id: 'asset:png', category: 'art', path: 'assets/hero.png', mediaType: 'image/png', manifestAssetId: 'png', }), route: 'image-canvas', normalizeSource: false, }, { resource: resource({ id: 'task:art:hero.webp', category: 'art', path: 'hero.webp', mediaType: '美术产物', }), route: 'image-canvas', normalizeSource: true, }, { resource: resource({ id: 'asset:gif', category: 'art', path: 'assets/hero.gif', mediaType: 'image/gif', manifestAssetId: 'gif', }), route: 'derive', editKind: 'image-reference', }, { resource: resource({ id: 'asset:svg', category: 'art', path: 'assets/icon.svg', mediaType: 'image/svg+xml', manifestAssetId: 'svg', }), route: 'derive', editKind: 'svg', }, { resource: resource({ id: 'task:art:intro.mp4', category: 'art', path: 'assets/intro.mp4', mediaType: '美术产物', }), route: 'derive', editKind: 'video', }, { resource: resource({ id: 'asset:bgm', category: 'audio', path: 'audio/theme.ogg', subtype: 'background-music', mediaType: 'audio/ogg', manifestAssetId: 'bgm', }), route: 'derive', editKind: 'background-music', }, { resource: resource({ id: 'asset:sfx', category: 'audio', path: 'audio/hit.wav', subtype: 'sound-effect', mediaType: 'audio/wav', manifestAssetId: 'sfx', }), route: 'derive', editKind: 'sound-effect', }, { resource: resource({ id: 'task:code:main.ts', category: 'document', path: 'src/main.ts', mediaType: '项目文档', }), route: 'derive', editKind: 'text', }, { resource: resource({ id: 'agent-result:design:message-1', category: 'document', path: '专业 Agent 文本回执', subtype: 'agent-result', producerTaskId: null, content: '回执', }), route: 'derive', editKind: 'agent-result', }, { resource: resource({ id: 'version:v1', category: 'version', path: '项目版本 · v1', subtype: 'project-version', producerTaskId: null, version: { versionId: 'v1', parentVersionId: null, projectRevision: 1, resourceBindings: [], createdReason: 'initial', createdAt: 1, label: '版本 1', childVersionIds: [], }, }), route: 'derive', editKind: 'version', }, ]; for (const expected of cases) { const capability = resolveProjectResourceEditCapability( expected.resource, ); expect(capability.route, expected.resource.id).toBe(expected.route); if (capability.route === 'derive') { expect(capability.editKind, expected.resource.id).toBe( expected.editKind, ); } else { expect(capability.normalizeSource, expected.resource.id).toBe( expected.normalizeSource, ); } } }); it('用路径补齐任务产物缺失的标准媒体类型', () => { expect( canonicalProjectedResourceMediaType( resource({ id: 'task:art:clip.mov', category: 'art', path: 'assets/clip.mov', mediaType: '美术产物', }), ), ).toBe('video/quicktime'); expect( canonicalProjectedResourceMediaType( resource({ id: 'task:audio:effect.wav', category: 'audio', path: 'audio/effect.wav', mediaType: '美术产物', }), ), ).toBe('audio/wav'); expect( canonicalProjectedResourceMediaType( resource({ id: 'task:code:main.ts', category: 'document', path: 'src/main.ts', mediaType: '项目文档', }), ), ).toBe('text/typescript'); }); it('按现役媒体接口限制编辑提示词长度', () => { expect(resourceEditPromptMaxLength('background-music')).toBe(140); expect(resourceEditPromptMaxLength('sound-effect')).toBe(1_900); expect(resourceEditPromptMaxLength('video')).toBe(4_000); expect(resourceEditPromptMaxLength('character-animation')).toBe(4_000); expect(resourceEditPromptMaxLength('text')).toBe(32_000); }); it('图片资源可进入角色动画派生入口', () => { const capability = resolveProjectCharacterAnimationCapability( resource({ id: 'asset:hero', category: 'art', path: 'assets/hero.png', mediaType: 'image/png', manifestAssetId: 'hero', }), ); expect(capability?.editKind).toBe('character-animation'); expect(capability?.generationMode).toBe('derive'); expect( resolveProjectCharacterAnimationCapability( resource({ id: 'asset:bgm', category: 'audio', path: 'audio/theme.mp3', mediaType: 'audio/mpeg', manifestAssetId: 'bgm', }), ), ).toBeNull(); }); });