Files
Agent-System/web/src/engine/replyDocument/replyDocument.test.ts
T
weichenandCursor b4388934a0 fix(runtime): hold taskrun fanout lock, guard goroutines, and escape math XSS
Serialize appendEvent sends under the subscriber mutex, wrap worker/memory goroutines with recover, lock synchronous chat SendMessage, and escape KaTeX error HTML.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 10:54:18 +00:00

150 lines
4.8 KiB
TypeScript

import { test, describe } from 'node:test';
import assert from 'node:assert/strict';
import { parseReplyMarkdown } from './parseReplyMarkdown';
import { renderClipboardHtml } from './renderClipboardHtml';
import { renderPlainText } from './renderPlainText';
import { serializeMarkdown } from './serializeMarkdown';
import { stripUnsafeHtml, sanitizeLinkHref, escapeHtml } from './sanitize';
import { deriveReplyTitle, sanitizeFilename } from '../../features/replyExport/title';
describe('Reply Document IR & Export Engine', () => {
test('deriveReplyTitle and sanitizeFilename derive title and safe file name', () => {
const md = '# 医疗器械临床评价报告\n\n本报告基于GB 9706标准进行评价。';
const title = deriveReplyTitle(md, '2026-09-10 10:00');
assert.equal(title, '医疗器械临床评价报告');
const safeName = sanitizeFilename(title);
assert.equal(safeName, '医疗器械临床评价报告');
});
test('deriveReplyTitle handles text without heading', () => {
const md = '根据最新医疗器械法规要求,必须建立完整的风险管理体系。';
const title = deriveReplyTitle(md);
assert.ok(title.startsWith('根据最新医疗器械法规要求'));
});
test('parseReplyMarkdown parses headers, lists, code, and tables', () => {
const sample = `
# 临床评价总结
1. 第一项评估
2. 第二项评估
- 无序要点 A
- 无序要点 B
> 这是一个引用说明
\`\`\`python
def evaluate():
return True
\`\`\`
| 参数 | 阈值 | 结果 |
| :--- | :---: | ---: |
| 漏电流 | 10uA | 合格 |
| 耐压 | 1500V | 合格 |
`;
const ir = parseReplyMarkdown(sample);
assert.equal(ir.title, '临床评价总结');
assert.ok(ir.nodes.length >= 6);
const types = ir.nodes.map((b) => b.type);
assert.ok(types.includes('heading'));
assert.ok(types.includes('list'));
assert.ok(types.includes('blockquote'));
assert.ok(types.includes('code'));
assert.ok(types.includes('table'));
});
test('renderClipboardHtml renders self-contained styling for office tools', () => {
const md = `# 标题
正文包含 **粗体** 和 *斜体* 以及 [链接](https://example.com)。
- 项目 1
- 项目 2
`;
const ir = parseReplyMarkdown(md);
const html = renderClipboardHtml(ir);
assert.ok(html.includes('<h1'));
assert.ok(html.includes('style="') || html.includes('<style>'));
assert.ok(html.includes('font-family:'));
assert.ok(html.includes('粗体'));
assert.ok(html.includes('斜体'));
assert.ok(html.includes('<ul'));
});
test('renderClipboardHtml converts LaTeX formulas to native MathML for Word/Office', () => {
const md = `关于超声刀振幅计算:
$$
f_0 = \\frac{c}{2L}
$$
其中声速与波长满足关系 $c = \\lambda f$。`;
const ir = parseReplyMarkdown(md);
const html = renderClipboardHtml(ir);
// 验证包含标准 MathML 命名空间与标签
assert.ok(html.includes('<math xmlns="http://www.w3.org/1998/Math/MathML"'));
assert.ok(html.includes('<mfrac>'));
assert.ok(html.includes('f_0'));
assert.ok(html.includes('math-block'));
});
test('renderPlainText strips formatting cleanly', () => {
const md = `# 临床报告
正文带 **重点**。
| 列1 | 列2 |
| --- | --- |
| A | B |
`;
const ir = parseReplyMarkdown(md);
const plain = renderPlainText(ir);
assert.ok(plain.includes('临床报告'));
assert.ok(plain.includes('正文带 重点。'));
assert.ok(plain.includes('列1\t列2'));
assert.ok(!plain.includes('**重点**'));
});
test('serializeMarkdown round-trips to valid markdown', () => {
const md = `# 临床报告
正文说明。
- 要点 1
- 要点 2
`;
const ir = parseReplyMarkdown(md);
const serialized = serializeMarkdown(ir);
assert.ok(serialized.includes('# 临床报告'));
assert.ok(serialized.includes('正文说明。'));
assert.ok(serialized.includes('- 要点 1'));
});
test('stripUnsafeHtml strips script tags and dangerous event handlers', () => {
const risky = '<p>Normal</p><script>alert(1)</script><a href="javascript:steal()" onclick="alert(2)">Link</a>';
const clean = stripUnsafeHtml(risky);
assert.ok(!clean.includes('<script>'));
assert.ok(!clean.includes('alert(1)'));
assert.ok(!clean.includes('onclick'));
assert.ok(clean.includes('Normal'));
assert.equal(sanitizeLinkHref('javascript:alert(1)'), '#');
});
test('escapeHtml escapes math error payloads used by BlockEditorModal catch path', () => {
const dirty = `<img src=x onerror=alert(1)><script>alert(2)</script>`;
const escaped = escapeHtml(dirty);
assert.ok(!escaped.includes('<script>'));
assert.ok(!escaped.includes('<img'));
assert.ok(escaped.includes('&lt;script&gt;'));
assert.ok(escaped.includes('&lt;img'));
});
});