5e32375f0e
架构变更: - 采用 pnpm workspaces 实现 Monorepo 结构 - 将现有代码迁移到 packages/core - 新增 packages/server HTTP 服务层 Server 功能: - REST API: 会话管理、工具管理、配置管理 - WebSocket: 实时双向通信支持 - SSE: 服务端事件推送 - Hono + Bun 作为运行时 API 端点: - GET/POST /api/sessions - 会话 CRUD - GET/POST /api/sessions/:id/messages - 消息管理 - GET /api/sessions/:id/events - SSE 事件流 - WS /api/ws/:sessionId - WebSocket 连接 - GET/POST /api/tools - 工具管理 - GET/PUT /api/config - 配置管理
251 lines
6.6 KiB
TypeScript
251 lines
6.6 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import {
|
|
CommandRegistry,
|
|
getCommandRegistry,
|
|
resetCommandRegistry,
|
|
} from '../../../src/commands/registry.js';
|
|
import type { Command } from '../../../src/commands/types.js';
|
|
|
|
// Mock loader
|
|
vi.mock('../../../src/commands/loader.js', () => ({
|
|
commandLoader: {
|
|
loadFromDirectory: vi.fn().mockResolvedValue([]),
|
|
getUserCommandsDir: vi.fn().mockReturnValue('/mock/user/commands'),
|
|
getProjectCommandsDir: vi.fn().mockReturnValue('/mock/project/commands'),
|
|
},
|
|
}));
|
|
|
|
// Mock builtin commands
|
|
vi.mock('../../../src/commands/builtin/index.js', () => ({
|
|
builtinCommands: [
|
|
{
|
|
name: 'test-builtin',
|
|
description: '内置测试命令',
|
|
template: '测试模板 $ARGUMENTS',
|
|
source: 'builtin',
|
|
},
|
|
],
|
|
}));
|
|
|
|
describe('CommandRegistry - Command 注册表', () => {
|
|
let registry: CommandRegistry;
|
|
|
|
beforeEach(() => {
|
|
resetCommandRegistry();
|
|
registry = new CommandRegistry();
|
|
});
|
|
|
|
describe('register - 注册', () => {
|
|
it('成功注册 Command', () => {
|
|
const command: Command = {
|
|
name: 'test',
|
|
description: '测试',
|
|
template: '模板',
|
|
source: 'user',
|
|
};
|
|
|
|
registry.register(command);
|
|
expect(registry.has('test')).toBe(true);
|
|
});
|
|
|
|
it('高优先级可以覆盖低优先级', () => {
|
|
const builtin: Command = {
|
|
name: 'same',
|
|
description: '内置版本',
|
|
template: '内置模板',
|
|
source: 'builtin',
|
|
};
|
|
|
|
const project: Command = {
|
|
name: 'same',
|
|
description: '项目版本',
|
|
template: '项目模板',
|
|
source: 'project',
|
|
};
|
|
|
|
registry.register(builtin);
|
|
registry.register(project);
|
|
|
|
const result = registry.get('same');
|
|
expect(result?.source).toBe('project');
|
|
expect(result?.description).toBe('项目版本');
|
|
});
|
|
|
|
it('低优先级不能覆盖高优先级', () => {
|
|
const project: Command = {
|
|
name: 'same',
|
|
description: '项目版本',
|
|
template: '项目模板',
|
|
source: 'project',
|
|
};
|
|
|
|
const builtin: Command = {
|
|
name: 'same',
|
|
description: '内置版本',
|
|
template: '内置模板',
|
|
source: 'builtin',
|
|
};
|
|
|
|
registry.register(project);
|
|
registry.register(builtin);
|
|
|
|
const result = registry.get('same');
|
|
expect(result?.source).toBe('project');
|
|
});
|
|
});
|
|
|
|
describe('get - 获取', () => {
|
|
it('获取存在的 Command', () => {
|
|
const command: Command = {
|
|
name: 'test',
|
|
description: '测试',
|
|
template: '模板',
|
|
source: 'user',
|
|
};
|
|
|
|
registry.register(command);
|
|
const result = registry.get('test');
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result?.name).toBe('test');
|
|
});
|
|
|
|
it('获取不存在的 Command 返回 undefined', () => {
|
|
const result = registry.get('non-existent');
|
|
expect(result).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('search - 搜索', () => {
|
|
beforeEach(() => {
|
|
const commands: Command[] = [
|
|
{
|
|
name: 'review',
|
|
description: '代码审查',
|
|
template: '审查模板',
|
|
source: 'builtin',
|
|
},
|
|
{
|
|
name: 'test',
|
|
description: '运行测试',
|
|
template: '测试模板',
|
|
source: 'builtin',
|
|
},
|
|
{
|
|
name: 'deploy/staging',
|
|
description: '部署到 staging',
|
|
template: '部署模板',
|
|
source: 'project',
|
|
},
|
|
];
|
|
|
|
for (const cmd of commands) {
|
|
registry.register(cmd);
|
|
}
|
|
});
|
|
|
|
it('按名称精确匹配', () => {
|
|
const results = registry.search('review');
|
|
expect(results.length).toBeGreaterThan(0);
|
|
expect(results[0].command.name).toBe('review');
|
|
expect(results[0].score).toBe(100);
|
|
});
|
|
|
|
it('按名称前缀匹配', () => {
|
|
const results = registry.search('deploy');
|
|
expect(results.length).toBeGreaterThan(0);
|
|
expect(results[0].command.name).toBe('deploy/staging');
|
|
});
|
|
|
|
it('按描述匹配', () => {
|
|
const results = registry.search('代码');
|
|
expect(results.length).toBeGreaterThan(0);
|
|
expect(results[0].command.name).toBe('review');
|
|
});
|
|
|
|
it('限制结果数量', () => {
|
|
const results = registry.search('', 1);
|
|
expect(results.length).toBeLessThanOrEqual(1);
|
|
});
|
|
});
|
|
|
|
describe('list - 列表', () => {
|
|
it('返回所有 Commands 的摘要', () => {
|
|
const command: Command = {
|
|
name: 'test',
|
|
description: '测试',
|
|
template: '模板',
|
|
source: 'user',
|
|
};
|
|
|
|
registry.register(command);
|
|
const list = registry.list();
|
|
|
|
expect(list.length).toBe(1);
|
|
expect(list[0].name).toBe('test');
|
|
expect(list[0].description).toBe('测试');
|
|
expect(list[0].source).toBe('user');
|
|
});
|
|
|
|
it('按名称排序', () => {
|
|
const commands: Command[] = [
|
|
{ name: 'zebra', description: '', template: '', source: 'user' },
|
|
{ name: 'alpha', description: '', template: '', source: 'user' },
|
|
{ name: 'beta', description: '', template: '', source: 'user' },
|
|
];
|
|
|
|
for (const cmd of commands) {
|
|
registry.register(cmd);
|
|
}
|
|
|
|
const list = registry.list();
|
|
expect(list[0].name).toBe('alpha');
|
|
expect(list[1].name).toBe('beta');
|
|
expect(list[2].name).toBe('zebra');
|
|
});
|
|
});
|
|
|
|
describe('getStats - 统计', () => {
|
|
it('返回正确的统计信息', () => {
|
|
const commands: Command[] = [
|
|
{ name: 'b1', description: '', template: '', source: 'builtin' },
|
|
{ name: 'b2', description: '', template: '', source: 'builtin' },
|
|
{ name: 'u1', description: '', template: '', source: 'user' },
|
|
{ name: 'p1', description: '', template: '', source: 'project' },
|
|
];
|
|
|
|
for (const cmd of commands) {
|
|
registry.register(cmd);
|
|
}
|
|
|
|
const stats = registry.getStats();
|
|
|
|
expect(stats.total).toBe(4);
|
|
expect(stats.bySource.builtin).toBe(2);
|
|
expect(stats.bySource.user).toBe(1);
|
|
expect(stats.bySource.project).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getCommandRegistry - 全局注册表', () => {
|
|
beforeEach(() => {
|
|
resetCommandRegistry();
|
|
});
|
|
|
|
it('返回单例实例', () => {
|
|
const registry1 = getCommandRegistry();
|
|
const registry2 = getCommandRegistry();
|
|
|
|
expect(registry1).toBe(registry2);
|
|
});
|
|
|
|
it('resetCommandRegistry 重置实例', () => {
|
|
const registry1 = getCommandRegistry();
|
|
resetCommandRegistry();
|
|
const registry2 = getCommandRegistry();
|
|
|
|
expect(registry1).not.toBe(registry2);
|
|
});
|
|
});
|