Details (original message): Implement the three-column workbench frontend: resizable/collapsible nav with overshorthand collapse, streaming chat with part renderers and process collapse, HITL choice cards with impact display and free-input pivot, plan/todos/artifacts/agents tabs, subagent tracker modal, schema-driven settings center with effect badges and masked secrets, profile management with import/export, plugin panel rendering, doc viewer with edit-save; contract shape tests.
71 lines
3.4 KiB
TypeScript
71 lines
3.4 KiB
TypeScript
// 契约形状测试:前端类型对黄金样本的运行时断言(三态一致的第三态验证)。
|
|
import { describe, expect, it } from 'vitest'
|
|
import type { Message, Part, StreamEvent, HITLCard, AgentProfile } from '../src/contracts'
|
|
|
|
const goldenMessage = {
|
|
id: 'msg_abc123def456', conversationId: 'cnv_abc123def456', runId: 'run_abc123def456',
|
|
role: 'assistant',
|
|
parts: [
|
|
{ type: 'thinking', text: '考虑先检索…', process: true },
|
|
{ type: 'tool_call', process: true, toolCall: { id: 'call_1', name: 'history_search', arguments: '{"q":"部署"}', durationMs: 12.5 } },
|
|
{ type: 'tool_result', process: true, toolResult: { callId: 'call_1', status: 'ok', summary: '3 条命中' } },
|
|
{ type: 'text', text: '根据历史记录……' },
|
|
{ type: 'artifact_ref', artifact: { artifactId: 'art_abc123def456', name: '报告.md', kind: 'document' } },
|
|
],
|
|
}
|
|
|
|
describe('契约形状(Core 黄金样本)', () => {
|
|
it('message 判别联合按 type 解析', () => {
|
|
const m = goldenMessage as unknown as Message
|
|
expect(m.role).toBe('assistant')
|
|
expect(m.parts.length).toBe(5)
|
|
const parts = m.parts as Part[]
|
|
expect(parts[1].toolCall?.name).toBe('history_search')
|
|
expect(parts[2].toolResult?.status).toBe('ok')
|
|
expect(parts[3].text).toContain('历史')
|
|
expect(parts[0].process).toBe(true)
|
|
})
|
|
|
|
it('stream event 信封字段', () => {
|
|
const e = JSON.parse(`{"seq":42,"runId":"run_1","conversationId":"cnv_1","ts":"2026-09-29T10:00:00Z","v":1,"type":"message.delta","payload":{"index":0,"text":"你"}}`) as StreamEvent
|
|
expect(e.seq).toBe(42)
|
|
expect(e.type).toBe('message.delta')
|
|
expect((e.payload as { text: string }).text).toBe('你')
|
|
})
|
|
|
|
it('hitl 卡片:选项含 impact(决策上下文契约)', () => {
|
|
const c = JSON.parse(`{"id":"hitl_1","runId":"run_1","conversationId":"cnv_1","question":"格式?","kind":"choice","choices":[{"id":"md","label":"Markdown 文档","description":"可版本管理","impact":"写入 artifacts","recommended":true}],"allowFreeInput":true,"createdAt":"2026-09-29T10:00:00Z","resolved":false}`) as HITLCard
|
|
expect(c.choices?.[0].impact).toBeTruthy()
|
|
expect(c.kind).toBe('choice')
|
|
})
|
|
|
|
it('profile 工具三清单', () => {
|
|
const p = JSON.parse(`{"id":"default","name":"通用助手","builtin":true,"source":"builtin","systemPrompt":"x","model":{},"tools":{"allow":["*"],"deny":["http.admin."],"confirm":["http."]},"context":{"maxHistoryMessages":40},"hitl":{"askOnAmbiguity":true},"budget":{}}`) as AgentProfile
|
|
expect(p.tools.allow).toEqual(['*'])
|
|
expect(p.tools.confirm).toEqual(['http.'])
|
|
})
|
|
})
|
|
|
|
// 事件应用逻辑的纯函数级验证(store 分支的样本驱动)。
|
|
describe('SSE 事件解析(api.ts handle 逻辑样本)', () => {
|
|
it('解析 data 帧并推进游标', () => {
|
|
let since = 0
|
|
const frames = [
|
|
'id: 41\nevent: message.delta\ndata: {"seq":41,"type":"message.delta","conversationId":"c1"}',
|
|
'id: 42\nevent: run.completed\ndata: {"seq":42,"type":"run.completed","conversationId":"c1"}',
|
|
]
|
|
const seen: string[] = []
|
|
for (const f of frames) {
|
|
for (const line of f.split('\n')) {
|
|
if (line.startsWith('data: ')) {
|
|
const e = JSON.parse(line.slice(6)) as StreamEvent
|
|
since = Math.max(since, e.seq)
|
|
seen.push(e.type)
|
|
}
|
|
}
|
|
}
|
|
expect(seen).toEqual(['message.delta', 'run.completed'])
|
|
expect(since).toBe(42)
|
|
})
|
|
})
|