import { afterAll, beforeAll, describe, expect, it } from 'vitest' import pino from 'pino' import { createApp, type App } from '../src/app' import { API_EVENTS, API_GROUPS } from '../src/api-reference/reference' import { renderApiReferenceHtml } from '../src/api-reference/render' import { findChrome } from '../src/api-reference/pdf' import { buildServer } from '../src/http/server' import { registeredV1Routes } from '../src/http/routes/v1' import type { EventPayloads } from '@agent-server/shared' let app: App let server: Awaited> beforeAll(async () => { const log = pino({ level: 'silent' }) app = createApp(log, { providers: {} }) server = await buildServer(app, { logger: log }) }) afterAll(() => server.close()) describe('API 协议文档', () => { it('与实际注册的 /v1 路由完全一致(新增或删除接口时必须同步修改 reference.ts)', () => { const key = (m: string, p: string) => `${m} ${p}` const actual = new Set(registeredV1Routes.map(r => key(r.method, r.path))) const documented = new Set(API_GROUPS.flatMap(g => g.endpoints.map(e => key(e.method, e.path)))) expect([...actual].filter(x => !documented.has(x)), '未写入文档的接口').toEqual([]) expect([...documented].filter(x => !actual.has(x)), '文档中有、但实际不存在的接口').toEqual([]) }) it('事件类型与统一事件模型一致', () => { // EventPayloads 的键在类型层面,这里用一个完整的对象字面量确保编译期同步 const all: Record = { 'user.message': true, 'run.queued': true, 'run.started': true, 'message.delta': true, 'message.completed': true, 'reasoning.delta': true, 'reasoning.completed': true, 'tool.started': true, 'tool.completed': true, 'file.changed': true, 'interaction.required': true, 'interaction.resolved': true, usage: true, 'run.completed': true, 'run.failed': true, 'run.interrupted': true, } expect(API_EVENTS.map(e => e.type).sort()).toEqual(Object.keys(all).sort()) }) it('渲染:示例使用传入的服务地址;转义特殊字符;没有脚本', () => { const html = renderApiReferenceHtml({ baseUrl: 'https://agent.example.com/', theme: 'dark', version: 'v1', generatedAt: new Date('2026-10-23T00:00:00Z') }) expect(html).toContain('curl -X POST https://agent.example.com/v1/sessions') expect(html).not.toContain('$BASE') expect(html).toContain('data-theme="dark"') expect(html).toContain('<凭证>') expect(html).not.toMatch(/