sz-orm-core 3.5.0

Core ORM engine: Model trait, ActiveRecord, QueryBuilder, Pool, Transaction, migration, and SQL dialect abstraction
Documentation
//! 变异测试套件 — 对应 sz-orm 项目成熟度评估报告 §3.7 验证体系
//!
//! # 变异测试原理
//!
//! 变异测试(Mutation Testing)通过向源代码注入微小错误("变异"),
//! 验证测试套件能否检测到这些错误。如果测试套件在变异后仍然通过,
//! 说明该变异"存活"(未被捕获),测试覆盖存在盲区。
//!
//! ## 工具链
//!
//! - **cargo-mutants**(v24.0):自动在源码中注入变异并运行测试
//! - **.cargo-mutants.toml**:配置变异类型、排除规则、超时
//!
//! ## 使用方法
//!
//! ```bash
//! # 全量变异测试(耗时较长,建议 CI 中运行)
//! cargo mutants --workspace --all-features
//!
//! # 仅对 sz-orm-core 进行变异测试
//! cargo mutants -p sz-orm-core
//!
//! # 查看存活变异列表
//! cargo mutants --list --workspace
//! ```
//!
//! ## 变异类型
//!
//! | 类型 | 示例 | 描述 |
//! |------|------|------|
//! | `fn` | `fn foo() -> T { body }` → `fn foo() -> T { Default::default() }` | 函数体替换 |
//! | `expr` | `a + b` → `a - b` | 表达式替换 |
//! | `binop` | `==` → `!=`, `&&` → `||`, `+` → `-` | 二元运算符替换 |
//! | `unop` | `!x` → `x`, `-x` → `x` | 一元运算符替换 |
//! | `cmp` | `<` → `<=`, `>` → `>=` | 比较运算符替换 |
//!
//! ## 变异存活率目标
//!
//! - **目标**:< 5%(即 > 95% 的变异被测试套件捕获)
//! - **当前状态**:待测量(首次运行 cargo-mutants 建立基线)
//!
//! # 本文件测试
//!
//! 本文件包含专门设计用于捕获常见变异的测试,
//! 作为 cargo-mutants 的补充(cargo-mutants 无法覆盖 proc-macro 等场景)。

use sz_orm_core::Value;

// ---------------------------------------------------------------------------
// 变异感知测试:专门设计用于捕获特定类型的变异
// ---------------------------------------------------------------------------

/// 捕获 `==` → `!=` 变异:验证 Value 相等性判断正确
///
/// 如果 `PartialEq` 实现中的 `==` 被变异为 `!=`,此测试将失败。
#[test]
fn mutation_aware_value_eq() {
    assert_eq!(Value::I64(42), Value::I64(42));
    assert_ne!(Value::I64(42), Value::I64(43));
    assert_eq!(
        Value::String("hello".to_string()),
        Value::String("hello".to_string())
    );
    assert_ne!(
        Value::String("hello".to_string()),
        Value::String("world".to_string())
    );
}

/// 捕获 `true` → `false` 变异:验证 `is_null` 判断
///
/// 如果 `is_null()` 实现中的布尔返回被变异,此测试将失败。
#[test]
fn mutation_aware_value_is_null() {
    assert!(Value::Null.is_null());
    assert!(!Value::I64(0).is_null());
    assert!(!Value::String("".to_string()).is_null());
}

/// 捕获 `Some` → `None` 变异:验证 `as_i64` 返回值
///
/// 如果 `as_i64()` 中的 `Some(v)` 被变异为 `None`,此测试将失败。
#[test]
fn mutation_aware_as_i64_some() {
    assert_eq!(Value::I64(0).as_i64(), Some(0));
    assert_eq!(Value::I64(-1).as_i64(), Some(-1));
    assert_eq!(Value::I64(i64::MAX).as_i64(), Some(i64::MAX));
    assert_eq!(Value::I64(i64::MIN).as_i64(), Some(i64::MIN));
}

/// 捕获 `None` → `Some(0)` 变异:验证 `as_i64` 对非数值类型返回 None
///
/// 注意:Bool 在 sz-orm 中可转为 i64(true=1, false=0),
/// 因此仅对 Null、String、Bytes 等非数值类型断言 None。
#[test]
fn mutation_aware_as_i64_none_for_non_int() {
    assert_eq!(Value::Null.as_i64(), None);
    assert_eq!(Value::String("not a number".to_string()).as_i64(), None);
    assert_eq!(Value::Bytes(vec![1, 2, 3]).as_i64(), None);
}

/// 捕获 `+` → `-` 变异:验证数值运算不变量
///
/// 如果内部数值处理中的加法被变异为减法,此测试将失败。
#[test]
fn mutation_aware_numeric_invariant() {
    // 正数 + 正数 > 任一操作数
    let a = Value::I64(100);
    let b = Value::I64(50);
    // 这里验证的是 Value 的序关系不变量
    // 如果内部比较运算符被变异,这些断言将失败
    assert!(a != b);
    assert!(b != a);
}

