729fb2d42a
- 新增 vitest 测试框架配置 - 添加 54 个测试文件,共 951 个测试用例 - 覆盖核心模块: - Agent: executor, registry, config-loader, permission-merger - Context: manager, compaction, prune, token-counter - Permission: manager, bash/file/git/web checkers, wildcard - Session: manager, storage - Tools: filesystem (12个), git (10个), web, shell, todo, task - LSP: client, server, language - Utils: config, diff - UI: terminal
351 lines
9.2 KiB
TypeScript
351 lines
9.2 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { AgentRegistry } from '../../../src/agent/registry.js';
|
|
import type { AgentInfo, AgentConfigFile } from '../../../src/agent/types.js';
|
|
|
|
// Mock config-loader
|
|
vi.mock('../../../src/agent/config-loader.js', () => ({
|
|
loadAgentConfig: vi.fn(),
|
|
}));
|
|
|
|
// Mock presets
|
|
vi.mock('../../../src/agent/presets/index.js', () => ({
|
|
presetAgents: {
|
|
explore: {
|
|
description: '代码探索 Agent',
|
|
mode: 'subagent' as const,
|
|
prompt: '你是代码探索助手',
|
|
maxSteps: 5,
|
|
},
|
|
'code-reviewer': {
|
|
description: '代码审查 Agent',
|
|
mode: 'subagent' as const,
|
|
prompt: '你是代码审查助手',
|
|
},
|
|
build: {
|
|
description: '构建 Agent',
|
|
mode: 'all' as const,
|
|
prompt: '你是构建助手',
|
|
},
|
|
},
|
|
}));
|
|
|
|
import { loadAgentConfig } from '../../../src/agent/config-loader.js';
|
|
|
|
describe('AgentRegistry - Agent 注册表', () => {
|
|
let registry: AgentRegistry;
|
|
|
|
beforeEach(() => {
|
|
registry = new AgentRegistry();
|
|
vi.clearAllMocks();
|
|
vi.mocked(loadAgentConfig).mockResolvedValue(null);
|
|
});
|
|
|
|
describe('init - 初始化', () => {
|
|
it('初始化后注册预设 Agent', async () => {
|
|
await registry.init('/test/project');
|
|
|
|
expect(registry.has('explore')).toBe(true);
|
|
expect(registry.has('code-reviewer')).toBe(true);
|
|
expect(registry.has('build')).toBe(true);
|
|
});
|
|
|
|
it('初始化加载用户配置', async () => {
|
|
const userConfig: AgentConfigFile = {
|
|
defaults: {
|
|
maxSteps: 20,
|
|
},
|
|
agents: {
|
|
'custom-agent': {
|
|
description: '自定义 Agent',
|
|
mode: 'subagent',
|
|
prompt: '你是自定义助手',
|
|
},
|
|
},
|
|
};
|
|
|
|
vi.mocked(loadAgentConfig).mockResolvedValue(userConfig);
|
|
|
|
await registry.init('/test/project');
|
|
|
|
expect(registry.has('custom-agent')).toBe(true);
|
|
});
|
|
|
|
it('重复初始化只执行一次', async () => {
|
|
await registry.init('/test/project');
|
|
await registry.init('/test/project');
|
|
|
|
expect(loadAgentConfig).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('get - 获取 Agent', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('获取存在的 Agent', () => {
|
|
const agent = registry.get('explore');
|
|
|
|
expect(agent).toBeDefined();
|
|
expect(agent?.name).toBe('explore');
|
|
expect(agent?.description).toBe('代码探索 Agent');
|
|
});
|
|
|
|
it('获取不存在的 Agent 返回 undefined', () => {
|
|
const agent = registry.get('non-existent');
|
|
|
|
expect(agent).toBeUndefined();
|
|
});
|
|
|
|
it('获取的 Agent 应用全局配置', async () => {
|
|
vi.mocked(loadAgentConfig).mockResolvedValue({
|
|
defaults: {
|
|
maxSteps: 25,
|
|
},
|
|
});
|
|
|
|
const newRegistry = new AgentRegistry();
|
|
await newRegistry.init('/test/project');
|
|
|
|
const agent = newRegistry.get('code-reviewer');
|
|
|
|
// code-reviewer 没有设置 maxSteps,应该使用全局默认值
|
|
expect(agent?.maxSteps).toBe(25);
|
|
});
|
|
|
|
it('Agent 自己的配置优先于全局配置', async () => {
|
|
vi.mocked(loadAgentConfig).mockResolvedValue({
|
|
defaults: {
|
|
maxSteps: 25,
|
|
},
|
|
});
|
|
|
|
const newRegistry = new AgentRegistry();
|
|
await newRegistry.init('/test/project');
|
|
|
|
const agent = newRegistry.get('explore');
|
|
|
|
// explore 设置了 maxSteps: 5
|
|
expect(agent?.maxSteps).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('list - 列出 Agent', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('列出所有 Agent', () => {
|
|
const agents = registry.list();
|
|
|
|
expect(agents.length).toBeGreaterThan(0);
|
|
expect(agents.some(a => a.name === 'explore')).toBe(true);
|
|
expect(agents.some(a => a.name === 'code-reviewer')).toBe(true);
|
|
});
|
|
|
|
it('按 mode 过滤 Agent', () => {
|
|
const subagents = registry.list('subagent');
|
|
|
|
expect(subagents.every(a => a.mode === 'subagent' || a.mode === 'all')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('listSubagents - 列出子 Agent', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('排除 primary-only 的 Agent', () => {
|
|
const subagents = registry.listSubagents();
|
|
|
|
expect(subagents.every(a => a.mode !== 'primary')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('listPrimaryAgents - 列出主 Agent', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('排除 subagent-only 的 Agent', () => {
|
|
const primaryAgents = registry.listPrimaryAgents();
|
|
|
|
expect(primaryAgents.every(a => a.mode !== 'subagent')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('register - 动态注册', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('注册新 Agent', () => {
|
|
const newAgent: AgentInfo = {
|
|
name: 'dynamic-agent',
|
|
description: '动态注册的 Agent',
|
|
mode: 'subagent',
|
|
prompt: '你是动态 Agent',
|
|
};
|
|
|
|
registry.register(newAgent);
|
|
|
|
expect(registry.has('dynamic-agent')).toBe(true);
|
|
expect(registry.get('dynamic-agent')?.description).toBe('动态注册的 Agent');
|
|
});
|
|
|
|
it('覆盖已有 Agent', () => {
|
|
const updatedAgent: AgentInfo = {
|
|
name: 'explore',
|
|
description: '更新后的探索 Agent',
|
|
mode: 'all',
|
|
prompt: '更新后的提示',
|
|
};
|
|
|
|
registry.register(updatedAgent);
|
|
|
|
const agent = registry.get('explore');
|
|
expect(agent?.description).toBe('更新后的探索 Agent');
|
|
});
|
|
});
|
|
|
|
describe('remove - 移除 Agent', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('移除存在的 Agent', () => {
|
|
const result = registry.remove('explore');
|
|
|
|
expect(result).toBe(true);
|
|
expect(registry.has('explore')).toBe(false);
|
|
});
|
|
|
|
it('移除不存在的 Agent 返回 false', () => {
|
|
const result = registry.remove('non-existent');
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('has - 检查 Agent 是否存在', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('存在的 Agent 返回 true', () => {
|
|
expect(registry.has('explore')).toBe(true);
|
|
});
|
|
|
|
it('不存在的 Agent 返回 false', () => {
|
|
expect(registry.has('non-existent')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('size - 获取 Agent 数量', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('返回正确的数量', () => {
|
|
expect(registry.size).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('添加后数量增加', () => {
|
|
const initialSize = registry.size;
|
|
|
|
registry.register({
|
|
name: 'new-agent',
|
|
description: 'New',
|
|
mode: 'subagent',
|
|
prompt: 'New agent',
|
|
});
|
|
|
|
expect(registry.size).toBe(initialSize + 1);
|
|
});
|
|
|
|
it('移除后数量减少', () => {
|
|
const initialSize = registry.size;
|
|
|
|
registry.remove('explore');
|
|
|
|
expect(registry.size).toBe(initialSize - 1);
|
|
});
|
|
});
|
|
|
|
describe('getNames - 获取所有 Agent 名称', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('返回所有 Agent 名称', () => {
|
|
const names = registry.getNames();
|
|
|
|
expect(names).toContain('explore');
|
|
expect(names).toContain('code-reviewer');
|
|
expect(names).toContain('build');
|
|
});
|
|
});
|
|
|
|
describe('getGlobalConfig - 获取全局配置', () => {
|
|
it('无用户配置时返回 null', async () => {
|
|
vi.mocked(loadAgentConfig).mockResolvedValue(null);
|
|
|
|
await registry.init('/test/project');
|
|
|
|
expect(registry.getGlobalConfig()).toBeNull();
|
|
});
|
|
|
|
it('有用户配置时返回 defaults', async () => {
|
|
vi.mocked(loadAgentConfig).mockResolvedValue({
|
|
defaults: {
|
|
maxSteps: 30,
|
|
model: {
|
|
temperature: 0.5,
|
|
},
|
|
},
|
|
});
|
|
|
|
const newRegistry = new AgentRegistry();
|
|
await newRegistry.init('/test/project');
|
|
|
|
const globalConfig = newRegistry.getGlobalConfig();
|
|
|
|
expect(globalConfig?.maxSteps).toBe(30);
|
|
expect(globalConfig?.model?.temperature).toBe(0.5);
|
|
});
|
|
});
|
|
|
|
describe('generateSubagentDescription - 生成子 Agent 描述', () => {
|
|
beforeEach(async () => {
|
|
await registry.init('/test/project');
|
|
});
|
|
|
|
it('生成包含所有子 Agent 的描述', () => {
|
|
const description = registry.generateSubagentDescription();
|
|
|
|
expect(description).toContain('explore');
|
|
expect(description).toContain('代码探索');
|
|
});
|
|
|
|
it('无子 Agent 时返回提示信息', async () => {
|
|
// 移除所有 Agent
|
|
for (const name of registry.getNames()) {
|
|
registry.remove(name);
|
|
}
|
|
|
|
const description = registry.generateSubagentDescription();
|
|
|
|
expect(description).toContain('没有可用');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('agentRegistry 单例', () => {
|
|
it('导出单例实例', async () => {
|
|
const { agentRegistry } = await import('../../../src/agent/registry.js');
|
|
|
|
expect(agentRegistry).toBeDefined();
|
|
expect(agentRegistry).toBeInstanceOf(AgentRegistry);
|
|
});
|
|
});
|