use datalogic_rs::{
DataLogic, EvaluationConfig, NanHandling, NumericCoercionConfig, TruthyEvaluator,
};
use serde_json::{Value, json};
use std::sync::Arc;
#[test]
fn test_nan_handling_throw_error() {
let engine = DataLogic::new();
let logic = json!({"+": [1, "not_a_number"]});
let compiled = engine.compile(&logic).unwrap();
let data = json!({});
let result = engine.evaluate_owned(&compiled, data);
assert!(result.is_err());
}
#[test]
fn test_nan_handling_ignore_value() {
let config = EvaluationConfig::default().with_nan_handling(NanHandling::IgnoreValue);
let engine = DataLogic::with_config(config);
let logic = json!({"+": [1, "not_a_number", 2]});
let compiled = engine.compile(&logic).unwrap();
let data = json!({});
let result = engine.evaluate_owned(&compiled, data).unwrap();
assert_eq!(result, json!(3)); }
#[test]
fn test_nan_handling_coerce_to_zero() {
let config = EvaluationConfig::default().with_nan_handling(NanHandling::CoerceToZero);
let engine = DataLogic::with_config(config);
let logic = json!({"+": [1, "not_a_number", 2]});
let compiled = engine.compile(&logic).unwrap();
let data = json!({});
let result = engine.evaluate_owned(&compiled, data).unwrap();
assert_eq!(result, json!(3)); }
#[test]
fn test_nan_handling_return_null() {
let config = EvaluationConfig::default().with_nan_handling(NanHandling::ReturnNull);
let engine = DataLogic::with_config(config);
let logic = json!({"+": [1, "not_a_number", 2]});
let compiled = engine.compile(&logic).unwrap();
let data = json!({});
let result = engine.evaluate_owned(&compiled, data).unwrap();
assert_eq!(result, json!(null));
}
#[test]
fn test_numeric_coercion_default() {
let engine = DataLogic::new();
let logic = json!({"+": ["", 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(5));
let logic = json!({"+": [true, false, 3]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(4));
let logic = json!({"+": [null, 10]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(10)); }
#[test]
fn test_numeric_coercion_strict() {
let config = EvaluationConfig::default()
.with_nan_handling(NanHandling::IgnoreValue)
.with_numeric_coercion(NumericCoercionConfig {
empty_string_to_zero: false,
null_to_zero: false,
bool_to_number: false,
strict_numeric: true,
undefined_to_zero: false,
});
let engine = DataLogic::with_config(config);
let logic = json!({"+": ["", 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(5));
let logic = json!({"+": [true, 3]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(3));
let logic = json!({"+": [null, 10]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(10)); }
#[test]
fn test_loose_equality_errors_default() {
let engine = DataLogic::new();
let logic = json!({"==": [[], 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({}));
assert!(result.is_err());
}
#[test]
fn test_loose_equality_errors_disabled() {
let config = EvaluationConfig::default().with_loose_equality_errors(false);
let engine = DataLogic::with_config(config);
let logic = json!({"==": [[], 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(false)); }
#[test]
fn test_safe_arithmetic_preset() {
let engine = DataLogic::with_config(EvaluationConfig::safe_arithmetic());
let logic = json!({"+": [1, "not_a_number", 2, [3, 4], 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(8));
let logic = json!({"==": [[], "string"]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(false));
}
#[test]
fn test_strict_preset() {
let engine = DataLogic::with_config(EvaluationConfig::strict());
let logic = json!({"+": [true, 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({}));
assert!(result.is_err());
let logic = json!({"+": [null, 5]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({}));
assert!(result.is_err());
}
#[test]
fn test_thread_safety() {
let config = EvaluationConfig::safe_arithmetic();
let engine = Arc::new(DataLogic::with_config(config));
let logic = json!({"+": [{"var": "a"}, {"var": "b"}]});
let compiled = Arc::new(engine.compile(&logic).unwrap());
let mut handles = vec![];
for i in 0..4 {
let engine = Arc::clone(&engine);
let compiled = Arc::clone(&compiled);
let handle = std::thread::spawn(move || {
let data = json!({"a": i * 10, "b": i});
engine.evaluate_owned(&compiled, data).unwrap()
});
handles.push(handle);
}
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(results[0], json!(0)); assert_eq!(results[1], json!(11)); assert_eq!(results[2], json!(22)); assert_eq!(results[3], json!(33)); }
#[test]
fn test_runtime_config_change() {
let logic = json!({"+": [1, "not_a_number"]});
let engine1 = DataLogic::new();
let compiled1 = engine1.compile(&logic).unwrap();
let result = engine1.evaluate_owned(&compiled1, json!({}));
assert!(result.is_err());
let engine2 = DataLogic::with_config(
EvaluationConfig::default().with_nan_handling(NanHandling::IgnoreValue),
);
let compiled2 = engine2.compile(&logic).unwrap();
let result = engine2.evaluate_owned(&compiled2, json!({})).unwrap();
assert_eq!(result, json!(1)); }
#[test]
fn test_subtraction_with_config() {
let config = EvaluationConfig::default().with_nan_handling(NanHandling::IgnoreValue);
let engine = DataLogic::with_config(config);
let logic = json!({"-": [10, "invalid", 3]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(7)); }
#[test]
fn test_multiplication_with_config() {
let config = EvaluationConfig::default().with_nan_handling(NanHandling::CoerceToZero);
let engine = DataLogic::with_config(config);
let logic = json!({"*": [2, "invalid", 3]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(6)); }
#[test]
fn test_comparison_with_config() {
let config = EvaluationConfig::default().with_numeric_coercion(NumericCoercionConfig {
empty_string_to_zero: false,
null_to_zero: false,
bool_to_number: true,
strict_numeric: false,
undefined_to_zero: false,
});
let engine = DataLogic::with_config(config);
let logic = json!({">": [true, false]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, json!(true));
let logic = json!({">": ["", -1]});
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({}));
assert!(result.is_err()); }
#[test]
fn test_truthy_evaluator_javascript() {
let engine = DataLogic::new();
let test_cases = vec![
(json!({"if": [0, "truthy", "falsy"]}), json!("falsy")),
(json!({"if": ["", "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [[], "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [{}, "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [null, "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [false, "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [1, "truthy", "falsy"]}), json!("truthy")),
(json!({"if": ["text", "truthy", "falsy"]}), json!("truthy")),
(json!({"if": [[1], "truthy", "falsy"]}), json!("truthy")),
];
let empty_obj_test = json!({"if": [{"var": "obj"}, "truthy", "falsy"]});
let compiled = engine.compile(&empty_obj_test).unwrap();
let result = engine
.evaluate_owned(&compiled, json!({"obj": {}}))
.unwrap();
assert_eq!(
result,
json!("falsy"),
"Empty object should be falsy in JavaScript mode"
);
let result = engine
.evaluate_owned(&compiled, json!({"obj": {"a": 1}}))
.unwrap();
assert_eq!(
result,
json!("truthy"),
"Non-empty object should be truthy in JavaScript mode"
);
for (logic, expected) in test_cases {
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, expected, "Failed for logic: {:?}", logic);
}
}
#[test]
fn test_truthy_evaluator_strict_boolean() {
let config = EvaluationConfig::default().with_truthy_evaluator(TruthyEvaluator::StrictBoolean);
let engine = DataLogic::with_config(config);
let test_cases = vec![
(json!({"if": [0, "truthy", "falsy"]}), json!("truthy")), (json!({"if": ["", "truthy", "falsy"]}), json!("truthy")), (json!({"if": [[], "truthy", "falsy"]}), json!("truthy")), (json!({"if": [{}, "truthy", "falsy"]}), json!("truthy")), (json!({"if": [null, "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [false, "truthy", "falsy"]}), json!("falsy")),
(json!({"if": [1, "truthy", "falsy"]}), json!("truthy")),
(json!({"if": ["text", "truthy", "falsy"]}), json!("truthy")),
];
for (logic, expected) in test_cases {
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, expected, "Failed for logic: {:?}", logic);
}
}
#[test]
fn test_truthy_evaluator_custom() {
use std::sync::Arc;
let custom_evaluator = Arc::new(|value: &Value| -> bool {
if let Some(n) = value.as_i64() {
n % 2 == 0
} else {
false
}
});
let config = EvaluationConfig::default()
.with_truthy_evaluator(TruthyEvaluator::Custom(custom_evaluator));
let engine = DataLogic::with_config(config);
let test_cases = vec![
(json!({"if": [0, "truthy", "falsy"]}), json!("truthy")), (json!({"if": [1, "truthy", "falsy"]}), json!("falsy")), (json!({"if": [2, "truthy", "falsy"]}), json!("truthy")), (json!({"if": [3, "truthy", "falsy"]}), json!("falsy")), (json!({"if": ["text", "truthy", "falsy"]}), json!("falsy")), (json!({"if": [[], "truthy", "falsy"]}), json!("falsy")), ];
for (logic, expected) in test_cases {
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, expected, "Failed for logic: {:?}", logic);
}
}
#[test]
fn test_truthy_in_logical_operators() {
let config = EvaluationConfig::default().with_truthy_evaluator(TruthyEvaluator::StrictBoolean);
let engine = DataLogic::with_config(config);
let test_cases = vec![
(json!({"and": [0, "result"]}), json!("result")), (json!({"and": [false, "result"]}), json!(false)), (json!({"or": [0, "result"]}), json!(0)), (json!({"or": [false, "result"]}), json!("result")), (json!({"!": [0]}), json!(false)), (json!({"!": [false]}), json!(true)), (json!({"!!": [0]}), json!(true)), (json!({"!!": [false]}), json!(false)), ];
for (logic, expected) in test_cases {
let compiled = engine.compile(&logic).unwrap();
let result = engine.evaluate_owned(&compiled, json!({})).unwrap();
assert_eq!(result, expected, "Failed for logic: {:?}", logic);
}
}