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
122 lines
4.5 KiB
TypeScript
122 lines
4.5 KiB
TypeScript
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)
|
||
})
|
||
})
|