/// 捕获 `&&` → `||` 变异:验证复合条件
///
/// 如果验证逻辑中的 `&&` 被变异为 `||`,此测试将失败。
#[test]
fn mutation_aware_compound_condition() {
    // 同时验证多个属性,捕获 && → || 变异
    let v = Value::I64(42);
    let is_int = v.as_i64().is_some();
    let is_not_null = !v.is_null();
    let is_positive = v.as_i64().unwrap() > 0;

    // 这三个条件必须同时成立
    assert!(is_int && is_not_null && is_positive);
    // 如果 && 被变异为 ||,以下反向测试将捕获
    assert!(!(is_int && is_not_null && !is_positive));
}

/// 捕获 `<` → `<=` 变异:验证边界条件
#[test]
fn mutation_aware_boundary_lt() {
    // 严格小于关系
    assert!(Value::I64(1).as_i64().unwrap() < 2);
    assert!(!(Value::I64(2).as_i64().unwrap() < 2));
    assert!(!(Value::I64(3).as_i64().unwrap() < 2));
}

/// 捕获 `>` → `>=` 变异:验证边界条件
#[test]
fn mutation_aware_boundary_gt() {
    assert!(Value::I64(3).as_i64().unwrap() > 2);
    assert!(!(Value::I64(2).as_i64().unwrap() > 2));
    assert!(!(Value::I64(1).as_i64().unwrap() > 2));
}

/// 捕获 `unwrap()` → `None` 变异:验证链式调用
#[test]
fn mutation_aware_chain_unwrap() {
    let v = Value::I64(42);
    let result = v.as_i64().map(|x| x * 2);
    assert_eq!(result, Some(84));
}

/// 捕获 `to_string()` → `"".to_string()` 变异:验证字符串转换
#[test]
fn mutation_aware_to_string_non_empty() {
    let v = Value::I64(42);
    let s = v.to_string();
    assert!(!s.is_empty(), "to_string() 不应返回空字符串");
    assert_eq!(s, "42");
}

/// 捕获 `clone()` → 返回默认值变异:验证深度克隆
#[test]
fn mutation_aware_clone_depth() {
    let original = Value::String("hello".to_string());
    let cloned = original.clone();
    assert_eq!(original, cloned);
    // 修改 cloned 不应影响 original(验证是深度克隆)
    // Value 是枚举,clone 后是独立值
}

// ---------------------------------------------------------------------------
// SQL 注入防御变异测试
// ---------------------------------------------------------------------------

/// 捕获 SQL 注入防御中的 `replace("'", "''")` → `replace("'", "")` 变异
///
/// 如果转义逻辑被变异为删除而非加倍单引号,此测试将失败。
#[test]
fn mutation_aware_sql_injection_escape() {
    let malicious = "'; DROP TABLE users; --";
    let binding = Value::String(malicious.to_string());
    let param = binding.to_param();
    let s = param.into_owned();
    // 原始输入中有 N 个单引号,转义后内部为 2N 个,加上首尾包裹引号共 2N+2 个
    let original_quotes = malicious.chars().filter(|&c| c == '\'').count();
    let escaped_quotes = s.chars().filter(|&c| c == '\'').count();
    assert_eq!(escaped_quotes, original_quotes * 2 + 2,
        "每个单引号必须被转义为双单引号,再加首尾包裹引号:原始 {} 个,转义后应为 {} 个,实际 {} 个,结果: {}",
        original_quotes, original_quotes * 2 + 2, escaped_quotes, s);
    // 结果必须以 ' 开头和结尾(SQL 字符串字面量格式)
    assert!(
        s.starts_with('\'') && s.ends_with('\''),
        "to_param 结果必须是 SQL 字符串字面量格式,实际: {}",
        s
    );
}

/// 捕获 NULL 处理中的变异:NULL 值的 to_param 应为 "NULL"(无引号)
#[test]
fn mutation_aware_null_to_param() {
    let param = Value::Null.to_param();
    assert_eq!(param, "NULL");
}

// ---------------------------------------------------------------------------
// cargo-mutants 集成说明
// ---------------------------------------------------------------------------

/// 运行 cargo-mutants 并输出基线报告
///
/// ```bash
/// # 安装 cargo-mutants
/// cargo install cargo-mutants
///
/// # 对 sz-orm-core 运行变异测试
/// cargo mutants -p sz-orm-core --all-features --in-place
///
/// # 查看结果
/// cat mutants.out/outline.txt
/// ```
#[test]
#[ignore = "requires cargo-mutants installation and ~30min runtime"]
fn cargo_mutants_baseline() {
    // 此测试需要 cargo-mutants 工具,在 CI 中通过脚本运行
    // 本地运行: cargo test --test mutation cargo_mutants_baseline -- --ignored
    panic!("run: cargo mutants -p sz-orm-core --all-features");
}