use crate::velesql::ast::Value;
use crate::velesql::{LogicalOp, Parser};
#[test]
fn test_bug_10_sum_star_should_fail() {
let sql = "SELECT SUM(*) FROM products";
let result = Parser::parse(sql);
assert!(
result.is_err(),
"SUM(*) should be rejected - only COUNT(*) is valid with *. Got: {:?}",
result
);
}
#[test]
fn test_bug_10_avg_star_should_fail() {
let sql = "SELECT AVG(*) FROM products";
let result = Parser::parse(sql);
assert!(
result.is_err(),
"AVG(*) should be rejected - only COUNT(*) is valid with *. Got: {:?}",
result
);
}
#[test]
fn test_bug_10_min_star_should_fail() {
let sql = "SELECT MIN(*) FROM products";
let result = Parser::parse(sql);
assert!(
result.is_err(),
"MIN(*) should be rejected - only COUNT(*) is valid with *"
);
}
#[test]
fn test_bug_10_max_star_should_fail() {
let sql = "SELECT MAX(*) FROM products";
let result = Parser::parse(sql);
assert!(
result.is_err(),
"MAX(*) should be rejected - only COUNT(*) is valid with *"
);
}
#[test]
fn test_bug_10_count_star_should_succeed() {
let sql = "SELECT COUNT(*) FROM products";
let result = Parser::parse(sql);
assert!(
result.is_ok(),
"COUNT(*) should parse successfully: {:?}",
result.err()
);
}
#[test]
fn test_bug_10_sum_column_should_succeed() {
let sql = "SELECT SUM(price) FROM products";
let result = Parser::parse(sql);
assert!(
result.is_ok(),
"SUM(column) should parse successfully: {:?}",
result.err()
);
}
#[test]
fn test_bug_6_having_or_tokens_captured() {
let sql = "SELECT category, COUNT(*) FROM products GROUP BY category HAVING COUNT(*) > 5 OR COUNT(*) < 100";
let result = Parser::parse(sql);
assert!(
result.is_ok(),
"Failed to parse HAVING with OR: {:?}",
result.err()
);
let query = result.unwrap();
let having = query
.select
.having
.as_ref()
.expect("HAVING clause should exist");
assert!(
!having.operators.is_empty(),
"HAVING operators should be captured. Got empty operators vec. Conditions: {:?}",
having.conditions
);
assert_eq!(
having.operators.len(),
1,
"Should have 1 operator for 2 conditions. Got: {:?}",
having.operators
);
assert!(
matches!(having.operators[0], LogicalOp::Or),
"Expected OR operator, got: {:?}",
having.operators[0]
);
}
#[test]
fn test_bug_6_having_and_tokens_captured() {
let sql = "SELECT category, COUNT(*) FROM products GROUP BY category HAVING COUNT(*) > 5 AND AVG(price) < 100";
let result = Parser::parse(sql);
assert!(
result.is_ok(),
"Failed to parse HAVING with AND: {:?}",
result.err()
);
let query = result.unwrap();
let having = query
.select
.having
.as_ref()
.expect("HAVING clause should exist");
assert!(
!having.operators.is_empty(),
"HAVING AND operators should be captured"
);
assert_eq!(
having.operators.len(),
1,
"Should have 1 operator for 2 conditions. Got: {:?}",
having.operators
);
assert!(
matches!(having.operators[0], LogicalOp::And),
"Expected AND operator, got: {:?}",
having.operators[0]
);
}
#[test]
fn test_bug_2_having_or_not_treated_as_and() {
let sql = "SELECT category, COUNT(*) FROM products GROUP BY category HAVING COUNT(*) > 10 OR AVG(price) > 50";
let result = Parser::parse(sql);
assert!(result.is_ok());
let query = result.unwrap();
let having = query.select.having.as_ref().unwrap();
assert_eq!(
having.conditions.len(),
2,
"Should have 2 HAVING conditions"
);
if having.operators.is_empty() {
panic!("Operators vec is empty - OR was not captured!");
} else {
assert!(
matches!(having.operators[0], crate::velesql::LogicalOp::Or),
"Operator should be OR, got: {:?}",
having.operators[0]
);
}
}
#[test]
fn test_bug_1_groupby_parses_correctly() {
let sql = "SELECT category, COUNT(*) FROM products GROUP BY category HAVING COUNT(*) > 5";
let result = Parser::parse(sql);
assert!(
result.is_ok(),
"GROUP BY + HAVING should parse: {:?}",
result.err()
);
let query = result.unwrap();
assert!(
query.select.group_by.is_some(),
"GROUP BY should be present"
);
assert!(query.select.having.is_some(), "HAVING should be present");
}
#[test]
fn test_bug_8_with_max_groups_case_insensitive() {
let sql_lower = "SELECT category FROM products GROUP BY category WITH (max_groups = 100)";
let sql_upper = "SELECT category FROM products GROUP BY category WITH (MAX_GROUPS = 100)";
let sql_mixed = "SELECT category FROM products GROUP BY category WITH (Max_Groups = 100)";
let result_lower = Parser::parse(sql_lower);
let result_upper = Parser::parse(sql_upper);
let result_mixed = Parser::parse(sql_mixed);
assert!(result_lower.is_ok(), "lowercase max_groups should work");
assert!(result_upper.is_ok(), "uppercase MAX_GROUPS should work");
assert!(result_mixed.is_ok(), "mixed case Max_Groups should work");
let query_lower = result_lower.unwrap();
let query_upper = result_upper.unwrap();
let with_lower = query_lower.select.with_clause.as_ref().unwrap();
let with_upper = query_upper.select.with_clause.as_ref().unwrap();
let opt_lower = with_lower
.options
.iter()
.find(|o| o.key.to_lowercase() == "max_groups");
let opt_upper = with_upper
.options
.iter()
.find(|o| o.key.to_lowercase() == "max_groups");
assert!(
opt_lower.is_some(),
"max_groups option should be found in lowercase query"
);
assert!(
opt_upper.is_some(),
"MAX_GROUPS option should be found in uppercase query"
);
}
#[test]
fn test_bug_5_correlated_field_dedup_in_subquery() {
let sql = "SELECT * FROM products WHERE price > (SELECT AVG(price) FROM discounts WHERE `products.category` = 1 AND `products.category` = 2)";
let result = Parser::parse(sql).expect("query should parse");
let comparison = match result.select.where_clause.as_ref() {
Some(crate::velesql::Condition::Comparison(comp)) => comp,
other => panic!("expected WHERE comparison with subquery, got: {other:?}"),
};
let subquery = match &comparison.value {
Value::Subquery(sub) => sub,
other => panic!("expected subquery value, got: {other:?}"),
};
assert_eq!(
subquery.correlations.len(),
1,
"duplicate outer references should be deduplicated"
);
assert_eq!(subquery.correlations[0].outer_table, "products");
assert_eq!(subquery.correlations[0].outer_column, "category");
}
#[test]
fn test_bug_5_string_literals_not_treated_as_correlations() {
let sql = "SELECT * FROM products WHERE price > (SELECT AVG(price) FROM discounts WHERE category = 'products.category')";
let result = Parser::parse(sql).expect("query should parse");
let comparison = match result.select.where_clause.as_ref() {
Some(crate::velesql::Condition::Comparison(comp)) => comp,
other => panic!("expected WHERE comparison with subquery, got: {other:?}"),
};
let subquery = match &comparison.value {
Value::Subquery(sub) => sub,
other => panic!("expected subquery value, got: {other:?}"),
};
assert!(
subquery.correlations.is_empty(),
"string literals must not create false correlations"
);
}