import { describe, it, expect, beforeEach, vi } from 'vitest'; // 可变状态 let mockExecuteResult = { success: true, text: '任务完成', steps: 3, }; let mockVisionConfig: { provider: string; apiKey: string; model: string; baseUrl?: string } | null = null; let mockRunInBackgroundResult = 'agent-123'; // Mock loadVisionConfig vi.mock('../../../../src/utils/config.js', () => ({ loadVisionConfig: () => mockVisionConfig, })); // Mock agent manager vi.mock('../../../../src/agent/manager.js', () => ({ getAgentManager: () => ({ runInBackground: vi.fn(async () => mockRunInBackgroundResult), }), })); // Mock agent registry 和 AgentExecutor vi.mock('../../../../src/agent/index.js', () => ({ agentRegistry: { listSubagents: vi.fn(() => [ { name: 'explore', description: '代码探索', mode: 'subagent' }, { name: 'vision', description: '图片分析', mode: 'subagent' }, ]), get: vi.fn(), }, AgentExecutor: class { execute() { return Promise.resolve(mockExecuteResult); } }, })); // Mock tool registry vi.mock('../../../../src/tools/registry.js', () => ({ toolRegistry: {}, })); // Mock session manager vi.mock('../../../../src/session/index.js', () => ({ SessionManager: vi.fn(), })); import { taskTool, initTaskContext, getTaskContext } from '../../../../src/tools/task/task.js'; import { agentRegistry } from '../../../../src/agent/index.js'; describe('taskTool - 扩展测试', () => { const createMockSession = () => ({ getSessionId: vi.fn(() => 'parent-session'), createChildSession: vi.fn(() => ({ id: 'child-session', messages: [], })), saveChildSession: vi.fn(), }); beforeEach(() => { vi.clearAllMocks(); initTaskContext(null as any, null as any); mockExecuteResult = { success: true, text: '任务完成', steps: 3, }; mockVisionConfig = null; mockRunInBackgroundResult = 'agent-123'; }); describe('getTaskContext', () => { it('返回上下文', () => { initTaskContext({ model: 'test' } as any, { sessionManager: true } as any); expect(getTaskContext()).not.toBeNull(); }); it('初始化 null 时返回包含 null 的对象', () => { initTaskContext(null as any, null as any); const ctx = getTaskContext(); expect(ctx?.baseConfig).toBeNull(); expect(ctx?.sessionManager).toBeNull(); }); }); describe('Vision Agent 处理', () => { it('Vision Agent 无配置时返回错误', async () => { mockVisionConfig = null; const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'vision', description: '图片分析 Agent', mode: 'subagent', prompt: '你是视觉助手', }); const result = await taskTool.execute({ description: 'analyze image', prompt: '分析图片', subagent_type: 'vision', }); expect(result.success).toBe(false); expect(result.error).toContain('Vision Agent 需要配置'); }); it('Vision Agent 有配置时成功执行', async () => { mockVisionConfig = { provider: 'openai', apiKey: 'vision-key', model: 'gpt-4o', }; const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'vision', description: '图片分析 Agent', mode: 'subagent', prompt: '你是视觉助手', }); const result = await taskTool.execute({ description: 'analyze image', prompt: '分析图片', subagent_type: 'vision', }); expect(result.success).toBe(true); }); }); describe('模型选择', () => { it('无效模型选择返回错误', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', model: 'invalid-model', }); expect(result.success).toBe(false); expect(result.error).toContain('无效的模型选择'); expect(result.error).toContain('sonnet, opus, haiku'); }); it('sonnet 模型选择有效', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'original-model' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', model: 'sonnet', }); expect(result.success).toBe(true); }); it('opus 模型选择有效', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'original-model' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', model: 'opus', }); expect(result.success).toBe(true); }); it('haiku 模型选择有效', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'original-model' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', model: 'haiku', }); expect(result.success).toBe(true); }); }); describe('后台运行模式', () => { it('后台运行返回 agentId', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'background task', prompt: 'do something', subagent_type: 'explore', run_in_background: true, }); expect(result.success).toBe(true); expect(result.output).toContain('agent-123'); expect(result.output).toContain('后台启动'); expect(result.metadata?.mode).toBe('background'); }); it('sessionId 不存在时使用 standalone', async () => { const mockSession = { getSessionId: vi.fn(() => null), createChildSession: vi.fn(() => ({ id: 'child-session', messages: [], })), saveChildSession: vi.fn(), }; initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'background task', prompt: 'do something', subagent_type: 'explore', run_in_background: true, }); expect(result.success).toBe(true); }); }); describe('同步执行模式元数据', () => { it('同步模式返回正确 metadata', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', }); expect(result.metadata?.mode).toBe('sync'); }); it('失败时也返回 metadata', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); mockExecuteResult = { success: false, text: '', steps: 1, }; const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', }); expect(result.success).toBe(false); expect(result.metadata).toBeDefined(); expect(result.metadata?.mode).toBe('sync'); }); it('失败时没有 error 消息时使用默认消息', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); mockExecuteResult = { success: false, text: '', steps: 1, // 没有 error 字段 } as any; const result = await taskTool.execute({ description: 'test', prompt: 'test', subagent_type: 'explore', }); expect(result.success).toBe(false); expect(result.error).toBe('子任务执行失败'); }); }); describe('图片传递', () => { it('同步模式传递图片', async () => { const mockSession = createMockSession(); initTaskContext({ model: 'test' } as any, mockSession as any); vi.mocked(agentRegistry.get).mockReturnValue({ name: 'explore', description: '探索 Agent', mode: 'subagent', prompt: '你是探索助手', }); const result = await taskTool.execute({ description: 'analyze images', prompt: 'describe these', subagent_type: 'explore', images: [{ data: 'base64data', mimeType: 'image/png' }], }); expect(result.success).toBe(true); }); }); });