OpenProgram 测试系统
本页定义测试分类、目录、依赖边界、执行命令和 CI 职责。测试层级由运行时依赖决定,产品域沿用 openprogram/ 的模块名称。结构迁移不改变产品行为;测试发现的真实产品缺陷按独立批次修复和验证。
1. 当前实现与已验证差距
Python 规模383 个 tracked Python 测试文件,约 10.5 万行。大部分集中在
tests/unit,但其中包含 HTTP client、subprocess、临时状态库和真实线程。共享状态
tests/conftest.py 在 collection 阶段修改代理、HOME 和多个进程全局对象。临时 HOME 未自动删除,局部状态恢复依赖全局 autouse fixture。并发与等待测试中存在固定
time.sleep,部分测试替换全局 threading.Thread。这会影响 pytest-xdist 的控制线程和失败诊断。仓库扫描部分检查通过
Path.rglob 扫描工作目录,会包含 ignored Finder 副本和嵌套 application repository,因此相同 commit 可产生不同结果。JavaScriptCLI 已使用 Vitest;Web 主要使用读取源代码后执行正则断言的脚本;desktop 主要使用 Node VM 和 fake Electron,CI 只执行其中一个脚本。
CIPython matrix 排除 integration;未执行 Ruff、CLI test/build 和完整 desktop checks;Web 构建与 Python frontend integration fixture 没有统一契约。
2. 参考框架及职责边界
参考信息来自各项目官方文档。仓库继续使用现有工具;本设计不增加新的通用 test runner。
| 工具 | 采用能力 | 未提供的能力 | OpenProgram 处理方式 |
|---|---|---|---|
| pytest | Python discovery、fixture、marker、warning 和 xfail 策略 | 不执行 JavaScript,也不保证 fixture 自身具备进程隔离 | 负责 Python contracts、unit、component、integration、e2e 和 live |
| pytest-xdist | 多进程分发 | 不能修复全局状态、后台线程或不安全 monkeypatch | 隔离问题修复并重复验证后,只启用固定 worker 数 |
| node:test | Node 22 内置断言、test 和 mock | 不提供真实 DOM 和浏览器交互 | 用于 Web 纯 TypeScript/JavaScript 逻辑,避免新增依赖 |
| Vitest | CLI 已有的 TypeScript test runner | 不覆盖 Python worker 和真实浏览器 | 仅保留在 CLI,不在 Web 再安装一份 |
| Playwright | 真实浏览器、用户可见行为和跨页面状态 | 不替代 unit/component 测试 | 复用 Python browser extra,使用 Playwright 管理的 Chromium |
| GitHub Actions | 版本矩阵、job 隔离和 required checks | 不定义测试语义 | 按测试层级拆分 jobs,使用相同的本地命令 |
3. 目标结构
tests/
contracts/{repository,security}/
unit/<product-domain>/
component/<product-domain>/
integration/<product-domain>/
e2e/{agent,context,web}/
live/{providers,channels}/
support/
conftest.py
| 层级 | 允许依赖 | 禁止依赖 | 默认执行 |
|---|---|---|---|
| contracts | AST、tracked files、manifest、registry、schema | 外部服务;用源码文本代替行为断言 | 每次 PR |
| unit | 内存对象、纯函数、局部 fake、单模块使用的隔离临时文件;生产模块内部受控且可清理的线程资源 | TestClient、socket、subprocess、测试代码直接创建的真实后台线程或线程池、固定 sleep | Python 3.11/3.12/3.13 |
| component | tmp_path、临时 SQLite、TestClient、fake provider、受控线程 | 外部网络和真实凭证 | Python 3.12 |
| integration | loopback HTTP/WS、真实 subprocess、多个生产模块 | 外部服务 | Python 3.12 |
| e2e | 公开 CLI、真实 worker、构建后的 Web、浏览器 | 开发者真实 HOME 和预先存在的构建产物 | 专用 CI job |
| live | 真实 provider、channel、远端服务 | 普通 PR required checks | 手动或定时 |
同一测试符合多个层级时,按依赖最强的层级分类。目录最多使用“层级/产品域”两级。marker 只描述执行能力:slow、browser、sandbox、live;不重复声明目录已经表达的层级。
4. 执行和状态隔离
- 仓库级 contracts 只读取 Git tracked 文件。通用扫描器由调用方传入 roots,并排除嵌套 repository 和构建目录。
- 顶层
conftest.py仅保留防止访问真实用户状态所必需的隔离;产品域 fixture 放在局部目录。 - 临时 HOME 具有确定的清理阶段。测试不能依赖真实
~/.openprogram。 - 等待并发结果使用 Event、Condition 或带截止时间的条件等待;unit 中禁止固定 sleep。
- 测试不能替换 Python 标准库的进程全局线程类,也不能直接创建真实线程或线程池。生产模块内部创建的后台资源必须在 fixture teardown 中 join 或关闭。
- 一个生产缺陷对应一个通过最低公开边界复现的 regression test。
5. Web、CLI 与 desktop
| 现有检查 | 目标 | 判定 |
|---|---|---|
| Web security/architecture source check | web/scripts/check-*.mjs | 只有源代码结构本身属于契约时保留 |
| Web pure helper behavior | web/tests/*.test.mjs + node:test | 直接 import 并断言输入输出 |
| Web user interaction | tests/e2e/web + Playwright | 操作构建后的页面并观察公开行为 |
| CLI TypeScript | 现有 Vitest | CI 执行 typecheck、test、build |
| desktop VM/fake Electron | desktop component checks | CI 执行完整 npm run check |
| 真实 Electron | release e2e | 初始重构不新增打包测试基础设施 |
6. CI 数据流
qualityRuff
contracts
docs
contracts
docs
unit matrixPython
3.11 / 3.12 / 3.13
3.11 / 3.12 / 3.13
componentPython 3.12
local resources
local resources
integrationlocal HTTP/WS
subprocess/MCP
subprocess/MCP
interfacesWeb
CLI
desktop
CLI
desktop
e2e / livenon-browser + browser required
live manual
live manual
Python 命令使用 uv run --locked。矩阵关闭 fail-fast,避免一个版本失败后取消其他结果。普通 PR 不执行 live。所有 CI 命令同时写入贡献文档。
7. 采用、调整与拒绝
| 状态 | 决定 | 边界 |
|---|---|---|
| 采用 | 层级优先目录、产品域二级目录、strict markers、strict xfail、固定 worker 数、Playwright-managed Chromium | 迁移时保持测试语义和数量 |
| 调整 | 保留必要的 Web source contracts,同时把纯逻辑迁移到 node:test | 不一次性重写全部 Web checks |
| 调整 | 初期只生成 coverage 报告 | 稳定后依据实测数据设置阈值 |
| 拒绝 | 新增 tox/nox、在 Web 再安装 Vitest、-n auto、全局任意覆盖率阈值 | 现有工具已经覆盖需求,或当前状态不具备稳定并行条件 |
| 排除 | Windows-specific 测试、打包和兼容性;keyring/Credential Manager | 不属于本次测试系统重构 |
8. 验收标准
- clean checkout 和包含 ignored/nested repository 文件的 checkout 收集相同 tracked tests,并产生相同 contracts 结果。
- unit 测试代码不直接使用 TestClient、subprocess、socket、真实后台线程或线程池、固定 sleep;生产模块内部线程资源必须在 teardown 中回收。
- Python required suites 无未处理线程 warning,连续三次固定 xdist worker 执行无 worker crash。
- 缺少前端产物的 component 行为与构建后 CSP 的 e2e 行为分别验证。
- CLI、Web、desktop 的 package scripts 全部由 CI 执行。
- 测试迁移前后 test 数量、skip 和 xfail 变化均有明确记录。
- required Python suite 为零失败;已知产品缺陷不能作为完成状态保留。
- 覆盖率先建立可复现基线,再以不高于实测基线的阈值阻止回退;阈值提高必须由新增有效测试支持。
- unit 结构契约识别直接和别名资源导入、sleep 函数直接导入与标准库线程类替换;autouse 运行时保护拦截测试代码直接启动真实线程、构造线程池或执行固定等待。unit 只允许模块限定的
asyncio.sleep(0),其位置参数与关键字参数形式均作为调度让步允许。 - npm 依赖风险按 workspace 单独审计;只采用兼容当前构建和测试的升级,不使用强制破坏性升级。
9. 实现状态
| 工作项 | 状态 | 完成证据 |
|---|---|---|
| 设计与基线 | 已完成 | 设计、站点构建、链接检查、clean-checkout baseline 和两阶段独立审查 |
| 输入枚举与共享状态 | 已完成 | Git-owned 输入枚举、临时 HOME 清理、局部 fixture、线程与 TLS 资源回收;专项及 unit 回归、独立规格和质量审查通过 |
| Python 目录迁移 | 已完成 | 目录与 marker 迁移完成;HTTP inventory 重复实现、agentic context 结构契约误报和 retry 测试隔离问题已修复;最终 required suite 为 5356 passed、10 skipped、1 xfailed,独立规格与质量审查通过 |
| Web/CLI/desktop | 已完成 | Web pure helper 使用 node:test,component 与 built-Web/browser e2e 分离;当前 Scheduler view-model 为 4 个 Node 单元测试,Web check/build、CLI typecheck/Vitest/build、desktop check 与独立审查通过 |
| CI 与贡献文档 | 已完成 | 十类独立 CI 结果、三版本 unit matrix、locked uv、非 browser e2e、完整 Web/CLI/desktop/browser 命令与贡献文档 contract;独立审查通过 |
| 等待、并发与大文件 | 已完成 | 结构契约覆盖资源导入、固定等待与全局线程类替换;unit autouse guard 检查测试直接创建线程/线程池、固定等待、生产资源 teardown 和全局状态恢复;5 个真实 runner/threadpool 文件共 104 个测试迁至 component;最终 required suite 为 5370 passed、10 skipped、2 deselected、1 xfailed |
| 覆盖率和最终验证 | 已完成 | API-key CLI unit 已隔离远程模型列表获取;Python 3.12 串行 unit branch-mode coverage 重复基线为 42.267228%,CI 先生成并上传 XML artifact,再以 6 位精度执行 40% floor;固定 -n 4 required selection 连续三次为 5370 passed、10 skipped、1 xfailed、0 worker crash;最终 Python、JavaScript、browser、docs、example、Desktop 目录包和静态检查门禁全部通过 |
10. 完善计划与实施记录
以下批次依次完成。每批先用公开或共享边界复现失败,再修复最小共享根因,执行专项与受影响测试,完成独立规格审查和质量审查后提交。前一批未通过审查时不开始下一批。
| 批次 | 范围 | 排除 | 门禁 | 状态 |
|---|---|---|---|---|
| required suite 归零 | 移除未被注册表使用的旧 channel 实现;修正 @function / @agentic_function 结构契约误报;隔离 retry 测试状态 | 不改变受管理 channel 实现和公开 API | 4 个失败节点专项通过;最终 required suite 为 5356 passed、10 skipped、1 xfailed;独立规格与质量审查通过 | 已完成 |
| 结构与等待契约 | 识别别名资源导入、线程类替换和非零异步等待;运行时拦截测试直接创建线程或线程池;迁移或改写实际违规测试 | 不实现 Python 控制流解释器,不按文件行数机械拆分测试 | 结构与运行时契约通过;unit 为 2434 passed、3 skipped;5 个迁移 component 文件为 104 passed;独立规格和质量审查通过 | 已完成 |
| 覆盖率门禁 | 记录精确 branch baseline;设置防回退阈值;只补关键未覆盖分支 | 不追求任意高覆盖率数字,不为覆盖率复制断言 | 隔离 Python 3.12.13 locked 环境连续两次均为 unit 2434 passed、3 skipped,branch-mode baseline 42.267228%;6 位精度的 40% floor 通过,43% 对照以退出码 2 失败;XML 在 floor 前上传,CI contract 通过 | 已完成 |
| JavaScript 依赖风险 | 分别审计 Web、CLI、desktop;升级可兼容依赖并验证 lockfile | 不执行 npm audit fix --force,不引入新 runner | 兼容升级后 Web 从 8 high 降至 5 high,CLI 从 4 high + 1 moderate 降至 1 moderate,desktop 从 5 high + 2 moderate 降至 2 high;剩余项分别要求 Next/eslint-config-next 16、esbuild 0.28、Electron 43 的独立兼容迁移;三个 workspace 的 npm ci 与既有 check/test/typecheck/build 全部通过 | 已完成 |
| 最终集成 | 合入最新 main,执行 Python、Web、CLI、desktop、browser、docs、examples 和 diff/status 门禁 | 不覆盖主工作区未提交修改,不远程 push | 串行 required suite 为 5370 passed、10 skipped、2 deselected、1 xfailed;固定 -n 4 连续三次均为 5370 passed、10 skipped、1 xfailed、0 worker crash;非 browser e2e 4 passed、2 deselected;unit coverage 两次为 42.267228%;Web 4 tests/typecheck/check/build、CLI 140 passed + 2 skipped/typecheck/build、desktop check 与 dist:dir、browser 2 passed、docs build/landing/0 broken links、example、Ruff 与 diff-check 全部通过;最终候选独立规格和质量审查通过,工作树 clean | 已完成 |