1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! 变异测试套件 — 对应 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");
}