use tellaro_query_language::parser::{AstNode, TqlParser, Value};
#[test]
fn test_simple_equality() {
let parser = TqlParser::new();
let ast = parser.parse("field eq 'value'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.operator, "eq");
assert_eq!(comp.value, Some(Value::String("value".to_string())));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_simple_equality_with_symbol() {
let parser = TqlParser::new();
let ast = parser.parse("field = 'value'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.operator, "eq");
assert_eq!(comp.value, Some(Value::String("value".to_string())));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_integer_comparison() {
let parser = TqlParser::new();
let ast = parser.parse("age > 25").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "age");
assert_eq!(comp.operator, "gt");
assert_eq!(comp.value, Some(Value::Integer(25)));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_float_comparison() {
let parser = TqlParser::new();
let ast = parser.parse("price >= 19.99").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "price");
assert_eq!(comp.operator, "gte");
assert_eq!(comp.value, Some(Value::Float(19.99)));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_boolean_comparison() {
let parser = TqlParser::new();
let ast = parser.parse("active eq true").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "active");
assert_eq!(comp.operator, "eq");
assert_eq!(comp.value, Some(Value::Boolean(true)));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_null_comparison() {
let parser = TqlParser::new();
let ast = parser.parse("field is null").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.operator, "is");
assert_eq!(comp.value, Some(Value::Null));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_exists_operator() {
let parser = TqlParser::new();
let ast = parser.parse("field exists").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.operator, "exists");
assert_eq!(comp.value, None);
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_not_exists_operator() {
let parser = TqlParser::new();
let ast = parser.parse("field not_exists").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.operator, "not_exists");
assert_eq!(comp.value, None);
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_contains_operator() {
let parser = TqlParser::new();
let ast = parser.parse("message contains 'error'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "message");
assert_eq!(comp.operator, "contains");
assert_eq!(comp.value, Some(Value::String("error".to_string())));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_and_logical_operator() {
let parser = TqlParser::new();
let ast = parser
.parse("field1 eq 'value1' AND field2 eq 'value2'")
.unwrap();
match ast {
AstNode::LogicalOp(logical) => {
assert_eq!(logical.operator, "and");
match *logical.left {
AstNode::Comparison(ref comp) => {
assert_eq!(comp.field, "field1");
assert_eq!(comp.operator, "eq");
}
_ => panic!("Expected Comparison in left operand"),
}
match *logical.right {
AstNode::Comparison(ref comp) => {
assert_eq!(comp.field, "field2");
assert_eq!(comp.operator, "eq");
}
_ => panic!("Expected Comparison in right operand"),
}
}
_ => panic!("Expected LogicalOp node"),
}
}
#[test]
fn test_or_logical_operator() {
let parser = TqlParser::new();
let ast = parser
.parse("status eq 'active' OR status eq 'pending'")
.unwrap();
match ast {
AstNode::LogicalOp(logical) => {
assert_eq!(logical.operator, "or");
}
_ => panic!("Expected LogicalOp node"),
}
}
#[test]
fn test_not_operator() {
let parser = TqlParser::new();
let ast = parser.parse("NOT field eq 'value'").unwrap();
match ast {
AstNode::UnaryOp(unary) => {
assert_eq!(unary.operator, "not");
match *unary.operand {
AstNode::Comparison(ref comp) => {
assert_eq!(comp.field, "field");
}
_ => panic!("Expected Comparison in operand"),
}
}
_ => panic!("Expected UnaryOp node"),
}
}
#[test]
fn test_parenthesized_expression() {
let parser = TqlParser::new();
let ast = parser
.parse("(field1 eq 'value1' AND field2 eq 'value2') OR field3 eq 'value3'")
.unwrap();
match ast {
AstNode::LogicalOp(logical) => {
assert_eq!(logical.operator, "or");
match *logical.left {
AstNode::LogicalOp(ref inner_logical) => {
assert_eq!(inner_logical.operator, "and");
}
_ => panic!("Expected LogicalOp in left operand"),
}
}
_ => panic!("Expected LogicalOp node"),
}
}
#[test]
fn test_complex_nested_expression() {
let parser = TqlParser::new();
let ast = parser
.parse("(field1 eq 'value1' OR field2 eq 'value2') AND NOT field3 eq 'value3'")
.unwrap();
match ast {
AstNode::LogicalOp(logical) => {
assert_eq!(logical.operator, "and");
match *logical.right {
AstNode::UnaryOp(ref unary) => {
assert_eq!(unary.operator, "not");
}
_ => panic!("Expected UnaryOp in right operand"),
}
}
_ => panic!("Expected LogicalOp node"),
}
}
#[test]
fn test_list_value() {
let parser = TqlParser::new();
let ast = parser
.parse("field in ['value1', 'value2', 'value3']")
.unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.operator, "in");
match comp.value {
Some(Value::List(ref list)) => {
assert_eq!(list.len(), 3);
assert_eq!(list[0], Value::String("value1".to_string()));
}
_ => panic!("Expected List value"),
}
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_between_operator() {
let parser = TqlParser::new();
let ast = parser.parse("age between [18, 65]").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "age");
assert_eq!(comp.operator, "between");
match comp.value {
Some(Value::List(ref list)) => {
assert_eq!(list.len(), 2);
assert_eq!(list[0], Value::Integer(18));
assert_eq!(list[1], Value::Integer(65));
}
_ => panic!("Expected List value for between operator"),
}
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_collection_any_operator() {
let parser = TqlParser::new();
let ast = parser.parse("ANY tags eq 'urgent'").unwrap();
match ast {
AstNode::CollectionOp(coll) => {
assert_eq!(coll.operator, "any");
assert_eq!(coll.field, "tags");
assert_eq!(coll.comparison_operator, "eq");
assert_eq!(coll.value, Value::String("urgent".to_string()));
}
_ => panic!("Expected CollectionOp node"),
}
}
#[test]
fn test_collection_all_operator() {
let parser = TqlParser::new();
let ast = parser.parse("ALL scores >= 80").unwrap();
match ast {
AstNode::CollectionOp(coll) => {
assert_eq!(coll.operator, "all");
assert_eq!(coll.field, "scores");
assert_eq!(coll.comparison_operator, "gte");
assert_eq!(coll.value, Value::Integer(80));
}
_ => panic!("Expected CollectionOp node"),
}
}
#[test]
fn test_field_with_dots() {
let parser = TqlParser::new();
let ast = parser.parse("user.profile.name eq 'John'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "user.profile.name");
assert_eq!(comp.operator, "eq");
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_field_with_mutator() {
let parser = TqlParser::new();
let ast = parser.parse("name | lowercase eq 'john'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "name");
assert_eq!(comp.operator, "eq");
match comp.field_mutators {
Some(ref mutators) => {
assert_eq!(mutators.len(), 1);
assert_eq!(mutators[0].name, "lowercase");
}
None => panic!("Expected field mutators"),
}
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_value_with_mutator() {
let parser = TqlParser::new();
let ast = parser.parse("name eq 'JOHN' | lowercase").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "name");
assert_eq!(comp.operator, "eq");
match comp.value_mutators {
Some(ref mutators) => {
assert_eq!(mutators.len(), 1);
assert_eq!(mutators[0].name, "lowercase");
}
None => panic!("Expected value mutators"),
}
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_mutator_with_arguments() {
let parser = TqlParser::new();
let ast = parser.parse("text | split(',') contains 'item'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "text");
match comp.field_mutators {
Some(ref mutators) => {
assert_eq!(mutators.len(), 1);
assert_eq!(mutators[0].name, "split");
assert_eq!(mutators[0].args.len(), 1);
assert_eq!(mutators[0].args[0], Value::String(",".to_string()));
}
None => panic!("Expected field mutators"),
}
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_type_hint() {
let parser = TqlParser::new();
let ast = parser.parse("field::string eq 'value'").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "field");
assert_eq!(comp.type_hint, Some("string".to_string()));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_extract_fields_simple() {
let parser = TqlParser::new();
let fields = parser
.extract_fields("name eq 'John' AND age > 25")
.unwrap();
assert_eq!(fields.len(), 2);
assert!(fields.contains(&"name".to_string()));
assert!(fields.contains(&"age".to_string()));
}
#[test]
fn test_extract_fields_nested() {
let parser = TqlParser::new();
let fields = parser
.extract_fields("user.profile.name eq 'John'")
.unwrap();
assert_eq!(fields.len(), 1);
assert_eq!(fields[0], "user.profile.name");
}
#[test]
fn test_extract_fields_deduplication() {
let parser = TqlParser::new();
let fields = parser
.extract_fields("name eq 'John' OR name eq 'Jane'")
.unwrap();
assert_eq!(fields.len(), 1);
assert_eq!(fields[0], "name");
}
#[test]
fn test_string_escape_sequences() {
let parser = TqlParser::new();
let ast = parser.parse(r#"message eq "line1\nline2""#).unwrap();
match ast {
AstNode::Comparison(comp) => match comp.value {
Some(Value::String(ref s)) => {
assert!(s.contains("\n"));
}
_ => panic!("Expected String value"),
},
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_negative_numbers() {
let parser = TqlParser::new();
let ast = parser.parse("temperature < -10").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.value, Some(Value::Integer(-10)));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_scientific_notation() {
let parser = TqlParser::new();
let ast = parser.parse("value eq 1.5e10").unwrap();
match ast {
AstNode::Comparison(comp) => match comp.value {
Some(Value::Float(f)) => {
assert!((f - 1.5e10).abs() < 1.0);
}
_ => panic!("Expected Float value"),
},
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_unquoted_string_equality() {
let parser = TqlParser::new();
let ast = parser.parse("user.name eq local_service").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "user.name");
assert_eq!(comp.operator, "eq");
assert_eq!(comp.value, Some(Value::String("local_service".to_string())));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_unquoted_string_inequality() {
let parser = TqlParser::new();
let ast = parser.parse("user.name != local_service").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "user.name");
assert_eq!(comp.operator, "ne");
assert_eq!(comp.value, Some(Value::String("local_service".to_string())));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_unquoted_string_contains() {
let parser = TqlParser::new();
let ast = parser.parse("hostname contains admin").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "hostname");
assert_eq!(comp.operator, "contains");
assert_eq!(comp.value, Some(Value::String("admin".to_string())));
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_unquoted_string_with_underscores() {
let parser = TqlParser::new();
let ast = parser.parse("service.name eq system_service_name").unwrap();
match ast {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "service.name");
assert_eq!(comp.operator, "eq");
assert_eq!(
comp.value,
Some(Value::String("system_service_name".to_string()))
);
}
_ => panic!("Expected Comparison node"),
}
}
#[test]
fn test_unquoted_string_in_list() {
let parser = TqlParser::new();
let ast_quoted = parser
.parse("status in ['active', 'pending', 'completed']")
.unwrap();
match ast_quoted {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "status");
assert_eq!(comp.operator, "in");
match comp.value {
Some(Value::List(values)) => {
assert_eq!(values.len(), 3);
assert_eq!(values[0], Value::String("active".to_string()));
}
_ => panic!("Expected List value"),
}
}
_ => panic!("Expected Comparison node for quoted list"),
}
}
#[test]
fn test_mixed_quoted_and_unquoted() {
let parser = TqlParser::new();
let ast = parser
.parse("name eq 'admin' and status eq active")
.unwrap();
match ast {
AstNode::LogicalOp(logical) => {
assert_eq!(logical.operator, "and");
match &*logical.left {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "name");
assert_eq!(comp.value, Some(Value::String("admin".to_string())));
}
_ => panic!("Expected Comparison node on left"),
}
match &*logical.right {
AstNode::Comparison(comp) => {
assert_eq!(comp.field, "status");
assert_eq!(comp.value, Some(Value::String("active".to_string())));
}
_ => panic!("Expected Comparison node on right"),
}
}
_ => panic!("Expected LogicalOp node"),
}
}