test(server): 补充 checkpoints/mcp/sse/adapter 测试

- 新增 checkpoints.test.ts: 29 个测试覆盖 Checkpoint 管理 API
- 新增 mcp.test.ts: 15 个测试覆盖 MCP 服务器管理 API
- 扩展 sse.test.ts: 添加 handleSSE 路由测试
- 扩展 adapter.test.ts: 添加 cancelProcessing, getContextUsage,
  compressContext, processMessage 测试

覆盖率提升: 60% -> 80.82%
测试数量: 288 -> 344
This commit is contained in:
2025-12-15 00:32:04 +08:00
parent 67e129fce2
commit 7bc4f006a0
4 changed files with 1165 additions and 4 deletions
@@ -338,4 +338,221 @@ describe('Agent Adapter', () => {
expect(stats.contextUsage).toBe('10k / 200k');
});
});
describe('cancelProcessing - 取消处理', () => {
it('更新会话状态为 idle', async () => {
const mockSessionManager = {
exists: vi.fn().mockReturnValue(true),
updateStatus: vi.fn(),
};
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue(mockSessionManager),
}));
vi.doMock('../../../src/sse.js', () => ({
emitStatusEvent: vi.fn(),
emitLogEvent: vi.fn(),
}));
const { cancelProcessing } = await import('../../../src/agent/adapter.js');
cancelProcessing('session-1');
expect(mockSessionManager.updateStatus).toHaveBeenCalledWith('session-1', 'idle');
});
});
describe('getContextUsage - 获取上下文使用情况', () => {
it('Core 未初始化时返回 null', async () => {
vi.resetModules();
const { getContextUsage } = await import('../../../src/agent/adapter.js');
const usage = getContextUsage('session-1');
expect(usage).toBeNull();
});
it('Agent 不存在时返回 null', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore, getContextUsage } = await import('../../../src/agent/adapter.js');
await initCore();
const usage = getContextUsage('non-existent-session');
expect(usage).toBeNull();
});
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, getContextUsage } = await import('../../../src/agent/adapter.js');
await initCore();
getOrCreateAgent('session-1');
const usage = getContextUsage('session-1');
expect(usage).not.toBeNull();
expect(usage?.formatted).toBe('10k / 200k');
expect(usage?.input).toBe(10000);
});
});
describe('compressContext - 上下文压缩', () => {
it('Core 未初始化时返回 null', async () => {
vi.resetModules();
const { compressContext } = await import('../../../src/agent/adapter.js');
const result = await compressContext('session-1');
expect(result).toBeNull();
});
it('Agent 不存在时返回 null', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
const { initCore, compressContext } = await import('../../../src/agent/adapter.js');
await initCore();
const result = await compressContext('non-existent-session');
expect(result).toBeNull();
});
it('压缩成功时返回结果', 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, compressContext } = await import('../../../src/agent/adapter.js');
await initCore();
getOrCreateAgent('session-1');
const result = await compressContext('session-1');
expect(result).not.toBeNull();
expect(result?.type).toBe('prune');
expect(result?.freedTokens).toBe(1000);
});
it('压缩失败时返回错误信息', async () => {
const mockCore = createMockCoreModule({
agent: {
compactHistory: vi.fn().mockRejectedValue(new Error('Compression failed')),
},
});
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, compressContext } = await import('../../../src/agent/adapter.js');
await initCore();
getOrCreateAgent('session-1');
const result = await compressContext('session-1');
expect(result).not.toBeNull();
expect(result?.status).toBe('failed_error');
expect(result?.error).toBe('Compression failed');
});
});
describe('processMessage - 消息处理', () => {
it('Agent 不可用时发送占位消息', async () => {
const mockSessionManager = {
exists: vi.fn().mockReturnValue(true),
updateStatus: vi.fn(),
addMessage: vi.fn().mockResolvedValue({ id: 'msg-1', role: 'assistant', content: 'placeholder' }),
};
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue(mockSessionManager),
}));
const mockBroadcast = vi.fn();
vi.doMock('../../../src/ws.js', () => ({
broadcastToSession: mockBroadcast,
}));
vi.doMock('../../../src/sse.js', () => ({
emitStatusEvent: vi.fn(),
emitLogEvent: vi.fn(),
}));
// 不初始化 core,使 agent 为 null
vi.resetModules();
const { processMessage } = await import('../../../src/agent/adapter.js');
await processMessage('session-1', 'Hello');
// 应该发送占位消息
expect(mockBroadcast).toHaveBeenCalledWith('session-1', expect.objectContaining({
type: 'chunk',
}));
});
it('成功处理消息并返回响应', async () => {
const mockCore = createMockCoreModule();
vi.doMock('@ai-assistant/core', () => mockCore);
const mockSessionManager = {
exists: vi.fn().mockReturnValue(true),
get: vi.fn().mockReturnValue({ id: 'session-1', name: 'Test' }),
updateStatus: vi.fn(),
addMessage: vi.fn().mockResolvedValue({ id: 'msg-1', role: 'assistant', content: 'response' }),
getMessages: vi.fn().mockReturnValue([]),
};
vi.doMock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue(mockSessionManager),
}));
vi.doMock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
}));
const mockBroadcast = vi.fn();
vi.doMock('../../../src/ws.js', () => ({
broadcastToSession: mockBroadcast,
}));
vi.doMock('../../../src/sse.js', () => ({
emitStatusEvent: vi.fn(),
emitLogEvent: vi.fn(),
}));
const { initCore, processMessage } = await import('../../../src/agent/adapter.js');
await initCore();
await processMessage('session-1', 'Hello');
// 应该发送 done 消息
expect(mockBroadcast).toHaveBeenCalledWith('session-1', expect.objectContaining({
type: 'done',
}));
});
});
});