mcpField
D
differential-fuzzer

differential-fuzzer

github.com/tursodatabase测试与质量25.1k1.2k 次安装今天更新
代码仓库

关于 differential-fuzzer

Differential Fuzzer 是用于以生成的 SQL 语句测试 Turso 与 SQLite 的 Rust 命令行工具,面向数据库开发者,可发现并复现行为差异,并提供缩减后的失败脚本及并排探测工具。

主要功能

  1. 01生成 SQL 进行差异测试
  2. 02比较 Turso 与 SQLite 结果
  3. 03按种子和配置重放运行
  4. 04缩减失败复现脚本
  5. 05并排探测 SQL 脚本

适用场景

  1. 01发现 Turso 与 SQLite 的结果差异
  2. 02根据种子复现失败
  3. 03将失败测试缩减为小脚本
  4. 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.dbTurso 数据库文件(仅在使用 --keep-files 时生成)
test-sqlite.dbSQLite 数据库文件(仅在使用 --keep-files 时生成)

重现错误

始终遵循以下步骤

  1. 在错误输出中找到种子和配置:

    INFO: Starting differential_fuzzer with config: SimConfig { seed: 12345, ..., weight_profile: Writes }
    
  2. 使用该种子和配置重新运行(种子只能在相同配置下重放):

    cargo run --bin differential_fuzzer -- --seed 12345 --profile writes --verbose --keep-files
    
  3. 先阅读最小化的重现内容。 如果出现 oracle 失败,模糊测试器会将以下文件写入 simulator-output/:

    • minimized.sql - 自动生成的缩减状态脚本,以及缩减后的失败语句。从这里开始。
    • turso-state.sql / sqlite-state.sql - 每个引擎的完整状态,保存为可重放的脚本;当你需要保留比最小化版本更多的内容时使用。
    • test.sql - 所有已执行的语句(失败语句会标记为 -- FAILED:)。如果失败取决于状态的构建过程,而不只是状态内容,最小化器会改为重放此历史记录。
    • schema.json - 失败时的表结构。
  4. 使用 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.

  5. 通过编辑脚本进行二分排查。 复制 minimized.sql,每次简化一处(将表达式替换为常量、删除一列、删除一行状态语句),并在每次编辑后重新运行 probe。差异标记会立即告诉你此次编辑是否保留了问题。这个循环通常会得到一行的最小用例,可以交给两个引擎上的 EXPLAIN。

  6. 在修复完成前,从最小用例创建一个永久的 *.sqltest 回归测试。 只保留暴露问题所需的架构、数据、语句和断言。种子和模糊测试器的最小化产物可用于调试,但不能替代此回归测试。只有当 .sqltest 无法表达相应行为时才使用 Rust 测试,并始终加载调试技能以供参考。

理解失败

Oracle 失败类型
  1. 行集不匹配 - Turso 返回的行与 SQLite 不同
  2. Turso 报错,但 SQLite 成功 - Turso 拒绝了有效 SQL
  3. SQLite 报错,但 Turso 成功 - Turso 接受了无效 SQL
  4. 架构不匹配 - DDL 执行后,表或列存在差异
警告(非致命)
  • 无序 LIMIT 不匹配 - 不带 ORDER BY 的 LIMIT 可能会返回不同但有效的行

关键源文件

文件用途
main.rsCLI 解析,入口点
runner.rs主模拟循环,在两个数据库上执行语句
oracle.rs比较 Turso 与 SQLite 的结果
schema.rs检查两个数据库的架构
memory/用于确定性模拟的内存 IO

跟踪

设置 RUST_LOG 以获取更详细的输出:

RUST_LOG=debug cargo run --bin differential_fuzzer -- --seed 12345

评论

支持 Markdown