mod common;
use common::test_scenario::TestScenario;
use linkrs_core::types::expr::Expression;
use linkrs_core::types::DataType;
use linkrs_core::value::{
DateTimeValue, DateValue, GeographyValue, List, NullType, TimeValue, Value,
};
use linkrs_query::executor::expression::evaluation_context::DefaultExpressionContext;
use linkrs_query::executor::expression::functions::FunctionRegistry;
use linkrs_query::executor::expression::{ExpressionContext, ExpressionEvaluator};
use linkrs_query::DataSet;
#[test]
fn test_value_null_type_variants() {
let null_variants = vec![
NullType::Null,
NullType::NaN,
NullType::BadData,
NullType::BadType,
NullType::ErrOverflow,
NullType::UnknownProp,
NullType::DivByZero,
NullType::OutOfRange,
];
for variant in &null_variants {
let value = Value::Null(variant.clone());
assert!(value.is_null());
assert_eq!(value.get_type(), DataType::Null);
}
assert!(NullType::BadData.is_bad());
assert!(NullType::BadType.is_bad());
assert!(NullType::ErrOverflow.is_bad());
assert!(NullType::OutOfRange.is_bad());
assert!(!NullType::Null.is_bad());
assert!(!NullType::NaN.is_bad());
assert!(NullType::NaN.is_computational_error());
assert!(NullType::DivByZero.is_computational_error());
assert!(NullType::ErrOverflow.is_computational_error());
}
#[test]
fn test_value_type_checking() {
assert_eq!(Value::Empty.get_type(), DataType::Empty);
assert_eq!(Value::Null(NullType::Null).get_type(), DataType::Null);
assert_eq!(Value::Bool(true).get_type(), DataType::Bool);
assert_eq!(Value::Int(42).get_type(), DataType::Int);
assert_eq!(Value::Float(1.5_f32).get_type(), DataType::Float);
assert_eq!(Value::string("test").get_type(), DataType::String);
assert!(Value::Int(42).is_numeric());
assert!(Value::Float(1.5_f32).is_numeric());
assert!(!Value::string("42").is_numeric());
assert!(!Value::Bool(true).is_numeric());
assert!(Value::Null(NullType::BadData).is_bad_null());
assert!(Value::Null(NullType::BadType).is_bad_null());
assert!(!Value::Null(NullType::Null).is_bad_null());
}
#[test]
fn test_value_boolean_conversion() {
assert_eq!(Value::Bool(true).to_bool(), Value::Bool(true));
assert_eq!(Value::Bool(false).to_bool(), Value::Bool(false));
assert_eq!(Value::string("true").to_bool(), Value::Bool(true));
assert_eq!(Value::string("TRUE").to_bool(), Value::Bool(true));
assert_eq!(Value::string("false").to_bool(), Value::Bool(false));
assert_eq!(Value::string("FALSE").to_bool(), Value::Bool(false));
assert_eq!(
Value::string("invalid").to_bool(),
Value::Null(NullType::Null)
);
assert_eq!(Value::Empty.to_bool(), Value::Null(NullType::Null));
assert_eq!(
Value::Null(NullType::Null).to_bool(),
Value::Null(NullType::Null)
);
assert_eq!(Value::Int(1).to_bool(), Value::Bool(true));
assert_eq!(Value::Int(0).to_bool(), Value::Bool(false));
assert_eq!(Value::SmallInt(1).to_bool(), Value::Bool(true));
assert_eq!(Value::BigInt(1).to_bool(), Value::Bool(true));
assert_eq!(Value::Float(1.0_f32).to_bool(), Value::Bool(true));
assert_eq!(Value::Float(0.0_f32).to_bool(), Value::Bool(false));
assert_eq!(Value::Double(1.0).to_bool(), Value::Bool(true));
assert_eq!(Value::Double(0.0).to_bool(), Value::Bool(false));
assert_eq!(
Value::List(Box::new(List::new())).to_bool(),
Value::Null(NullType::BadData)
);
}
#[test]
fn test_value_integer_conversion() {
assert_eq!(Value::Int(42).to_int(), Value::Int(42));
assert_eq!(Value::Int(-100).to_int(), Value::Int(-100));
assert_eq!(Value::Float(2.7_f32).to_int(), Value::Int(2));
assert_eq!(Value::Float(-2.9).to_int(), Value::Int(-2));
assert_eq!(Value::Float(f32::NAN).to_int(), Value::Null(NullType::Null));
assert_eq!(
Value::Float(f32::INFINITY).to_int(),
Value::Null(NullType::Null)
);
assert_eq!(
Value::Float(f32::NEG_INFINITY).to_int(),
Value::Null(NullType::Null)
);
assert_eq!(Value::string("42").to_int(), Value::Int(42));
assert_eq!(Value::string("-100").to_int(), Value::Int(-100));
assert_eq!(
Value::string("invalid").to_int(),
Value::Null(NullType::Null)
);
assert_eq!(Value::Bool(true).to_int(), Value::Int(1));
assert_eq!(Value::Bool(false).to_int(), Value::Int(0));
}
#[test]
fn test_value_float_conversion() {
assert_eq!(Value::Float(1.5_f32).to_float(), Value::Float(1.5_f32));
assert_eq!(Value::Int(42).to_float(), Value::Float(42.0));
assert_eq!(Value::string("1.5").to_float(), Value::Float(1.5_f32));
assert_eq!(Value::string("-2.5").to_float(), Value::Float(-2.5));
assert_eq!(
Value::string("invalid").to_float(),
Value::Null(NullType::Null)
);
assert_eq!(Value::Bool(true).to_float(), Value::Float(1.0));
assert_eq!(Value::Bool(false).to_float(), Value::Float(0.0));
}
#[test]
fn test_value_arithmetic_operations() {
assert_eq!(
Value::Int(10).add(&Value::Int(5)).expect("should succeed"),
Value::Int(15)
);
assert_eq!(
Value::Float(3.5)
.add(&Value::Float(2.5))
.expect("should succeed"),
Value::Float(6.0)
);
assert_eq!(
Value::Int(10)
.add(&Value::Float(2.5))
.expect("should succeed"),
Value::Float(12.5)
);
assert_eq!(
Value::string("Hello, ")
.add(&Value::string("World"))
.expect("should succeed"),
Value::string("Hello, World")
);
assert_eq!(
Value::Int(10).sub(&Value::Int(3)).expect("should succeed"),
Value::Int(7)
);
assert_eq!(
Value::Float(10.5)
.sub(&Value::Float(3.5))
.expect("should succeed"),
Value::Float(7.0)
);
assert_eq!(
Value::Int(6).mul(&Value::Int(7)).expect("should succeed"),
Value::Int(42)
);
assert_eq!(
Value::Float(3.0)
.mul(&Value::Float(4.0))
.expect("should succeed"),
Value::Float(12.0)
);
assert_eq!(
Value::Int(10).div(&Value::Int(2)).expect("should succeed"),
Value::Int(5)
);
assert_eq!(
Value::Float(10.0)
.div(&Value::Float(4.0))
.expect("should succeed"),
Value::Float(2.5)
);
assert!(Value::Int(10).div(&Value::Int(0)).is_err());
assert!(Value::Float(10.0).div(&Value::Float(0.0)).is_err());
assert_eq!(
Value::Int(10).rem(&Value::Int(3)).expect("should succeed"),
Value::Int(1)
);
assert!(Value::Int(10).rem(&Value::Int(0)).is_err());
}
#[test]
fn test_value_comparison() {
assert!(Value::Int(10) > Value::Int(5));
assert!(Value::Int(5) < Value::Int(10));
assert_eq!(Value::Int(10), Value::Int(10));
assert!(Value::Float(3.5_f32) > Value::Float(2.0));
assert_eq!(Value::Float(f32::NAN), Value::Float(f32::NAN));
assert!(Value::string("b") > Value::string("a"));
assert_eq!(Value::string("test"), Value::string("test"));
assert!(Value::Bool(true) > Value::Bool(false));
assert!(Value::Int(1) < Value::string("a"));
}
#[test]
fn test_value_unary_operations() {
assert_eq!(
Value::Int(42).neg().expect("should succeed"),
Value::Int(-42)
);
assert_eq!(
Value::Float(2.5_f32).neg().expect("should succeed"),
Value::Float(-2.5_f32)
);
assert!(Value::string("test").neg().is_err());
assert_eq!(
Value::Int(-42).abs().expect("should succeed"),
Value::Int(42)
);
assert_eq!(
Value::Float(-2.5_f32).abs().expect("should succeed"),
Value::Float(2.5_f32)
);
assert!(Value::string("test").abs().is_err());
assert_eq!(
Value::string("hello").len().expect("should succeed"),
Value::Int(5)
);
assert_eq!(
Value::List(Box::new(linkrs_core::List {
values: vec![Value::Int(1), Value::Int(2)]
}))
.len()
.expect("list lengthshould succeed"),
Value::Int(2)
);
assert_eq!(
Value::Map(Box::<std::collections::HashMap<_, _>>::default())
.len()
.expect("map lengthshould succeed"),
Value::Int(0)
);
}
#[test]
fn test_value_complex_types() {
let date = DateValue {
year: 2024,
month: 6,
day: 15,
};
assert_eq!(date.year, 2024);
assert_eq!(date.month, 6);
assert_eq!(date.day, 15);
let time = TimeValue {
hour: 14,
minute: 30,
sec: 45,
microsec: 0,
};
assert_eq!(time.hour, 14);
assert_eq!(time.minute, 30);
let datetime = DateTimeValue {
year: 2024,
month: 6,
day: 15,
hour: 14,
minute: 30,
sec: 0,
microsec: 0,
};
assert_eq!(datetime.year, 2024);
let geo = GeographyValue {
latitude: 39.9042,
longitude: 116.4074,
};
assert_eq!(geo.latitude, 39.9042);
assert_eq!(geo.longitude, 116.4074);
use linkrs_core::value::IntervalValue;
let interval = IntervalValue::new(14, 3, 4_500_000_000);
assert_eq!(interval.months, 14);
assert_eq!(interval.days, 3);
assert_eq!(interval.microseconds, 4_500_000_000);
let iv_years = IntervalValue::from_years(2);
assert_eq!(iv_years.months, 24);
let iv_days = IntervalValue::from_days(5);
assert_eq!(iv_days.days, 5);
let iv_iso = IntervalValue::parse("P1Y2M3DT4H5M6S").unwrap();
assert_eq!(iv_iso.months, 14);
assert_eq!(iv_iso.days, 3);
let iv_pg = IntervalValue::parse("1 year 2 months 3 days").unwrap();
assert_eq!(iv_pg.months, 14);
assert_eq!(iv_pg.days, 3);
let iv1 = IntervalValue::from_days(3);
let iv2 = IntervalValue::from_hours(12);
let iv_sum = iv1 + iv2;
assert_eq!(iv_sum.days, 3);
assert_eq!(iv_sum.microseconds, 12 * 3_600_000_000);
let iv_neg = -IntervalValue::from_days(5);
assert_eq!(iv_neg.days, -5);
let iv_str = IntervalValue::new(14, 3, 4 * 3_600_000_000 + 5 * 60_000_000 + 6 * 1_000_000);
assert_eq!(iv_str.to_iso8601(), "P1Y2M3DT4H5M6S");
assert_eq!(iv_str.to_postgresql(), "1 year 2 mons 3 days 04:05:06");
let mut dataset = DataSet::new();
dataset.col_names.push("name".to_string());
dataset.col_names.push("age".to_string());
dataset
.rows
.push(vec![Value::string("Alice"), Value::Int(30)]);
assert_eq!(dataset.col_names.len(), 2);
assert_eq!(dataset.rows.len(), 1);
}
#[test]
fn test_value_hash_and_equality() {
use std::collections::HashSet;
let value1 = Value::Int(42);
let value2 = Value::Int(42);
assert_eq!(value1.hash_value(), value2.hash_value());
let nan1 = Value::Float(f32::NAN);
let nan2 = Value::Float(f32::NAN);
assert_eq!(nan1.hash_value(), nan2.hash_value());
let pos_zero = Value::Float(0.0);
let neg_zero = Value::Float(-0.0);
assert_eq!(pos_zero.hash_value(), neg_zero.hash_value());
let mut set = HashSet::new();
set.insert(Value::Int(1));
set.insert(Value::Int(2));
set.insert(Value::Int(1)); assert_eq!(set.len(), 2);
}
#[test]
fn test_datatype_variants() {
let types = vec![
DataType::Empty,
DataType::Null,
DataType::Bool,
DataType::SmallInt,
DataType::Int,
DataType::BigInt,
DataType::Float,
DataType::Double,
DataType::String,
DataType::Date,
DataType::Time,
DataType::DateTime,
DataType::Vertex,
DataType::Edge,
DataType::Path,
DataType::List(Box::new(DataType::String)),
DataType::Map(Box::new(DataType::Empty)),
DataType::Set(Box::new(DataType::Int)),
DataType::Geography,
DataType::DataSet,
DataType::FixedString(100),
DataType::Blob,
];
for dtype in &types {
let cloned = dtype.clone();
assert_eq!(*dtype, cloned);
}
}
#[test]
fn test_expression_literal_creation() {
let expr = Expression::literal(42i64);
match &expr {
Expression::Literal(v) => assert_eq!(*v, Value::Int(42)),
_ => panic!("Expected Literal Expression"),
}
let expr = Expression::literal("hello".to_string());
match &expr {
Expression::Literal(v) => assert_eq!(*v, Value::string("hello")),
_ => panic!("Expected Literal Expression"),
}
let expr = Expression::literal(true);
match &expr {
Expression::Literal(v) => assert_eq!(*v, Value::Bool(true)),
_ => panic!("The “Expected Literal Expression” refers to a situation where the system or a user expects a specific, literal value to be provided as an input. This expectation is based on the context, the rules of the system, or the requirements of the task at hand. If the provided value does not match the expected literal value, it may lead to errors or issues in the system’s functionality."),
}
}
#[test]
fn test_expression_variable_creation() {
let expr = Expression::variable("x");
match &expr {
Expression::Variable(name) => assert_eq!(name, "x"),
_ => panic!("Expected Variable Expression"),
}
}
#[test]
fn test_expression_binary_creation() {
use linkrs_core::types::BinaryOperator;
let left = Expression::literal(10i64);
let right = Expression::literal(5i64);
let expr = Expression::Binary {
left: Box::new(left),
op: BinaryOperator::Add,
right: Box::new(right),
};
match &expr {
Expression::Binary { op, .. } => assert!(matches!(op, BinaryOperator::Add)),
_ => panic!("Expected Binary expression"),
}
}
#[test]
fn test_expression_unary_creation() {
use linkrs_core::types::UnaryOperator;
let operand = Expression::literal(true);
let expr = Expression::Unary {
op: UnaryOperator::Not,
operand: Box::new(operand),
};
match &expr {
Expression::Unary { op, .. } => assert!(matches!(op, UnaryOperator::Not)),
_ => panic!("Expected a unary expression."),
}
}
#[test]
fn test_evaluator_literal() {
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::literal(42i64);
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(42));
let expr = Expression::literal("test".to_string());
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::string("test"));
let expr = Expression::literal(true);
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_evaluator_variable() {
let mut ctx = DefaultExpressionContext::new();
ctx.set_variable("x".to_string(), Value::Int(100));
ctx.set_variable("name".to_string(), Value::string("Alice"));
let expr = Expression::variable("x");
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(100));
let expr = Expression::variable("name");
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::string("Alice"));
}
#[test]
fn test_evaluator_binary_arithmetic() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: BinaryOperator::Add,
right: Box::new(Expression::literal(5i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(15));
let expr = Expression::Binary {
left: Box::new(Expression::literal(20i64)),
op: BinaryOperator::Subtract,
right: Box::new(Expression::literal(8i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(12));
let expr = Expression::Binary {
left: Box::new(Expression::literal(6i64)),
op: BinaryOperator::Multiply,
right: Box::new(Expression::literal(7i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(42));
let expr = Expression::Binary {
left: Box::new(Expression::literal(20i64)),
op: BinaryOperator::Divide,
right: Box::new(Expression::literal(4i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(5));
}
#[test]
fn test_evaluator_binary_comparison() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::literal(5i64)),
op: BinaryOperator::Equal,
right: Box::new(Expression::literal(5i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Bool(true));
let expr = Expression::Binary {
left: Box::new(Expression::literal(5i64)),
op: BinaryOperator::NotEqual,
right: Box::new(Expression::literal(3i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Bool(true));
let expr = Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: BinaryOperator::GreaterThan,
right: Box::new(Expression::literal(5i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Bool(true));
let expr = Expression::Binary {
left: Box::new(Expression::literal(3i64)),
op: BinaryOperator::LessThan,
right: Box::new(Expression::literal(7i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_evaluator_binary_logical() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::literal(true)),
op: BinaryOperator::And,
right: Box::new(Expression::literal(true)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("ANDshould succeed");
assert_eq!(result, Value::Bool(true));
let expr = Expression::Binary {
left: Box::new(Expression::literal(true)),
op: BinaryOperator::And,
right: Box::new(Expression::literal(false)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("ANDshould succeed");
assert_eq!(result, Value::Bool(false));
let expr = Expression::Binary {
left: Box::new(Expression::literal(false)),
op: BinaryOperator::Or,
right: Box::new(Expression::literal(true)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("ORshould succeed");
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_evaluator_unary() {
use linkrs_core::types::UnaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Unary {
op: UnaryOperator::Not,
operand: Box::new(Expression::literal(true)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("NOTshould succeed");
assert_eq!(result, Value::Bool(false));
let expr = Expression::Unary {
op: UnaryOperator::Not,
operand: Box::new(Expression::literal(false)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("NOTshould succeed");
assert_eq!(result, Value::Bool(true));
let expr = Expression::Unary {
op: UnaryOperator::Minus,
operand: Box::new(Expression::literal(42i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(-42));
}
#[test]
fn test_evaluator_nested_expression() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: BinaryOperator::Add,
right: Box::new(Expression::literal(5i64)),
}),
op: BinaryOperator::Multiply,
right: Box::new(Expression::literal(2i64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(30));
let expr = Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: BinaryOperator::Add,
right: Box::new(Expression::Binary {
left: Box::new(Expression::literal(5i64)),
op: BinaryOperator::Multiply,
right: Box::new(Expression::literal(2i64)),
}),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("should succeed");
assert_eq!(result, Value::Int(20));
}
#[test]
fn test_evaluator_can_evaluate() {
let const_expr = Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: linkrs_core::types::BinaryOperator::Add,
right: Box::new(Expression::literal(5i64)),
};
assert!(ExpressionEvaluator::can_evaluate(&const_expr));
let var_expr = Expression::variable("x");
assert!(!ExpressionEvaluator::can_evaluate(&var_expr));
}
#[test]
fn test_function_registry_builtins() {
let registry = FunctionRegistry::new();
let result = registry
.execute("abs", &[Value::Int(-42)])
.expect("absfunction executionshould succeed");
assert_eq!(result, Value::Int(42));
let result = registry
.execute("abs", &[Value::Float(-2.5_f32)])
.expect("absfunction executionshould succeed");
assert_eq!(result, Value::Float(2.5_f32));
let result = registry
.execute("length", &[Value::string("hello")])
.expect("lengthfunction executionshould succeed");
assert_eq!(result, Value::Int(5));
let result = registry
.execute("to_int", &[Value::string("42")])
.expect("to_intfunction executionshould succeed");
assert_eq!(result, Value::Int(42));
}
#[test]
fn test_function_registry_errors() {
let registry = FunctionRegistry::new();
let result = registry.execute("undefined_func", &[Value::Int(1)]);
assert!(result.is_err());
let result = registry.execute("abs", &[]);
assert!(result.is_err());
}
#[test]
fn test_basic_context_variables() {
let mut ctx = DefaultExpressionContext::new();
ctx.set_variable("x".to_string(), Value::Int(100));
ctx.set_variable("y".to_string(), Value::string("test"));
assert_eq!(ctx.get_variable("x"), Some(Value::Int(100)));
assert_eq!(ctx.get_variable("y"), Some(Value::string("test")));
assert_eq!(ctx.get_variable("z"), None);
}
#[test]
fn test_basic_context_functions() {
let _ctx = DefaultExpressionContext::new();
let registry = FunctionRegistry::new();
let result = registry.execute("abs", &[Value::Int(-42)]);
assert!(result.is_ok());
assert_eq!(
result.expect("Failed to execute abs function"),
Value::Int(42)
);
let result = registry.execute("length", &[Value::string("hello")]);
assert!(result.is_ok());
assert_eq!(
result.expect("Failed to execute length function"),
Value::Int(5)
);
let result = registry.execute("undefined_func", &[Value::Int(1)]);
assert!(result.is_err());
}
#[test]
fn test_basic_context_cache() {
let mut ctx = DefaultExpressionContext::new();
ctx.set_variable("cached_key1".to_string(), Value::Int(42));
assert_eq!(ctx.get_variable("cached_key1"), Some(Value::Int(42)));
assert_eq!(ctx.get_variable("nonexistent"), None);
}
#[test]
fn test_context_parent_child() {
let mut parent = DefaultExpressionContext::new();
parent.set_variable("parent_var".to_string(), Value::Int(100));
let mut child = DefaultExpressionContext::new();
child.set_variable("child_var".to_string(), Value::Int(200));
assert_eq!(child.get_variable("child_var"), Some(Value::Int(200)));
assert_eq!(parent.get_variable("parent_var"), Some(Value::Int(100)));
}
#[test]
fn test_complex_arithmetic_expression() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::Binary {
left: Box::new(Expression::Binary {
left: Box::new(Expression::literal(100i64)),
op: BinaryOperator::Subtract,
right: Box::new(Expression::literal(50i64)),
}),
op: BinaryOperator::Multiply,
right: Box::new(Expression::literal(2i64)),
}),
op: BinaryOperator::Add,
right: Box::new(Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: BinaryOperator::Divide,
right: Box::new(Expression::literal(5i64)),
}),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx)
.expect("complex expression evaluationshould succeed");
assert_eq!(result, Value::Int(102));
}
#[test]
fn test_mixed_type_operations() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::literal(10i64)),
op: BinaryOperator::Add,
right: Box::new(Expression::literal(5.5f64)),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx)
.expect("mixed-type evaluationshould succeed");
assert_eq!(result, Value::Float(15.5));
}
#[test]
fn test_string_concatenation() {
use linkrs_core::types::BinaryOperator;
let mut ctx = DefaultExpressionContext::new();
let expr = Expression::Binary {
left: Box::new(Expression::literal("Hello, ".to_string())),
op: BinaryOperator::Add,
right: Box::new(Expression::literal("World!".to_string())),
};
let result = ExpressionEvaluator::evaluate(&expr, &mut ctx)
.expect("string concat evaluationshould succeed");
assert_eq!(result, Value::string("Hello, World!"));
}
#[test]
fn test_list_operations() {
let list = Value::List(Box::new(linkrs_core::List {
values: vec![Value::Int(1), Value::Int(2), Value::Int(3)],
}));
assert_eq!(
list.len().expect("list lengthshould succeed"),
Value::Int(3)
);
assert_eq!(list.get_type(), DataType::List(Box::new(DataType::Int)));
let empty_list = Value::List(Box::new(linkrs_core::List { values: vec![] }));
assert_eq!(
empty_list.len().expect("list lengthshould succeed"),
Value::Int(0)
);
}
#[test]
fn test_map_operations() {
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(Value::string("name"), Value::string("Alice"));
map.insert(Value::string("age"), Value::Int(30));
let map_value = Value::Map(Box::new(map));
assert_eq!(
map_value.len().expect("map lengthshould succeed"),
Value::Int(2)
);
assert_eq!(
map_value.get_type(),
DataType::Map(Box::new(DataType::Empty))
);
}
#[test]
fn test_value_memory_estimation() {
let int_val = Value::Int(42);
assert!(int_val.estimated_size() > 0);
let string_val = Value::string("hello world");
assert!(string_val.estimated_size() >= std::mem::size_of::<Value>() + "hello world".len());
let list_val = Value::List(Box::new(linkrs_core::List {
values: vec![Value::Int(1), Value::Int(2)],
}));
assert!(list_val.estimated_size() > int_val.estimated_size());
}
#[test]
fn test_null_type_display() {
assert_eq!(NullType::Null.to_string(), "NULL");
assert_eq!(NullType::NaN.to_string(), "NaN");
assert_eq!(NullType::BadData.to_string(), "BAD_DATA");
assert_eq!(NullType::BadType.to_string(), "BAD_TYPE");
assert_eq!(NullType::ErrOverflow.to_string(), "ERR_OVERFLOW");
assert_eq!(NullType::UnknownProp.to_string(), "UNKNOWN_PROP");
assert_eq!(NullType::DivByZero.to_string(), "DIV_BY_ZERO");
assert_eq!(NullType::OutOfRange.to_string(), "OUT_OF_RANGE");
}
#[test]
fn test_default_values() {
let default_null: NullType = Default::default();
assert_eq!(default_null, NullType::Null);
let default_date: DateValue = Default::default();
assert_eq!(default_date.year, 1970);
assert_eq!(default_date.month, 1);
assert_eq!(default_date.day, 1);
let default_time: TimeValue = Default::default();
assert_eq!(default_time.hour, 0);
assert_eq!(default_time.minute, 0);
let default_geo: GeographyValue = Default::default();
assert_eq!(default_geo.latitude, 0.0);
assert_eq!(default_geo.longitude, 0.0);
}
#[test]
fn test_sql_arithmetic_evaluation() {
TestScenario::new()
.expect("Failed to create test scenario")
.setup_space("core_sql_arith")
.query("RETURN 1 + 2 AS result")
.assert_success()
.assert_result_count(1)
.assert_result_columns(&["result"])
.assert_result_contains(vec![Value::Int(3)]);
}
#[test]
fn test_sql_builtin_abs_and_length() {
TestScenario::new()
.expect("Failed to create test scenario")
.setup_space("core_sql_funcs")
.query("RETURN abs(-42) AS a, length('hello') AS b")
.assert_success()
.assert_result_count(1)
.assert_result_contains(vec![Value::Int(42), Value::Int(5)]);
}
#[test]
fn test_sql_type_coercion_to_int() {
TestScenario::new()
.expect("Failed to create test scenario")
.setup_space("core_sql_coerce")
.query("RETURN to_int('42') AS v")
.assert_success()
.assert_result_count(1)
.assert_result_contains(vec![Value::Int(42)]);
}
#[test]
fn test_sql_unknown_function_errors() {
TestScenario::new()
.expect("Failed to create test scenario")
.setup_space("core_sql_unknown_fn")
.query("RETURN undefined_func_xyz(1) AS v")
.assert_error();
}