use liwe::query::{parse_filter_expression, Filter, KeyOp};
#[test]
fn parses_block_style_field_eq() {
let f = parse_filter_expression("status: draft").unwrap();
match f {
Filter::Field { path, op } => {
assert_eq!(path.segments(), &["status".to_string()]);
let _ = op;
}
other => panic!("expected Field, got {:?}", other),
}
}
#[test]
fn parses_flow_style_mapping() {
let f = parse_filter_expression("{status: draft, priority: 5}").unwrap();
match f {
Filter::And(parts) => assert_eq!(parts.len(), 2),
other => panic!("expected And, got {:?}", other),
}
}
#[test]
fn parses_top_level_dollar_operator() {
let f = parse_filter_expression("$key: notes/foo").unwrap();
match f {
Filter::Key(KeyOp::Eq(k)) => assert_eq!(k.to_string(), "notes/foo"),
other => panic!("expected Key($eq), got {:?}", other),
}
}
#[test]
fn parses_field_op_expression() {
let f = parse_filter_expression("priority: { $gt: 3 }").unwrap();
match f {
Filter::Field { path, op: _ } => {
assert_eq!(path.segments(), &["priority".to_string()]);
}
other => panic!("expected Field, got {:?}", other),
}
}
#[test]
fn empty_input_yields_empty_and() {
let f = parse_filter_expression("").unwrap();
match f {
Filter::And(parts) => assert!(parts.is_empty()),
other => panic!("expected empty And, got {:?}", other),
}
}
#[test]
fn whitespace_only_input_yields_empty_and() {
let f = parse_filter_expression(" \n ").unwrap();
match f {
Filter::And(parts) => assert!(parts.is_empty()),
other => panic!("expected empty And, got {:?}", other),
}
}
#[test]
fn rejects_mixed_dollar_and_bare_at_same_level() {
let err = parse_filter_expression("{$eq: foo, name: bar}");
assert!(err.is_err(), "expected MixedDollarAndBare error");
}
#[test]
fn parses_nested_not() {
let f = parse_filter_expression("$not: { $not: { status: draft } }").unwrap();
match f {
Filter::Not(inner) => match *inner {
Filter::Not(_) => {}
other => panic!("expected nested Not, got {:?}", other),
},
other => panic!("expected Not, got {:?}", other),
}
}
#[test]
fn parses_or_with_list_of_filters() {
let expr = "$or: [{ status: draft }, { status: review }]";
let f = parse_filter_expression(expr).unwrap();
match f {
Filter::Or(parts) => assert_eq!(parts.len(), 2),
other => panic!("expected Or, got {:?}", other),
}
}
#[test]
fn parses_graph_anchor_with_max_depth() {
let expr = "$includedBy: { match: { $key: projects/alpha }, maxDepth: 5 }";
let f = parse_filter_expression(expr).unwrap();
match f {
Filter::IncludedBy(anchor) => {
match &anchor.match_filter {
Filter::Key(KeyOp::Eq(k)) => assert_eq!(k.to_string(), "projects/alpha"),
other => panic!("expected Key(Eq), got {:?}", other),
}
assert_eq!(anchor.max_depth, 5);
}
other => panic!("expected IncludedBy, got {:?}", other),
}
}
#[test]
fn parses_graph_anchor_scalar_shorthand() {
let expr = "$includedBy: projects/alpha";
let f = parse_filter_expression(expr).unwrap();
match f {
Filter::IncludedBy(anchor) => {
match &anchor.match_filter {
Filter::Key(KeyOp::Eq(k)) => assert_eq!(k.to_string(), "projects/alpha"),
other => panic!("expected Key(Eq), got {:?}", other),
}
assert_eq!(anchor.max_depth, 1);
assert_eq!(anchor.min_depth, 1);
}
other => panic!("expected IncludedBy, got {:?}", other),
}
}
#[test]
fn malformed_yaml_is_an_error() {
let err = parse_filter_expression("status: draft, : bad");
assert!(err.is_err(), "expected parse error");
}