use crate::velesql::ast::IntrospectionStatement;
use crate::velesql::Parser;
#[test]
fn test_parse_show_collections() {
let query = Parser::parse("SHOW COLLECTIONS").expect("SHOW COLLECTIONS should parse");
assert!(query.is_introspection_query());
assert!(!query.is_select_query());
assert!(!query.is_ddl_query());
let intro = query
.introspection
.expect("Expected introspection statement");
assert_eq!(intro, IntrospectionStatement::ShowCollections);
}
#[test]
fn test_parse_show_collections_with_semicolon() {
let query = Parser::parse("SHOW COLLECTIONS;").expect("trailing semicolon should parse");
let intro = query
.introspection
.expect("Expected introspection statement");
assert_eq!(intro, IntrospectionStatement::ShowCollections);
}
#[test]
fn test_parse_show_collections_case_insensitive() {
let query = Parser::parse("show collections").expect("lowercase should parse");
assert!(query.is_introspection_query());
let query2 = Parser::parse("Show Collections").expect("mixed case should parse");
assert!(query2.is_introspection_query());
}
#[test]
fn test_parse_describe_collection() {
let query =
Parser::parse("DESCRIBE COLLECTION docs").expect("DESCRIBE COLLECTION should parse");
assert!(query.is_introspection_query());
let intro = query
.introspection
.expect("Expected introspection statement");
match intro {
IntrospectionStatement::DescribeCollection(desc) => {
assert_eq!(desc.name, "docs");
}
other => panic!("Expected DescribeCollection, got {other:?}"),
}
}
#[test]
fn test_parse_describe_without_collection_keyword() {
let query = Parser::parse("DESCRIBE docs").expect("DESCRIBE without COLLECTION should parse");
assert!(query.is_introspection_query());
let intro = query
.introspection
.expect("Expected introspection statement");
match intro {
IntrospectionStatement::DescribeCollection(desc) => {
assert_eq!(desc.name, "docs");
}
other => panic!("Expected DescribeCollection, got {other:?}"),
}
}
#[test]
fn test_parse_describe_collection_with_semicolon() {
let query =
Parser::parse("DESCRIBE COLLECTION docs;").expect("trailing semicolon should parse");
assert!(query.is_introspection_query());
let intro = query
.introspection
.expect("Expected introspection statement");
match intro {
IntrospectionStatement::DescribeCollection(desc) => {
assert_eq!(desc.name, "docs");
}
other => panic!("Expected DescribeCollection, got {other:?}"),
}
}
#[test]
fn test_parse_describe_case_insensitive() {
let query = Parser::parse("describe collection my_coll").expect("lowercase should parse");
let intro = query
.introspection
.expect("Expected introspection statement");
match intro {
IntrospectionStatement::DescribeCollection(desc) => {
assert_eq!(desc.name, "my_coll");
}
other => panic!("Expected DescribeCollection, got {other:?}"),
}
}
#[test]
fn test_parse_explain_select() {
let query =
Parser::parse("EXPLAIN SELECT * FROM docs LIMIT 10").expect("EXPLAIN SELECT should parse");
assert!(query.is_introspection_query());
let intro = query
.introspection
.expect("Expected introspection statement");
match intro {
IntrospectionStatement::Explain(inner) => {
assert!(inner.is_select_query());
assert_eq!(inner.select.from, "docs");
assert_eq!(inner.select.limit, Some(10));
}
other => panic!("Expected Explain, got {other:?}"),
}
}
#[test]
fn test_parse_explain_with_semicolon() {
let query = Parser::parse("EXPLAIN SELECT * FROM docs LIMIT 5;")
.expect("trailing semicolon should parse");
assert!(query.is_introspection_query());
}
#[test]
fn test_parse_explain_case_insensitive() {
let query = Parser::parse("explain select * from docs limit 5")
.expect("lowercase EXPLAIN should parse");
assert!(query.is_introspection_query());
}
#[test]
fn test_parse_show_reject_unknown() {
let result = Parser::parse("SHOW TABLES");
assert!(result.is_err(), "SHOW TABLES should fail to parse");
}
#[test]
fn test_parse_show_alone_fails() {
let result = Parser::parse("SHOW");
assert!(result.is_err(), "SHOW alone should fail to parse");
}
#[test]
fn test_parse_describe_missing_name() {
let result = Parser::parse("DESCRIBE COLLECTION");
assert!(
result.is_err(),
"DESCRIBE COLLECTION without name should fail"
);
}
#[test]
fn test_parse_explain_without_query() {
let result = Parser::parse("EXPLAIN");
assert!(result.is_err(), "EXPLAIN alone should fail to parse");
}
#[test]
fn test_introspection_query_type_flags() {
let query = Parser::parse("SHOW COLLECTIONS").expect("should parse");
assert!(query.is_introspection_query());
assert!(!query.is_select_query());
assert!(!query.is_match_query());
assert!(!query.is_dml_query());
assert!(!query.is_train());
assert!(!query.is_ddl_query());
}
#[test]
fn test_describe_query_type_flags() {
let query = Parser::parse("DESCRIBE docs").expect("should parse");
assert!(query.is_introspection_query());
assert!(!query.is_select_query());
}
#[test]
fn test_explain_query_type_flags() {
let query = Parser::parse("EXPLAIN SELECT * FROM docs LIMIT 1").expect("should parse");
assert!(query.is_introspection_query());
assert!(!query.is_select_query());
}
#[test]
fn test_let_with_introspection_parses_but_validation_rejects() {
use crate::velesql::validation::QueryValidator;
let query = Parser::parse("LET x = 1 SHOW COLLECTIONS").expect("grammar allows LET + SHOW");
assert!(query.is_introspection_query());
assert!(!query.let_bindings.is_empty());
let result = QueryValidator::validate(&query);
assert!(
result.is_err(),
"Validator should reject LET + introspection"
);
}
#[test]
fn test_explain_with_vector_near_in_inner_query() {
let sql = "EXPLAIN SELECT * FROM docs WHERE vector NEAR $v AND category = 'tech' LIMIT 10";
let query = Parser::parse(sql).expect("EXPLAIN with NEAR should parse");
assert!(query.is_introspection_query());
let IntrospectionStatement::Explain(inner) = query
.introspection
.as_ref()
.expect("introspection should be present")
else {
panic!("Expected Explain variant");
};
assert!(inner.select.where_clause.is_some(), "inner WHERE present");
assert_eq!(inner.select.limit, Some(10));
}
#[test]
fn test_describe_with_quoted_identifier() {
let sql = "DESCRIBE COLLECTION `select`";
let query = Parser::parse(sql).expect("DESCRIBE with quoted keyword should parse");
let IntrospectionStatement::DescribeCollection(desc) = query
.introspection
.as_ref()
.expect("introspection should be present")
else {
panic!("Expected DescribeCollection variant");
};
assert_eq!(
desc.name, "select",
"quoted keyword should be unquoted in AST"
);
}
#[test]
fn test_explain_match_is_rejected() {
let result = Parser::parse("EXPLAIN MATCH (a)-[:REL]->(b) RETURN a");
assert!(
result.is_err(),
"EXPLAIN MATCH should fail (only compound_query supported)"
);
}