use sqlx::{MySql, QueryBuilder};
use serde_json::json;
use crate::params::{FilterExpr, Op, FilterCond, QueryParams};
use crate::whitelist::FieldWhitelist;
use crate::builder::build_filter;
#[test]
fn test_whitelist_check() {
let allowed = &["id", "name", "email"];
let whitelist = FieldWhitelist::new(allowed);
assert!(whitelist.check("id").is_ok());
assert!(whitelist.check("name").is_ok());
assert!(whitelist.check("password").is_err());
assert!(whitelist.check("drop table").is_err());
}
#[test]
fn test_builder_simple_eq() {
let allowed = &["name"];
let whitelist = FieldWhitelist::new(allowed);
let cond = FilterExpr::Cond(FilterCond {
field: Some("name".to_string()),
op: Op::Eq,
value: Some(json!("Alice")),
});
let mut qb = QueryBuilder::<MySql>::new("");
build_filter(&mut qb, &cond, &whitelist).expect("build failed");
assert_eq!(qb.sql(), "name = ?");
}
#[test]
fn test_builder_nested_and_or() {
let allowed = &["age", "role", "active"];
let whitelist = FieldWhitelist::new(allowed);
let expr = FilterExpr::And {
and: vec![
FilterExpr::Or {
or: vec![
FilterExpr::Cond(FilterCond {
field: Some("age".to_string()),
op: Op::Gt,
value: Some(json!(18)),
}),
FilterExpr::Cond(FilterCond {
field: Some("role".to_string()),
op: Op::Eq,
value: Some(json!("admin")),
}),
]
},
FilterExpr::Cond(FilterCond {
field: Some("active".to_string()),
op: Op::Eq,
value: Some(json!(true)),
}),
]
};
let mut qb = QueryBuilder::<MySql>::new("");
build_filter(&mut qb, &expr, &whitelist).expect("build failed");
assert_eq!(qb.sql(), "((age > ? OR role = ?) AND active = ?)");
}
#[test]
fn test_builder_in_operator() {
let allowed = &["status"];
let whitelist = FieldWhitelist::new(allowed);
let cond = FilterExpr::Cond(FilterCond {
field: Some("status".to_string()),
op: Op::In,
value: Some(json!([1, 2, 3])),
});
let mut qb = QueryBuilder::<MySql>::new("");
build_filter(&mut qb, &cond, &whitelist).expect("build failed");
assert_eq!(qb.sql(), "status IN (?, ?, ?)");
}
#[test]
fn test_builder_illegal_field() {
let allowed = &["name"];
let whitelist = FieldWhitelist::new(allowed);
let cond = FilterExpr::Cond(FilterCond {
field: Some("password".to_string()), op: Op::Eq,
value: Some(json!("123456")),
});
let mut qb = QueryBuilder::<MySql>::new("");
let result = build_filter(&mut qb, &cond, &whitelist);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Illegal field: password");
}
#[test]
fn test_deserialize_query_params() {
let json_str = r#"{
"page": 1,
"page_size": 20,
"sort": [{"field": "created_at", "desc": true}],
"filter": {
"field": "name",
"op": "like",
"value": "test"
}
}"#;
let params: QueryParams = serde_json::from_str(json_str).expect("deserialize failed");
assert_eq!(params.page, 1);
assert_eq!(params.page_size, 20);
assert_eq!(params.sort.len(), 1);
assert_eq!(params.sort[0].field, "created_at");
assert!(params.sort[0].desc);
if let Some(FilterExpr::Cond(c)) = params.filter {
assert_eq!(c.field.unwrap(), "name");
assert!(matches!(c.op, Op::Like));
assert_eq!(c.value.unwrap(), json!("test"));
} else {
panic!("Filter should be a condition");
}
}