/** * 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'); }); }); });