关于 differential-fuzzer
Differential Fuzzer 是用于以生成的 SQL 语句测试 Turso 与 SQLite 的 Rust 命令行工具,面向数据库开发者,可发现并复现行为差异,并提供缩减后的失败脚本及并排探测工具。
主要功能
- 01生成 SQL 进行差异测试
- 02比较 Turso 与 SQLite 结果
- 03按种子和配置重放运行
- 04缩减失败复现脚本
- 05并排探测 SQL 脚本
适用场景
- 01发现 Turso 与 SQLite 的结果差异
- 02根据种子复现失败
- 03将失败测试缩减为小脚本
- 04把发现的问题转为回归测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tursodatabase/ turso --skill differential-fuzzer安装后在 Claude Code 中输入/differential-fuzzer确认已加载。
详细介绍
差分模糊测试器
始终加载调试技能以供参考
差分模糊测试器会将生成的 SQL 语句在 Turso 中的结果与 SQLite 进行比较,以查找正确性问题。
位置
testing/differential-oracle/fuzzer/
运行模糊测试器
单次运行
# Basic run (100 statements, random seed)
cargo run --bin differential_fuzzer
# With specific seed for reproducibility
cargo run --bin differential_fuzzer -- --seed 12345
# More statements with verbose output
cargo run --bin differential_fuzzer -- -n 1000 --verbose
# Keep database files after run (for debugging)
cargo run --bin differential_fuzzer -- --seed 12345 --keep-files
# All options
cargo run --bin differential_fuzzer -- \
--seed <SEED> # Deterministic seed
-n <NUM> # Number of statements (default: 100)
-t <NUM> # Number of tables (default: 2)
-c <NUM> # Columns per table (default: 5)
--verbose # Print each SQL statement
--keep-files # Persist .db files to disk
持续模糊测试(循环模式)
# Run forever with random seeds
cargo run --bin differential_fuzzer -- loop
# Run 50 iterations
cargo run --bin differential_fuzzer -- loop 50
Docker 运行器(CI/生产环境)
# Build and run from repo root
docker build -f testing/differential-oracle/fuzzer/docker-runner/Dockerfile -t fuzzer .
docker run -e GITHUB_TOKEN=xxx -e SLACK_WEBHOOK_URL=xxx fuzzer
Docker 运行器的环境变量:
TIME_LIMIT_MINUTES- 总运行时长(默认值:1440 = 24h)PER_RUN_TIMEOUT_SECONDS- 单次运行超时时间(默认值:1200 = 20min)NUM_STATEMENTS- 每次运行的语句数(默认值:1000)LOG_TO_STDOUT- 输出模糊测试器日志(默认值:false)GITHUB_TOKEN- 用于自动提交问题SLACK_WEBHOOK_URL- 用于发送通知
输出文件
所有输出都写入 simulator-output/ 目录:
| 文件 | 说明 |
|---|---|
test.sql | 所有已执行的 SQL 语句。失败的语句以 -- FAILED: 为前缀,错误以 -- ERROR: 为前缀 |
schema.json | 运行结束时(或失败时)的数据库架构 |
test.db | Turso 数据库文件(仅在使用 --keep-files 时生成) |
test-sqlite.db | SQLite 数据库文件(仅在使用 --keep-files 时生成) |
重现错误
始终遵循以下步骤
-
在错误输出中找到种子和配置:
INFO: Starting differential_fuzzer with config: SimConfig { seed: 12345, ..., weight_profile: Writes } -
使用该种子和配置重新运行(种子只能在相同配置下重放):
cargo run --bin differential_fuzzer -- --seed 12345 --profile writes --verbose --keep-files -
先阅读最小化的重现内容。 如果出现 oracle 失败,模糊测试器会将以下文件写入
simulator-output/:minimized.sql- 自动生成的缩减状态脚本,以及缩减后的失败语句。从这里开始。turso-state.sql/sqlite-state.sql- 每个引擎的完整状态,保存为可重放的脚本;当你需要保留比最小化版本更多的内容时使用。test.sql- 所有已执行的语句(失败语句会标记为-- FAILED:)。如果失败取决于状态的构建过程,而不只是状态内容,最小化器会改为重放此历史记录。schema.json- 失败时的表结构。
-
使用
differential_probe检查重现内容。 它会在 Turso 和 SQLite 上并排运行每行一条语句的脚本,输出每条语句的两种结果,标记差异,并比较最终表内容。退出代码为 1 表示存在差异。cargo run -q -p differential-fuzzer --bin differential_probe -- \ simulator-output/minimized.sql使用它,而不是将 SQL 通过管道传入两个 shell:tursodb shell 无法执行
ATTACH ':memory:' AS aux,因此带有aux架构的模糊测试器重现内容只能通过 probe 正确运行。也可以从 stdin 读取:echo "SELECT ~X'96';" | cargo run -q -p differential-fuzzer --bin differential_probe. -
通过编辑脚本进行二分排查。 复制
minimized.sql,每次简化一处(将表达式替换为常量、删除一列、删除一行状态语句),并在每次编辑后重新运行 probe。差异标记会立即告诉你此次编辑是否保留了问题。这个循环通常会得到一行的最小用例,可以交给两个引擎上的EXPLAIN。 -
在修复完成前,从最小用例创建一个永久的
*.sqltest回归测试。 只保留暴露问题所需的架构、数据、语句和断言。种子和模糊测试器的最小化产物可用于调试,但不能替代此回归测试。只有当.sqltest无法表达相应行为时才使用 Rust 测试,并始终加载调试技能以供参考。
理解失败
Oracle 失败类型
- 行集不匹配 - Turso 返回的行与 SQLite 不同
- Turso 报错,但 SQLite 成功 - Turso 拒绝了有效 SQL
- SQLite 报错,但 Turso 成功 - Turso 接受了无效 SQL
- 架构不匹配 - DDL 执行后,表或列存在差异
警告(非致命)
- 无序 LIMIT 不匹配 - 不带 ORDER BY 的 LIMIT 可能会返回不同但有效的行
关键源文件
| 文件 | 用途 |
|---|---|
main.rs | CLI 解析,入口点 |
runner.rs | 主模拟循环,在两个数据库上执行语句 |
oracle.rs | 比较 Turso 与 SQLite 的结果 |
schema.rs | 检查两个数据库的架构 |
memory/ | 用于确定性模拟的内存 IO |
跟踪
设置 RUST_LOG 以获取更详细的输出:
RUST_LOG=debug cargo run --bin differential_fuzzer -- --seed 12345