iridium-db 0.3.0

A high-performance vector-graph hybrid storage and indexing engine
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())
    );
}