#![allow(clippy::unwrap_used)]
mod basic {
use insta::assert_snapshot;
#[test]
fn snapshot_postgres_basic_select() {
let sql = r#"SELECT data FROM "v_user""#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_select_with_limit() {
let sql = r#"SELECT data FROM "v_user" LIMIT 10"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_select_with_offset() {
let sql = r#"SELECT data FROM "v_user" LIMIT 20 OFFSET 10"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_select_with_order_by_asc() {
let sql = r#"SELECT data FROM "v_post" ORDER BY data->>'created_at' ASC"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_select_with_order_by_desc() {
let sql = r#"SELECT data FROM "v_post" ORDER BY data->>'title' DESC LIMIT 10"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_where_eq_operator() {
let sql = r#"SELECT data FROM "v_user" WHERE data->>'email' = $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_where_like_operator() {
let sql = r#"SELECT data FROM "v_user" WHERE data->>'name' ILIKE $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_where_in_operator() {
let sql = r#"SELECT data FROM "v_user" WHERE data->>'id' = ANY($1::UUID[])"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_where_is_null() {
let sql = r#"SELECT data FROM "v_post" WHERE data->>'deleted_at' IS NULL"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_where_is_not_null() {
let sql = r#"SELECT data FROM "v_post" WHERE data->>'published_at' IS NOT NULL"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_where_gt_operator() {
let sql = r#"SELECT data FROM "v_post" WHERE (data->>'created_at')::TIMESTAMP > $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_multiple_where_clauses() {
let sql = r#"SELECT data FROM "v_post" WHERE data->>'published' = true AND data->>'author_id' = $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_with_field_projection() {
let sql = r#"SELECT jsonb_build_object('id', data->>'id', 'name', data->>'name', 'email', data->>'email') FROM "v_user" LIMIT 10"#;
assert_snapshot!(sql);
}
}
mod edge_cases {
use insta::assert_snapshot;
#[test]
fn snapshot_null_handling_is_null() {
let sql = r#"SELECT data FROM "v_post" WHERE data->>'deleted_at' IS NULL"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_special_characters_in_like() {
let sql = r#"SELECT data FROM "v_post" WHERE data->>'title' ILIKE $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_reserved_keywords_quoted() {
let sql = r#"SELECT data FROM "user" WHERE data->>'from' = $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_type_casting_timestamp() {
let sql =
r#"SELECT data FROM "v_post" WHERE (data->>'created_at')::TIMESTAMP > $1::TIMESTAMP"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_type_casting_uuid() {
let sql = r#"SELECT data FROM "v_user" WHERE data->>'id' = ANY($1::UUID[])"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_boolean_literal() {
let sql = r#"SELECT data FROM "v_post" WHERE data->>'published' = true"#;
assert_snapshot!(sql);
}
}
mod relay_aggregation {
use insta::assert_snapshot;
#[test]
fn snapshot_relay_pagination_keyset() {
let sql =
r#"SELECT data FROM "v_post" WHERE data->>'id' > $1 ORDER BY data->>'id' ASC LIMIT 11"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_aggregate_query_sum() {
let sql = r#"SELECT SUM((data->>'amount')::NUMERIC) as total FROM "tf_sales""#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_aggregate_query_with_group_by() {
let sql = r#"SELECT data->>'category' as category, SUM((data->>'amount')::NUMERIC) as total FROM "tf_sales" GROUP BY data->>'category'"#;
assert_snapshot!(sql);
}
}
mod generated_sql {
#[allow(unused_imports)]
use fraiseql_core::db::where_sql_generator::WhereSqlGenerator;
use fraiseql_core::db::{
PostgresDialect, WhereClause, WhereOperator, postgres::PostgresWhereGenerator,
};
use insta::assert_snapshot;
use serde_json::json;
const fn pg() -> PostgresWhereGenerator {
PostgresWhereGenerator::new(PostgresDialect)
}
#[test]
fn generated_pg_eq() {
let clause = WhereClause::Field {
path: vec!["email".to_string()],
operator: WhereOperator::Eq,
value: json!("alice@example.com"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_neq() {
let clause = WhereClause::Field {
path: vec!["status".to_string()],
operator: WhereOperator::Neq,
value: json!("deleted"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_gt() {
let clause = WhereClause::Field {
path: vec!["score".to_string()],
operator: WhereOperator::Gt,
value: json!(100),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_gte() {
let clause = WhereClause::Field {
path: vec!["score".to_string()],
operator: WhereOperator::Gte,
value: json!(100),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_lt() {
let clause = WhereClause::Field {
path: vec!["age".to_string()],
operator: WhereOperator::Lt,
value: json!(18),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_lte() {
let clause = WhereClause::Field {
path: vec!["age".to_string()],
operator: WhereOperator::Lte,
value: json!(65),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_like() {
let clause = WhereClause::Field {
path: vec!["title".to_string()],
operator: WhereOperator::Like,
value: json!("%rust%"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_ilike() {
let clause = WhereClause::Field {
path: vec!["title".to_string()],
operator: WhereOperator::Ilike,
value: json!("%rust%"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_contains() {
let clause = WhereClause::Field {
path: vec!["name".to_string()],
operator: WhereOperator::Contains,
value: json!("alice"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_icontains() {
let clause = WhereClause::Field {
path: vec!["name".to_string()],
operator: WhereOperator::Icontains,
value: json!("alice"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_in_operator() {
let clause = WhereClause::Field {
path: vec!["status".to_string()],
operator: WhereOperator::In,
value: json!(["active", "pending", "review"]),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_nin_operator() {
let clause = WhereClause::Field {
path: vec!["status".to_string()],
operator: WhereOperator::Nin,
value: json!(["deleted", "banned"]),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_is_null_true() {
let clause = WhereClause::Field {
path: vec!["deleted_at".to_string()],
operator: WhereOperator::IsNull,
value: json!(true),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_is_null_false() {
let clause = WhereClause::Field {
path: vec!["published_at".to_string()],
operator: WhereOperator::IsNull,
value: json!(false),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_startswith() {
let clause = WhereClause::Field {
path: vec!["username".to_string()],
operator: WhereOperator::Startswith,
value: json!("admin"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_endswith() {
let clause = WhereClause::Field {
path: vec!["email".to_string()],
operator: WhereOperator::Endswith,
value: json!("@example.com"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_and_two_fields() {
let clause = WhereClause::And(vec![
WhereClause::Field {
path: vec!["published".to_string()],
operator: WhereOperator::Eq,
value: json!(true),
},
WhereClause::Field {
path: vec!["author_id".to_string()],
operator: WhereOperator::Eq,
value: json!("00000000-0000-0000-0000-000000000001"),
},
]);
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_or_two_fields() {
let clause = WhereClause::Or(vec![
WhereClause::Field {
path: vec!["role".to_string()],
operator: WhereOperator::Eq,
value: json!("admin"),
},
WhereClause::Field {
path: vec!["role".to_string()],
operator: WhereOperator::Eq,
value: json!("superuser"),
},
]);
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_nested_and_or() {
let clause = WhereClause::And(vec![
WhereClause::Field {
path: vec!["active".to_string()],
operator: WhereOperator::Eq,
value: json!(true),
},
WhereClause::Or(vec![
WhereClause::Field {
path: vec!["role".to_string()],
operator: WhereOperator::Eq,
value: json!("admin"),
},
WhereClause::Field {
path: vec!["role".to_string()],
operator: WhereOperator::Eq,
value: json!("mod"),
},
]),
]);
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_deep_nested_path() {
let clause = WhereClause::Field {
path: vec!["address".to_string(), "city".to_string()],
operator: WhereOperator::Eq,
value: json!("Paris"),
};
let (sql, _params) = pg().generate(&clause).unwrap();
assert_snapshot!(sql);
}
#[test]
fn generated_pg_param_offset_two() {
let clause = WhereClause::Field {
path: vec!["name".to_string()],
operator: WhereOperator::Eq,
value: json!("Alice"),
};
let gen = PostgresWhereGenerator::new(PostgresDialect);
let (sql, _params) = gen.generate_with_param_offset(&clause, 2).unwrap();
assert_snapshot!(sql);
}
}
mod cte_queries {
use insta::assert_snapshot;
#[test]
fn snapshot_postgres_cte_basic() {
let sql = "WITH top_scores AS (\
SELECT data FROM \"v_score\" WHERE (data->>'score')::numeric >= 80\
) \
SELECT data FROM top_scores ORDER BY data->>'score' DESC";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_cte_recursive() {
let sql = "WITH RECURSIVE counter(n) AS (\
SELECT 1 \
UNION ALL \
SELECT n + 1 FROM counter WHERE n < 5\
) \
SELECT n FROM counter";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_cte_multiple() {
let sql = "WITH active_users AS (\
SELECT data FROM \"v_user\" WHERE data->>'active' = 'true'\
), user_posts AS (\
SELECT data FROM \"v_post\" WHERE data->>'author_id' IN (SELECT data->>'id' FROM active_users)\
) \
SELECT data FROM user_posts ORDER BY data->>'created_at' DESC";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_cte_with_aggregation() {
let sql = "WITH category_totals AS (\
SELECT data->>'category' AS category, SUM((data->>'amount')::NUMERIC) AS total \
FROM \"tf_sales\" GROUP BY data->>'category'\
) \
SELECT * FROM category_totals WHERE total > 1000 ORDER BY total DESC";
assert_snapshot!(sql);
}
}
mod json_access_parity {
use insta::assert_snapshot;
#[test]
fn snapshot_postgres_json_single_level() {
let sql = r#"SELECT data->>'email' FROM "v_user" WHERE data->>'email' = $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_json_nested() {
let sql =
r#"SELECT data->'address'->>'city' FROM "v_user" WHERE data->'address'->>'city' = $1"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_json_deep_nested() {
let sql = r#"SELECT data->'profile'->'social'->>'twitter' FROM "v_user""#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_jsonb_contains() {
let sql = r#"SELECT data FROM "v_user" WHERE data::jsonb @> $1::jsonb"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_jsonb_contained_by() {
let sql = r#"SELECT data FROM "v_user" WHERE data::jsonb <@ $1::jsonb"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_jsonb_overlap() {
let sql = r#"SELECT data FROM "v_user" WHERE data->'tags'::jsonb && $1::jsonb"#;
assert_snapshot!(sql);
}
}
mod fts_parity {
use insta::assert_snapshot;
#[test]
fn snapshot_postgres_fts_matches() {
let sql =
r#"SELECT data FROM "v_post" WHERE to_tsvector(data->>'content') @@ to_tsquery($1)"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_fts_plain_query() {
let sql = r#"SELECT data FROM "v_post" WHERE to_tsvector(data->>'content') @@ plainto_tsquery($1)"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_fts_phrase_query() {
let sql = r#"SELECT data FROM "v_post" WHERE to_tsvector(data->>'content') @@ phraseto_tsquery($1)"#;
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_fts_websearch_query() {
let sql = r#"SELECT data FROM "v_post" WHERE to_tsvector(data->>'content') @@ websearch_to_tsquery($1)"#;
assert_snapshot!(sql);
}
}
mod aggregate_dialect_variants {
use insta::assert_snapshot;
#[test]
fn snapshot_postgres_stddev() {
let sql = "SELECT STDDEV_SAMP(revenue) AS stddev_revenue FROM \"tf_sales\"";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_variance() {
let sql = "SELECT VAR_SAMP(revenue) AS var_revenue FROM \"tf_sales\"";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_string_agg() {
let sql = "SELECT STRING_AGG(name, ', ') AS names FROM \"v_user\"";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_array_agg() {
let sql = "SELECT ARRAY_AGG(tag) AS tags FROM \"v_post\"";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_bool_and() {
let sql = "SELECT BOOL_AND(active) AS all_active FROM \"v_user\"";
assert_snapshot!(sql);
}
#[test]
fn snapshot_postgres_date_trunc_day() {
let sql = "SELECT DATE_TRUNC('day', occurred_at) AS day, COUNT(*) FROM \"tf_sales\" GROUP BY DATE_TRUNC('day', occurred_at)";
assert_snapshot!(sql);
}
}