fix(export): 为函数图像与导出产物增加资源上限

针对 PR 审阅「函数数量没有限制,可能生成数百 MB 的 SVG」:

- parser: 单块 function-plot 表达式上限 _MAX_EXPRESSIONS=16,超限整块回退
- html: 单篇文档函数图像上限 _MAX_FUNCTION_PLOTS=16,超出回退源码占位
- service: 输入源 MAX_MARKDOWN_CHARS、产物 MAX_EXPORT_BYTES,超限分别
  拒绝创建或标记 failed(EXPORT_OUTPUT_TOO_LARGE)
- 补充 4 条回归测试与文档说明

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
yxx
2026-09-05 23:31:53 +08:00
co-authored by Claude Code
parent b87f94551b
commit 124024a547
7 changed files with 121 additions and 4 deletions
+22
View File
@@ -247,3 +247,25 @@ def test_render_svg_extreme_range_no_nan() -> None:
assert "nan" not in rendered.content
assert "inf" not in rendered.content
assert any("range" in w for w in rendered.warnings)
# --------------------------------------------------------------------------- #
# 审阅回归:函数/图像数量上限
# --------------------------------------------------------------------------- #
def test_parse_source_rejects_too_many_expressions() -> None:
# P1:单块表达式数量超限应整块回退,避免海量采样求值
source = "\n".join(f"y = x + {i}" for i in range(50))
result = parse_source(source)
assert result.plot is None
assert any(d.code == "FUNCTION_PLOT_TOO_MANY_EXPRESSIONS" for d in result.diagnostics)
def test_html_exporter_limits_function_plot_count() -> None:
# P1:文档内函数图像数量超限,超出部分回退占位,不耗尽资源
blocks = "\n\n".join("```function-plot\ny = x\n```" for _ in range(20))
result = asyncio.run(HtmlExporter().export(parse_document(blocks), ExportOptions()))
html = result.content.decode("utf-8")
# 上限 16:前 16 个渲染为 SVG,其余 4 个回退占位
assert html.count('<figure class="function-plot">') == 16
assert html.count('<pre class="function-plot">') == 4
assert any("函数图像数量超过上限" in w for w in result.warnings)