From ff1eaab8203eca921969e958283ea4e11cb37806 Mon Sep 17 00:00:00 2001 From: WeiCHEN Date: Tue, 8 Sep 2026 18:09:56 +0800 Subject: [PATCH] =?UTF-8?q?test(workbench):=20=E5=A2=9E=E5=8A=A0=20DOCX/XL?= =?UTF-8?q?SX=20=E5=AF=BC=E5=87=BA=E4=B8=8E=E4=BC=9A=E8=AF=9D=E9=9A=94?= =?UTF-8?q?=E7=A6=BB=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 验证 DOCX 二进制 Buffer 生成、TextRun 解析与 Blob 输出 - 验证 XLSX 多表提取、自适应列宽与工作簿 Sheet 命名规范 - 验证 sess-001 与 sess-002 对话隔离及动态追加互不串线 Co-authored-by: Cursor --- web/src/data/storage.test.ts | 81 +++++++++++++++++++++++++++++ web/src/engine/renderers.test.ts | 88 ++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 web/src/data/storage.test.ts create mode 100644 web/src/engine/renderers.test.ts diff --git a/web/src/data/storage.test.ts b/web/src/data/storage.test.ts new file mode 100644 index 00000000..30494f8c --- /dev/null +++ b/web/src/data/storage.test.ts @@ -0,0 +1,81 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { DEFAULT_MESSAGES, DEFAULT_SESSIONS } from './storage'; +import { ChatMessage } from '../types/workbench'; + +test('多项目会话消息隔离测试 (Multi-project Session Messages Strict Isolation)', () => { + // 1. 验证预设会话消息按 sessionId 严格分离 + const sess1Msgs = DEFAULT_MESSAGES.filter((m) => m.sessionId === 'sess-001'); + const sess2Msgs = DEFAULT_MESSAGES.filter((m) => m.sessionId === 'sess-002'); + + assert.ok(sess1Msgs.length >= 2, 'sess-001 应当包含其专属消息'); + assert.ok(sess2Msgs.length >= 2, 'sess-002 应当包含其专属消息'); + + // 验证 sess-001 包含内窥镜关键字,不包含超声探头 + const sess1Combined = sess1Msgs.map((m) => m.content).join(' '); + assert.ok(sess1Combined.includes('荧光内窥镜')); + assert.ok(!sess1Combined.includes('超声声输出安全标准')); + + // 验证 sess-002 包含超声探头关键字,不包含腹腔镜初稿 + const sess2Combined = sess2Msgs.map((m) => m.content).join(' '); + assert.ok(sess2Combined.includes('便携式掌上无线超声诊断仪')); + assert.ok(!sess2Combined.includes('4K超高清荧光内窥镜系统立项与可行性报告')); + + // 2. 模拟向 sess-001 追加一条新消息 + const allMessages: ChatMessage[] = [...DEFAULT_MESSAGES]; + const newMsgSess1: ChatMessage = { + id: 'm-new-1', + sessionId: 'sess-001', + role: 'user', + content: '请测算一下双棱镜分光成本', + timestamp: '14:30', + }; + allMessages.push(newMsgSess1); + + // 3. 验证此时切到 sess-002 时,绝不出现新消息 + const filteredSess2 = allMessages.filter((m) => m.sessionId === 'sess-002'); + assert.equal(filteredSess2.length, sess2Msgs.length, 'sess-002 的消息条数应当完全不受 sess-001 变动的影响'); + assert.ok(!filteredSess2.some((m) => m.content.includes('双棱镜分光成本'))); + + // 4. 验证此时切到 sess-001 时,准确多了一条消息 + const filteredSess1 = allMessages.filter((m) => m.sessionId === 'sess-001'); + assert.equal(filteredSess1.length, sess1Msgs.length + 1); + assert.ok(filteredSess1.some((m) => m.content.includes('双棱镜分光成本'))); +}); + +test('新建项目会话独立初始化隔离测试 (New Session Spawning & Isolation)', () => { + const allSessions = [...DEFAULT_SESSIONS]; + const newSessId = 'sess-new-999'; + + const newSess = { + sessionId: newSessId, + projectId: 999, + title: '神经外科双极电凝系统', + deviceType: '高频手术设备', + stage: 'V0.1' as const, + stageLabel: 'V0.1 需求摸底', + messageCount: 1, + updatedAt: '刚刚', + }; + allSessions.unshift(newSess); + + const initialMsgs: ChatMessage[] = [ + ...DEFAULT_MESSAGES, + { + id: 'm-init-999', + sessionId: newSessId, + role: 'system', + content: '水木东方协同参谋小队已就位,当前项目【神经外科双极电凝系统】进入需求摸底阶段。', + timestamp: '刚刚', + }, + ]; + + // 检查新项目消息隔离 + const sess999Msgs = initialMsgs.filter((m) => m.sessionId === newSessId); + assert.equal(sess999Msgs.length, 1); + assert.ok(sess999Msgs[0].content.includes('神经外科双极电凝系统')); + + // 检查原项目消息数量未变 + assert.equal(initialMsgs.filter((m) => m.sessionId === 'sess-001').length, 2); + assert.equal(initialMsgs.filter((m) => m.sessionId === 'sess-002').length, 2); +}); diff --git a/web/src/engine/renderers.test.ts b/web/src/engine/renderers.test.ts new file mode 100644 index 00000000..cd101b14 --- /dev/null +++ b/web/src/engine/renderers.test.ts @@ -0,0 +1,88 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { markdownToBlockAST } from './markdownToBlock'; +import { buildDocxModel, exportDocumentToDocx, parseInlineTextRuns } from './docxRenderer'; +import { buildXlsxWorkbook, exportDocumentToXlsx } from './xlsxRenderer'; +import { Packer } from 'docx'; + +const SAMPLE_MD = `# 4K超高清荧光内窥镜系统立项与可行性报告 + +本产品主要用于微创腹腔手术中的组织成像与荧光导航。 + +> **[合规依据]** 本项目需严格遵守 GB 9706.1-2020 医用电气安全标准。 + +## 关键技术指标与参数对比 + +| 参数项 | 指标要求 | 检验依据 | 验证结果 | +| 光学分辨率 | 4K (3840x2160) | YY 0068.1 | 符合 | +| 荧光波长 | 805nm 近红外 | 临床验证 | 符合 | +| 延迟时间 | < 35ms 超低延迟 | 内部实测 | 优于竞品 | + +## 研发工时与费用测算 + +| 阶段 | 责任工程师 | 预计工时 (h) | 费率 (元/h) | 小计金额 (万元) | +| V0.1 需求摸底 | 光学方案专家 | 80 | 600 | 4.80 | +| V0.5 方案对齐 | FPGA硬件工程师 | 160 | 450 | 7.20 | +| V1.0 样机研制 | 嵌入式与算法架构师 | 240 | 500 | 12.00 | + +$$ +SNR = 10 \\cdot \\log_{10}\\left(\\frac{P_{signal}}{P_{noise}}\\right) +$$ +`; + +test('DOCX 渲染器行内文本解析测试 (parseInlineTextRuns)', () => { + const text = '依据 **GB 9706.1-2020** 检验,*延迟* 小于 `35ms`。'; + const runs = parseInlineTextRuns(text); + + assert.ok(runs.length >= 4); +}); + +test('DOCX 渲染器全量编译测试 (buildDocxModel & exportDocumentToDocx)', async () => { + const ast = markdownToBlockAST(SAMPLE_MD, { + docId: 'doc_docx_test_001', + taskKey: 'feasibility_reg', + author: '张工', + reviewer: '李主管', + approver: '王总监', + version: 'V1.0', + lifecycleState: 'drafting', + }); + + const doc = buildDocxModel(ast); + assert.ok(doc); + + // 验证 Packer 编译输出 Buffer 大小正常 + const buffer = await Packer.toBuffer(doc); + assert.ok(buffer.length > 5000, `生成的 DOCX Buffer 应当大于 5KB,实际为 ${buffer.length} 字节`); + + // 验证 exportDocumentToDocx 输出 Blob 正常 + const blob = await exportDocumentToDocx(ast); + assert.equal(blob.type, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); + assert.ok(blob.size > 5000); +}); + +test('XLSX 渲染器多表提取与工作簿编译测试 (buildXlsxWorkbook & exportDocumentToXlsx)', async () => { + const ast = markdownToBlockAST(SAMPLE_MD, { + docId: 'doc_xlsx_test_001', + taskKey: 'feasibility_reg', + author: '张工', + reviewer: '李主管', + approver: '王总监', + version: 'V1.0', + lifecycleState: 'drafting', + }); + + const wb = buildXlsxWorkbook(ast); + assert.ok(wb); + assert.ok(wb.SheetNames.length >= 3, `应该包含概览 Sheet 和 2 个表格 Sheet,实际有: ${wb.SheetNames.join(', ')}`); + assert.equal(wb.SheetNames[0], '文档概览与签署'); + + // 验证表 1 数据 + const sheet1 = wb.Sheets[wb.SheetNames[1]]; + assert.ok(sheet1); + + // 验证 exportDocumentToXlsx 输出 Blob 正常 + const blob = await exportDocumentToXlsx(ast); + assert.equal(blob.type, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + assert.ok(blob.size > 1000); +});