use super::*;
#[test]
fn parse_match_return() {
let ast = parse("MATCH (n) RETURN n").unwrap();
assert_eq!(ast.match_aliases, vec!["n"]);
assert_eq!(ast.match_alias, "n");
assert_eq!(
ast.return_items,
vec![ProjectionItem::Identifier("n".to_string())]
);
assert_eq!(ast.return_alias, "n");
assert_eq!(ast.limit, None);
}
#[test]
fn parse_match_where_return_limit() {
let ast =
parse("MATCH (n) WHERE vector.cosine(n.legal_risk_emb, $vec) > 0.8 RETURN n LIMIT 25")
.unwrap();
assert_eq!(ast.match_aliases, vec!["n"]);
assert_eq!(ast.return_alias, "n");
assert_eq!(ast.limit, Some(25));
let pred = ast.where_predicate.unwrap();
assert_eq!(pred.function, "vector.cosine");
assert_eq!(pred.target, "n.legal_risk_emb");
assert_eq!(pred.param, "$vec");
assert_eq!(pred.operator, ">");
assert_eq!(pred.threshold, "0.8");
}
#[test]
fn parse_bitmap_where_predicate() {
let ast = parse("MATCH (n) WHERE bitmap.contains(idx_country, \"US\") = 1 RETURN n LIMIT 25")
.unwrap();
let pred = ast.where_bitmap_predicate.unwrap();
assert_eq!(pred.function, "bitmap.contains");
assert_eq!(pred.index_name, "idx_country");
assert_eq!(pred.value_key, "US");
}
#[test]
fn parse_multi_match_with_aggregate_return() {
let ast =
parse("MATCH (n), (m) WITH n, count(m) AS cnt RETURN n, collect(m.id) AS mids LIMIT 10")
.unwrap();
assert_eq!(ast.match_aliases, vec!["n", "m"]);
assert_eq!(
ast.with_items,
vec![
ProjectionItem::Identifier("n".to_string()),
ProjectionItem::Function {
name: "count".to_string(),
argument: "m".to_string(),
alias: Some("cnt".to_string())
}
]
);
assert_eq!(
ast.return_items,
vec![
ProjectionItem::Identifier("n".to_string()),
ProjectionItem::Function {
name: "collect".to_string(),
argument: "m.id".to_string(),
alias: Some("mids".to_string())
}
]
);
assert_eq!(ast.limit, Some(10));
}
#[test]
fn reject_missing_return() {
let err = parse("MATCH (n)").unwrap_err();
assert_eq!(
err,
QueryError::InvalidSyntax("missing RETURN clause".to_string())
);
}
#[test]
fn reject_invalid_limit() {
let err = parse("MATCH (n) RETURN n LIMIT nope").unwrap_err();
assert_eq!(
err,
QueryError::InvalidSyntax("invalid LIMIT value".to_string())
);
}
#[test]
fn validate_allows_return_alias_mismatch_from_p22() {
let ast = parse("MATCH (n) RETURN n.id").unwrap();
let typed = validate(&ast, &Catalog).unwrap();
assert_eq!(typed.ast.return_alias, "n.id");
}
#[test]
fn validate_rejects_unknown_projection_alias() {
let ast = parse("MATCH (n) RETURN m").unwrap();
let err = validate(&ast, &Catalog).unwrap_err();
assert_eq!(
err,
QueryError::InvalidSemantic("RETURN references unknown alias 'm'".to_string())
);
}
#[test]
fn scalar_where_valid_field_parses() {
let ast = parse("MATCH (n) WHERE n.adjacency_degree > 5 RETURN n").unwrap();
let typed = validate(&ast, &Catalog).unwrap();
let pred = typed.scalar_predicate.unwrap();
assert_eq!(pred.field, "adjacency_degree");
assert_eq!(pred.operator, ">");
assert_eq!(pred.value, 5.0);
}
#[test]
fn scalar_where_unsupported_field_errors() {
let ast = parse("MATCH (n) WHERE n.foo > 5 RETURN n").unwrap();
let err = validate(&ast, &Catalog).unwrap_err();
assert!(matches!(err, QueryError::InvalidSemantic(_)));
if let QueryError::InvalidSemantic(msg) = err {
assert!(msg.contains("unsupported scalar field"));
}
}
#[test]
fn scalar_where_non_numeric_value_errors() {
let ast = parse("MATCH (n) WHERE n.delta_count > banana RETURN n").unwrap();
let err = validate(&ast, &Catalog).unwrap_err();
assert!(matches!(err, QueryError::InvalidSemantic(_)));
if let QueryError::InvalidSemantic(msg) = err {
assert!(msg.contains("not a valid number"));
}
}
#[test]
fn validate_accepts_bitmap_where_execution() {
let ast =
parse("MATCH (n) WHERE bitmap.contains(idx_country, US) = 1 RETURN n LIMIT 10").unwrap();
validate(&ast, &Catalog).unwrap();
}
#[test]
fn validate_requires_with_function_alias() {
let ast = parse("MATCH (n) WITH count(n) RETURN n").unwrap();
let err = validate(&ast, &Catalog).unwrap_err();
assert_eq!(
err,
QueryError::InvalidSemantic("WITH function projections must use AS alias".to_string())
);
}