test(server): 添加 server 模块单元测试

- 新增 vitest 配置和测试基础设施
- 添加 adapter.test.ts: 测试 Core 模块初始化和 Agent 管理 (18 tests)
- 添加 token.test.ts: 测试 Token 生成、验证和中间件 (25 tests)
- 添加 handler.test.ts: 测试权限处理器 (18 tests)
- 添加 ws.test.ts: 测试 WebSocket 连接和消息处理 (19 tests)
- 添加 sse.test.ts: 测试 SSE 事件发送 (14 tests)
- 添加 sessions.test.ts: 测试会话路由 (16 tests)
- 添加 config.test.ts: 测试配置路由 (10 tests)
- 添加 context.test.ts: 测试上下文压缩路由 (9 tests)
- 添加 providers.test.ts: 测试 Provider 管理路由 (18 tests)
- 添加 manager.test.ts: 测试 SessionManager (48 tests)

总计 195 个测试,覆盖率从 0% 提升至 29.59%
This commit is contained in:
2025-12-15 00:07:32 +08:00
parent 503e4c4ccd
commit 5835799b69
17 changed files with 3258 additions and 3 deletions
@@ -0,0 +1,341 @@
/**
* Agent Adapter 测试
*
* 测试 initCore、getOrCreateAgent、配置错误处理等关键功能
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
createMockCoreModule,
createMockProviderRegistry,
createMockAgentRegistry,
createConfigurationErrorMock,
} from '../../mocks/core.mock.js';
// 由于 adapter.ts 使用动态 import,需要特殊处理
// 我们需要在每个测试中重新导入模块以确保 mock 生效
describe('Agent Adapter', () => {
// 存储原始模块状态
let originalCwd: typeof process.cwd;
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
originalCwd = process.cwd;
});
afterEach(() => {
process.cwd = originalCwd;
vi.restoreAllMocks();
});
describe('initCore - Core 模块初始化', () => {
it('成功加载 Core 模块时返回 true', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore } = await import('../../../src/agent/adapter.js');
const result = await initCore();
expect(result).toBe(true);
});
it('Core 模块缺少必要导出时返回 false', async () => {
const incompleteCore = {
Agent: undefined,
toolRegistry: undefined,
loadConfig: undefined,
};
vi.doMock('@ai-assistant/core', () => incompleteCore);
const { initCore } = await import('../../../src/agent/adapter.js');
const result = await initCore();
expect(result).toBe(false);
});
it('Core 模块加载失败时返回 false', async () => {
vi.doMock('@ai-assistant/core', () => {
throw new Error('Module not found');
});
const { initCore } = await import('../../../src/agent/adapter.js');
const result = await initCore();
expect(result).toBe(false);
});
it('调用 ProviderRegistry.init()', async () => {
const mockProviderRegistry = createMockProviderRegistry({
isInitialized: vi.fn().mockReturnValue(false),
});
const mockCore = createMockCoreModule({
providerRegistry: mockProviderRegistry,
});
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore } = await import('../../../src/agent/adapter.js');
await initCore();
expect(mockCore.getProviderRegistry().init).toHaveBeenCalled();
});
it('ProviderRegistry 已初始化时不重复调用 init()', async () => {
const mockProviderRegistry = createMockProviderRegistry({
isInitialized: vi.fn().mockReturnValue(true),
});
const mockCore = createMockCoreModule({
providerRegistry: mockProviderRegistry,
});
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore } = await import('../../../src/agent/adapter.js');
await initCore();
expect(mockCore.getProviderRegistry().init).not.toHaveBeenCalled();
});
it('调用 AgentRegistry.init() 并传入 process.cwd()', async () => {
const testCwd = '/test/workdir';
process.cwd = vi.fn().mockReturnValue(testCwd);
const mockAgentRegistry = createMockAgentRegistry({
isInitialized: vi.fn().mockReturnValue(false),
});
const mockCore = createMockCoreModule({
agentRegistry: mockAgentRegistry,
});
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore } = await import('../../../src/agent/adapter.js');
await initCore();
expect(mockCore.agentRegistry.init).toHaveBeenCalledWith(testCwd);
});
it('AgentRegistry 已初始化时不重复调用 init()', async () => {
const mockAgentRegistry = createMockAgentRegistry({
isInitialized: vi.fn().mockReturnValue(true),
});
const mockCore = createMockCoreModule({
agentRegistry: mockAgentRegistry,
});
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore } = await import('../../../src/agent/adapter.js');
await initCore();
expect(mockCore.agentRegistry.init).not.toHaveBeenCalled();
});
});
describe('isCoreAvailable - Core 模块可用性检查', () => {
it('Core 未初始化时返回 false', async () => {
vi.resetModules();
const { isCoreAvailable } = await import('../../../src/agent/adapter.js');
expect(isCoreAvailable()).toBe(false);
});
it('Core 初始化后返回 true', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore, isCoreAvailable } = await import('../../../src/agent/adapter.js');
await initCore();
expect(isCoreAvailable()).toBe(true);
});
});
describe('getOrCreateAgent - Agent 创建与缓存', () => {
it('Core 未初始化时返回 null', async () => {
vi.resetModules();
const { getOrCreateAgent } = await import('../../../src/agent/adapter.js');
const agent = getOrCreateAgent('session-1');
expect(agent).toBeNull();
});
it('成功创建 Agent 实例', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
// Mock session manager
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
get: vi.fn().mockReturnValue({ id: 'session-1' }),
}),
}));
// Mock permission handler
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const { initCore, getOrCreateAgent } = await import('../../../src/agent/adapter.js');
await initCore();
const agent = getOrCreateAgent('session-1');
expect(agent).not.toBeNull();
expect(mockCore.Agent).toHaveBeenCalled();
});
it('重复获取同一 session 返回缓存的 Agent', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
}),
}));
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const { initCore, getOrCreateAgent } = await import('../../../src/agent/adapter.js');
await initCore();
const agent1 = getOrCreateAgent('session-1');
const agent2 = getOrCreateAgent('session-1');
expect(agent1).toBe(agent2);
// Agent 构造函数只应被调用一次
expect(mockCore.Agent).toHaveBeenCalledTimes(1);
});
it('ConfigurationError 时返回 null', async () => {
const mockCore = createMockCoreModule({
loadConfig: createConfigurationErrorMock('未配置 API Key', 'deepseek'),
});
vi.doMock('@ai-assistant/core', () => mockCore);
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
}),
}));
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const { initCore, getOrCreateAgent } = await import('../../../src/agent/adapter.js');
await initCore();
const agent = getOrCreateAgent('session-1');
expect(agent).toBeNull();
});
it('非 ConfigurationError 时重新抛出', async () => {
const mockCore = createMockCoreModule({
loadConfig: vi.fn().mockImplementation(() => {
throw new Error('Other error');
}),
});
vi.doMock('@ai-assistant/core', () => mockCore);
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
}),
}));
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const { initCore, getOrCreateAgent } = await import('../../../src/agent/adapter.js');
await initCore();
expect(() => getOrCreateAgent('session-1')).toThrow('Other error');
});
});
describe('destroyAgent - Agent 销毁', () => {
it('从缓存中移除 Agent', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
}),
}));
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const { initCore, getOrCreateAgent, destroyAgent } = await import('../../../src/agent/adapter.js');
await initCore();
// 创建 Agent
getOrCreateAgent('session-1');
expect(mockCore.Agent).toHaveBeenCalledTimes(1);
// 销毁 Agent
destroyAgent('session-1');
// 再次获取应创建新 Agent
getOrCreateAgent('session-1');
expect(mockCore.Agent).toHaveBeenCalledTimes(2);
});
});
describe('getAgentStats - Agent 统计信息', () => {
it('Core 未初始化时返回 available: false', async () => {
vi.resetModules();
const { getAgentStats } = await import('../../../src/agent/adapter.js');
const stats = getAgentStats('session-1');
expect(stats).toEqual({ available: false });
});
it('Agent 不存在时返回 available: true 但无其他信息', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore, getAgentStats } = await import('../../../src/agent/adapter.js');
await initCore();
const stats = getAgentStats('non-existent-session');
expect(stats).toEqual({ available: true });
});
it('Agent 存在时返回完整统计信息', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
}),
}));
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const { initCore, getOrCreateAgent, getAgentStats } = await import('../../../src/agent/adapter.js');
await initCore();
// 创建 Agent
getOrCreateAgent('session-1');
const stats = getAgentStats('session-1');
expect(stats.available).toBe(true);
expect(stats.toolCount).toEqual({ core: 5, discovered: 0, total: 5 });
expect(stats.contextUsage).toBe('10k / 200k');
});
});
});