A pure-MoonBit declarative API test runner: parses .probe cases, sends HTTP requests, asserts responses, and emits console / JUnit XML reports.
| 项目 | 内容 |
|---|---|
| 项目名称 | MoonProbe |
| 参赛者 | 吴海婷 |
| 联系方式 | 19177746962@163.com |
| GitHub 仓库 | https://github.com/wuhaiting321/MoonProbe |
| 项目方向 | MoonBit 开发者工具 / API 测试基础设施 |
| 是否为移植项目 | 否,原创项目 |
| 模块名 | wuhaiting321/moonprobe(遵循 mooncakes.io 的 <author>/<module> 命名规范) |
| 代码规模 | 5332 行 MoonBit 源码 + 5398 行测试代码,28 个 .mbt 文件,合计约 10730 行 |
| 测试状态 | moon test --target wasm-gc 全部通过(352 个用例,0 失败,含 5 个验收回归用例);js 后端在此基础上另含 9 个真实网络用例 |
| 构建状态 | wasm-gc 与 js 后端 moon check --deny-warn 零错误、零告警 |
| 开源许可证 | Apache-2.0 |
| 依赖 | 版本要求 | 说明 |
|---|---|---|
| MoonBit 工具链 | moonc ≥ 0.10.14 | 项目以 MoonBit 为主要实现语言;CI 每次都安装官方发布的最新工具链进行验证 |
| Node.js | ≥ 18,运行 .probe 用例时需要 | js 后端用 Node.js 内置的 net / tls 作为字节管道(经 process.execPath 启动子进程),不调用 curl 等任何外部命令;moon check / moon test 不需要 |
| 网络 | 运行会访问真实站点的用例时需要 | 单元测试完全离线,不受网络影响 |
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
export PATH="$HOME/.moon/bin:$PATH"
moon version --all # 应显示 moonc v0.10.14 或更高若 cli.moonbitlang.com 不可达,可改用镜像 https://cli.moonbitlang.cn/install/unix.sh (CI 中即按此顺序回退重试)。Windows 安装包见 https://www.moonbitlang.com/download/。
git clone https://github.com/wuhaiting321/MoonProbe.git
cd MoonProbemoon run cli --target js -- examples/example_com.probe[PASS] example.com answers with 200
Summary: 1 total, 1 passed, 0 failed in 3742 ms
Result: PASSED运行 CLI 必须带 --target js,原因见 5.5 节。
moon add wuhaiting321/moonprobe| 包 | 职责 | 源码规模 |
|---|---|---|
| parser/ | 词法分析、AST 定义、.probe 文件解析(含 config: 块) | 1833 行 |
| runner/ | 配置合并、占位符插值、重试与钩子编排、变量提取 | 1101 行 |
| http/ | 自研 HTTP/1.1 协议栈:URL、请求渲染、响应分帧、宿主机字节管道 | 1137 行 |
| assert/ | 状态码、严格 JSON 数值、文本、长度、类型、schema、耗时断言 | 912 行 |
| reporter/ | 控制台报告输出、JUnit XML 报告生成 | 155 行 |
| cli/ | 命令行入口、宿主文件读写 FFI | 180 行 |
| tests/regression/ | 9 月验收失败点的端到端回归用例(仅测试) | 211 行测试 |
| 能力 | 实现位置 | 当前行为 | 边界 / 未实现 |
|---|---|---|---|
| URL 解析 | http/url.mbt | scheme、host、IPv6、端口、path、query、fragment 全量解析;userinfo 丢弃 | 只接受绝对 URL;相对 URL 由运行器的 base_url 补全 |
| fragment | http/url.mbt | 保留在 Url 值中供回读,request_target() 只输出 path?query | fragment 不参与请求,也不会被提升为查询串 |
| 请求渲染 | http/client.mbt | Host 必填(IPv6 加方括号、默认端口省略)、CRLF 行尾、默认 User-Agent / Accept / Connection: close | 请求体整段渲染后发送,未实现流式分块上传或 multipart 自动编码 |
| Content-Length | http/client.mbt | 按 UTF-8 字节数计算;调用方自带的 Content-Length / Transfer-Encoding 被丢弃后重算 | 不发送请求侧 chunked |
| 100 Continue | http/parser.mbt | 解析响应时跳过除 101 外的全部 1xx,并计入 interim | 客户端不主动发送 Expect: 100-continue,不会等待中间响应再写正文 |
| 103 Early Hints | http/parser.mbt | 同上,作为中间响应跳过 | 不读取中间响应的头内容 |
| chunked 响应 | http/parser.mbt | 支持 chunk 扩展与 trailer | 不校验 trailer 字段语义 |
| 无正文响应 | http/parser.mbt | 204 / 304 / HEAD 即使带 Content-Length 也不读正文 | — |
| 头部续行 | http/parser.mbt | obs-fold 折叠为单行,名称按 ASCII 折叠大小写 | — |
| TCP / TLS | http/host_js.mbt | 由宿主的 Node.js 内置 net / tls 提供字节管道,经 process.execPath 启动 | 每次请求一个连接(Connection: close),不复用 keep-alive |
| 协议范围 | — | HTTP/1.1 | 不支持 HTTP/2、HTTP/3、代理、自动重定向与压缩内容解码 |
# 套件级全局配置:对本文件的所有用例生效
config:
base_url: "https://httpbin.org" # 相对 url 的基址
global_timeout: 8000 # 每个用例的默认超时(毫秒)
global_headers: # 每个用例都会带上的请求头
Accept: "application/json"
Authorization: "Bearer ${API_TOKEN}" # 值里可以引用宿主环境变量
name: "1. read the token from the response"
request:
method: GET
url: "/response-headers" # 相对 url:拼接 config 里的 base_url
query:
# httpbin 会把查询参数原样变成响应头,方便演示三种取值来源
echo: "demo-token-42"
X-Trace-Id: "trace-7f3a"
Set-Cookie: "session=abc123"
expect:
status: 200
json:
echo: "demo-token-42" # 点号路径校验嵌套字段
contains: "demo-token-42" # 原始响应文本必须包含该子串
type:
echo: string # JSON 类型
duration_ms <= 2000 # 耗时预算
set: token = echo # 从响应 JSON 取值
set: trace = header.X-Trace-Id # 从响应头取值
set: sid = cookie.session # 从 Cookie 取值
name: "2. send the extracted token back"
request:
method: GET
url: "/get"
timeout: 3000 # 用例级超时,优先于 global_timeout
query:
echo: "{{token}}" # 引用上一个用例提取的变量
headers:
Authorization: "Bearer {{token}}"
expect:
status: 200
json:
args.echo: "demo-token-42"
schema:
args: object! # 必需字段用 `!` 标记
echo: string! # object 下的缩进行是它的嵌套结构
url: string!# 注释既可独占一行(缩进随意),也可跟在任意一行末尾;两者都在词法阶段 被剥离,所以既不影响缩进判定,也不会混进值里。引号内的 # 不算注释,紧贴 文本的 # 也不算——url: https://x/a#frag 会保留 #frag。值本身要以 # 开头时必须加引号,例如 contains: "#tag"。
expect:
json:
code: 999 # 要求数字 999,7、7.0 之外的一切数值都不通过
code: "999" # 要求字符串 "999",数字 999 不通过
ratio: 7.5 # 非整数:按十进制规范形式精确比较
ratio: 7.0 # 数值为整数,按 Int 比较,等价于 7
active: true # Bool 只匹配 true / false,不匹配 1 / 0
extra: null # Null 只匹配 JSON null
name: alice # 未加引号且不是数字/布尔/null,按字符串 alice 比较| 维度 | .http / hurl / Karate | moon test | MoonProbe |
|---|---|---|---|
| 用例形态 | 文本 / 脚本 | MoonBit 源码 | .probe 文本 |
| 验证对象 | 运行中的服务(黑盒) | 源码函数(白盒) | 运行中的服务(黑盒) |
| 实现语言 | 各自宿主语言 | MoonBit | 纯 MoonBit |
| CI 报告 | 各自格式 | moon test 输出 | 终端报告 + JUnit XML |
| 用例间状态传递 | 部分支持 | 依赖代码变量 | set: + {{var}} 上下文串联 |
| 套件级配置与环境变量 | 各自语法 | 无 | config: 块 + ${ENV} 注入 |
| 断言族 | 视工具而定 | 由代码自行编写 | 状态码 / JSON / 文本 / 长度 / 类型 / schema / 耗时 |
# 静态检查与单元测试(不依赖网络与 Node.js)
moon check --target wasm-gc
moon test --target wasm-gc
# 只跑 9 月验收回归套件(它同样包含在上一条命令里)
moon test --target wasm-gc -p wuhaiting321/moonprobe/tests/regression
# 运行示例用例:CLI 必须显式指定 --target js
moon run cli --target js -- examples/httpbin.probe
# 生成 JUnit XML 报告
moon run cli --target js -- examples/auth_chain.probe --junit junit.xml运行 CLI 必须使用 --target js。 CLI 依赖 JS 宿主读写 .probe 用例文件与报告 文件,并通过 Node.js 内置的 net / tls 建立 TCP 连接(见 http/host_js.mbt); 用其他后端(如 native)运行会给出明确的“无网络传输”错误。模块的 preferred_target 也已设为 js。
bash check_验收.sh # Linux / macOS
powershell -ExecutionPolicy Bypass -File .\check_验收.ps1 # Windows| # | 实质工作 | 交付物 | 代码增量 |
|---|---|---|---|
| 1 | 断言引擎类型系统增强:JSON 数值严格比较 | assert/assert.mbt 重写 JSON 期望分类与十进制规范化比较;新增 assert/json_numeric_test.mbt(32 个数值 / 类型用例);parser 保留 json: 期望值的引号状态 | +358 行源码,+249 行测试,解析器 +71/-12 |
| 2 | 移除 curl,自研 HTTP/1.1 协议栈 | 新增 http/ 包(url.mbt、client.mbt、parser.mbt、stream.mbt、base64.mbt、host_js.mbt、host_stub.mbt)与 runner/transport.mbt;删除 runner/http_js.mbt、runner/http_stub.mbt 中的 curl FFI | +1947 行(1137 源码 + 810 测试),删除 125 行 |
| 3 | 核心功能重新定位:错误诊断降级,HTTP 协议合规性上位 | README「三、核心功能范围」重组:删除「错误诊断」独立条目并并入 5.2 解析器章节,新增「HTTP 协议合规性」 | 文档结构重构 |
| 4 | 9 月验收回归套件 | 新增 tests/regression/:5 条端到端用例 + moon.pkg,接入 moon test 与 CI | +211 行测试 |
| # | 官方要求 | 本项目对应证据 |
|---|---|---|
| 1 | 以 MoonBit 为主要实现语言,moonc 不低于 0.10.14 | 全部 28 个 .mbt 文件、5332 行源码均为 MoonBit;本地验证使用 moonc v0.10.16,CI 每次安装官方最新工具链并打印 moon version --all |
| 2 | GitHub 仓库公开可访问,提交记录清晰 | 仓库 https://github.com/wuhaiting321/MoonProbe 公开;提交按 feat(...) / docs(...) / chore(...) 分模块,每个提交可独立编译与测试 |
| 3 | 源代码结构清晰,能完成声明的核心功能 | parser/(词法 + AST + 解析)、assert/(断言引擎)、http/(自研 HTTP/1.1 栈)、runner/(执行与重试)、reporter/(终端 + JUnit XML)、cli/(入口);核心功能与第三章条目一一对应 |
| 4 | README 说明目标、安装、用法、示例,并可复现 | 第四章给出安装与三个示例,第五章给出语法、实现与构建命令,5.2.1 给出协议边界;所有命令均在本机与 CI 实际执行 |
| 5 | 使用持续集成,覆盖检查、构建、测试 | ci.yml:moon fmt --check、wasm-gc / js 双后端 moon check --deny-warn、moon test --target wasm-gc、回归套件单步、双后端 moon build、真实网络示例 |
| 6 | 至少一个可运行示例或最小使用样例 | examples/example_com.probe:moon run cli --target js -- examples/example_com.probe,输出 [PASS] example.com answers with 200 |
| 7 | 完整测试,覆盖核心功能路径 | 352 个用例全部通过;其中 tests/regression/ 5 条端到端用例锁定 9 月验收失败点,assert/json_numeric_test.mbt 32 条数值边界用例,http/ 包 56 条协议用例 |
| 8 | 发布到 mooncakes.io | wuhaiting321/moonprobe@0.2.1,moon add wuhaiting321/moonprobe@0.2.1 |
| 9 | 采用 OSI 认可的开源许可证;参考或移植需符合原许可证 | Apache-2.0(LICENSE);非移植项目,原创性说明见 5.7,仅依赖 Apache-2.0 的官方标准库 |
Total tests: 352, passed: 352, failed: 0.
[PASS] example.com answers with 200
Summary: 1 total, 1 passed, 0 failed in 2218 ms
Result: PASSEDInstall
Download zipA pure-MoonBit declarative API test runner: parses .probe cases, sends HTTP requests, asserts responses, and emits console / JUnit XML reports.