Craft Agents 文档工具冒烟测试指南:基于 uv 的 CLI 工具验证体系解析
2026/9/17 7:38:20 网站建设 项目流程

Craft Agents 文档工具冒烟测试指南:基于 uv 的 CLI 工具验证体系解析

【免费下载链接】craft-agents-oss项目地址: https://gitcode.com/GitHub_Trending/cr/craft-agents-oss

导读

本文围绕 Craft Agents 仓库中apps/electron/resources/scripts/tests/目录下的文档工具冒烟测试(Document Tool Smoke Tests)展开,说明该测试目录如何对随 Electron 应用打包的 8 个 CLI 文档工具(PDF、XLSX、DOCX、PPTX、图片、iCalendar、文档差异、MarkItDown)进行端到端验证。读完本文,你将掌握这些测试的运行方式、测试基础设施(共享 harness)的实现原理、wrapper 二进制与 uv 内联脚本的调用链,以及如何在修改工具脚本后补充相应的冒烟测试。

测试目录的定位与设计初衷

冒烟测试(Smoke Test)的核心目标是"快速验证关键路径可用",而不是穷尽边界行为。Craft Agents 将随应用分发的文档处理 CLI 工具作为打包资产,因此需要在持续集成中确认每个工具在真实环境下能够正常启动、执行核心子命令并产生预期输出。

