use rand::Rng;
use serde_json::{json, Map, Value};
use super::dictionary::{Dictionary, TokenCategory};
pub struct JsonMutator;
impl JsonMutator {
pub fn type_confuse(value: &Value, rng: &mut impl Rng) -> Value {
match value {
Value::String(s) => match rng.gen_range(0..6) {
0 => Value::Number(s.len().into()),
1 => Value::Bool(!s.is_empty()),
2 => json!([s]),
3 => json!({"value": s}),
4 => Value::Null,
_ => Value::Number((-1_i64).into()),
},
Value::Number(n) => match rng.gen_range(0..5) {
0 => Value::String(n.to_string()),
1 => Value::Bool(n.as_f64().unwrap_or(0.0) != 0.0),
2 => json!([n]),
3 => Value::Null,
_ => Value::String(format!("{}x", n)),
},
Value::Bool(b) => match rng.gen_range(0..5) {
0 => Value::String(b.to_string()),
1 => Value::Number(if *b { 1 } else { 0 }.into()),
2 => json!([b]),
3 => Value::Null,
_ => Value::String(if *b { "yes" } else { "no" }.to_string()),
},
Value::Array(arr) => match rng.gen_range(0..4) {
0 => Value::String(format!("{:?}", arr)),
1 => json!({"array": arr}),
2 => Value::Null,
_ => {
if arr.is_empty() {
json!([null])
} else {
arr.first().cloned().unwrap_or(Value::Null)
}
}
},
Value::Object(obj) => match rng.gen_range(0..4) {
0 => Value::String(serde_json::to_string(obj).unwrap_or_default()),
1 => json!([obj]),
2 => Value::Null,
_ => {
if obj.is_empty() {
json!({"key": "value"})
} else {
obj.values().next().cloned().unwrap_or(Value::Null)
}
}
},
Value::Null => match rng.gen_range(0..5) {
0 => Value::String("null".to_string()),
1 => Value::Number(0.into()),
2 => Value::Bool(false),
3 => json!([]),
_ => json!({}),
},
}
}
pub fn boundary_value(value: &Value, rng: &mut impl Rng) -> Value {
match value {
Value::Number(_) => {
let boundaries: &[i64] = &[
i64::MAX,
i64::MIN,
i32::MAX as i64,
i32::MIN as i64,
0,
-1,
1,
-0,
i16::MAX as i64,
i16::MIN as i64,
255,
256,
-128,
127,
];
Value::Number(boundaries[rng.gen_range(0..boundaries.len())].into())
}
Value::String(_) => {
let boundaries = [
"",
" ",
"\0",
"\n",
"\r\n",
"\t",
&"a".repeat(10000),
&"A".repeat(65536),
"null",
"undefined",
"NaN",
"Infinity",
"-Infinity",
"true",
"false",
"0",
"-1",
];
Value::String(boundaries[rng.gen_range(0..boundaries.len())].to_string())
}
Value::Array(_) => {
let idx = rng.gen_range(0..6);
match idx {
0 => json!([]),
1 => json!([null]),
2 => json!([[]]),
3 => Value::Array(vec![Value::Array(vec![Value::Array(vec![])]); 5]),
4 => Value::Array(vec![Value::Null; 1000]),
_ => json!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
}
}
Value::Object(_) => {
let choices: &[Value] = &[
json!({}),
json!({"": ""}),
json!({"null": null}),
json!({"__proto__": {}}),
json!({"constructor": {}}),
json!({"toString": null}),
];
choices[rng.gen_range(0..choices.len())].clone()
}
Value::Bool(_) => {
if rng.gen_bool(0.5) {
Value::Bool(rng.gen_bool(0.5))
} else {
Value::String(if rng.gen_bool(0.5) { "true" } else { "false" }.to_string())
}
}
Value::Null => {
let choices: &[Value] = &[
Value::Null,
Value::String("null".to_string()),
Value::String("".to_string()),
Value::Number(0.into()),
Value::Bool(false),
];
choices[rng.gen_range(0..choices.len())].clone()
}
}
}
pub fn deep_nest(depth: usize, array_mode: bool) -> Value {
let mut v = json!("leaf");
for _ in 0..depth {
if array_mode {
v = json!([v]);
} else {
v = json!({"nested": v});
}
}
v
}
pub fn unicode_inject(s: &str, dict: &Dictionary, rng: &mut impl Rng) -> String {
let injection = dict
.random_from(TokenCategory::Unicode, rng)
.unwrap_or("\u{0000}");
match rng.gen_range(0..4) {
0 => format!("{}{}", injection, s), 1 => format!("{}{}", s, injection), 2 => format!("{}{}{}", s, injection, s), _ => format!("{}{}{}", injection, s, injection), }
}
pub fn mutate_string(s: &str, rng: &mut impl Rng) -> String {
if s.is_empty() {
return "a".to_string();
}
let mut chars: Vec<char> = s.chars().collect();
match rng.gen_range(0..6) {
0 => {
if !chars.is_empty() {
let idx = rng.gen_range(0..chars.len());
chars.remove(idx);
}
}
1 => {
let idx = rng.gen_range(0..=chars.len());
let c = (rng.gen_range(32u8..127u8)) as char;
chars.insert(idx, c);
}
2 => {
if !chars.is_empty() {
let idx = rng.gen_range(0..chars.len());
chars[idx] = (rng.gen_range(32u8..127u8)) as char;
}
}
3 => {
let s: String = chars.iter().collect();
return format!("{}{}", s, s);
}
4 => {
chars.reverse();
}
_ => {
for c in &mut chars {
if c.is_uppercase() {
*c = c.to_lowercase().next().unwrap_or(*c);
} else if c.is_lowercase() {
*c = c.to_uppercase().next().unwrap_or(*c);
}
}
}
}
chars.into_iter().collect()
}
pub fn mutate_object(obj: &Map<String, Value>, rng: &mut impl Rng) -> Map<String, Value> {
let mut result = obj.clone();
match rng.gen_range(0..5) {
0 => {
if !result.is_empty() {
let keys: Vec<String> = result.keys().cloned().collect();
let key = &keys[rng.gen_range(0..keys.len())];
result.remove(key);
}
}
1 => {
let key = format!("fuzz_{}", rng.gen_range(0..1000));
result.insert(key, json!("fuzzed"));
}
2 => {
if !result.is_empty() {
let keys: Vec<String> = result.keys().cloned().collect();
let key = &keys[rng.gen_range(0..keys.len())];
if let Some(value) = result.get(key).cloned() {
result.insert(format!("{}_dup", key), value);
}
}
}
3 => {
if !result.is_empty() {
let keys: Vec<String> = result.keys().cloned().collect();
let key = &keys[rng.gen_range(0..keys.len())];
result.insert(key.clone(), Value::Null);
}
}
_ => {
result.insert("__proto__".to_string(), json!({"polluted": true}));
}
}
result
}
pub fn mutate_array(arr: &[Value], rng: &mut impl Rng) -> Vec<Value> {
let mut result = arr.to_vec();
match rng.gen_range(0..5) {
0 => {
if !result.is_empty() {
let idx = rng.gen_range(0..result.len());
result.remove(idx);
}
}
1 => {
result.push(json!("fuzzed"));
}
2 => {
let copy = result.clone();
result.extend(copy);
}
3 => {
result.reverse();
}
_ => {
if result.is_empty() {
result.push(Value::Null);
} else {
let idx = rng.gen_range(0..=result.len());
result.insert(idx, Value::Null);
}
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::SeedableRng;
#[test]
fn type_confusion_string() {
let value = json!("test");
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(!matches!(mutated, Value::String(s) if s == "test"));
}
}
#[test]
fn type_confusion_number() {
let value = json!(42);
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(mutated != json!(42) || !mutated.is_number());
}
}
#[test]
fn type_confusion_number_seeded() {
let value = json!(100);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_boolean()
|| mutated.is_array()
|| mutated.is_null()
);
}
}
#[test]
fn type_confusion_bool_true() {
let value = json!(true);
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_number()
|| mutated.is_array()
|| mutated.is_null()
);
}
}
#[test]
fn type_confusion_bool_false() {
let value = json!(false);
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_number()
|| mutated.is_array()
|| mutated.is_null()
);
}
}
#[test]
fn type_confusion_bool_seeded() {
let value = json!(true);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_number()
|| mutated.is_array()
|| mutated.is_null()
);
}
}
#[test]
fn type_confusion_array_empty() {
let value = json!([]);
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_object()
|| mutated.is_null()
|| mutated.is_array()
);
}
}
#[test]
fn type_confusion_array_with_values() {
let value = json!([1, 2, 3]);
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_object()
|| mutated.is_null()
|| mutated.is_number()
);
}
}
#[test]
fn type_confusion_array_seeded() {
let value = json!([1, 2, 3]);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::type_confuse(&value, &mut rng);
let is_valid = mutated.is_string()
|| mutated.is_object()
|| mutated.is_null()
|| mutated.is_number();
assert!(is_valid);
}
}
#[test]
fn type_confusion_object_empty() {
let value = json!({});
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_array()
|| mutated.is_null()
|| mutated.is_object()
);
}
}
#[test]
fn type_confusion_object_with_values() {
let value = json!({"key": "value"});
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_array()
|| mutated.is_null()
|| mutated == json!("value")
);
}
}
#[test]
fn type_confusion_object_seeded() {
let value = json!({"name": "test"});
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::type_confuse(&value, &mut rng);
let is_valid = mutated.is_string()
|| mutated.is_array()
|| mutated.is_null()
|| mutated.is_object();
assert!(is_valid);
}
}
#[test]
fn type_confusion_null() {
let value = json!(null);
let mut rng = rand::thread_rng();
for _ in 0..10 {
let mutated = JsonMutator::type_confuse(&value, &mut rng);
assert!(
mutated.is_string()
|| mutated.is_number()
|| mutated.is_boolean()
|| mutated.is_array()
|| mutated.is_object()
);
}
}
#[test]
fn type_confusion_null_seeded() {
let value = json!(null);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::type_confuse(&value, &mut rng);
let is_valid = mutated.is_string()
|| mutated.is_number()
|| mutated.is_boolean()
|| mutated.is_array()
|| mutated.is_object();
assert!(is_valid);
}
}
#[test]
fn boundary_values() {
let mut rng = rand::thread_rng();
let num = json!(42);
let boundary = JsonMutator::boundary_value(&num, &mut rng);
assert!(boundary.is_number());
let str = json!("hello");
let boundary = JsonMutator::boundary_value(&str, &mut rng);
assert!(boundary.is_string());
}
#[test]
fn boundary_values_array() {
let mut rng = rand::thread_rng();
let arr = json!([1, 2, 3]);
for _ in 0..10 {
let boundary = JsonMutator::boundary_value(&arr, &mut rng);
assert!(boundary.is_array());
}
}
#[test]
fn boundary_values_array_seeded() {
let arr = json!([1, 2, 3]);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let boundary = JsonMutator::boundary_value(&arr, &mut rng);
assert!(boundary.is_array());
}
}
#[test]
fn boundary_values_object() {
let mut rng = rand::thread_rng();
let obj = json!({"key": "value"});
for _ in 0..10 {
let boundary = JsonMutator::boundary_value(&obj, &mut rng);
assert!(boundary.is_object());
}
}
#[test]
fn boundary_values_object_seeded() {
let obj = json!({"key": "value"});
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let boundary = JsonMutator::boundary_value(&obj, &mut rng);
assert!(boundary.is_object());
}
}
#[test]
fn boundary_values_bool() {
let mut rng = rand::thread_rng();
let bool_val = json!(true);
for _ in 0..10 {
let boundary = JsonMutator::boundary_value(&bool_val, &mut rng);
assert!(boundary.is_boolean() || boundary.is_string());
}
}
#[test]
fn boundary_values_bool_seeded() {
let bool_val = json!(false);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let boundary = JsonMutator::boundary_value(&bool_val, &mut rng);
assert!(boundary.is_boolean() || boundary.is_string());
}
}
#[test]
fn boundary_values_null() {
let mut rng = rand::thread_rng();
let null_val = json!(null);
for _ in 0..10 {
let boundary = JsonMutator::boundary_value(&null_val, &mut rng);
assert!(
boundary.is_null()
|| boundary.is_string()
|| boundary.is_number()
|| boundary.is_boolean()
);
}
}
#[test]
fn boundary_values_null_seeded() {
let null_val = json!(null);
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let boundary = JsonMutator::boundary_value(&null_val, &mut rng);
assert!(
boundary.is_null()
|| boundary.is_string()
|| boundary.is_number()
|| boundary.is_boolean()
);
}
}
#[test]
fn boundary_values_string_seeded() {
let str_val = json!("test");
for seed in 0..30 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let boundary = JsonMutator::boundary_value(&str_val, &mut rng);
assert!(boundary.is_string());
}
}
#[test]
fn boundary_values_number_seeded() {
let num = json!(42);
for seed in 0..30 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let boundary = JsonMutator::boundary_value(&num, &mut rng);
assert!(boundary.is_number());
}
}
#[test]
fn deep_nesting() {
let nested_obj = JsonMutator::deep_nest(10, false);
let json_str = serde_json::to_string(&nested_obj).unwrap();
assert!(json_str.contains("nested"));
let nested_arr = JsonMutator::deep_nest(10, true);
let json_str = serde_json::to_string(&nested_arr).unwrap();
assert!(json_str.starts_with('['));
}
#[test]
fn deep_nesting_zero_depth() {
let nested_obj = JsonMutator::deep_nest(0, false);
assert_eq!(nested_obj, json!("leaf"));
let nested_arr = JsonMutator::deep_nest(0, true);
assert_eq!(nested_arr, json!("leaf"));
}
#[test]
fn deep_nesting_one_level() {
let nested_obj = JsonMutator::deep_nest(1, false);
assert_eq!(nested_obj, json!({"nested": "leaf"}));
let nested_arr = JsonMutator::deep_nest(1, true);
assert_eq!(nested_arr, json!(["leaf"]));
}
#[test]
fn deep_nesting_very_deep() {
let nested = JsonMutator::deep_nest(100, false);
let json_str = serde_json::to_string(&nested).unwrap();
let nested_count = json_str.matches("nested").count();
assert_eq!(nested_count, 100);
}
#[test]
fn unicode_inject_prepend() {
let dict = Dictionary::mcp_default();
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
let result = JsonMutator::unicode_inject("test", &dict, &mut rng);
assert!(result.contains("test"));
}
#[test]
fn unicode_inject_append() {
let dict = Dictionary::mcp_default();
let mut rng = rand::rngs::SmallRng::seed_from_u64(1);
let result = JsonMutator::unicode_inject("hello", &dict, &mut rng);
assert!(result.contains("hello"));
}
#[test]
fn unicode_inject_middle() {
let dict = Dictionary::mcp_default();
let mut rng = rand::rngs::SmallRng::seed_from_u64(2);
let result = JsonMutator::unicode_inject("world", &dict, &mut rng);
assert!(result.contains("world"));
}
#[test]
fn unicode_inject_surround() {
let dict = Dictionary::mcp_default();
let mut rng = rand::rngs::SmallRng::seed_from_u64(3);
let result = JsonMutator::unicode_inject("text", &dict, &mut rng);
assert!(result.contains("text"));
}
#[test]
fn unicode_inject_various_seeds() {
let dict = Dictionary::mcp_default();
for seed in 0..20 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let result = JsonMutator::unicode_inject("input", &dict, &mut rng);
assert!(result.contains("input"));
assert!(result.len() >= 5);
}
}
#[test]
fn unicode_inject_empty_string() {
let dict = Dictionary::mcp_default();
let mut rng = rand::thread_rng();
let result = JsonMutator::unicode_inject("", &dict, &mut rng);
assert!(!result.is_empty() || result.is_empty()); }
#[test]
fn string_mutation() {
let mut rng = rand::thread_rng();
let original = "hello world";
for _ in 0..10 {
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty() || original.is_empty());
}
}
#[test]
fn string_mutation_empty() {
let mut rng = rand::thread_rng();
let result = JsonMutator::mutate_string("", &mut rng);
assert_eq!(result, "a");
}
#[test]
fn string_mutation_seeded_delete() {
for seed in 0..100 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let original = "hello";
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty());
}
}
#[test]
fn string_mutation_seeded_insert() {
for seed in 0..100 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let original = "test";
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty());
}
}
#[test]
fn string_mutation_seeded_replace() {
for seed in 0..100 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let original = "abcd";
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty());
}
}
#[test]
fn string_mutation_seeded_duplicate() {
for seed in 0..100 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let original = "xy";
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty());
}
}
#[test]
fn string_mutation_seeded_reverse() {
for seed in 0..100 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let original = "abc";
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty());
}
}
#[test]
fn string_mutation_seeded_case_flip() {
for seed in 0..100 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let original = "Hello";
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(!mutated.is_empty());
}
}
#[test]
fn string_mutation_single_char() {
let mut rng = rand::thread_rng();
let original = "x";
for _ in 0..20 {
let mutated = JsonMutator::mutate_string(original, &mut rng);
assert!(mutated.len() <= 2 || mutated == "xx");
}
}
#[test]
fn object_mutation() {
let mut rng = rand::thread_rng();
let obj: Map<String, Value> =
serde_json::from_str(r#"{"key": "value", "num": 42}"#).unwrap();
for _ in 0..10 {
let mutated = JsonMutator::mutate_object(&obj, &mut rng);
assert!(mutated.len() != obj.len() || mutated != obj);
}
}
#[test]
fn object_mutation_empty() {
let mut rng = rand::thread_rng();
let obj: Map<String, Value> = Map::new();
for _ in 0..10 {
let mutated = JsonMutator::mutate_object(&obj, &mut rng);
let _ = mutated.len();
}
}
#[test]
fn object_mutation_seeded() {
let obj: Map<String, Value> = serde_json::from_str(r#"{"a": 1, "b": 2}"#).unwrap();
for seed in 0..30 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_object(&obj, &mut rng);
let _ = mutated.len();
}
}
#[test]
fn object_mutation_single_key() {
let mut rng = rand::thread_rng();
let obj: Map<String, Value> = serde_json::from_str(r#"{"only": "one"}"#).unwrap();
for _ in 0..20 {
let mutated = JsonMutator::mutate_object(&obj, &mut rng);
let _ = mutated.len(); }
}
#[test]
fn mutate_array_empty() {
let mut rng = rand::thread_rng();
let arr: Vec<Value> = vec![];
for _ in 0..10 {
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
let _ = mutated.len();
}
}
#[test]
fn mutate_array_single() {
let mut rng = rand::thread_rng();
let arr: Vec<Value> = vec![json!(1)];
for _ in 0..10 {
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
let _ = mutated.len();
}
}
#[test]
fn mutate_array_multiple() {
let mut rng = rand::thread_rng();
let arr: Vec<Value> = vec![json!(1), json!(2), json!(3)];
for _ in 0..10 {
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
let _ = mutated.len();
}
}
#[test]
fn mutate_array_seeded() {
let arr: Vec<Value> = vec![json!("a"), json!("b"), json!("c")];
for seed in 0..30 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
let _ = mutated.len();
}
}
#[test]
fn mutate_array_remove() {
let arr: Vec<Value> = vec![json!(1), json!(2)];
let mut saw_removed = false;
for seed in 0..50 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
if mutated.len() < arr.len() {
saw_removed = true;
break;
}
}
assert!(saw_removed);
}
#[test]
fn mutate_array_add() {
let arr: Vec<Value> = vec![json!(1)];
let mut saw_added = false;
for seed in 0..50 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
if mutated.len() > arr.len() && mutated.contains(&json!("fuzzed")) {
saw_added = true;
break;
}
}
assert!(saw_added);
}
#[test]
fn mutate_array_duplicate() {
let arr: Vec<Value> = vec![json!(1), json!(2)];
let mut saw_duplicated = false;
for seed in 0..50 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
if mutated.len() == arr.len() * 2 {
saw_duplicated = true;
break;
}
}
assert!(saw_duplicated);
}
#[test]
fn mutate_array_reverse() {
let arr: Vec<Value> = vec![json!(1), json!(2), json!(3)];
let mut saw_reversed = false;
for seed in 0..50 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
if mutated.len() == arr.len() && mutated[0] == json!(3) && mutated[2] == json!(1) {
saw_reversed = true;
break;
}
}
assert!(saw_reversed);
}
#[test]
fn mutate_array_insert_null() {
let arr: Vec<Value> = vec![json!(1), json!(2)];
let mut saw_null_inserted = false;
for seed in 0..50 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
if mutated.len() == arr.len() + 1 && mutated.contains(&Value::Null) {
saw_null_inserted = true;
break;
}
}
assert!(saw_null_inserted);
}
#[test]
fn mutate_array_empty_insert_null() {
let arr: Vec<Value> = vec![];
let mut saw_null = false;
for seed in 0..50 {
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
let mutated = JsonMutator::mutate_array(&arr, &mut rng);
if mutated == vec![Value::Null] {
saw_null = true;
break;
}
}
assert!(saw_null);
}
}