fraiseql-db 2.16.0

Database abstraction layer for FraiseQL v2
Documentation
#![allow(clippy::unwrap_used)] // Reason: test code, panics are acceptable

use serde_json::json;

use super::*;
use crate::where_clause::{WhereClause, WhereOperator};

#[test]
fn test_simple_equality() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["email".to_string()],
        operator: WhereOperator::Eq,
        value:    json!("test@example.com"),
    };

    let (sql, params) = gen.generate(&clause).unwrap();
    assert_eq!(sql, "data->>'email' = $1");
    assert_eq!(params, vec![json!("test@example.com")]);
}

#[test]
fn test_icontains() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["name".to_string()],
        operator: WhereOperator::Icontains,
        value:    json!("alice"),
    };

    let (sql, params) = gen.generate(&clause).unwrap();
    assert_eq!(sql, "data->>'name' ILIKE '%' || $1 || '%'");
    assert_eq!(params, vec![json!("alice")]);
}

#[test]
fn test_and_clause() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::And(vec![
        WhereClause::Field {
            path:     vec!["status".to_string()],
            operator: WhereOperator::Eq,
            value:    json!("active"),
        },
        WhereClause::Field {
            path:     vec!["age".to_string()],
            operator: WhereOperator::Gte,
            value:    json!(18),
        },
    ]);

    let (sql, params) = gen.generate(&clause).unwrap();
    assert!(sql.contains("AND"), "Expected AND: {sql}");
    assert_eq!(params.len(), 2);
}

#[test]
fn test_nested_path() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["address".to_string(), "city".to_string()],
        operator: WhereOperator::Eq,
        value:    json!("Paris"),
    };

    let (sql, _) = gen.generate(&clause).unwrap();
    // Nested path: data->'address'->>'city'
    assert!(sql.contains("data->"), "Expected JSONB path: {sql}");
    assert!(sql.contains("address"), "Expected 'address' segment: {sql}");
    assert!(sql.contains("city"), "Expected 'city' segment: {sql}");
}

#[test]
fn test_is_null() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["deleted_at".to_string()],
        operator: WhereOperator::IsNull,
        value:    json!(true),
    };

    let (sql, params) = gen.generate(&clause).unwrap();
    assert_eq!(sql, "data->>'deleted_at' IS NULL");
    assert!(params.is_empty());
}

#[test]
fn test_param_offset() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["email".to_string()],
        operator: WhereOperator::Eq,
        value:    json!("a@b.com"),
    };

    let (sql, _) = gen.generate_with_param_offset(&clause, 2).unwrap();
    assert!(sql.contains("$3"), "Expected $3, got: {sql}");
}

#[test]
fn test_in_operator() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["status".to_string()],
        operator: WhereOperator::In,
        value:    json!(["active", "pending"]),
    };

    let (sql, params) = gen.generate(&clause).unwrap();
    assert_eq!(sql, "data->>'status' IN ($1, $2)");
    assert_eq!(params.len(), 2);
}

#[test]
fn test_vector_cosine_distance_threshold_predicate() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["embedding".to_string()],
        operator: WhereOperator::CosineDistance,
        value:    json!({"vector": [0.1, 0.2, 0.3], "threshold": 0.25}),
    };

    let (sql, params) = gen.generate(&clause).unwrap();
    assert_eq!(
        sql, "((data->>'embedding')::vector <=> $1::text::vector) <= $2::text::float8",
        "#386: boolean predicate, parenthesised cast, parameterised text vector"
    );
    assert_eq!(params, vec![json!("[0.1,0.2,0.3]"), json!(0.25)]);
}

#[test]
fn test_ltree_ancestor_of() {
    let gen = PostgresWhereGenerator::new(PostgresDialect);
    let clause = WhereClause::Field {
        path:     vec!["category_path".to_string()],
        operator: WhereOperator::AncestorOf,
        value:    json!("europe.france"),
    };

    let (sql, params) = gen.generate(&clause).unwrap();
    assert!(sql.contains("@>"), "Expected @>: {sql}");
    assert!(sql.contains("ltree"), "Expected ::ltree: {sql}");
    assert_eq!(params.len(), 1);
}