Windmill 集成测试体系深度解析:基于 docker-compose 的升级兼容性与端到端回归方案
【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill
Windmill 集成测试(integration tests)是该开源工作流引擎为保障"每次提交到 main 分支"后的端到端质量而设计的自动化测试体系:先部署最新发布的稳定镜像,再升级到本次提交构建的 dev 镜像,并借助WMILL_RUNNING_DEV环境变量验证"旧数据 + 新版本"的升级兼容性。读完本文,你将掌握该测试套件的完整执行流程、docker-compose 测试基础设施、各测试用例的断言逻辑,以及如何在本地复现这套端到端回归方案。
测试定位与核心设计思想
集成测试位于仓库的 integration_tests 目录,其设计目标非常明确:在每次 push 到 main 分支时(排除 tag 提交)自动运行,验证 Windmill 全链路功能在真实部署环境下的可用性。
该体系最核心的设计思想是升级兼容性验证。传统集成测试往往只在单一版本上运行,而 Windmill 的这套测试采用"先跑稳定版、再跑 dev 版"的双轮模式:
- 第一轮:拉取
[version.txt](https://link.gitcode.com/i/8a71e67f94201581555595ad68a4c777)中记录的最新已发布镜像,部署完整的 Windmill 栈,运行全部测试; - 第二轮:将栈升级到本次提交构建的 dev 镜像(tag 为
dev),再次运行全部测试。
两轮之间,脚本、Flow、Schedule 等实体对象不会被删除,而是带着第一轮部署的数据直接进入第二轮。此时WMILL_RUNNING_DEV环境变量被置为1,测试代码据此切换行为——这正是验证"在旧版本 Windmill 上部署的 scripts/flows/schedules,升级后不会损坏"的关键机制。部分测试只有在WMILL_RUNNING_DEV == 1时才跳过或才运行,从而将"升级回归"与"纯 dev 新功能验证"两类场景清晰隔离。
测试流水线的四个阶段
整个流程由 run.sh 脚本驱动(set -euo pipefail保证任何一步失败即中止):
| 阶段 | 操作 | 说明 |
|---|---|---|
| 1. 准备 Python 环境 | python -m venv .venv/并pip install -r requirements.txt | 创建独立虚拟环境,避免污染系统 Python |
| 2. 启动稳定版栈 | WM_IMAGE=... WM_VERSION=... docker compose up -d | 部署[docker-compose.yml](https://link.gitcode.com/i/9c908a919323cb4999ba606daf37bc0c)定义的完整服务 |
| 3. 第一轮测试 | .venv/bin/python -m unittest -v test | 针对已发布稳定版本运行全部测试类 |
| 4. 升级并重测 | WM_VERSION=dev docker compose up -d,然后WMILL_RUNNING_DEV=1 python -m unittest -v test | 升级到 dev 镜像后重跑,并同步收集日志到./logs/docker-compose.log |
其中docker compose logs --no-color --follow会将容器日志重定向到integration_tests/logs目录,便于 CI 失败时归档排查。依赖清单见 requirements.txt,核心为httpx(HTTP 客户端)、docker(容器管理,用于 agent worker 测试)与gitpython。
测试基础设施:docker-compose 服务拓扑
docker-compose.yml 定义了测试所需的完整服务栈,共 5 个服务:
- db:
postgres:14,暴露5432端口,通过 healthcheck(pg_isready)等待就绪,数据持久化到db_data卷。注释指出如需外部数据库,可将 replicas 设为 0 并在.env中指定DATABASE_URL; - windmill_server:
${WM_IMAGE}:${WM_VERSION},暴露8000端口,设置DATABASE_URL、MODE=server,healthcheck 用curl -f http://localhost:8000/api/version探测 API 可用性,并依赖 db 健康; - windmill_worker:同样使用
${WM_IMAGE}:${WM_VERSION},MODE=worker、WORKER_GROUP=default,限制 1 CPU / 2048M 内存,挂载 Docker socket(允许 worker 内部运行 Docker 容器)及worker_dependency_cache卷;注释提示调试时可KEEP_JOB_DIR=true并挂载/tmp/windmill; - npm_registry:
verdaccio/verdaccio(端口4873),为 SDK 测试提供私有 NPM registry,配置见 verdaccio/config.yaml——监听localhost:4873与npm_registry:4873,包默认代理到官方 npmjs; - pypi_server:
pypiserver/pypiserver:latest(端口8080),为 Python SDK 测试提供私有 PyPI 服务。
镜像与版本由 common.sh 统一注入:
export WM_VERSION=$(cat ${root_dirpath}/version.txt) export WM_VERSION_DEV="dev" export WM_IMAGE="ghcr.io/windmill-labs/windmill-ee"即:稳定版版本号取自 version.txt(当前仓库为1.809.0),dev 版固定为dev标签,镜像统一使用 Enterprise 版镜像windmill-ee。
测试用例集:从多语言脚本到 Agent Worker
所有测试类通过 test/init.py 聚合导出,python -m unittest -v test会自动发现并运行它们。
多语言脚本回归测试
test/identity_script_test.py 验证 Windmill 支持的 6 种脚本语言(bash、bun、deno、go、python3、php)能否正确执行"恒等函数"并回传结果:
- 第一轮(非 dev)创建脚本:
create_script(path="u/admin/{lang}_identity_script", content=..., language=lang); - 每个语言一个独立测试方法,断言
run_sync返回原值。有趣的是 bash 断言为字符串"5",注释说明"bash only knows strings"——体现了对不同语言类型系统的差异化处理; - dev 轮(
WMILL_RUNNING_DEV=1)则删除脚本,完成对旧版本部署脚本的清理验证。
跨语言 Flow 流水线测试
test/increment_flow_test.py 是测试套件中最具代表性的用例:构建一个 5 模块的 Flow(deno → bun → python3 → go → bash),每个模块对输入x执行+1,并通过input_transforms串联:
{ "id": "b", "value": { "type": "rawscript", "content": "export async function main(x: number) {\\n return x + 1\\n}\\n", "language": "bun", "input_transforms": { "x": { "expr": "results.a", "type": "javascript" } } } }最终断言输入x=5经过+5(第一个模块的flow_input.x + 5)再加 4 次+1后,bash 输出字符串"15"。这同时验证了跨语言数据传递、results.{id}引用语法与 Flow 调度引擎的正确性。
Schedule 定时调度测试
test/schedule_test.py 创建基于脚本(u/admin/scheduled_script)和基于 Flow(u/admin/scheduled_flow)的两个 Schedule,cron 表达式为*/5 * * * * *(每 5 秒),时区Europe/Paris。测试轮询jobs/list?script_path_exact=...接口,等待 30 秒内出现created_at晚于测试开始时间的运行记录,从而证明调度器在稳定版与升级后的 dev 版上都能持续触发任务。
Agent Worker 测试
test/agent_workers.py 是套件中依赖 Docker SDK 的特殊用例,覆盖 Windmill 的 Agent 模式:
- 通过
/api/agent_workers/create_agent_token生成jwt_agent_前缀的 JWT token,并解析 payload 断言worker_group、tags、exp字段; - 用 Docker SDK 启动一个
MODE=agent+AGENT_TOKEN=...的容器,通过get_workers_list轮询确认 Agent 成功连接; - 创建一个带
agent_test自定义标签的 bash 脚本并同步运行,验证标签路由让任务落到 Agent worker 上。
容器清理通过atexit.register(cleanup_docker_container)兜底,防止测试崩溃后残留容器。
WindmillClient:测试与后端 API 的桥梁
所有测试都复用 test/wmill_integration_test_utils.py 中的WindmillClient,它封装了完整的 API 交互,是理解测试原理的关键:
- 登录与会话:向
/api/auth/loginPOSTadmin@windmill.dev / changeme(与 docker-compose 的默认管理员一致),换取 Bearer token,client 超时设为 60 秒——注释说明"Go/Rust 首次编译可能耗时 10 秒以上"; - License 注入:
_set_license_key读取LICENSE_KEY环境变量并 POST 到/api/settings/global/license_key,这就是 README 中WM_LICENSE_KEY_CI的落地方式——Enterprise 版本必须携带有效 license key,全部测试才能运行; - Workspace 管理:自动创建
integration-tests工作区(已存在则跳过),对应真实用户按 workspace 隔离资源的模型; - 核心执行方法
run_sync(path, args, type):POST 到/api/w/{workspace}/jobs/run_wait_result/{type}/{path},type="p"运行脚本、type="f"运行 Flow。该端点在后端实现于 jobs.rs 的run_wait_result_script_by_path,会先校验 license key(check_license_key_valid)、校验 scope(jobs:run:scripts:{path})、执行参数预处理,再调用内部实现排队执行并等待结果; - 部署状态轮询:
create_script提交后轮询/scripts/deployment_status/h/{hash},直至lock字段非空(部署成功)或出现lock_error_logs(部署失败),并带有 60 秒超时; - 此外还封装了 Flow、Schedule、Variable、Resource、Git Sync 配置、fork workspace、Gitea 客户端等大量辅助方法。
本地运行指南:免镜像的轻量调试
README 明确指出,本地直接跑测试并不轻松——因为需要一份包含最新代码的 Docker 镜像。但测试本质上只是向http://localhost:8000发起 API 调用,因此可以绕过容器直接用本地编译的二进制运行。
前置条件:WindmillEnterprise 版本,需要 license key,设置为环境变量WM_LICENSE_KEY_CI。
以运行identity_script_test.py为例,终端 1 启动本地服务:
cargo run --features enterprise终端 2 运行测试:
python -m unittest -v test.TestIdentityScript这种"真实后端 + Python 测试客户端"的模式让开发者无需构建 Docker 镜像即可快速验证单点功能。注意部分测试需要额外准备(详见下文 SDK 测试)。
SDK 测试的特殊准备:私有 registry 发布
README 特别提示:SDK 相关测试需要 Windmill SDK 包先发布到私有 NPM registry 或 PyPI 服务器,自定义准备逻辑封装在 build.sh 中。该脚本的完整流程为:
source common.sh读取镜像与版本变量,docker pull稳定版与 dev 版镜像;- 调用
.github/change-versions.sh将版本号临时改为${WM_VERSION}-dev; - 启动
npm_registry服务,通过 curl 向 Verdaccio 注册windmill用户(密码changeme),再用npm-cli-login完成登录; - 执行 typescript-client/publish.sh 将 TypeScript SDK 发布到
http://localhost:4873; docker compose down清理,并通过change-versions.sh恢复原版本号。
脚本末尾的注释# TODO: publish Python SDK to private PyPI registry表明 Python SDK 的发布逻辑尚未实现——这也解释了 README TODO 列表中"Test Python SDK"的由来。
CI 集成:与镜像构建流水线的联动
这套测试并非孤立运行,而是作为 Docker 镜像发布流水线的质量关卡。在 .github/workflows/docker-image.yml 中,run_integration_testjob 依赖build_ee(Enterprise 镜像构建),运行在ubicloud上,步骤为:
- name: Prepare test run run: cd integration_tests && ./build.sh - name: Test run timeout-minutes: 15 env: LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }} run: cd integration_tests && ./run.sh - name: Archive logs uses: actions/upload-artifact@v4 with: path: integration_tests/logs两个步骤都带有条件! startsWith(github.ref, 'refs/tags/v'),与 README 所述"每次 push 到 main(排除 tags)"一致;测试失败时日志会作为 artifact 归档;tag_latestjob 又依赖run_integration_test,集成测试通过后才允许打latesttag,可见该套件是发布门禁的重要一环。
已知缺口:待补充的测试场景
README 的 TODO 列表清晰标注了当前套件尚未覆盖的领域,对希望贡献测试代码的开发者是很好的切入点:
- 测试 Python SDK
- 测试 job 失败场景(当前主要验证成功路径)
- 测试专用 worker(dedicated workers)
- Schedule 的 Error handlers 与 Recovery handlers
- 并发限制(Concurrency limits)
小结
Windmill 的集成测试体系提供了一套可复用的"升级兼容性 + 端到端回归"参考范式:以 docker-compose 构建接近生产的服务拓扑,用轻量 Python 客户端直连真实 API,通过环境变量切换"旧数据跑新版本"的验证模式,并让该套件嵌入 CI 作为发布门禁。对于需要保障"数据库与已部署实体跨版本不破坏"的开发者,这套方案的思路(稳定版播种 → dev 版回归 → 差异化断言)可以直接借鉴。
【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考