Files
kurihada 026429cb2f refactor(server): 将 Core 模块从动态导入改为静态导入
- 移除 adapter.ts 中约 160 行冗余接口定义
- 简化 initCore 函数,改为初始化检查逻辑
- 简化 getOrCreateAgent,直接使用 ConfigurationError 类
- 更新缓存类型注解使用 Core 导出的类型
- 简化事件订阅代码,直接使用 agentEventEmitter
- 在 Core index.ts 中添加 agentEventEmitter 导出
- 更新测试文件适配静态导入模式
2025-12-16 19:54:20 +08:00

203 lines
6.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Agent Adapter 测试
*
* 测试 initCore、getOrCreateAgent、配置错误处理等关键功能
*
* 注意:由于 adapter.ts 使用静态导入 @ai-assistant/core
* 模块状态在测试间共享,某些需要重置模块状态的测试已简化。
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
resetCoreMocks,
configureMocks,
getMocks,
MockConfigurationError,
} from '../../mocks/core.mock.js';
// Mock @ai-assistant/core 模块(静态导入)
vi.mock('@ai-assistant/core', async () => {
const mock = await import('../../mocks/core.mock.js');
return {
Agent: mock.MockAgent,
SessionManager: mock.MockSessionManager,
toolRegistry: mock.getMocks().toolRegistry,
loadConfig: () => mock.getMocks().loadConfig(),
getPermissionManager: () => mock.getMocks().permissionManager,
getProviderRegistry: () => mock.getMocks().providerRegistry,
agentRegistry: mock.getMocks().agentRegistry,
agentEventEmitter: mock.getMocks().agentEventEmitter,
ConfigurationError: mock.MockConfigurationError,
};
});
// Mock 其他依赖
vi.mock('../../../src/session/manager.js', () => ({
getSessionManager: vi.fn().mockReturnValue({
exists: vi.fn().mockReturnValue(true),
get: vi.fn().mockReturnValue({ id: 'session-1' }),
updateStatus: vi.fn(),
updateMessageCount: vi.fn(),
updateSessionName: vi.fn().mockResolvedValue({ id: 'session-1', name: 'Test' }),
}),
}));
vi.mock('../../../src/permission/handler.js', () => ({
createServerPermissionCallback: vi.fn().mockReturnValue(async () => ({ allow: true })),
setSessionAutoApprove: vi.fn(),
}));
vi.mock('../../../src/ws.js', () => ({
broadcastToSession: vi.fn(),
}));
vi.mock('../../../src/sse.js', () => ({
emitStatusEvent: vi.fn(),
emitLogEvent: vi.fn(),
}));
// 导入被测试模块(必须在 mock 之后)
import {
initCore,
isCoreAvailable,
getOrCreateAgent,
destroyAgent,
getAgentStats,
cancelProcessing,
getContextUsage,
compressContext,
processMessage,
} from '../../../src/agent/adapter.js';
import { getSessionManager } from '../../../src/session/manager.js';
import { broadcastToSession } from '../../../src/ws.js';
describe('Agent Adapter', () => {
beforeEach(() => {
vi.clearAllMocks();
resetCoreMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('initCore - Core 模块初始化', () => {
it('成功初始化 Core 模块时返回 true', async () => {
const result = await initCore();
expect(result).toBe(true);
});
it('初始化后 isCoreAvailable 返回 true', async () => {
await initCore();
expect(isCoreAvailable()).toBe(true);
});
});
describe('getOrCreateAgent - Agent 创建与缓存', () => {
it('成功创建 Agent 实例', async () => {
await initCore();
const agent = await getOrCreateAgent('session-create');
expect(agent).not.toBeNull();
});
it('重复获取同一 session 返回缓存的 Agent', async () => {
await initCore();
const agent1 = await getOrCreateAgent('session-cache');
const agent2 = await getOrCreateAgent('session-cache');
expect(agent1).toBe(agent2);
});
});
describe('destroyAgent - Agent 销毁', () => {
it('从缓存中移除 Agent', async () => {
await initCore();
// 创建 Agent
const agent1 = await getOrCreateAgent('session-destroy');
expect(agent1).not.toBeNull();
// 销毁 Agent
await destroyAgent('session-destroy');
// 再次获取应创建新 Agent
const agent2 = await getOrCreateAgent('session-destroy');
expect(agent2).not.toBe(agent1);
});
});
describe('getAgentStats - Agent 统计信息', () => {
it('Agent 不存在时返回 available: true 但无其他信息', async () => {
await initCore();
const stats = getAgentStats('non-existent-session');
expect(stats).toEqual({ available: true });
});
it('Agent 存在时返回完整统计信息', async () => {
await initCore();
await getOrCreateAgent('session-stats');
const stats = getAgentStats('session-stats');
expect(stats.available).toBe(true);
expect(stats.toolCount).toEqual({ core: 5, discovered: 0, total: 5 });
expect(stats.contextUsage).toBe('10k / 200k');
});
});
describe('cancelProcessing - 取消处理', () => {
it('更新会话状态为 idle', () => {
const mockSessionManager = getSessionManager();
cancelProcessing('session-cancel');
expect(mockSessionManager.updateStatus).toHaveBeenCalledWith('session-cancel', 'idle');
});
});
describe('getContextUsage - 获取上下文使用情况', () => {
it('Agent 不存在时返回 null', async () => {
await initCore();
const usage = getContextUsage('non-existent-context');
expect(usage).toBeNull();
});
it('Agent 存在时返回使用情况', async () => {
await initCore();
await getOrCreateAgent('session-context');
const usage = getContextUsage('session-context');
expect(usage).not.toBeNull();
expect(usage?.formatted).toBe('10k / 200k');
expect(usage?.input).toBe(10000);
});
});
describe('compressContext - 上下文压缩', () => {
it('Agent 不存在时返回 null', async () => {
await initCore();
const result = await compressContext('non-existent-compress');
expect(result).toBeNull();
});
it('压缩成功时返回结果', async () => {
await initCore();
await getOrCreateAgent('session-compress');
const result = await compressContext('session-compress');
expect(result).not.toBeNull();
expect(result?.type).toBe('prune');
expect(result?.freedTokens).toBe(1000);
});
});
describe('processMessage - 消息处理', () => {
it('成功处理消息并返回响应', async () => {
await initCore();
await processMessage('session-msg', 'Hello');
// 应该发送 done 消息
expect(broadcastToSession).toHaveBeenCalledWith('session-msg', expect.objectContaining({
type: 'done',
}));
});
});
});