Files
Agent-System-Frontend/src/rdmAuditPresentation.test.ts
T
weichen 95e1948a5f Distinguish retired and stale trace relations
Why: Retired edges and compliance edges awaiting review both fell back to a generic lifecycle label.

Evidence: Presentation tests, production build, knip, and diff checks pass.

Impact: The trace network now shows 已停用 and 待复核 as distinct human-readable states.

Refs: readable trace network
2026-10-15 06:52:42 +00:00

122 lines
4.5 KiB
TypeScript
Raw 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.
import { describe, expect, it } from 'vitest'
import {
describeAuditEvent, describeTreeItem, humanizeKind, humanizeRelation, humanizeState,
isTreeItemExpandable, relationOtherEndpoint,
} from './rdmAuditPresentation'
describe('RDM audit presentation', () => {
it('turns a structured document event into a readable action and impact', () => {
expect(describeAuditEvent({
seq: 18,
at: '2026-10-15T08:00:00Z',
op: 'doc.instantiate',
actor: 'alice',
source: 'template:dhf-proposal@v2',
target: { kind: 'doc', id: 'doc-1', title: '项目建议书' },
detail: '{"raw":"must not be shown"}',
operationId: 'op-1',
disclosure: 'ok',
result: 'committed',
impact: '新增 12 个递归节点',
reason: '从已发布模板建立项目实例',
})).toEqual({
action: '创建文档实例',
subject: 'Alice',
object: '文档“项目建议书”',
outcome: '已生效',
reason: '从已发布模板建立项目实例',
impact: '新增 12 个递归节点',
source: '模板:DHF 项目建议书(版本 v2)',
})
})
it('does not expose unknown operation codes or raw JSON as user-facing text', () => {
const view = describeAuditEvent({
seq: 19,
at: '2026-10-15T08:01:00Z',
op: 'internal.weird_opcode',
actor: 'user-42',
source: 'host',
target: { kind: 'node', id: 'node-secret' },
detail: '{"secret":"raw payload"}',
operationId: 'op-2',
disclosure: 'restricted',
})
expect(view.action).toBe('更新项目内容')
expect(JSON.stringify(view)).not.toContain('internal.weird_opcode')
expect(JSON.stringify(view)).not.toContain('raw payload')
expect(view.outcome).toBe('部分信息受限')
expect(view.object).toBe('节点(node-secret)')
expect(view.source).toBe('系统受信操作记录')
})
it('preserves domain provenance and object identity in readable audit rows', () => {
expect(describeAuditEvent({
seq: 20,
at: '2026-10-15T08:02:00Z',
op: 'edge.candidate',
actor: 'model-runner',
source: 'model',
target: { kind: 'edge', id: 'edg-7' },
operationId: 'op-3',
disclosure: 'model-candidate',
})).toMatchObject({
action: '提出追溯关系候选',
object: '追溯边(edg-7)',
source: '模型分析候选',
})
expect(describeAuditEvent({
seq: 21,
at: '2026-10-15T08:03:00Z',
op: 'section.add',
actor: 'alice',
source: 'human',
target: { kind: 'section', id: 'sec-2' },
operationId: 'op-4',
disclosure: 'ok',
}).source).toBe('项目成员操作')
})
it('humanizes tree, relationship, and lifecycle vocabulary', () => {
expect(humanizeKind('section')).toBe('章节')
expect(humanizeState('candidate')).toBe('待确认')
expect(humanizeState('retired')).toBe('已停用')
expect(humanizeState('stale')).toBe('待复核')
expect(humanizeRelation({ layer: 'compliance', type: 'verifies', state: 'active' })).toEqual({
layer: '合规关系',
type: '验证',
state: '已生效',
})
})
it('allows recursive sections to expand when the server reports children', () => {
expect(isTreeItemExpandable({ kind: 'doc', id: 'doc-1', title: '文档', hasChildren: true })).toBe(true)
expect(isTreeItemExpandable({ kind: 'doc', id: 'doc-2', title: '空文档', hasChildren: false })).toBe(false)
expect(isTreeItemExpandable({ kind: 'section', id: 'sec-1', title: '章节', hasChildren: true })).toBe(true)
expect(isTreeItemExpandable({ kind: 'paragraph', id: 'p-1', title: '段落', hasChildren: false })).toBe(false)
})
it('gives untitled template nodes a distinct readable name', () => {
expect(describeTreeItem({
kind: 'paragraph',
id: 'dn-1',
title: '',
order: 6,
templateBlockId: 'dhf.dx.a7850d1881.3.placeholder',
})).toEqual({ kind: '段落', title: '模板内容 7(3)' })
expect(describeTreeItem({ kind: 'table', id: 'dn-2', title: '', order: 2 })).toEqual({
kind: '表格',
title: '模板内容 3',
})
})
it('shows the endpoint opposite the selected object in a trace relationship', () => {
const relation = {
from: { kind: 'slice', id: 'slc-6' },
to: { kind: 'section', id: 'sec-2' },
}
expect(relationOtherEndpoint(relation, { kind: 'section', id: 'sec-2' })).toEqual(relation.from)
expect(relationOtherEndpoint(relation, { kind: 'slice', id: 'slc-6' })).toEqual(relation.to)
})
})