use crate::parser::ast::stmt::*;
use crate::parser::parsing::Parser;
#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use super::*;
fn parse_statement(query: &str) -> Result<Stmt, crate::parser::core::error::ParseError> {
let mut parser = Parser::new(query);
let parser_result = parser.parse()?;
Ok(parser_result.ast.stmt.clone())
}
fn assert_rejected(query: &str) {
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_err() || parser.has_errors(),
"query must be rejected: {query}"
);
}
#[test]
fn test_insert_edge_basic() {
let query = "INSERT EDGE KNOWS(since) VALUES 1 -> 2:('2020-01-01')";
let result = parse_statement(query);
assert!(
result.is_ok(),
"INSERT EDGE parse should succeed: {:?}",
result.err()
);
let stmt = result.expect("INSERT EDGE parsing should succeed");
assert_eq!(stmt.kind(), "INSERT");
if let Stmt::Insert(insert_stmt) = stmt {
if let InsertTarget::Edge {
edge_name, edges, ..
} = insert_stmt.target
{
assert_eq!(edge_name, "KNOWS");
assert_eq!(edges.len(), 1);
let (_, _, _, values) = &edges[0];
assert_eq!(values.len(), 1);
} else {
panic!("Expectations for the Edge target");
}
} else {
panic!("The expected Insert statement");
}
}
#[test]
fn test_insert_edge_with_rank() {
let query = "INSERT EDGE KNOWS(since) VALUES 1 -> 2 @0:('2020-01-01')";
let result = parse_statement(query);
assert!(
result.is_ok(),
"INSERT EDGE with rank Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("INSERT EDGE with rank parsing should succeed!");
assert_eq!(stmt.kind(), "INSERT");
if let Stmt::Insert(insert_stmt) = stmt {
if let InsertTarget::Edge {
edge_name, edges, ..
} = insert_stmt.target
{
assert_eq!(edge_name, "KNOWS");
assert_eq!(edges.len(), 1);
let (_, _, rank, _) = &edges[0];
assert!(rank.is_some(), "The “rank” should definitely be included.");
} else {
panic!("Expectations for the Edge target");
}
} else {
panic!("The “Expect” statement is used to specify the expected behavior or output of a system or process. It helps in verifying that the system is functioning as intended by checking whether the actual results match the expected results.");
}
}
#[test]
fn test_insert_edge_multiple() {
let query = "INSERT EDGE KNOWS(since) VALUES 1 -> 2:('2020-01-01'), 2 -> 3:('2021-01-01')";
let result = parse_statement(query);
assert!(
result.is_ok(),
"INSERT Multiple side parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("INSERT multiple edge parsing should succeed");
assert_eq!(stmt.kind(), "INSERT");
}
#[test]
fn test_insert_edge_multiple_properties() {
let query = "INSERT EDGE KNOWS(since, weight) VALUES 1 -> 2:('2020-01-01', 0.9)";
let result = parse_statement(query);
assert!(
result.is_ok(),
"INSERT EDGE Multiple attribute parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("INSERT EDGE multi-attribute parsing should succeed");
assert_eq!(stmt.kind(), "INSERT");
}
#[test]
fn test_insert_vertex_basic() {
let query = "INSERT VERTEX Person(name, age) VALUES 1:('Alice', 30)";
let result = parse_statement(query);
assert!(
result.is_ok(),
"INSERT VERTEX Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("INSERT VERTEX parsing should succeed");
assert_eq!(stmt.kind(), "INSERT");
}
#[test]
fn test_insert_vertex_multiple() {
let query = "INSERT VERTEX Person(name, age) VALUES 1:('Alice', 30), 2:('Bob', 25)";
let result = parse_statement(query);
assert!(
result.is_ok(),
"INSERT Multiple vertex resolution should succeed: {:?}",
result.err()
);
let stmt = result.expect("INSERT multiple vertex resolution should succeed");
assert_eq!(stmt.kind(), "INSERT");
}
#[test]
fn test_delete_edge_basic() {
let query = "DELETE EDGE KNOWS 1 -> 2";
let result = parse_statement(query);
assert!(
result.is_ok(),
"DELETE EDGE Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("DELETE EDGE parsing should succeed");
assert_eq!(stmt.kind(), "DELETE");
}
#[test]
fn test_delete_edge_with_rank() {
let query = "DELETE EDGE KNOWS 1 -> 2 @0";
let result = parse_statement(query);
assert!(
result.is_ok(),
"DELETE EDGE with rank Parse should succeed: {:?}",
result.err()
);
let stmt = result.expect("DELETE EDGE with rank parsing should succeed");
assert_eq!(stmt.kind(), "DELETE");
}
#[test]
fn test_delete_edge_multiple() {
let query = "DELETE EDGE KNOWS 1 -> 2, 2 -> 3";
let result = parse_statement(query);
assert!(
result.is_ok(),
"DELETE Multiple side parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("DELETE multiple edge parsing should succeed");
assert_eq!(stmt.kind(), "DELETE");
}
#[test]
fn test_set_property_basic() {
let query = "SET p.age = 26";
let result = parse_statement(query);
assert!(
result.is_ok(),
"SET attribute parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("SET attribute parsing should succeed");
assert_eq!(stmt.kind(), "SET");
}
#[test]
fn test_set_property_multiple() {
let query = "SET p.age = 26, p.name = 'Alice'";
let result = parse_statement(query);
assert!(
result.is_ok(),
"SET Multiple attribute parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("SET multiple attribute parsing should succeed");
assert_eq!(stmt.kind(), "SET");
}
#[test]
fn test_set_property_with_expression() {
let query = "SET p.age = p.age + 1";
let result = parse_statement(query);
assert!(
result.is_ok(),
"SET Parsing with expression should succeed: {:?}",
result.err()
);
let stmt = result.expect("SET with expression parsing should succeed");
assert_eq!(stmt.kind(), "SET");
}
#[test]
fn test_set_map_overwrite() {
let query = "SET n = {age: 20}";
let result = parse_statement(query);
assert!(
result.is_ok(),
"SET map overwrite parsing should succeed: {:?}",
result.err()
);
if let crate::parser::ast::Stmt::Set(set) = result.expect("SET parsing should succeed") {
assert_eq!(set.assignments.len(), 1);
assert!(set.assignments[0].is_map_overwrite);
} else {
panic!("Expected SET statement");
}
}
#[test]
fn test_set_map_on_property_path_is_not_overwrite() {
let query = "SET n.props = {age: 20}";
let result = parse_statement(query);
assert!(
result.is_ok(),
"SET property path with map value should succeed: {:?}",
result.err()
);
if let crate::parser::ast::Stmt::Set(set) = result.expect("SET parsing should succeed") {
assert!(!set.assignments[0].is_map_overwrite);
} else {
panic!("Expected SET statement");
}
}
#[test]
fn test_set_variable_scalar_is_not_overwrite() {
let query = "SET n = 20";
let result = parse_statement(query);
assert!(
result.is_ok(),
"SET variable scalar parsing should succeed: {:?}",
result.err()
);
if let crate::parser::ast::Stmt::Set(set) = result.expect("SET parsing should succeed") {
assert!(!set.assignments[0].is_map_overwrite);
} else {
panic!("Expected SET statement");
}
}
#[test]
fn test_update_vertex_basic() {
let query = "UPDATE 1 SET age = 26";
let result = parse_statement(query);
assert!(
result.is_ok(),
"UPDATE vertex resolution should succeed: {:?}",
result.err()
);
let stmt = result.expect("UPDATE vertex resolution should succeed");
assert_eq!(stmt.kind(), "UPDATE");
}
#[test]
fn test_delete_vertex_basic() {
let query = "DELETE VERTEX person FROM 1";
let result = parse_statement(query);
assert!(
result.is_ok(),
"DELETE VERTEX Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("DELETE VERTEX parsing should succeed!");
assert_eq!(stmt.kind(), "DELETE");
if let Stmt::Delete(delete_stmt) = stmt {
if let DeleteTarget::Vertices { tag, vids } = delete_stmt.target {
assert_eq!(tag, "person");
assert_eq!(vids.len(), 1);
} else {
panic!("Expected vertex delete target");
}
} else {
panic!("Expected DELETE statement");
}
}
#[test]
fn test_find_shortest_path_basic() {
let query = "FIND SHORTEST PATH FROM 1 TO 2 OVER connect";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND SHORTEST PATH Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND SHORTEST PATH parsing should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
find_path_stmt.shortest,
"It should be the query for the shortest path."
);
assert!(
find_path_stmt.weight_expression.is_none(),
"Expression with no right to be evaluated (or expressed)"
);
} else {
panic!("Expectations for the FindPath statement");
}
}
#[test]
fn test_find_weighted_shortest_path() {
let query = "FIND SHORTEST PATH FROM 1 TO 2 OVER connect WEIGHT weight";
let result = parse_statement(query);
assert!(
result.is_ok(),
"With rights FIND SHORTEST PATH Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("Parsing the FIND SHORTEST PATH with rights should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
find_path_stmt.shortest,
"It should be the shortest path query."
);
assert_eq!(
find_path_stmt.weight_expression,
Some("weight".to_string()),
"There should be a weight expression."
);
} else {
panic!("The expectation for the FindPath statement");
}
}
#[test]
fn test_find_weighted_shortest_path_with_ranking() {
let query = "FIND SHORTEST PATH FROM 1 TO 2 OVER connect WEIGHT ranking";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND SHORTEST PATH parsing with ranking weights should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND SHORTEST PATH parsing using ranking weights should succeed");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
find_path_stmt.shortest,
"It should be the query for the shortest path."
);
assert_eq!(
find_path_stmt.weight_expression,
Some("ranking".to_string()),
"There should be an expression for the ranking weights."
);
} else {
panic!("Expectation for the FindPath statement");
}
}
#[test]
fn test_find_all_paths() {
let query = "FIND ALL PATH FROM 1 TO 2 OVER connect";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND ALL PATH Parsing should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND ALL PATH parsing should succeed");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
!find_path_stmt.shortest,
"It should refer to all path queries."
);
} else {
panic!("Expectations for the FindPath statement");
}
}
#[test]
fn test_find_shortest_path_with_steps() {
let query = "FIND SHORTEST PATH FROM 1 TO 2 OVER connect UPTO 5 STEPS";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND SHORTEST PATH parsing with step limit should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND SHORTEST PATH parsing with a step limit should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
find_path_stmt.shortest,
"It should be the query for the shortest path."
);
assert_eq!(
find_path_stmt.max_steps,
Some(5),
"There should be a maximum number of steps, which is 5."
);
} else {
panic!("Expectation for the FindPath statement");
}
}
#[test]
fn test_find_path_with_loop() {
let query = "FIND ALL PATH WITH LOOP FROM 1 TO 2 OVER connect";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND PATH parsing with WITH LOOP should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND PATH parsing with WITH LOOP should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
find_path_stmt.with_loop,
"Self-loop edges should be allowed."
);
assert!(
!find_path_stmt.with_trail,
"By default, edges do not repeat."
);
assert!(
!find_path_stmt.with_walk,
"By default, vertices do not repeat."
);
} else {
panic!("Expectation for the FindPath statement");
}
}
#[test]
fn test_find_path_with_trail() {
let query = "FIND ALL PATH WITH TRAIL FROM 1 TO 2 OVER connect";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND PATH parsing with WITH TRAIL should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND PATH parsing with WITH TRAIL should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
!find_path_stmt.with_loop,
"Self-loop edges stay under vertex uniqueness."
);
assert!(find_path_stmt.with_trail, "Edges should not repeat.");
assert!(
!find_path_stmt.with_walk,
"By default, vertices do not repeat."
);
} else {
panic!("Expectation for the FindPath statement");
}
}
#[test]
fn test_find_path_with_walk() {
let query = "FIND ALL PATH WITH WALK FROM 1 TO 2 OVER connect";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND PATH parsing with WITH WALK should succeed: {:?}",
result.err()
);
let stmt = result.expect("FIND PATH parsing with WITH WALK should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
!find_path_stmt.with_loop,
"Self-loop edges stay under vertex uniqueness."
);
assert!(
!find_path_stmt.with_trail,
"Walk relaxation subsumes trail."
);
assert!(find_path_stmt.with_walk, "Vertices should repeat.");
} else {
panic!("Expectation for the FindPath statement");
}
}
#[test]
fn test_find_path_with_cycle_keyword_rejected() {
let mut parser = Parser::new("FIND ALL PATH WITH CYCLE FROM 1 TO 2 OVER connect");
let result = parser.parse();
assert!(
result.is_err() || parser.has_errors(),
"query must be rejected: FIND ALL PATH WITH CYCLE FROM 1 TO 2 OVER connect"
);
}
#[test]
fn test_find_path_with_loop_and_walk() {
let query = "FIND ALL PATH WITH LOOP WITH WALK FROM 1 TO 2 OVER connect";
let result = parse_statement(query);
assert!(
result.is_ok(),
"FIND PATH with WITH LOOP WITH WALK should parse successfully: {:?}",
result.err()
);
let stmt = result.expect("FIND PATH parsing with WITH LOOP WITH WALK should succeed!");
assert_eq!(stmt.kind(), "FIND PATH");
if let Stmt::FindPath(find_path_stmt) = stmt {
assert!(
find_path_stmt.with_loop,
"Self-loop edges should be allowed."
);
assert!(
find_path_stmt.with_walk,
"Vertices should be allowed to repeat."
);
} else {
panic!("Expectation for the FindPath statement");
}
}
#[test]
fn test_error_recovery_collects_multiple_errors() {
let query = "MATCH (n:Person RETURN n";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_err() || parser.has_errors());
}
#[test]
fn test_trailing_tokens_are_rejected() {
let mut parser = Parser::new("COMMIT junk");
let result = parser.parse();
assert!(
result.is_ok(),
"statement should still parse structurally: {:?}",
result.err()
);
assert!(parser.has_errors(), "trailing token must be reported");
let result = result.expect("statement should parse");
assert!(
matches!(result.ast.stmt(), Stmt::CommitTransaction(_)),
"leading COMMIT should still parse"
);
}
#[test]
fn test_trailing_semicolon_is_allowed() {
let mut parser = Parser::new("MATCH (v) RETURN v;");
let result = parser.parse();
assert!(
result.is_ok(),
"trailing semicolon parses: {:?}",
result.err()
);
assert!(!parser.has_errors(), "trailing semicolon is not an error");
}
#[test]
fn test_consecutive_match_clauses_merge() {
let stmt = parse_statement(
"MATCH (a:tag) WHERE a.x > 1 MATCH (b:tag {name: 'n'}) MATCH (c) WHERE c.y RETURN c",
)
.expect("consecutive MATCH should parse");
let Stmt::Match(match_stmt) = stmt else {
panic!("expected Match statement");
};
assert_eq!(match_stmt.patterns.len(), 3, "all three patterns merge");
assert!(
match_stmt.where_clause.is_some(),
"WHERE clauses combine into one expression"
);
assert!(
match_stmt.return_clause.is_some(),
"RETURN clause is preserved"
);
}
#[test]
fn test_consecutive_match_no_merge_after_return() {
let mut parser = Parser::new("MATCH (a) RETURN a MATCH (b)");
let result = parser.parse();
assert!(result.is_ok(), "statement structure still parses");
assert!(
parser.has_errors(),
"MATCH after RETURN must be reported as an error"
);
}
#[test]
fn test_trailing_semicolon_is_allowed_after_consecutive_match() {
let mut parser = Parser::new("MATCH (a) MATCH (b) RETURN b;");
let result = parser.parse();
assert!(
result.is_ok(),
"trailing semicolon after merged MATCH parses: {:?}",
result.err()
);
assert!(!parser.has_errors(), "trailing semicolon is not an error");
}
#[test]
fn test_error_recovery_stops_at_limit() {
let query = "INVALID INVALID INVALID INVALID INVALID INVALID";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_err());
}
#[test]
fn test_clause_recovery_single_error_continues() {
let query = "MATCH (v) WHERE v.age > RETURN v";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"statement should parse structurally after clause recovery: {:?}",
result.err()
);
let errors = parser.take_errors();
assert_eq!(errors.len(), 1, "exactly one clause error expected");
let error = &errors.errors[0];
assert!(error.recovered, "clause error must be marked as recovered");
assert!(
error.message.contains("Return"),
"diagnostics should name the offending token: {}",
error.message
);
if let Ok(parser_result) = result {
if let Stmt::Match(stmt) = parser_result.ast.stmt.clone() {
assert!(
stmt.return_clause.is_some(),
"RETURN clause must survive WHERE recovery"
);
} else {
panic!("expected Match statement");
}
}
}
#[test]
fn test_clause_recovery_multiple_errors() {
let query = "MATCH (v) WHERE v.age > RETURN v LIMIT";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"statement should parse structurally after clause recovery: {:?}",
result.err()
);
let errors = parser.take_errors();
assert_eq!(errors.len(), 2, "both clause errors must be recorded");
assert!(
errors.errors.iter().all(|e| e.recovered),
"all clause errors must be marked as recovered"
);
}
#[test]
fn test_clause_recovery_across_union_operands() {
let query = "MATCH (v) WHERE v.age > RETURN v UNION MATCH (v) WHERE v.age > RETURN v";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"statement should parse structurally after clause recovery: {:?}",
result.err()
);
let errors = parser.take_errors();
assert_eq!(errors.len(), 2, "one clause error per union operand");
assert!(
errors.errors.iter().all(|e| e.recovered),
"all clause errors must be marked as recovered"
);
}
#[test]
fn test_clause_recovery_stops_at_limit() {
let broken = "MATCH (v) WHERE v.age > RETURN v UNION ";
let query = format!(
"{}{}{}{}{}{}",
broken, broken, broken, broken, broken, broken
);
let mut parser = Parser::new(&query);
let result = parser.parse();
assert!(result.is_err(), "recovery limit must abort parsing");
}
#[test]
fn test_clause_recovery_expected_tokens_attached() {
let query = "MATCH (v) RETURN v LIMIT";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(
result.is_ok(),
"statement should parse structurally after clause recovery: {:?}",
result.err()
);
let errors = parser.take_errors();
assert_eq!(errors.len(), 1);
let error = &errors.errors[0];
assert!(
!error.expected_tokens.is_empty(),
"expected token candidates must be attached"
);
assert!(error.recovered);
}
#[test]
fn test_clause_recovery_normal_path_unchanged() {
let query = "MATCH (v) WHERE v.age > 30 RETURN v ORDER BY v.age LIMIT 10";
let mut parser = Parser::new(query);
let result = parser.parse();
assert!(result.is_ok(), "valid query parses: {:?}", result.err());
assert!(
!parser.has_errors(),
"valid query must not produce any diagnostics"
);
if let Ok(parser_result) = result {
if let Stmt::Match(stmt) = parser_result.ast.stmt.clone() {
assert!(stmt.where_clause.is_some());
let return_clause = stmt.return_clause.as_ref().expect("RETURN present");
assert!(return_clause.order_by.is_some());
assert_eq!(return_clause.limit.as_ref().map(|l| l.count), Some(10));
} else {
panic!("expected Match statement");
}
}
}
#[test]
fn test_create_sequence_basic() {
let stmt = parse_statement("CREATE SEQUENCE seq1").expect("CREATE SEQUENCE should parse");
if let Stmt::Create(create) = stmt {
assert!(!create.if_not_exists);
if let CreateTarget::Sequence {
name,
start,
increment,
..
} = &create.target
{
assert_eq!(name, "seq1");
assert_eq!(*start, None);
assert_eq!(*increment, None);
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
}
#[test]
fn test_create_sequence_with_options() {
let stmt = parse_statement("CREATE SEQUENCE seq2 START = 100")
.expect("CREATE SEQUENCE with START= should parse");
if let Stmt::Create(create) = stmt {
if let CreateTarget::Sequence { name, start, .. } = &create.target {
assert_eq!(name, "seq2");
assert_eq!(*start, Some(100));
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
let stmt = parse_statement("CREATE SEQUENCE seq2 INCREMENT = 5")
.expect("CREATE SEQUENCE with INCREMENT= should parse");
if let Stmt::Create(create) = stmt {
if let CreateTarget::Sequence {
name, increment, ..
} = &create.target
{
assert_eq!(name, "seq2");
assert_eq!(*increment, Some(5));
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
let stmt = parse_statement("CREATE SEQUENCE seq2 MINVALUE = 1 MAXVALUE = 1000")
.expect("CREATE SEQUENCE with MIN/MAX should parse");
if let Stmt::Create(create) = stmt {
if let CreateTarget::Sequence {
name,
min_value,
max_value,
..
} = &create.target
{
assert_eq!(name, "seq2");
assert_eq!(*min_value, Some(1));
assert_eq!(*max_value, Some(1000));
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
let stmt = parse_statement("CREATE SEQUENCE seq2 CYCLE")
.expect("CREATE SEQUENCE with CYCLE should parse");
if let Stmt::Create(create) = stmt {
if let CreateTarget::Sequence { name, cycle, .. } = &create.target {
assert_eq!(name, "seq2");
assert!(*cycle);
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
let stmt = parse_statement("CREATE SEQUENCE seq2 NOCYCLE")
.expect("CREATE SEQUENCE with NOCYCLE should parse");
if let Stmt::Create(create) = stmt {
if let CreateTarget::Sequence { name, cycle, .. } = &create.target {
assert_eq!(name, "seq2");
assert!(!*cycle);
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
let stmt = parse_statement(
"CREATE SEQUENCE seq2 START = 100 INCREMENT = 5 MINVALUE = 1 MAXVALUE = 1000 CYCLE",
)
.expect("CREATE SEQUENCE with all options should parse");
if let Stmt::Create(create) = stmt {
if let CreateTarget::Sequence {
name,
start,
increment,
min_value,
max_value,
cycle,
} = &create.target
{
assert_eq!(name, "seq2");
assert_eq!(*start, Some(100));
assert_eq!(*increment, Some(5));
assert_eq!(*min_value, Some(1));
assert_eq!(*max_value, Some(1000));
assert!(*cycle);
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
}
#[test]
fn test_create_sequence_if_not_exists() {
let stmt = parse_statement("CREATE SEQUENCE IF NOT EXISTS seq3")
.expect("CREATE SEQUENCE IF NOT EXISTS should parse");
if let Stmt::Create(create) = stmt {
assert!(create.if_not_exists);
if let CreateTarget::Sequence { name, .. } = &create.target {
assert_eq!(name, "seq3");
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Create statement");
}
}
#[test]
fn test_drop_sequence() {
let stmt = parse_statement("DROP SEQUENCE seq1").expect("DROP SEQUENCE should parse");
if let Stmt::Drop(drop) = stmt {
assert!(!drop.if_exists);
if let DropTarget::Sequence(name) = &drop.target {
assert_eq!(name, "seq1");
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Drop statement");
}
}
#[test]
fn test_drop_sequence_if_exists() {
let stmt = parse_statement("DROP SEQUENCE IF EXISTS seq1")
.expect("DROP SEQUENCE IF EXISTS should parse");
if let Stmt::Drop(drop) = stmt {
assert!(drop.if_exists);
if let DropTarget::Sequence(name) = &drop.target {
assert_eq!(name, "seq1");
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Drop statement");
}
}
#[test]
fn test_alter_sequence_increment() {
let stmt = parse_statement("ALTER SEQUENCE seq1 INCREMENT = 10")
.expect("ALTER SEQUENCE should parse");
if let Stmt::Alter(alter) = stmt {
if let AlterTarget::Sequence {
name, increment, ..
} = &alter.target
{
assert_eq!(name, "seq1");
assert_eq!(*increment, Some(10));
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Alter statement");
}
}
#[test]
fn test_alter_sequence_bounds() {
let stmt = parse_statement("ALTER SEQUENCE seq1 MINVALUE = 0 MAXVALUE = 999999 NOCYCLE")
.expect("ALTER SEQUENCE with bounds should parse");
if let Stmt::Alter(alter) = stmt {
if let AlterTarget::Sequence {
name,
min_value,
max_value,
cycle,
..
} = &alter.target
{
assert_eq!(name, "seq1");
assert_eq!(*min_value, Some(0));
assert_eq!(*max_value, Some(999999));
assert_eq!(*cycle, Some(false));
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Alter statement");
}
}
#[test]
fn test_alter_sequence_cycle() {
let stmt = parse_statement("ALTER SEQUENCE seq1 CYCLE")
.expect("ALTER SEQUENCE CYCLE should parse");
if let Stmt::Alter(alter) = stmt {
if let AlterTarget::Sequence { cycle, .. } = &alter.target {
assert_eq!(*cycle, Some(true));
} else {
panic!("expected Sequence target");
}
} else {
panic!("expected Alter statement");
}
}
#[test]
fn test_with_recursive_cte() {
let stmt = parse_statement(
"WITH RECURSIVE r AS (MATCH (a:Person) WHERE a.id == 1 RETURN a AS n UNION ALL MATCH (m:r)-[:KNOWS]->(x) RETURN x AS n) 1 AS one",
)
.expect("WITH RECURSIVE should parse");
if let Stmt::With(with) = stmt {
assert!(with.recursive);
assert_eq!(with.ctes.len(), 1);
assert_eq!(with.ctes[0].name, "r");
assert!(
matches!(
with.ctes[0].body.as_ref(),
Stmt::SetOperation(setop) if setop.op_type == SetOperationType::UnionAll
),
"recursive CTE body must be anchor UNION ALL step"
);
assert_eq!(with.items.len(), 1);
} else {
panic!("expected With statement");
}
}
#[test]
fn test_with_plain_cte() {
let stmt = parse_statement("WITH v AS (MATCH (a:Person) RETURN a AS n) 1 AS one")
.expect("WITH with CTE should parse");
if let Stmt::With(with) = stmt {
assert!(!with.recursive);
assert_eq!(with.ctes.len(), 1);
assert_eq!(with.ctes[0].name, "v");
} else {
panic!("expected With statement");
}
}
#[test]
fn test_with_without_cte_unchanged() {
let stmt = parse_statement("WITH 1 AS one").expect("plain WITH should parse");
if let Stmt::With(with) = stmt {
assert!(!with.recursive);
assert!(with.ctes.is_empty());
assert_eq!(with.items.len(), 1);
} else {
panic!("expected With statement");
}
}
#[test]
fn test_create_type_alias_text() {
let stmt = parse_statement("CREATE TYPE uid AS my_int")
.expect("CREATE TYPE with alias reference should parse");
if let Stmt::CreateType(create) = stmt {
assert_eq!(create.name, "uid");
assert_eq!(create.underlying_type, linkrs_core::DataType::Unknown);
assert_eq!(create.underlying_type_text, "my_int");
} else {
panic!("expected CreateType statement");
}
}
#[test]
fn test_create_type_builtin() {
let stmt = parse_statement("CREATE TYPE uid AS INT")
.expect("CREATE TYPE with builtin should parse");
if let Stmt::CreateType(create) = stmt {
assert_eq!(create.name, "uid");
assert_eq!(create.underlying_type, linkrs_core::DataType::Int);
assert_eq!(create.underlying_type_text, "INT");
} else {
panic!("expected CreateType statement");
}
}
#[test]
fn test_merge_first_on_without_keyword_rejected() {
assert_rejected("MERGE (v:Person {name: 'Alice'}) ON SET v.age = 30");
}
#[test]
fn test_merge_dangling_on_rejected() {
assert_rejected("MERGE (v:Person {name: 'Alice'}) ON");
}
#[test]
fn test_merge_on_clauses_still_parse() {
let stmt = parse_statement(
"MERGE (v:Person {name: 'Alice'}) ON MATCH SET v.age = 30 ON CREATE SET v.age = 0",
)
.expect("MERGE with ON MATCH / ON CREATE should parse");
assert_eq!(stmt.kind(), "MERGE");
}
}