video-flow-b/app/service/test/Scene.test.ts

566 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { getSceneList, getSceneData, updateText, updateTag, regenerateScene, getSceneShots, applySceneToShots } from '@/api/video_flow';
import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
import { TextEditUseCase } from '../usecase/TextEditUseCase';
import { TagEditUseCase } from '../usecase/TagEditUseCase';
import { SceneItem, TextItem, TagItem } from '../domain/Item';
import { SceneEntity, AITextEntity, TagEntity, ShotEntity, ShotStatus } from '../domain/Entities';
// Mock API模块
jest.mock('@/api/video_flow', () => ({
getSceneList: jest.fn(),
getSceneData: jest.fn(),
updateText: jest.fn(),
updateTag: jest.fn(),
regenerateScene: jest.fn(),
getSceneShots: jest.fn(),
applySceneToShots: jest.fn(),
}));
// Mock UseCase模块
jest.mock('../usecase/SceneEditUseCase');
jest.mock('../usecase/TextEditUseCase');
jest.mock('../usecase/TagEditUseCase');
// Mock Domain模块
jest.mock('../domain/Item', () => ({
SceneItem: jest.fn(),
TextItem: jest.fn(),
TagItem: jest.fn(),
}));
describe('SceneService 业务逻辑测试', () => {
let mockSceneEditUseCase: jest.Mocked<SceneEditUseCase>;
let mockTextEditUseCase: jest.Mocked<TextEditUseCase>;
let mockTagEditUseCase: jest.Mocked<TagEditUseCase>;
// 测试数据
const mockSceneEntity: SceneEntity = {
id: 'scene1',
name: '测试场景',
imageUrl: 'http://example.com/scene1.jpg',
tagIds: ['tag1', 'tag2'],
generateTextId: 'text1',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
};
const mockTextEntity: AITextEntity = {
id: 'text1',
content: '这是AI生成的场景文本内容',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
};
const mockTagEntity1: TagEntity = {
id: 'tag1',
name: '场景标签1',
content: '场景标签内容1',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
};
const mockTagEntity2: TagEntity = {
id: 'tag2',
name: '场景标签2',
content: '场景标签内容2',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
};
const mockShotEntity: ShotEntity = {
id: 'shot1',
name: '分镜1',
sketchUrl: 'http://example.com/sketch1.jpg',
videoUrl: 'http://example.com/video1.mp4',
roleList: [],
sceneList: [],
content: [],
status: ShotStatus.sketchLoading,
shot: [],
scriptId: 'script1',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
};
beforeEach(() => {
jest.clearAllMocks();
// 设置Mock UseCase实例
mockSceneEditUseCase = {
AIgenerateScene: jest.fn(),
applyScene: jest.fn(),
refreshSceneData: jest.fn(),
} as any;
mockTextEditUseCase = {
getOptimizedContent: jest.fn(),
updateText: jest.fn(),
} as any;
mockTagEditUseCase = {
updateTag: jest.fn(),
} as any;
// 设置Mock构造函数
(SceneEditUseCase as jest.MockedClass<typeof SceneEditUseCase>).mockImplementation(() => mockSceneEditUseCase);
(TextEditUseCase as jest.MockedClass<typeof TextEditUseCase>).mockImplementation(() => mockTextEditUseCase);
(TagEditUseCase as jest.MockedClass<typeof TagEditUseCase>).mockImplementation(() => mockTagEditUseCase);
// 设置Mock Item构造函数
(SceneItem as jest.MockedClass<typeof SceneItem>).mockImplementation((entity) => ({
entity,
metadata: {},
disableEdit: entity.disableEdit,
type: 3,
} as any));
(TextItem as jest.MockedClass<typeof TextItem>).mockImplementation((entity) => ({
entity,
metadata: {},
disableEdit: entity.disableEdit,
type: 0,
} as any));
(TagItem as jest.MockedClass<typeof TagItem>).mockImplementation((entity) => ({
entity,
metadata: {},
disableEdit: entity.disableEdit,
type: 2,
} as any));
});
describe('数据初始化测试', () => {
it('应该成功获取场景列表', async () => {
const mockScenes = [mockSceneEntity];
(getSceneList as jest.Mock).mockResolvedValue({
successful: true,
data: mockScenes,
message: 'success',
});
const result = await getSceneList({ projectId: 'project1' });
expect(getSceneList).toHaveBeenCalledWith({ projectId: 'project1' });
expect(result.successful).toBe(true);
expect(result.data).toEqual(mockScenes);
});
it('获取场景列表失败时应该返回错误信息', async () => {
(getSceneList as jest.Mock).mockResolvedValue({
successful: false,
message: '获取失败',
});
const result = await getSceneList({ projectId: 'project1' });
expect(result.successful).toBe(false);
expect(result.message).toBe('获取失败');
});
it('应该成功获取场景数据', async () => {
(getSceneData as jest.Mock).mockResolvedValue({
successful: true,
data: {
text: mockTextEntity,
tags: [mockTagEntity1, mockTagEntity2],
},
});
const result = await getSceneData({ sceneId: 'scene1' });
expect(getSceneData).toHaveBeenCalledWith({ sceneId: 'scene1' });
expect(result.successful).toBe(true);
expect(result.data.text).toEqual(mockTextEntity);
expect(result.data.tags).toEqual([mockTagEntity1, mockTagEntity2]);
});
});
describe('修改文本和标签测试', () => {
it('应该成功修改AI文本', async () => {
const updatedTextEntity = { ...mockTextEntity, content: '更新后的场景文本' };
(updateText as jest.Mock).mockResolvedValue({
successful: true,
data: updatedTextEntity,
});
const result = await updateText({
textId: 'text1',
content: '新的场景文本内容'
});
expect(updateText).toHaveBeenCalledWith({
textId: 'text1',
content: '新的场景文本内容'
});
expect(result.successful).toBe(true);
expect(result.data.content).toBe('更新后的场景文本');
});
it('应该成功修改标签内容', async () => {
const updatedTagEntity = { ...mockTagEntity1, content: '更新后的场景标签' };
(updateTag as jest.Mock).mockResolvedValue({
successful: true,
data: updatedTagEntity,
});
const result = await updateTag({
tagId: 'tag1',
content: '新的场景标签内容'
});
expect(updateTag).toHaveBeenCalledWith({
tagId: 'tag1',
content: '新的场景标签内容'
});
expect(result.successful).toBe(true);
expect(result.data.content).toBe('更新后的场景标签');
});
});
describe('文本AI优化测试', () => {
it('应该成功优化AI文本', async () => {
const optimizedContent = '优化后的场景文本内容';
const updatedTextEntity = { ...mockTextEntity, content: optimizedContent };
mockTextEditUseCase.getOptimizedContent.mockResolvedValue(optimizedContent);
mockTextEditUseCase.updateText.mockResolvedValue({
entity: updatedTextEntity,
metadata: {},
disableEdit: false,
type: 0,
} as any);
(updateText as jest.Mock).mockResolvedValue({
successful: true,
data: updatedTextEntity,
});
// 模拟优化流程
const optimizedContentResult = await mockTextEditUseCase.getOptimizedContent();
const updateResult = await mockTextEditUseCase.updateText(optimizedContentResult);
expect(mockTextEditUseCase.getOptimizedContent).toHaveBeenCalled();
expect(mockTextEditUseCase.updateText).toHaveBeenCalledWith(optimizedContent);
expect(updateResult.entity.content).toBe(optimizedContent);
});
it('没有文本内容时优化应该抛出错误', async () => {
const emptyTextEntity = { ...mockTextEntity, content: '' };
mockTextEditUseCase.getOptimizedContent.mockRejectedValue(new Error('没有可优化的文本内容'));
await expect(mockTextEditUseCase.getOptimizedContent()).rejects.toThrow('没有可优化的文本内容');
});
});
describe('重新生成场景测试', () => {
it('应该成功重新生成场景', async () => {
const newSceneEntity = { ...mockSceneEntity, id: 'scene2', name: '新场景' };
(regenerateScene as jest.Mock).mockResolvedValue({
successful: true,
data: newSceneEntity,
});
mockSceneEditUseCase.AIgenerateScene.mockResolvedValue(newSceneEntity);
const result = await regenerateScene({
prompt: '重新生成场景',
tagTypes: ['tag1', 'tag2'],
sceneId: 'scene1'
});
expect(regenerateScene).toHaveBeenCalledWith({
prompt: '重新生成场景',
tagTypes: ['tag1', 'tag2'],
sceneId: 'scene1'
});
expect(result.successful).toBe(true);
expect(result.data.id).toBe('scene2');
expect(result.data.name).toBe('新场景');
});
it('重新生成场景失败时应该返回错误信息', async () => {
(regenerateScene as jest.Mock).mockResolvedValue({
successful: false,
message: '重新生成失败',
});
const result = await regenerateScene({
prompt: '重新生成场景',
tagTypes: ['tag1', 'tag2'],
sceneId: 'scene1'
});
expect(result.successful).toBe(false);
expect(result.message).toBe('重新生成失败');
});
});
describe('重新获取场景数据测试', () => {
it('应该成功重新获取场景数据并更新实体', async () => {
const mockTextEntity = {
id: 'text1',
content: '更新后的场景AI文本',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
};
const mockTags = [
{
id: 'tag1',
name: '更新场景标签1',
content: '更新场景内容1',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
},
{
id: 'tag2',
name: '更新场景标签2',
content: '更新场景内容2',
updatedAt: Date.now(),
loadingProgress: 100,
disableEdit: false,
}
];
(getSceneData as jest.Mock).mockResolvedValue({
successful: true,
data: {
text: mockTextEntity,
tags: mockTags,
},
});
// 模拟SceneItem的setEntity方法
const mockSetEntity = jest.fn();
(SceneItem as jest.MockedClass<typeof SceneItem>).mockImplementation((entity) => ({
entity,
metadata: {},
disableEdit: entity.disableEdit,
type: 3,
setEntity: mockSetEntity,
} as any));
const sceneItem = new SceneItem(mockSceneEntity);
const useCase = new SceneEditUseCase(sceneItem);
const result = await useCase.refreshSceneData();
expect(getSceneData).toHaveBeenCalledWith({ sceneId: 'scene1' });
expect(result.text).toEqual(mockTextEntity);
expect(result.tags).toEqual(mockTags);
expect(mockSetEntity).toHaveBeenCalledWith(expect.objectContaining({
generateTextId: 'text1',
tagIds: ['tag1', 'tag2'],
updatedAt: expect.any(Number),
}));
});
it('场景ID不存在时应该抛出错误', async () => {
const emptySceneEntity = { ...mockSceneEntity, id: '' };
const sceneItem = new SceneItem(emptySceneEntity);
const useCase = new SceneEditUseCase(sceneItem);
await expect(useCase.refreshSceneData()).rejects.toThrow('场景ID不存在无法获取场景数据');
});
it('API调用失败时应该抛出错误', async () => {
(getSceneData as jest.Mock).mockResolvedValue({
successful: false,
message: '获取失败',
});
const sceneItem = new SceneItem(mockSceneEntity);
const useCase = new SceneEditUseCase(sceneItem);
await expect(useCase.refreshSceneData()).rejects.toThrow('获取场景数据失败: 获取失败');
});
});
describe('场景应用到多个分镜测试', () => {
it('应该成功获取场景分镜列表', async () => {
const mockShots = [mockShotEntity];
(getSceneShots as jest.Mock).mockResolvedValue({
successful: true,
data: {
shots: mockShots,
appliedShotIds: [],
},
});
const result = await getSceneShots({ sceneId: 'scene1' });
expect(getSceneShots).toHaveBeenCalledWith({ sceneId: 'scene1' });
expect(result.successful).toBe(true);
expect(result.data.shots).toEqual(mockShots);
expect(result.data.appliedShotIds).toEqual([]);
});
it('应该成功应用场景到选中的分镜', async () => {
(applySceneToShots as jest.Mock).mockResolvedValue({
successful: true,
data: { success: true },
});
mockSceneEditUseCase.applyScene.mockResolvedValue({} as any);
const result = await applySceneToShots({
sceneId: 'scene1',
shotIds: ['shot1', 'shot2']
});
expect(applySceneToShots).toHaveBeenCalledWith({
sceneId: 'scene1',
shotIds: ['shot1', 'shot2']
});
expect(result.successful).toBe(true);
});
it('应用场景失败时应该返回错误信息', async () => {
(applySceneToShots as jest.Mock).mockResolvedValue({
successful: false,
message: '应用失败',
});
const result = await applySceneToShots({
sceneId: 'scene1',
shotIds: ['shot1']
});
expect(result.successful).toBe(false);
expect(result.message).toBe('应用失败');
});
it('应该正确处理已应用的分镜状态', async () => {
const mockShots = [mockShotEntity];
(getSceneShots as jest.Mock).mockResolvedValue({
successful: true,
data: {
shots: mockShots,
appliedShotIds: ['shot1'], // 分镜1已应用
},
});
const result = await getSceneShots({ sceneId: 'scene1' });
expect(result.data.appliedShotIds).toEqual(['shot1']);
expect(result.data.shots).toEqual(mockShots);
});
});
describe('UseCase业务逻辑测试', () => {
it('SceneEditUseCase应该正确初始化', () => {
const sceneItem = new SceneItem(mockSceneEntity);
const useCase = new SceneEditUseCase(sceneItem);
expect(SceneEditUseCase).toHaveBeenCalledWith(sceneItem);
expect(useCase).toBeDefined();
});
it('TextEditUseCase应该正确初始化', () => {
const textItem = new TextItem(mockTextEntity);
const useCase = new TextEditUseCase(textItem);
expect(TextEditUseCase).toHaveBeenCalledWith(textItem);
expect(useCase).toBeDefined();
});
it('TagEditUseCase应该正确初始化', () => {
const tagItem = new TagItem(mockTagEntity1);
const useCase = new TagEditUseCase(tagItem);
expect(TagEditUseCase).toHaveBeenCalledWith(tagItem);
expect(useCase).toBeDefined();
});
});
describe('Domain实体测试', () => {
it('SceneItem应该正确包装SceneEntity', () => {
const sceneItem = new SceneItem(mockSceneEntity);
expect(SceneItem).toHaveBeenCalledWith(mockSceneEntity);
expect(sceneItem.entity).toEqual(mockSceneEntity);
expect(sceneItem.disableEdit).toBe(false);
});
it('TextItem应该正确包装AITextEntity', () => {
const textItem = new TextItem(mockTextEntity);
expect(TextItem).toHaveBeenCalledWith(mockTextEntity);
expect(textItem.entity).toEqual(mockTextEntity);
expect(textItem.disableEdit).toBe(false);
});
it('TagItem应该正确包装TagEntity', () => {
const tagItem = new TagItem(mockTagEntity1);
expect(TagItem).toHaveBeenCalledWith(mockTagEntity1);
expect(tagItem.entity).toEqual(mockTagEntity1);
expect(tagItem.disableEdit).toBe(false);
});
});
describe('错误处理测试', () => {
it('API调用失败时应该正确处理错误', async () => {
(getSceneList as jest.Mock).mockRejectedValue(new Error('网络错误'));
await expect(getSceneList({ projectId: 'project1' })).rejects.toThrow('网络错误');
});
it('API返回失败状态时应该正确处理', async () => {
(getSceneList as jest.Mock).mockResolvedValue({
successful: false,
message: '服务器错误',
});
const result = await getSceneList({ projectId: 'project1' });
expect(result.successful).toBe(false);
expect(result.message).toBe('服务器错误');
});
it('UseCase未初始化时应该抛出相应错误', async () => {
const sceneItem = new SceneItem(mockSceneEntity);
const useCase = new SceneEditUseCase(sceneItem);
// 模拟UseCase未初始化的情况
mockSceneEditUseCase.AIgenerateScene.mockRejectedValue(new Error('场景编辑UseCase未初始化'));
await expect(useCase.AIgenerateScene({} as any, [])).rejects.toThrow('场景编辑UseCase未初始化');
});
});
describe('场景数据完整性测试', () => {
it('应该验证场景实体的完整性', () => {
const sceneItem = new SceneItem(mockSceneEntity);
expect(sceneItem.entity.id).toBe('scene1');
expect(sceneItem.entity.name).toBe('测试场景');
expect(sceneItem.entity.imageUrl).toBe('http://example.com/scene1.jpg');
expect(sceneItem.entity.tagIds).toEqual(['tag1', 'tag2']);
expect(sceneItem.entity.generateTextId).toBe('text1');
});
it('应该验证文本实体的完整性', () => {
const textItem = new TextItem(mockTextEntity);
expect(textItem.entity.id).toBe('text1');
expect(textItem.entity.content).toBe('这是AI生成的场景文本内容');
});
it('应该验证标签实体的完整性', () => {
const tagItem = new TagItem(mockTagEntity1);
expect(tagItem.entity.id).toBe('tag1');
expect(tagItem.entity.name).toBe('场景标签1');
expect(tagItem.entity.content).toBe('场景标签内容1');
});
});
});