git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
224 lines
5.8 KiB
Rust
224 lines
5.8 KiB
Rust
//! Cypher query parser tests
|
|
//!
|
|
//! Tests for parsing valid and invalid Cypher queries to ensure syntax correctness.
|
|
|
|
use ruvector_graph::cypher::parse_cypher;
|
|
|
|
// ============================================================================
|
|
// Valid Cypher Queries
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_parse_simple_match() {
|
|
let result = parse_cypher("MATCH (n) RETURN n");
|
|
assert!(result.is_ok(), "Parse failed: {:?}", result.err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_match_with_label() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN n");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_match_with_properties() {
|
|
let result = parse_cypher("MATCH (n:Person {name: 'Alice'}) RETURN n");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_match_relationship() {
|
|
let result = parse_cypher("MATCH (a)-[r:KNOWS]->(b) RETURN a, r, b");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_match_undirected_relationship() {
|
|
let result = parse_cypher("MATCH (a)-[r:FRIEND]-(b) RETURN a, b");
|
|
assert!(result.is_ok(), "Parse failed: {:?}", result.err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_match_path() {
|
|
let result = parse_cypher("MATCH p = (a)-[:KNOWS*1..3]->(b) RETURN p");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_create_node() {
|
|
let result = parse_cypher("CREATE (n:Person {name: 'Bob', age: 30})");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_create_relationship() {
|
|
let result =
|
|
parse_cypher("CREATE (a:Person {name: 'Alice'})-[r:KNOWS]->(b:Person {name: 'Bob'})");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_merge() {
|
|
let result = parse_cypher("MERGE (n:Person {name: 'Charlie'})");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_delete() {
|
|
let result = parse_cypher("MATCH (n:Person {name: 'Alice'}) DELETE n");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_set_property() {
|
|
let result = parse_cypher("MATCH (n:Person {name: 'Alice'}) SET n.age = 31");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_remove_property() {
|
|
let result = parse_cypher("MATCH (n:Person {name: 'Alice'}) REMOVE n.age");
|
|
assert!(result.is_ok(), "Parse failed: {:?}", result.err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_where_clause() {
|
|
let result = parse_cypher("MATCH (n:Person) WHERE n.age > 25 RETURN n");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_order_by() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN n ORDER BY n.age DESC");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_limit() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN n LIMIT 10");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_skip() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN n SKIP 5 LIMIT 10");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_aggregate_count() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN COUNT(n)");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_aggregate_sum() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN SUM(n.age)");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_aggregate_avg() {
|
|
let result = parse_cypher("MATCH (n:Person) RETURN AVG(n.age)");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_with_clause() {
|
|
let result = parse_cypher("MATCH (n:Person) WITH n.age AS age WHERE age > 25 RETURN age");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_optional_match() {
|
|
let result = parse_cypher("OPTIONAL MATCH (n:Person)-[r:KNOWS]->(m) RETURN n, m");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
// ============================================================================
|
|
// Complex Query Tests
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
#[ignore = "Complex multi-direction patterns with <- not yet fully implemented"]
|
|
fn test_parse_complex_graph_pattern() {
|
|
let result = parse_cypher(
|
|
"
|
|
MATCH (user:User)-[:PURCHASED]->(product:Product)<-[:PURCHASED]-(other:User)
|
|
WHERE other.id <> 123
|
|
WITH other, COUNT(*) AS commonProducts
|
|
WHERE commonProducts > 3
|
|
RETURN other.name
|
|
ORDER BY commonProducts DESC
|
|
LIMIT 10
|
|
",
|
|
);
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_variable_length_path() {
|
|
let result =
|
|
parse_cypher("MATCH (a:Person)-[:KNOWS*1..5]->(b:Person) WHERE a.name = 'Alice' RETURN b");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_multiple_patterns() {
|
|
let result = parse_cypher(
|
|
"
|
|
MATCH (a:Person)-[:KNOWS]->(b:Person)
|
|
MATCH (b)-[:WORKS_AT]->(c:Company)
|
|
RETURN a.name, b.name, c.name
|
|
",
|
|
);
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_collect_aggregation() {
|
|
let result = parse_cypher(
|
|
"MATCH (p:Person)-[:KNOWS]->(f:Person) RETURN p.name, COLLECT(f.name) AS friends",
|
|
);
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
// ============================================================================
|
|
// Edge Cases
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
#[ignore = "Empty query validation not yet implemented"]
|
|
fn test_parse_empty_query() {
|
|
let result = parse_cypher("");
|
|
// Empty query should fail
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "Whitespace-only query validation not yet implemented"]
|
|
fn test_parse_whitespace_only() {
|
|
let result = parse_cypher(" \n\t ");
|
|
// Whitespace only should fail
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_parameters() {
|
|
let result = parse_cypher("MATCH (n:Person {name: $name, age: $age}) RETURN n");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_list_literal() {
|
|
let result = parse_cypher("RETURN [1, 2, 3, 4, 5] AS numbers");
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "Map literal in RETURN not yet implemented"]
|
|
fn test_parse_map_literal() {
|
|
let result = parse_cypher("RETURN {name: 'Alice', age: 30} AS person");
|
|
assert!(result.is_ok());
|
|
}
|