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:
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Context Route 测试
|
||||
*
|
||||
* 测试上下文压缩 REST API 端点
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { Hono } from 'hono';
|
||||
|
||||
// Use vi.hoisted to create mocks before vi.mock is hoisted
|
||||
const { mockExists, mockGetContextUsage, mockCompressContext } = vi.hoisted(() => ({
|
||||
mockExists: vi.fn(),
|
||||
mockGetContextUsage: vi.fn(),
|
||||
mockCompressContext: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/session/manager.js', () => ({
|
||||
getSessionManager: vi.fn(() => ({
|
||||
exists: mockExists,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/agent/adapter.js', () => ({
|
||||
getContextUsage: mockGetContextUsage,
|
||||
compressContext: mockCompressContext,
|
||||
}));
|
||||
|
||||
import { contextRouter } from '../../../src/routes/context.js';
|
||||
|
||||
// Create test app
|
||||
const app = new Hono();
|
||||
app.route('', contextRouter);
|
||||
|
||||
describe('Context Route', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET /sessions/:id/context - 获取上下文使用情况', () => {
|
||||
it('返回上下文使用信息', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockGetContextUsage.mockReturnValue({
|
||||
input: 50000,
|
||||
contextLimit: 200000,
|
||||
available: 150000,
|
||||
usagePercent: 25,
|
||||
formatted: '50K/150K (25%)',
|
||||
shouldCompress: false,
|
||||
});
|
||||
|
||||
const res = await app.request('/sessions/session-1/context');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(json.data.input).toBe(50000);
|
||||
expect(json.data.usagePercent).toBe(25);
|
||||
});
|
||||
|
||||
it('会话不存在返回 404', async () => {
|
||||
mockExists.mockReturnValue(false);
|
||||
|
||||
const res = await app.request('/sessions/non-existent/context');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(json.success).toBe(false);
|
||||
expect(json.error).toBe('Session not found');
|
||||
});
|
||||
|
||||
it('Agent 未初始化时返回默认值', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockGetContextUsage.mockReturnValue(null);
|
||||
|
||||
const res = await app.request('/sessions/session-1/context');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(json.data.input).toBe(0);
|
||||
expect(json.data.usagePercent).toBe(0);
|
||||
expect(json.data.shouldCompress).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /sessions/:id/compress - 触发手动压缩', () => {
|
||||
it('成功压缩上下文', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockCompressContext.mockResolvedValue({
|
||||
type: 'summarize',
|
||||
freedTokens: 10000,
|
||||
newTokenCount: 40000,
|
||||
});
|
||||
|
||||
const res = await app.request('/sessions/session-1/compress', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ force: false }),
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(json.data.type).toBe('summarize');
|
||||
expect(json.data.freedTokens).toBe(10000);
|
||||
});
|
||||
|
||||
it('强制压缩', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockCompressContext.mockResolvedValue({
|
||||
type: 'summarize',
|
||||
freedTokens: 20000,
|
||||
newTokenCount: 30000,
|
||||
});
|
||||
|
||||
const res = await app.request('/sessions/session-1/compress', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ force: true }),
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(mockCompressContext).toHaveBeenCalledWith('session-1', true);
|
||||
});
|
||||
|
||||
it('无请求体时默认不强制压缩', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockCompressContext.mockResolvedValue({
|
||||
type: 'prune',
|
||||
freedTokens: 5000,
|
||||
});
|
||||
|
||||
const res = await app.request('/sessions/session-1/compress', {
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(mockCompressContext).toHaveBeenCalledWith('session-1', false);
|
||||
});
|
||||
|
||||
it('会话不存在返回 404', async () => {
|
||||
mockExists.mockReturnValue(false);
|
||||
|
||||
const res = await app.request('/sessions/non-existent/compress', {
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(json.success).toBe(false);
|
||||
expect(json.error).toBe('Session not found');
|
||||
});
|
||||
|
||||
it('Agent 未初始化返回 400', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockCompressContext.mockResolvedValue(null);
|
||||
|
||||
const res = await app.request('/sessions/session-1/compress', {
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(json.success).toBe(false);
|
||||
expect(json.error).toBe('Agent not initialized for this session');
|
||||
});
|
||||
|
||||
it('压缩失败返回 500', async () => {
|
||||
mockExists.mockReturnValue(true);
|
||||
mockCompressContext.mockRejectedValue(new Error('Compression failed'));
|
||||
|
||||
const res = await app.request('/sessions/session-1/compress', {
|
||||
method: 'POST',
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(500);
|
||||
expect(json.success).toBe(false);
|
||||
expect(json.error).toBe('Compression failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user