use super::helpers::evaluate_comparison_tristate;
use crate::datatypes::values::Value;
use crate::graph::languages::cypher::ast::ComparisonOp;
use chrono::NaiveDate;
const ORDERING_OPS: [ComparisonOp; 4] = [
ComparisonOp::LessThan,
ComparisonOp::LessThanEq,
ComparisonOp::GreaterThan,
ComparisonOp::GreaterThanEq,
];
fn eval(left: &Value, op: &ComparisonOp, right: &Value) -> Option<bool> {
evaluate_comparison_tristate(left, op, right).expect("comparison must not error")
}
fn map_value(key: &str, value: i64) -> Value {
let mut map = crate::datatypes::prop_map::PropMap::new();
map.insert(key.to_string(), Value::Int64(value));
Value::Map(map)
}
fn incomparable_pairs() -> Vec<(Value, Value)> {
vec![
(Value::Int64(1), Value::String("a".into())),
(Value::Float64(1.5), Value::String("a".into())),
(Value::Boolean(true), Value::Int64(1)),
(Value::List(vec![Value::Int64(1)]), Value::Int64(2)),
(map_value("a", 1), Value::Int64(1)),
(
Value::DateTime(NaiveDate::from_ymd_opt(2024, 3, 15).unwrap()),
Value::String("not-a-date".into()),
),
(
Value::List(vec![Value::Int64(1)]),
Value::List(vec![Value::Int64(2)]),
),
(map_value("a", 1), map_value("a", 2)),
]
}
#[test]
fn an_ordering_comparison_without_a_rule_is_null_in_both_directions() {
for (left, right) in incomparable_pairs() {
for op in ORDERING_OPS {
assert_eq!(
eval(&left, &op, &right),
None,
"{left:?} {op:?} {right:?} must be null"
);
assert_eq!(
eval(&right, &op, &left),
None,
"{right:?} {op:?} {left:?} must be null"
);
}
}
}
#[test]
fn equality_across_types_stays_two_valued() {
for (left, right) in incomparable_pairs() {
assert_eq!(eval(&left, &ComparisonOp::Equals, &right), Some(false));
assert_eq!(eval(&left, &ComparisonOp::NotEquals, &right), Some(true));
assert_eq!(eval(&right, &ComparisonOp::Equals, &left), Some(false));
assert_eq!(eval(&right, &ComparisonOp::NotEquals, &left), Some(true));
}
}
#[test]
fn nan_answers_false_not_null() {
let nan = Value::Float64(f64::NAN);
for other in [
Value::Int64(1),
Value::Float64(1.0),
Value::Float64(f64::NAN),
Value::UniqueId(7),
] {
for op in ORDERING_OPS {
assert_eq!(
eval(&nan, &op, &other),
Some(false),
"NaN {op:?} {other:?} must be false"
);
assert_eq!(
eval(&other, &op, &nan),
Some(false),
"{other:?} {op:?} NaN must be false"
);
}
}
}
#[test]
fn a_null_operand_is_still_null() {
for op in ORDERING_OPS {
assert_eq!(eval(&Value::Null, &op, &Value::Int64(1)), None);
assert_eq!(eval(&Value::Int64(1), &op, &Value::Null), None);
}
}
#[test]
fn same_family_ordering_is_unchanged() {
for (left, right, less, less_eq, greater, greater_eq) in [
(Value::Int64(1), Value::Int64(2), true, true, false, false),
(Value::Int64(2), Value::Int64(2), false, true, false, true),
(
Value::Float64(1.5),
Value::Int64(2),
true,
true,
false,
false,
),
(
Value::String("a".into()),
Value::String("b".into()),
true,
true,
false,
false,
),
(
Value::Boolean(false),
Value::Boolean(true),
true,
true,
false,
false,
),
(
Value::DateTime(NaiveDate::from_ymd_opt(2024, 3, 15).unwrap()),
Value::String("2024-03-16".into()),
true,
true,
false,
false,
),
] {
assert_eq!(
eval(&left, &ComparisonOp::LessThan, &right),
Some(less),
"{left:?} < {right:?}"
);
assert_eq!(
eval(&left, &ComparisonOp::LessThanEq, &right),
Some(less_eq)
);
assert_eq!(
eval(&left, &ComparisonOp::GreaterThan, &right),
Some(greater)
);
assert_eq!(
eval(&left, &ComparisonOp::GreaterThanEq, &right),
Some(greater_eq)
);
}
}