import { describe, expect, it } from 'vitest' import { buildWorkspaceEntries, rdmAuditReady, rdmExportReady, exportNeedsDraft, RDM_AUDIT_COMMANDS, RDM_EXPORT_COMMANDS } from '../src/workspaces' // 三栏工作台(2026-10-11):文件浏览并入第三栏「文件」入口;体系工具内置「代理」。 // 2026-10-12 增量 2(审计入口能力门控):「追溯审计」不再无条件内置——普通基座 // 没有任何 rdm.inspect.* 命令,入口展示后进入即 400。新契约:仅当命令面探测 // 通过(opts.rdmAudit)才纳入 rdm-audit;代理仍无条件以 ⌘1 补位。 describe('buildWorkspaceEntries', () => { it('没有领域插件且无 RDM 能力:只有代理占 ⌘1', () => { const entries = buildWorkspaceEntries([]) expect(entries.map(e => `${e.shortcut} ${e.title}`)).toEqual(['⌘1 代理']) }) it('没有领域插件且 RDM 能力通过:代理 ⌘1、追溯审计 ⌘2', () => { const entries = buildWorkspaceEntries([], { rdmAudit: true }) expect(entries.map(e => `${e.shortcut} ${e.title}`)).toEqual(['⌘1 代理', '⌘2 追溯审计']) }) it('声明了 shortcut 的工作区占住该位,内置工具让位(能力开)', () => { const entries = buildWorkspaceEntries([ { id: 'trace', title: '追溯网', shortcut: '1' }, ], { rdmAudit: true }) expect(entries.map(e => e.title)).toEqual(['追溯网', '代理', '追溯审计']) expect(entries.map(e => e.shortcut)).toEqual(['⌘1', '⌘2', '⌘3']) expect(entries[0].key).toBe('ws:trace') }) it('无 RDM 能力:让位后只有代理,不出现追溯审计', () => { const entries = buildWorkspaceEntries([ { id: 'trace', title: '追溯网', shortcut: '1' }, ]) expect(entries.map(e => e.title)).toEqual(['追溯网', '代理']) }) it('未编号的工作区排在内置工具后面', () => { const entries = buildWorkspaceEntries([ { id: 'mem', title: '记忆库' }, ], { rdmAudit: true }) expect(entries.map(e => e.title)).toEqual(['代理', '追溯审计', '记忆库']) }) it('九位占满时低序内置保留,溢出项截断', () => { const server = Array.from({ length: 8 }, (_, i) => ({ id: `w${i}`, title: `w${i}`, shortcut: String(i + 1) })) const entries = buildWorkspaceEntries(server, { rdmAudit: true }) // ⌘1–8 被声明占住;代理取 ⌘9;追溯审计溢出截断(slice 0,9)。 expect(entries.filter(e => e.key.startsWith('ws:'))).toHaveLength(8) expect(entries.map(e => e.title)).toContain('代理') expect(entries.map(e => e.title)).not.toContain('追溯审计') }) }) // 能力探测依据后端 GET /api/commands(server.go listCommands:{ id, title, …, enabled })。 // 审计工作区消费七个 rdm.inspect.* 命令;任一缺失或 enabled=false 都视为能力未就绪。 describe('rdmAuditReady', () => { const all = RDM_AUDIT_COMMANDS.map(id => ({ id, enabled: true })) it('七命令齐备且启用 → true', () => { expect(rdmAuditReady(all)).toBe(true) }) it('普通基座空清单 / 缺任一命令 → false', () => { expect(rdmAuditReady([])).toBe(false) expect(rdmAuditReady(all.filter(c => c.id !== 'rdm.inspect.history'))).toBe(false) }) it('存在但 enabled=false → false', () => { const disabled = all.map(c => (c.id === 'rdm.inspect.capabilities' ? { ...c, enabled: false } : c)) expect(rdmAuditReady(disabled)).toBe(false) }) it('清单不可用(未登录/请求失败)→ false,不展示入口', () => { expect(rdmAuditReady(null)).toBe(false) expect(rdmAuditReady(undefined)).toBe(false) }) it('导出命令不参与七命令就绪门', () => { expect(rdmAuditReady(all)).toBe(true) const withExportOff = [ ...all, { id: 'rdm.inspect.export.create', enabled: false }, ] expect(rdmAuditReady(withExportOff)).toBe(true) }) }) describe('rdmExportReady', () => { const audit = RDM_AUDIT_COMMANDS.map(id => ({ id, enabled: true })) const exp = RDM_EXPORT_COMMANDS.map(id => ({ id, enabled: true })) it('三条导出命令启用才显示导出区', () => { expect(rdmExportReady([...audit, ...exp])).toBe(true) expect(rdmExportReady(audit)).toBe(false) expect(rdmExportReady(exp.filter(c => c.id !== 'rdm.inspect.export.download'))).toBe(false) }) it('正式报告不完整时才要求草稿确认', () => { expect(exportNeedsDraft('正式报告不完整(合规缺口)——传 draft:true 生成草稿')).toBe(true) expect(exportNeedsDraft('项目不存在')).toBe(false) }) })