Repository navigation
fix(dingtalk): split oversized markdown lines - #5299
Conversation
|
@qwen-code /triage |
✅ Local verification report — PR #5299
|
| Check | Result |
|---|---|
PR full suite (markdown.test.ts) |
✅ 26/26 pass |
| BASE as-is | ✅ 17 pass (PR adds 9 new tests) |
| Genuine-regression — PR's test file on BASE source | ✅ all 9 new tests FAIL (e.g. 5000-char line → base returns 1 chunk, expected 1 to be 2), 17 originals still pass → tests truly catch the bug |
tsc --build (npm run build) |
✅ exit 0 |
eslint markdown.ts + test |
✅ exit 0 |
prettier --check |
✅ clean |
git diff --check |
✅ exit 0 |
Property/fuzz A/B (2,200 diverse inputs, seeded & reproducible)
Generators: plain multi-line text, single long lines, boundary-straddling lines, inline-backtick plain text, well-formed fenced code blocks, mixed docs, and fence-delimiter-straddle cases. Invariants: every chunk ≤ 3800 (LEN); plain text reconstructs exactly (PLAIN_EXACT); non-fence payload preserved (PAYLOAD); only paired fences injected (BACKTICK_PARITY, Δ%6==0); no newline loss (NEWLINE); balanced fences per chunk for well-formed inputs (FENCE_BALANCE).
| Invariant violations | PR (after) | main (before) |
|---|---|---|
| LEN (chunk > 3800) | 0 | 1,160 |
| PLAIN_EXACT (lossless plain text) | 0 | 588 |
NEWLINE (no \n dropped) |
0 | 876 |
| PAYLOAD / BACKTICK_PARITY / FENCE_BALANCE | 0 | 0 |
| max chunk length produced | 3,800 | 11,987 (3.2× the limit) |
The PR packs right up to 3800 (no over-conservative splitting) while never exceeding it; 1,003 code-fence boundary injections were exercised. The invariants are discriminating (main fails LEN/PLAIN_EXACT/NEWLINE, PR passes all), so the green result is meaningful, not vacuous. Note main also silently drops the \n at every chunk boundary for multi-line text — this PR fixes that too.
Adversarial edge cases (on the compiled dist, 30 s timeout guard)
All terminate instantly with no chunk > 3800:
| Input | chunks | max len |
|---|---|---|
| 8000 backticks, no newline | 3 | 3798 |
2000 ``` fence-triples |
2 | 3798 |
```+5000 backticks+``` |
2 | 3798 |
| exactly 3800 / 3801 | 1 / 2 | 3800 / 3800 |
| fence at exact boundary | 3 | 3800 |
| single 200,000-char line | 53 | 3800 |
No infinite loops (each available<=0/pieceLength===0 path flushes and always recovers available>0).
Reproduce
git worktree add --detach /tmp/wt-after $(gh pr view 5299 --json headRefOid -q .headRefOid)
git worktree add --detach /tmp/wt-before origin/main
for w in wt-after wt-before; do ln -s "$PWD/node_modules" /tmp/$w/node_modules; done
cd /tmp/wt-after/packages/channels/dingtalk
../../../../node_modules/.bin/vitest run --config vitest.config.ts src/markdown.test.ts # 26/26
# Prove the tests catch the bug: run PR's test on BASE source -> 9 fail🇨🇳 中文版(点击展开)
✅ 本地验证报告 — PR #5299 fix(dingtalk): split oversized markdown lines
结论:强烈建议合并 —— 修复了一个真实且影响面较广的正确性 bug,且无回归。 在 main 上,splitChunks 只在整行之间切分,所以任何单行超过 CHUNK_LIMIT(3800)就会溢出,产生 DingTalk 会拒绝/截断的超长 chunk。本 PR 会切分超长行、保留换行,并在 chunk 边界正确闭合/重开代码围栏(fence)。它在 PR 自带测试、2200 例 fuzz、以及一批极端输入下都站得住脚。
验证方式
在 tmux 中、两个隔离的 detached worktree(pr5299-head = 2ab8661b,origin/main = 905041f9)里运行真实 vitest,软链接复用仓库 node_modules。环境:Node v22.22.2、vitest 3.2.4。dingtalk 包在 merge-base 与当前 main 之间字节一致;可干净合并。
| 检查项 | 结果 |
|---|---|
PR 全量测试(markdown.test.ts) |
✅ 26/26 通过 |
| BASE 原样 | ✅ 17 通过(PR 新增 9 个测试) |
| 回归测试有效性 —— 用 PR 的测试跑 BASE 源码 | ✅ 9 个新测试全部失败(如 5000 字符单行:base 只返回 1 个 chunk,expected 1 to be 2),原有 17 个仍通过 → 说明测试确实抓到了 bug |
tsc --build(npm run build) |
✅ exit 0 |
eslint markdown.ts + 测试 |
✅ exit 0 |
prettier --check |
✅ 通过 |
git diff --check |
✅ exit 0 |
属性/fuzz 对比(2200 个多样化输入,固定种子可复现)
生成器涵盖:纯多行文本、超长单行、边界附近的长行、含内联反引号的纯文本、规范的代码围栏块、混合文档、以及围栏分隔符跨边界的情况。不变量:每个 chunk ≤ 3800(LEN);纯文本可精确还原(PLAIN_EXACT);非围栏内容不丢失(PAYLOAD);只注入成对围栏(BACKTICK_PARITY,Δ%6==0);不丢换行(NEWLINE);规范输入下每个 chunk 围栏平衡(FENCE_BALANCE)。
| 不变量违反数 | PR(修复后) | main(修复前) |
|---|---|---|
| LEN(chunk > 3800) | 0 | 1160 |
| PLAIN_EXACT(纯文本无损) | 0 | 588 |
NEWLINE(不丢 \n) |
0 | 876 |
| PAYLOAD / BACKTICK_PARITY / FENCE_BALANCE | 0 | 0 |
| 产生的最大 chunk 长度 | 3800 | 11987(超限 3.2 倍) |
PR 会一直填到 3800(不会过度保守地提前切分)但绝不超过;过程中触发了 1003 次代码围栏边界注入。这些不变量是有区分度的(main 在 LEN/PLAIN_EXACT/NEWLINE 上失败、PR 全通过),所以“全绿”是有意义的、并非空跑。另外 main 在多行文本的每个 chunk 边界还会悄悄丢掉一个 \n,本 PR 一并修复了。
极端输入(在编译产物 dist 上跑,30 秒超时保护)
全部瞬间结束、且没有任何 chunk > 3800:
| 输入 | chunk 数 | 最大长度 |
|---|---|---|
| 8000 个反引号、无换行 | 3 | 3798 |
2000 个 ``` 三连 |
2 | 3798 |
```+5000 反引号+``` |
2 | 3798 |
| 恰好 3800 / 3801 | 1 / 2 | 3800 / 3800 |
| 围栏正好落在边界 | 3 | 3800 |
| 单行 200000 字符 | 53 | 3800 |
无死循环(每个 available<=0 / pieceLength===0 分支都会 flush 并恢复到 available>0)。
Verified locally with real vitest runs under tmux on isolated worktrees of the PR head and origin/main; bug breadth quantified by a 2,200-case seeded property fuzz (A/B), robustness checked with adversarial all-backtick / 200k-char inputs on the compiled dist.
|
@qwen-code /triage |
Summary
Test Plan
AI Assistance Disclosure
I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.