use powdb_query::parser::parse;
#[test]
fn empty_string_is_err() {
assert!(parse("").is_err());
}
#[test]
fn whitespace_only_is_err() {
assert!(parse(" \t\n ").is_err());
}
#[test]
fn comment_only_is_err() {
assert!(parse("# just a comment").is_err());
}
#[test]
fn simple_table_scan() {
let result = parse("User");
assert!(result.is_ok(), "plain table scan should parse: {result:?}");
}
#[test]
fn filter_with_int_literal() {
let result = parse("User filter .age > 30");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn filter_with_string_literal() {
let result = parse("User filter .name = \"Alice\"");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn order_limit_offset() {
let result = parse("User order .name asc limit 10 offset 5");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn projection_braces() {
let result = parse("User { .name, .age }");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn i64_max_literal() {
let q = format!("User filter .id = {}", i64::MAX);
let result = parse(&q);
assert!(result.is_ok(), "i64::MAX should parse: {result:?}");
}
#[test]
fn i64_min_literal() {
let q = format!("User filter .id = {}", i64::MIN);
let _result = parse(&q);
}
#[test]
fn zero_literal() {
let result = parse("User filter .count = 0");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn negative_float() {
let q = "User filter .score > -3.14";
let _result = parse(q); }
#[test]
fn empty_string_literal() {
let result = parse("User filter .name = \"\"");
assert!(
result.is_ok(),
"empty string literal should parse: {result:?}"
);
}
#[test]
fn unicode_in_string_literal() {
let result = parse("User filter .name = \"cafe\\u0301\"");
let _ = result;
}
#[test]
fn string_with_spaces() {
let result = parse("User filter .name = \"hello world\"");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn long_identifier() {
let long_name = "A".repeat(1000);
let q = format!("{long_name} filter .id = 1");
let result = parse(&q);
assert!(result.is_ok(), "long identifier should parse: {result:?}");
}
#[test]
fn underscore_identifier() {
let result = parse("my_table filter .my_col = 1");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn identifier_with_digits() {
let result = parse("Table123 filter .col456 = 1");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn missing_closing_brace() {
let result = std::panic::catch_unwind(|| parse("User { .name, .age"));
match result {
Ok(Err(_)) => {} Err(_) => {} Ok(Ok(_)) => panic!("missing closing brace should not parse successfully"),
}
}
#[test]
fn missing_closing_paren() {
let result = parse("User filter .id in (1, 2, 3");
assert!(result.is_err(), "missing closing paren should error");
}
#[test]
fn dangling_filter() {
let result = parse("User filter");
assert!(result.is_err(), "filter without expression should error");
}
#[test]
fn dangling_order() {
let result = parse("User order");
assert!(result.is_err(), "order without field should error");
}
#[test]
fn keyword_filter_as_table_name() {
let _ = parse("filter filter .x = 1");
}
#[test]
fn keyword_order_as_table_name() {
let _ = parse("order order .x asc");
}
#[test]
fn keyword_limit_as_table_name() {
let _ = parse("limit limit 10");
}
#[test]
fn keyword_insert_as_table_name() {
let _ = parse("insert insert { name := \"x\" }");
}
#[test]
fn keyword_delete_as_table_name() {
let _ = parse("delete delete filter .id = 1");
}
#[test]
fn keyword_update_as_table_name() {
let _ = parse("update update filter .id = 1 { name := \"x\" }");
}
#[test]
fn keyword_type_as_table_name() {
let _ = parse("type type { name: str }");
}
#[test]
fn deeply_nested_and_or() {
let clause = ".a = 1";
let q = format!(
"User filter {}",
(0..50).map(|_| clause).collect::<Vec<_>>().join(" and ")
);
let result = parse(&q);
assert!(result.is_ok(), "deep AND chain should parse: {result:?}");
}
#[test]
fn nested_parens() {
let result = parse("User filter (((.age > 10)))");
assert!(result.is_ok(), "nested parens should parse: {result:?}");
}
#[test]
fn mixed_and_or_precedence() {
let result = parse("User filter .a = 1 or .b = 2 and .c = 3");
assert!(result.is_ok(), "mixed and/or should parse: {result:?}");
}
#[test]
fn count_query() {
let result = parse("count(User)");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn sum_with_filter() {
let result = parse("sum(User filter .active = true { .price })");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn insert_basic() {
let result = parse("insert User { name := \"Alice\", age := 30 }");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn update_with_filter() {
let result = parse("User filter .id = 1 update { name := \"Bob\" }");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn delete_with_filter() {
let result = parse("User filter .id = 1 delete");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn create_type_basic() {
let result = parse("type Product { name: str, price: float }");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn create_type_empty_fields() {
let result = parse("type Empty { }");
let _ = result;
}
#[test]
fn alter_add_column() {
let result = parse("alter User add column status: str");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn alter_drop_column() {
let result = parse("alter User drop column status");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn drop_table() {
let result = parse("drop User");
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn multiple_filters_should_error_or_parse() {
let _ = parse("User filter .a = 1 filter .b = 2");
}
#[test]
fn trailing_comma_in_projection() {
let _ = parse("User { .name, .age, }");
}
#[test]
fn only_dot_ident() {
let result = parse(".name");
assert!(result.is_err());
}
#[test]
fn in_list_empty() {
let _ = parse("User filter .id in ()");
}
#[test]
fn between_expression() {
let result = parse("User filter .age between 18 and 65");
let _ = result;
}
#[test]
fn like_expression() {
let result = parse("User filter .name like \"%alice%\"");
let _ = result;
}
#[test]
fn case_when_expression() {
let result = parse("User { status: case when .age > 65 then \"senior\" else \"regular\" end }");
let _ = result;
}