use diffo::*;
use serde::Serialize;
use std::rc::Rc;
#[test]
fn test_comparator_ignores_timestamps() {
#[derive(Serialize)]
struct Event {
id: u64,
name: String,
timestamp: u64,
}
let old = Event {
id: 1,
name: "login".into(),
timestamp: 100,
};
let new = Event {
id: 1,
name: "login".into(),
timestamp: 200, };
let diff = diff(&old, &new).unwrap();
assert!(!diff.is_empty());
assert!(diff.get("timestamp").is_some());
let config = DiffConfig::new().comparator(
"",
Rc::new(|old, new| {
old.get_field("id") == new.get_field("id")
&& old.get_field("name") == new.get_field("name")
}),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty());
}
#[test]
fn test_comparator_by_id_only() {
#[derive(Serialize)]
struct User {
id: u64,
name: String,
email: String,
}
let old = vec![
User {
id: 1,
name: "Alice".into(),
email: "alice@old.com".into(),
},
User {
id: 2,
name: "Bob".into(),
email: "bob@old.com".into(),
},
];
let new = vec![
User {
id: 1,
name: "Alice Updated".into(),
email: "alice@new.com".into(),
},
User {
id: 2,
name: "Bob Updated".into(),
email: "bob@new.com".into(),
},
];
let config = DiffConfig::new()
.comparator(
"?0?",
Rc::new(|old, new| old.get_field("id") == new.get_field("id")),
)
.comparator(
"?1?",
Rc::new(|old, new| old.get_field("id") == new.get_field("id")),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty(), "Users should be equal when IDs match");
}
#[test]
fn test_comparator_case_insensitive() {
#[derive(Serialize)]
struct Config {
name: String,
value: String,
}
let old = Config {
name: "database".into(),
value: "PostgreSQL".into(),
};
let new = Config {
name: "database".into(),
value: "postgresql".into(), };
let diff = diff(&old, &new).unwrap();
assert!(!diff.is_empty());
let config = DiffConfig::new().comparator(
"value",
Rc::new(|old, new| {
if let (Some(old_str), Some(new_str)) = (old.as_string(), new.as_string()) {
old_str.to_lowercase() == new_str.to_lowercase()
} else {
old == new
}
}),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(
diff.is_empty(),
"Should be equal with case-insensitive comparison"
);
}
#[test]
fn test_comparator_with_pattern_matching() {
#[derive(Serialize)]
struct Data {
stable: String,
temp1: String,
temp2: String,
}
let old = Data {
stable: "same".into(),
temp1: "old_temp1".into(),
temp2: "old_temp2".into(),
};
let new = Data {
stable: "same".into(),
temp1: "new_temp1".into(),
temp2: "new_temp2".into(),
};
let config = DiffConfig::new().comparator(
"temp*",
Rc::new(|_old, _new| {
true
}),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty(), "Should ignore temp fields");
}
#[test]
fn test_comparator_returns_false_continues_diffing() {
#[derive(Serialize)]
struct Item {
value: i32,
}
let old = Item { value: 10 };
let new = Item { value: 20 };
let config = DiffConfig::new().comparator(
"",
Rc::new(|_old, _new| {
false }),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(
!diff.is_empty(),
"Should show diff when comparator returns false"
);
assert!(diff.get("value").is_some());
}
#[test]
fn test_comparator_with_helper_methods() {
#[derive(Serialize)]
struct Product {
id: u64,
price: f64,
name: String,
}
let old = Product {
id: 1,
price: 99.99,
name: "Widget".into(),
};
let new = Product {
id: 1,
price: 100.00,
name: "Widget".into(),
};
let config = DiffConfig::new().comparator(
"",
Rc::new(|old, new| {
let id_match = old.get_field("id") == new.get_field("id");
let name_match = old.get_field("name") == new.get_field("name");
id_match && name_match
}),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty(), "Should be equal when ignoring price");
}
#[test]
fn test_comparator_with_nested_paths() {
#[derive(Serialize)]
struct Outer {
inner: Inner,
other: String,
}
#[derive(Serialize)]
struct Inner {
value: i32,
}
let old = Outer {
inner: Inner { value: 10 },
other: "same".into(),
};
let new = Outer {
inner: Inner { value: 20 },
other: "same".into(),
};
let config = DiffConfig::new().comparator(
"inner",
Rc::new(|_old, _new| {
true }),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty(), "Should ignore inner changes");
}
#[test]
fn test_multiple_comparators() {
#[derive(Serialize)]
struct Data {
field_a: String,
field_b: String,
field_c: String,
}
let old = Data {
field_a: "old_a".into(),
field_b: "old_b".into(),
field_c: "same".into(),
};
let new = Data {
field_a: "new_a".into(),
field_b: "new_b".into(),
field_c: "same".into(),
};
let config = DiffConfig::new()
.comparator("field_a", Rc::new(|_old, _new| true)) .comparator("field_b", Rc::new(|_old, _new| true));
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty(), "Should ignore both field_a and field_b");
}
#[test]
fn test_comparator_with_none_values() {
#[derive(Serialize)]
struct Data {
optional: Option<String>,
}
let old = Data {
optional: Some("value".into()),
};
let new = Data { optional: None };
let config = DiffConfig::new().comparator(
"optional",
Rc::new(|_old, _new| {
true }),
);
let diff = diff_with(&old, &new, &config).unwrap();
assert!(diff.is_empty(), "Should treat Some and None as equal");
}