该目录位于 apps/electron/resources/scripts/tests/,测试对象是 apps/electron/resources/scripts/ 下的工具脚本。与常规单元测试直接调用函数不同,这里的冒烟测试执行的是resources/bin/*下的 wrapper 二进制(而非直接运行脚本),从而完整覆盖"打包 → 分发 → 通过 wrapper 启动"这条真实使用链路。

从测试目录看,共包含 8 个冒烟测试模块与 1 个共享测试基础设施:

测试模块对应 wrapper覆盖的工具脚本
test_pdf_tool_smoke.pypdf-toolpdf_tool.py
test_xlsx_tool_smoke.pyxlsx-toolxlsx_tool.py
test_docx_tool_smoke.pydocx-tooldocx_tool.py
test_pptx_tool_smoke.pypptx-toolpptx_tool.py
test_img_tool_smoke.pyimg-toolimg_tool.py
test_ical_tool_smoke.pyical-toolical_tool.py
test_doc_diff_smoke.pydoc-diffdoc_diff.py
test_markitdown_smoke.pymarkitdownmarkitdown_cli.py

运行全部冒烟测试

原文档提供了两种运行方式,均从仓库根目录执行。

方式一:直接使用python3 -m unittest指定全部测试模块

python3 -m unittest \ apps.electron.resources.scripts.tests.test_pdf_tool_smoke \ apps.electron.resources.scripts.tests.test_xlsx_tool_smoke \ apps.electron.resources.scripts.tests.test_docx_tool_smoke \ apps.electron.resources.scripts.tests.test_pptx_tool_smoke \ apps.electron.resources.scripts.tests.test_img_tool_smoke \ apps.electron.resources.scripts.tests.test_ical_tool_smoke \ apps.electron.resources.scripts.tests.test_doc_diff_smoke \ apps.electron.resources.scripts.tests.test_markitdown_smoke

方式二:使用根目录脚本(推荐)

bun run test:doc-tools

根目录 package.json 中的脚本定义如下,它本质上就是把方式一的 8 个模块拼接到一条python3 -m unittest命令中:

"test:doc-tools": "python3 -m unittest apps.electron.resources.scripts.tests.test_pdf_tool_smoke apps.electron.resources.scripts.tests.test_xlsx_tool_smoke apps.electron.resources.scripts.tests.test_docx_tool_smoke apps.electron.resources.scripts.tests.test_pptx_tool_smoke apps.electron.resources.scripts.tests.test_img_tool_smoke apps.electron.resources.scripts.tests.test_ical_tool_smoke apps.electron.resources.scripts.tests.test_doc_diff_smoke apps.electron.resources.scripts.tests.test_markitdown_smoke"

值得注意的是,test:doc-tools已经被纳入统一的开发校验流程validate:dev(见 package.json),该流程依次执行类型检查、共享包测试与文档工具冒烟测试,说明这套冒烟测试是仓库质量门禁的一部分。

运行单个测试套件

当只修改了某一个工具(例如 XLSX 工具)时,可以只运行对应的套件以缩短反馈回路:

python3 -m unittest apps.electron.resources.scripts.tests.test_xlsx_tool_smoke

同样地,8 个模块均遵循apps.electron.resources.scripts.tests.test_<tool>_smoke的命名约定,任何工具都可以用同样的方式单独运行。

共享测试基础设施_tool_test_harness.py解析

所有测试套件都依赖同一个共享模块 apps/electron/resources/scripts/tests/_tool_test_harness.py。它把"如何定位脚本、如何选择 uv、如何构建环境变量、如何执行 wrapper"这些公共逻辑收敛到一处,避免 8 个测试文件各自重复实现。

路径定位

harness 通过Path(__file__).resolve().parents[5]从测试文件自身向上回溯得到仓库根目录(REPO_ROOT),并据此推导出两个关键目录:

  • BIN_DIR = REPO_ROOT / "apps" / "electron" / "resources" / "bin":wrapper 二进制目录
  • SCRIPTS_DIR = REPO_ROOT / "apps" / "electron" / "resources" / "scripts":工具脚本目录

由于路径从文件位置推导而非依赖环境变量,测试可以在任意工作目录下运行,只要仓库结构完整。

平台键解析与 uv 选择

resolve_platform_key()负责把当前运行环境归一化为{os}-{arch}形式的键(如darwin-arm64linux-x64win32-x64),用于定位BIN_DIR下对应平台的捆绑uv

resolve_uv_binary()的解析顺序是:

  1. 优先使用捆绑的BIN_DIR/<platform_key>/uv(Windows 上为uv.exe);
  2. 若捆绑 uv 缺失,回退到系统PATH上的uv(通过shutil.which);
  3. 两者都不可用时抛出FileNotFoundError,并明确指出"既没有捆绑 uv 也没有 PATH 上的 uv"。

这正是原文档 Notes 中"若捆绑的uv在你的平台缺失,harness 回退到 PATH 上的uv"这一行为的源码实现。

wrapper 解析与执行

resolve_wrapper(tool_name)BIN_DIR中查找同名 wrapper,Windows 平台追加.cmd后缀。run_tool(tool_name, *args)则完成"构建环境 → 解析 wrapper → 以仓库根目录为工作目录执行"的完整流程,并通过capture_output=True捕获 stdout/stderr 供测试断言。

build_env()是环境准备的核心,它设置三个关键环境变量:

  • CRAFT_UV:指向解析出的 uv 可执行文件绝对路径;
  • CRAFT_SCRIPTS:指向工具脚本目录;
  • PATH:把BIN_DIRuv.parent前置插入,确保 wrapper 及其依赖可被发现。

wrapper 二进制与 uv 内联脚本的调用链

以 DOCX 工具为例,apps/electron/resources/bin/docx-tool 的完整内容是一个极简的 POSIX shell 脚本:

#!/bin/sh exec "$CRAFT_UV" run --python 3.12 "$CRAFT_SCRIPTS/docx_tool.py" "$@"

它把参数原样透传给docx_tool.py,形成"测试 → wrapper → uv → 内联脚本"的完整调用链。工具脚本本身采用 PEP 723 内联脚本元数据声明依赖,例如 apps/electron/resources/scripts/xlsx_tool.py 头部声明:

# /// script # requires-python = ">=3.12" # dependencies = ["openpyxl>=3.1,<4", "click>=8.3,<9"] # ///

uv 会根据这份元数据自动创建隔离环境并解析依赖,这正是 wrapper 中exec "$CRAFT_UV" run能开箱即用的原因。类似地,pdf_tool.py 声明了pypdfium2pypdfimg2pdfPillowclickpython-pptxpython-docx等依赖,doc_diff.py 则依赖markitdownpython-docxdiff-match-patchclick,并显式屏蔽了 markitdown 的 ffmpeg 缺失告警。BIN_DIR下每种工具都同时提供无后缀的 shell 脚本与.cmd(Windows)两个变体(如docx-tool/docx-tool.cmdpdf-tool/pdf-tool.cmd),保证跨平台可用。

各套件冒烟覆盖点:从源码看测试意图

8 个套件都遵循setUpClass中构建夹具(fixture)、测试方法中执行工具并断言、tearDownClass中清理临时目录的同一模式。以下是各套件的核心覆盖点。

PDF 工具(test_pdf_tool_smoke.py)

test_pdf_tool_smoke.py 的夹具构建展示了工具之间的协作:先用img-tool resize把 1×1 透明 PNG 放大为 200×200 的三张图,再用pdf-tool from-image合成输入 PDF。其断言重点放在"加固行为"上:

  • extract --pages 999越界页必须失败且报out of bounds
  • reorder --order 1,2 --reverse冲突参数必须失败且报mutually exclusive
  • duplicate --copies 1低于下限必须失败且报x>=2
  • to-pptx --pages 999越界选择必须优雅失败,且不能在 stderr 中出现IndexError原始堆栈;
  • sanitize正常路径必须返回 0 且输出文件存在。

这与 pdf_tool.py 文档字符串中的说明一致:页范围解析是严格的,越界页与畸形--pages输入现在会返回显式错误而不是被静默忽略。

XLSX 工具(test_xlsx_tool_smoke.py)

test_xlsx_tool_smoke.py 覆盖write(含--type number写入数字 42)、info(sheet_count ≥ 1)、read --format json(校验行内容与类型)、export --format csv(输出文件存在)、add-sheet(新 sheet 出现在 info 输出中),并验证读取不存在的 sheet 时报not found。其中 JSON 读取断言rows[0]["name"] == "alice"rows[0]["score"] == 42,说明read的 JSON 输出保留了首行作为表头并正确处理了数值类型。

DOCX 工具(test_docx_tool_smoke.py)

test_docx_tool_smoke.py 串起一条完整的"创建 → 提取 → 模板填充 → 文本替换"工作流:create --text "# Report\n\nHello **world**"生成文档后,extract能取回标题与正文;template --data '{"name":"Balint"}'完成{{name}}占位符填充;replace --find Balint --replace-with "Craft Agent"完成文本替换并可通过再次extract验证。负向用例验证传入非法 JSON({not-json})时失败并报Error parsing JSON

PPTX 工具(test_pptx_tool_smoke.py)

test_pptx_tool_smoke.py 用create --text配合---分页符生成多页幻灯片,断言infoslide_count >= 2extract能取回各页文本;负向用例验证extract --slide 99越界时报out of range

图片工具(test_img_tool_smoke.py)

test_img_tool_smoke.py 覆盖info(stdout 输出 JSON 且format == "PNG")、resize --scale 2convert --format jpg的完整链路;负向用例验证resize --scale 0失败并报must be positive

iCalendar 工具(test_ical_tool_smoke.py)

test_ical_tool_smoke.py 以 JSON 数组形式传入事件数据调用create --data生成.ics文件,随后read --format json校验event_count与事件summaryfilter --start/--end按时间窗过滤;负向用例用not-a-valid-ics验证畸形文件读取时 stderr 含Error:

文档差异工具(test_doc_diff_smoke.py)

test_doc_diff_smoke.py 验证doc-diff对两个文本文件执行--format summary时输出包含Comparison:Similarity:字样;负向用例验证输入文件不存在时失败并报does not exist。结合 doc_diff.py 的实现可知,该工具先把两个文档经 markitdown 转换为 Markdown,再用diff-match-patch计算并展示差异。

MarkItDown 工具(test_markitdown_smoke.py)

test_markitdown_smoke.py 覆盖两条路径:纯文本文件直接透传(stdout 含hello craft),以及 DOCX 回退路径——先用docx-tool create生成.docx,再验证markitdown能将其转换为 Markdown 并取回正文文本。

临时夹具的自动创建与清理

原文档 Notes 中"测试在运行时创建临时夹具并自动清理"的机制,在各套件中体现为setUpClass/tearDownClass配合tempfile.TemporaryDirectory

cls.tmpdir_obj = tempfile.TemporaryDirectory(prefix="xlsx-tool-smoke-") cls.tmpdir = Path(cls.tmpdir_obj.name)

每个套件使用带工具名的前缀(如pdf-tool-smoke-img-tool-smoke-doc-diff-smoke-)创建独立临时目录,所有输入输出都限定在该目录内;tearDownClass调用cleanup()自动删除整个目录,既保证测试互不干扰,也不会在仓库中残留产物。这种方式也让冒烟测试可以放心地写文件、生成 PDF/PPTX/XLSX 等二进制产物,而不必担心污染源码树。

修改工具后如何补充冒烟测试

原文档的 Contributor expectation 明确指出:如果你修改了apps/electron/resources/scripts/下的任何脚本,或apps/electron/resources/bin/下的 wrapper,就必须在本目录更新或新增相应的冒烟测试。实践上可遵循以下流程:

  1. 定位对应套件:根据修改的工具脚本在tests/目录中找到同名test_<tool>_smoke.py,例如改xlsx_tool.py则更新test_xlsx_tool_smoke.py
  2. 从 harness 引入工具:通过from ._tool_test_harness import build_env, run_tool复用环境构建与 wrapper 执行逻辑,并在setUpClass中调用build_env()获取环境;
  3. 补充正向与负向断言:参考现有模式,正向用例验证返回码为 0 且输出符合预期(文件存在 / stdout 包含关键字 / JSON 字段正确),负向用例验证非法输入返回非 0 且 stderr 含明确的错误提示(如out of boundsmust be positivedoes not exist);
  4. 单独跑通再全量验证:先python3 -m unittest apps.electron.resources.scripts.tests.test_<tool>_smoke快速迭代,最后执行bun run test:doc-tools确认不破坏其他工具套件。

小结

Craft Agents 的文档工具冒烟测试是一套围绕"打包分发链路"设计的最小化端到端验证体系:通过共享 harness 统一处理平台键解析、uv 选择、wrapper 定位与环境变量注入,让 8 个工具套件以一致的方式经由真实 wrapper 二进制执行;通过内置的负向用例验证工具的"加固行为"(越界、冲突参数、非法输入都能显式报错而非抛出原始堆栈);通过临时夹具的自动创建与清理保证测试可重复、无残留。对于任何修改文档工具脚本或 wrapper 的贡献者,本目录都是必须同步维护的测试阵地。

【免费下载链接】craft-agents-oss项目地址: https://gitcode.com/GitHub_Trending/cr/craft-agents-oss

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询