use diffx_core::*;
use regex::Regex;
use serde_json::{json, Value};
#[test]
fn test_diff_basic_modification() {
let old = json!({"name": "Alice", "age": 30});
let new = json!({"name": "Alice", "age": 31});
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 1);
match &results[0] {
DiffResult::Modified(path, old_val, new_val) => {
assert_eq!(path, "age");
assert_eq!(old_val, &json!(30));
assert_eq!(new_val, &json!(31));
}
_ => panic!("Expected Modified result"),
}
}
#[test]
fn test_diff_added_field() {
let old = json!({"name": "Alice"});
let new = json!({"name": "Alice", "age": 30});
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 1);
match &results[0] {
DiffResult::Added(path, value) => {
assert_eq!(path, "age");
assert_eq!(value, &json!(30));
}
_ => panic!("Expected Added result"),
}
}
#[test]
fn test_diff_removed_field() {
let old = json!({"name": "Alice", "age": 30});
let new = json!({"name": "Alice"});
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 1);
match &results[0] {
DiffResult::Removed(path, value) => {
assert_eq!(path, "age");
assert_eq!(value, &json!(30));
}
_ => panic!("Expected Removed result"),
}
}
#[test]
fn test_diff_type_changed() {
let old = json!({"value": 123});
let new = json!({"value": "123"});
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 1);
match &results[0] {
DiffResult::TypeChanged(path, old_val, new_val) => {
assert_eq!(path, "value");
assert_eq!(old_val, &json!(123));
assert_eq!(new_val, &json!("123"));
}
_ => panic!("Expected TypeChanged result"),
}
}
#[test]
fn test_diff_no_changes() {
let old = json!({"name": "Alice", "age": 30});
let new = json!({"name": "Alice", "age": 30});
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_diff_with_epsilon() {
let old = json!({"value": 1.0});
let new = json!({"value": 1.001});
let options = DiffOptions {
epsilon: Some(0.01),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 0);
let options = DiffOptions {
epsilon: Some(0.0001),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 1); }
#[test]
fn test_diff_with_array_id_key() {
let old = json!({
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
});
let new = json!({
"users": [
{"id": 2, "name": "Bob"},
{"id": 1, "name": "Alice Updated"}
]
});
let options = DiffOptions {
array_id_key: Some("id".to_string()),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 1);
match &results[0] {
DiffResult::Modified(path, _, new_val) => {
assert!(path.contains("[id=1]"));
assert!(path.contains("name"));
assert_eq!(new_val, &json!("Alice Updated"));
}
_ => panic!("Expected Modified result"),
}
}
#[test]
fn test_diff_with_ignore_keys_regex() {
let old = json!({
"data": "important",
"timestamp": "2023-01-01",
"debug_info": "old"
});
let new = json!({
"data": "important",
"timestamp": "2023-01-02",
"debug_info": "new"
});
let regex = Regex::new(r"^(timestamp|debug_)").unwrap();
let options = DiffOptions {
ignore_keys_regex: Some(regex),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 0); }
#[test]
fn test_diff_with_path_filter() {
let old = json!({
"config": {"value": 1},
"metadata": {"value": 2}
});
let new = json!({
"config": {"value": 10},
"metadata": {"value": 20}
});
let options = DiffOptions {
path_filter: Some("config".to_string()),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 1);
match &results[0] {
DiffResult::Modified(path, _, _) => {
assert!(path.contains("config"));
}
_ => panic!("Expected Modified result"),
}
}
#[test]
fn test_diff_with_output_format() {
let old = json!({"name": "Alice"});
let new = json!({"name": "Bob"});
for format in OutputFormat::value_variants() {
let options = DiffOptions {
output_format: Some(*format),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 1);
let formatted = format_output(&results, *format).unwrap();
assert!(!formatted.is_empty());
}
}
#[test]
fn test_diff_with_diffx_specific_options() {
let old = json!({"text": "Hello World"});
let new = json!({"text": "HELLO WORLD"});
let diffx_options = DiffxSpecificOptions {
ignore_case: Some(true),
ignore_whitespace: Some(false),
..Default::default()
};
let options = DiffOptions {
diffx_options: Some(diffx_options),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 0); }
#[test]
fn test_diff_with_ignore_whitespace() {
let old = json!({"text": "Hello World"});
let new = json!({"text": "HelloWorld"});
let diffx_options = DiffxSpecificOptions {
ignore_whitespace: Some(true),
..Default::default()
};
let options = DiffOptions {
diffx_options: Some(diffx_options),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 0); }
#[test]
fn test_diff_arrays_by_index() {
let old = json!([1, 2, 3]);
let new = json!([1, 3, 4]);
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 2);
}
#[test]
fn test_diff_arrays_with_id_key() {
let old = json!([
{"id": "a", "value": 1},
{"id": "b", "value": 2}
]);
let new = json!([
{"id": "b", "value": 20},
{"id": "c", "value": 3}
]);
let options = DiffOptions {
array_id_key: Some("id".to_string()),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert_eq!(results.len(), 3);
}
#[test]
fn test_diff_arrays_mixed_id_and_index() {
let old = json!([
{"id": "a", "value": 1},
{"value": 2}, {"id": "b", "value": 3}
]);
let new = json!([
{"id": "b", "value": 30},
{"value": 20}, {"id": "c", "value": 4}
]);
let options = DiffOptions {
array_id_key: Some("id".to_string()),
..Default::default()
};
let results = diff(&old, &new, Some(&options)).unwrap();
assert!(!results.is_empty());
}
#[test]
fn test_diff_nested_objects() {
let old = json!({
"user": {
"profile": {
"name": "Alice",
"settings": {
"theme": "dark"
}
}
}
});
let new = json!({
"user": {
"profile": {
"name": "Alice",
"settings": {
"theme": "light",
"notifications": true
}
}
}
});
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 2);
}
#[test]
fn test_diff_large_dataset() {
let mut old_data = serde_json::Map::new();
let mut new_data = serde_json::Map::new();
for i in 0..1000 {
old_data.insert(format!("key_{i}"), json!(i));
new_data.insert(format!("key_{i}"), json!(i + 1));
}
let old = Value::Object(old_data);
let new = Value::Object(new_data);
let results = diff(&old, &new, None).unwrap();
assert_eq!(results.len(), 1000);
}
#[test]
fn test_output_format_parsing() {
assert_eq!(
OutputFormat::parse_format("json").unwrap(),
OutputFormat::Json
);
assert_eq!(
OutputFormat::parse_format("yaml").unwrap(),
OutputFormat::Yaml
);
assert_eq!(
OutputFormat::parse_format("yml").unwrap(),
OutputFormat::Yaml
);
assert_eq!(
OutputFormat::parse_format("diffx").unwrap(),
OutputFormat::Diffx
);
assert!(OutputFormat::parse_format("invalid").is_err());
}
#[test]
fn test_all_output_formats() {
let results = vec![DiffResult::Added("test".to_string(), json!("value"))];
for format in OutputFormat::value_variants() {
let output = format_output(&results, *format).unwrap();
assert!(!output.is_empty());
match format {
OutputFormat::Json => assert!(output.contains("{")),
OutputFormat::Yaml => assert!(output.contains("Added")),
OutputFormat::Diffx => assert!(output.contains("Added")),
}
}
}
#[test]
fn test_parse_json() {
let content = r#"{"name": "test", "value": 123}"#;
let result = parse_json(content).unwrap();
assert_eq!(result["name"], json!("test"));
assert_eq!(result["value"], json!(123));
}
#[test]
fn test_parse_csv() {
let content = "name,age\nAlice,30\nBob,25";
let result = parse_csv(content).unwrap();
if let Value::Array(records) = result {
assert_eq!(records.len(), 2);
assert_eq!(records[0]["name"], json!("Alice"));
assert_eq!(records[0]["age"], json!("30"));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_parse_yaml() {
let content = "name: test\nvalue: 123";
let result = parse_yaml(content).unwrap();
assert_eq!(result["name"], json!("test"));
assert_eq!(result["value"], json!(123));
}
#[test]
fn test_parse_invalid_json() {
let content = "invalid json {";
let result = parse_json(content);
assert!(result.is_err());
}
#[test]
fn test_value_type_name() {
assert_eq!(value_type_name(&json!(null)), "Null");
assert_eq!(value_type_name(&json!(true)), "Boolean");
assert_eq!(value_type_name(&json!(123)), "Number");
assert_eq!(value_type_name(&json!("test")), "String");
assert_eq!(value_type_name(&json!([])), "Array");
assert_eq!(value_type_name(&json!({})), "Object");
}
#[test]
fn test_lightweight_diff_result_conversion() {
let result = DiffResult::Added("test".to_string(), json!({"key": "value"}));
let lightweight = LightweightDiffResult::from(&result);
match lightweight {
LightweightDiffResult::Added(path, value_str) => {
assert_eq!(path, "test");
assert!(value_str.contains("key"));
assert!(value_str.contains("value"));
}
_ => panic!("Expected Added result"),
}
}