#[path = "testutils/mod.rs"]
mod testutils;
use graphlite::Value;
use testutils::test_fixture::TestFixture;
fn create_simple_test_fixture() -> Result<TestFixture, Box<dyn std::error::Error>> {
let fixture = TestFixture::new()?;
let graph_name = format!(
"simple_set_ops_{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_nanos()
% 1000000
);
fixture.query(&format!(
"CREATE GRAPH /{}/{}",
fixture.schema_name(),
graph_name
))?;
fixture.query(&format!(
"SESSION SET GRAPH /{}/{}",
fixture.schema_name(),
graph_name
))?;
fixture.query(
"INSERT (p1:Person {name: 'Alice Smith', age: 28, salary: 75000, city: 'Austin'})",
)?;
fixture.query(
"INSERT (p2:Person {name: 'Bob Johnson', age: 35, salary: 65000, city: 'Seattle'})",
)?;
fixture.query(
"INSERT (p3:Person {name: 'Charlie Brown', age: 32, salary: 90000, city: 'Austin'})",
)?;
fixture.query(
"INSERT (p4:Person {name: 'Diana Wilson', age: 29, salary: 80000, city: 'New York'})",
)?;
fixture
.query("INSERT (p5:Person {name: 'Eve Davis', age: 40, salary: 55000, city: 'Seattle'})")?;
fixture.query(
"INSERT (p6:Person {name: 'Frank Miller', age: 26, salary: 60000, city: 'New York'})",
)?;
Ok(fixture)
}
fn create_tutorial_test_fixture() -> Result<TestFixture, Box<dyn std::error::Error>> {
let fixture = TestFixture::new()?;
let graph_name = format!(
"tutorial_set_ops_{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_nanos()
% 1000000
);
fixture.query(&format!(
"CREATE GRAPH /{}/{}",
fixture.schema_name(),
graph_name
))?;
fixture.query(&format!(
"SESSION SET GRAPH /{}/{}",
fixture.schema_name(),
graph_name
))?;
fixture.query(
"INSERT (p1:Person {name: 'Alice Smith', age: 28, salary: 75000, city: 'Austin'})",
)?;
fixture.query(
"INSERT (p2:Person {name: 'Bob Johnson', age: 35, salary: 65000, city: 'Seattle'})",
)?;
fixture.query(
"INSERT (p3:Person {name: 'Charlie Brown', age: 32, salary: 90000, city: 'Austin'})",
)?;
fixture.query(
"INSERT (p4:Person {name: 'Diana Wilson', age: 29, salary: 80000, city: 'New York'})",
)?;
fixture
.query("INSERT (p5:Person {name: 'Eve Davis', age: 40, salary: 55000, city: 'Seattle'})")?;
fixture.query(
"INSERT (p6:Person {name: 'Frank Miller', age: 26, salary: 60000, city: 'New York'})",
)?;
fixture.query("INSERT (d1:Department {name: 'IT'})")?;
fixture.query("INSERT (d2:Department {name: 'Engineering'})")?;
fixture.query("INSERT (d3:Department {name: 'Sales'})")?;
fixture.query("MATCH (p:Person {name: 'Alice Smith'}), (d:Department {name: 'IT'}) INSERT (p)-[:WORKS_IN]->(d)")?;
fixture.query("MATCH (p:Person {name: 'Bob Johnson'}), (d:Department {name: 'Engineering'}) INSERT (p)-[:WORKS_IN]->(d)")?;
fixture.query("MATCH (p:Person {name: 'Charlie Brown'}), (d:Department {name: 'Engineering'}) INSERT (p)-[:WORKS_IN]->(d)")?;
Ok(fixture)
}
#[test]
fn test_simple_union_operation() {
let fixture = create_simple_test_fixture().expect("Failed to create simple test fixture");
let query = "MATCH (p:Person) WHERE p.age < 30 RETURN p.name UNION MATCH (p:Person) WHERE p.age > 35 RETURN p.name";
let result = fixture
.query(query)
.expect("Simple UNION query should succeed");
assert!(
result.rows.len() >= 3,
"UNION should return at least 3 rows, got {}",
result.rows.len()
);
let names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["p.name"].as_string().unwrap().to_string())
.collect();
log::debug!("Simple UNION results: {:?}", names);
assert!(names.contains(&"Alice Smith".to_string()));
assert!(names.contains(&"Frank Miller".to_string()));
assert!(names.contains(&"Eve Davis".to_string()));
}
#[test]
fn test_union_with_where_clauses_deduplication() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "MATCH (p:Person) WHERE p.age < 30 RETURN p.name UNION MATCH (p:Person) WHERE p.salary < 70000 RETURN p.name";
let result = fixture.query(query).expect("UNION query should succeed");
assert!(
result.rows.len() >= 3,
"UNION should return at least 3 unique rows"
);
let names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["p.name"].as_string().unwrap().to_string())
.collect();
log::debug!("UNION results: {:?}", names);
let mut sorted_names = names.clone();
sorted_names.sort();
sorted_names.dedup();
assert_eq!(
names.len(),
sorted_names.len(),
"UNION should not contain duplicate names"
);
}
#[test]
fn test_union_all_preserves_duplicates() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'IT'}) RETURN p.name
UNION ALL
MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'Engineering'}) RETURN p.name
";
let result = fixture
.query(query)
.expect("UNION ALL query should succeed");
log::debug!("UNION ALL results: {} rows", result.rows.len());
if result.rows.len() > 0 {
let names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["p.name"].as_string().unwrap().to_string())
.collect();
log::debug!("UNION ALL names: {:?}", names);
}
}
#[test]
fn test_intersect_with_conditions() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person) WHERE p.age > 30 RETURN p.name
INTERSECT
MATCH (p:Person) WHERE p.salary > 80000 RETURN p.name
";
let result = fixture
.query(query)
.expect("INTERSECT query should succeed");
log::debug!("INTERSECT results: {} rows", result.rows.len());
if result.rows.len() > 0 {
let names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["p.name"].as_string().unwrap().to_string())
.collect();
log::debug!("INTERSECT names: {:?}", names);
}
}
#[test]
fn test_except_with_filter() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person) RETURN p.name
EXCEPT
MATCH (p:Person) WHERE p.city = 'Austin' RETURN p.name
";
let result = fixture.query(query).expect("EXCEPT query should succeed");
log::debug!("EXCEPT results: {} rows", result.rows.len());
let names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["p.name"].as_string().unwrap().to_string())
.collect();
log::debug!("EXCEPT names: {:?}", names);
assert!(
!names.contains(&"Alice Smith".to_string()),
"Alice Smith should be excluded (Austin)"
);
assert!(
!names.contains(&"Charlie Brown".to_string()),
"Charlie Brown should be excluded (Austin)"
);
assert!(
names.contains(&"Bob Johnson".to_string()),
"Bob Johnson should be included (Seattle)"
);
assert!(
names.contains(&"Diana Wilson".to_string()),
"Diana Wilson should be included (New York)"
);
assert!(
names.contains(&"Eve Davis".to_string()),
"Eve Davis should be included (Seattle)"
);
assert!(
names.contains(&"Frank Miller".to_string()),
"Frank Miller should be included (New York)"
);
}
#[test]
fn test_union_variable_inheritance() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person) WHERE p.age < 30 RETURN p.name, p.age
UNION
MATCH (p:Person) WHERE p.salary < 70000 RETURN p.name, p.age
";
let result = fixture
.query(query)
.expect("UNION with multiple columns should succeed");
assert_eq!(
result.variables.len(),
2,
"UNION should return 2 columns (name, age)"
);
assert!(
result.variables.contains(&"p.name".to_string()),
"Should have p.name column"
);
assert!(
result.variables.contains(&"p.age".to_string()),
"Should have p.age column"
);
log::debug!(
"UNION multi-column results: {} rows, columns: {:?}",
result.rows.len(),
result.variables
);
}
#[test]
fn test_nested_set_operations() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person) WHERE p.age < 35 RETURN p.name
INTERSECT
(
MATCH (p:Person) WHERE p.salary > 60000 RETURN p.name
UNION
MATCH (p:Person) WHERE p.city = 'New York' RETURN p.name
)
";
match fixture.query(query) {
Ok(result) => {
log::debug!(
"Nested set operations succeeded: {} rows",
result.rows.len()
);
}
Err(e) => {
log::debug!("Nested set operations not yet supported: {}", e);
}
}
}
#[test]
fn test_basic_queries() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query1 = "MATCH (d:Department) RETURN d.name";
let result1 = fixture
.query(query1)
.expect("Department query should succeed");
log::debug!("Departments: {} rows", result1.rows.len());
for row in &result1.rows {
let dept_name = row.values["d.name"].as_string().unwrap();
log::debug!(" Department: {}", dept_name);
}
let query2 = "MATCH (d:Department {name: 'IT'}) RETURN d.name";
let result2 = fixture
.query(query2)
.expect("IT department query should succeed");
log::debug!("IT departments: {} rows", result2.rows.len());
for row in &result2.rows {
let dept_name = row.values["d.name"].as_string().unwrap();
log::debug!(" IT Department: {}", dept_name);
}
let query3 = "MATCH (p:Person)-[:WORKS_IN]->(d:Department) RETURN p.name, d.name";
let result3 = fixture
.query(query3)
.expect("Basic relationship query should succeed");
log::debug!("Basic relationships: {} rows", result3.rows.len());
for row in &result3.rows {
let person = row.values["p.name"].as_string().unwrap();
let dept = row.values["d.name"].as_string().unwrap();
log::debug!(" {} -> {}", person, dept);
}
let query4 = "MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'IT'}) RETURN p.name";
let result4 = fixture
.query(query4)
.expect("IT relationship query should succeed");
log::debug!("IT relationship: {} rows", result4.rows.len());
for row in &result4.rows {
let person = row.values["p.name"].as_string().unwrap();
log::debug!(" {} works in IT", person);
}
}
#[test]
fn test_debug_relationships() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query1 = "MATCH (p:Person)-[:WORKS_IN]->(d:Department) RETURN p.name, d.name";
let result1 = fixture
.query(query1)
.expect("Basic relationship query should succeed");
log::debug!("All WORKS_IN relationships: {} rows", result1.rows.len());
for row in &result1.rows {
let person = row.values["p.name"].as_string().unwrap();
let dept = row.values["d.name"].as_string().unwrap();
log::debug!(" {} works in {}", person, dept);
}
let query2 = "MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'IT'}) RETURN p.name";
let result2 = fixture
.query(query2)
.expect("IT relationship query should succeed");
log::debug!("People in IT: {} rows", result2.rows.len());
for row in &result2.rows {
let person = row.values["p.name"].as_string().unwrap();
log::debug!(" {} works in IT", person);
}
let query3 = "MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'Engineering'}) RETURN p.name";
let result3 = fixture
.query(query3)
.expect("Engineering relationship query should succeed");
log::debug!("People in Engineering: {} rows", result3.rows.len());
for row in &result3.rows {
let person = row.values["p.name"].as_string().unwrap();
log::debug!(" {} works in Engineering", person);
}
}
#[test]
fn test_union_all_with_relationships() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = r#"
MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'IT'}) RETURN p.name
UNION ALL
MATCH (p:Person)-[:WORKS_IN]->(d:Department {name: 'Engineering'}) RETURN p.name
"#;
let result = fixture
.query(query)
.expect("UNION ALL with relationships should succeed");
log::debug!("UNION ALL relationship results: {} rows", result.rows.len());
let names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["p.name"].as_string().unwrap().to_string())
.collect();
log::debug!("Names found: {:?}", names);
assert_eq!(
result.rows.len(),
3,
"Should find exactly 3 people in IT and Engineering"
);
assert!(
names.contains(&"Alice Smith".to_string()),
"Should find Alice in IT"
);
assert!(
names.contains(&"Bob Johnson".to_string()),
"Should find Bob in Engineering"
);
assert!(
names.contains(&"Charlie Brown".to_string()),
"Should find Charlie in Engineering"
);
}
#[test]
fn test_duplicate_edge_insertion() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture
.query("INSERT (alice:Person {name: 'Alice Smith'})")
.expect("Insert Alice should succeed");
fixture
.query("INSERT (proj1:Project {name: 'Customer Analytics Platform'})")
.expect("Insert Project should succeed");
let relationship_query = "MATCH (alice:Person {name: 'Alice Smith'}), (proj1:Project {name: 'Customer Analytics Platform'}) INSERT (alice)-[:ASSIGNED_TO {role: 'Project Lead', allocation: 0.8}]->(proj1)";
let result1 = fixture.query(relationship_query);
log::debug!("First relationship insertion: {:?}", result1);
let result2 = fixture.query(relationship_query);
log::debug!("Second relationship insertion: {:?}", result2);
let count_query =
"MATCH (p:Person)-[r:ASSIGNED_TO]->(proj:Project) RETURN count(r) as relationship_count";
let count_result = fixture
.query(count_query)
.expect("Count query should succeed");
let count = count_result.rows[0].values["relationship_count"]
.as_number()
.unwrap();
log::debug!("Total ASSIGNED_TO relationships: {}", count);
}
#[test]
fn test_duplicate_node_insertion() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
let insert_query = r#"INSERT (proj1:Project {
name: 'Customer Analytics Platform',
budget: 500000,
start_date: '2023-01-15',
status: 'active'
})"#;
let result1 = fixture.query(insert_query);
log::debug!("First insertion result: {:?}", result1);
let result2 = fixture.query(insert_query);
log::debug!("Second insertion result: {:?}", result2);
let count_query = "MATCH (p:Project) RETURN count(p) as project_count";
let count_result = fixture
.query(count_query)
.expect("Count query should succeed");
let count = count_result.rows[0].values["project_count"]
.as_number()
.unwrap();
log::debug!("Total Project nodes: {}", count);
let all_projects = fixture
.query("MATCH (p:Project) RETURN p.name")
.expect("Project query should succeed");
log::debug!("Found {} project nodes:", all_projects.rows.len());
for row in &all_projects.rows {
let name = row.values["p.name"].as_string().unwrap();
log::debug!(" Project: {}", name);
}
if count == 1.0 {
log::debug!("✅ Duplicate insertion was ignored/deduplicated");
} else if count == 2.0 {
log::debug!("⚠️ Duplicate insertion created multiple nodes");
} else {
panic!("Unexpected node count: {}", count);
}
}
#[test]
fn test_set_multiple_relationship_properties() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture.query("MATCH (n) DETACH DELETE n").ok();
fixture
.query("INSERT (alice:Person {name: 'Alice Smith'})")
.expect("Insert Alice should succeed");
fixture
.query("INSERT (bob:Person {name: 'Bob Johnson'})")
.expect("Insert Bob should succeed");
fixture.query("MATCH (alice:Person {name: 'Alice Smith'}), (bob:Person {name: 'Bob Johnson'}) INSERT (alice)-[:KNOWS {strength: 'weak', lastContact: '2023-01-01'}]->(bob)").expect("Insert relationship should succeed");
let nodes_result = fixture
.query("MATCH (n:Person) RETURN n.name")
.expect("Query should succeed");
log::debug!("Nodes found:");
for row in &nodes_result.rows {
log::debug!(" Person: {:?}", row.values.get("n.name"));
}
let edges_result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) RETURN a.name, b.name, r.strength, r.lastContact").expect("Query should succeed");
log::debug!("Edges found:");
for row in &edges_result.rows {
log::debug!(
" Edge: {:?} -[KNOWS]-> {:?}, strength: {:?}, lastContact: {:?}",
row.values.get("a.name"),
row.values.get("b.name"),
row.values.get("r.strength"),
row.values.get("r.lastContact")
);
}
let initial_result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice Smith' AND b.name = 'Bob Johnson' RETURN r.strength, r.lastContact").expect("Query should succeed");
log::debug!("Initial values:");
for row in &initial_result.rows {
log::debug!(" strength: {:?}", row.values.get("r.strength"));
log::debug!(" lastContact: {:?}", row.values.get("r.lastContact"));
}
let multi_set_result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice Smith' AND b.name = 'Bob Johnson' SET r.strength = 'strong', r.lastContact = '2024-01-10'");
log::debug!("Multi-SET result: {:?}", multi_set_result);
fixture
.query("INSERT (john:Person {name: 'John Doe', age: 30, email: 'john@example.com'})")
.expect("Insert John Doe should succeed");
let node_set_result = fixture.query("MATCH (person:Person {name: 'John Doe'}) SET person.age = 46, person.email = 'john.d@email.com'");
log::debug!("Node SET result: {:?}", node_set_result);
let verify_result = fixture
.query(
"MATCH (person:Person {name: 'John Doe'}) RETURN person.name, person.age, person.email",
)
.expect("Verify should succeed");
log::debug!("Verification result:");
for row in &verify_result.rows {
log::debug!(
" Name: {:?}, Age: {:?}, Email: {:?}",
row.values.get("person.name"),
row.values.get("person.age"),
row.values.get("person.email")
);
}
let updated_result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice Smith' AND b.name = 'Bob Johnson' RETURN r.strength, r.lastContact").expect("Query should succeed");
log::debug!("Updated values:");
for row in &updated_result.rows {
log::debug!(" strength: {:?}", row.values.get("r.strength"));
log::debug!(" lastContact: {:?}", row.values.get("r.lastContact"));
}
assert_eq!(updated_result.rows.len(), 1);
let row = &updated_result.rows[0];
assert_eq!(row.values["r.strength"].as_string().unwrap(), "strong");
assert_eq!(
row.values["r.lastContact"].as_string().unwrap(),
"2024-01-10"
);
}
#[test]
fn test_set_operations_with_different_node_types() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person) RETURN p.name as entity_name
UNION
MATCH (d:Department) RETURN d.name as entity_name
";
let result = fixture
.query(query)
.expect("UNION with different node types should succeed");
log::debug!("Cross-type UNION results: {} rows", result.rows.len());
let entity_names: Vec<String> = result
.rows
.iter()
.map(|row| row.values["entity_name"].as_string().unwrap().to_string())
.collect();
log::debug!("Entity names: {:?}", entity_names);
assert!(
result.rows.len() >= 6,
"Should have at least 6 entities (people)"
);
}
#[test]
fn test_set_operations_error_handling() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let invalid_queries = vec![
"MATCH (p:Person) RETURN p.name UNION MATCH (p:Person) RETURN p.age",
"MATCH (p:Person) RETURN p.name UNION",
"MATCH (p:Person) RETURN p.name INTERSECT INVALID",
];
for query in invalid_queries {
match fixture.query(query) {
Ok(_) => {
log::debug!(
"Query unexpectedly succeeded (may be validation issue): {}",
query
);
}
Err(e) => {
log::debug!(
"Query correctly failed with error: {} for query: {}",
e,
query
);
}
}
}
}
#[test]
fn test_node_set_tutorial_example() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture
.query("INSERT (john:Person {name: 'John Doe', age: 30, email: 'john@example.com'})")
.expect("Insert should succeed");
let result = fixture.query("MATCH (person:Person {name: 'John Doe'}) SET person.age = 46, person.email = 'john.d@email.com'");
assert!(result.is_ok(), "SET query should succeed");
let result = result.unwrap();
assert_eq!(
result.rows_affected, 2,
"Should update 2 properties (counted as 2 affected rows)"
);
let verify = fixture
.query(
"MATCH (person:Person {name: 'John Doe'}) RETURN person.name, person.age, person.email",
)
.expect("Verify should succeed");
assert_eq!(verify.rows.len(), 1, "Should return exactly 1 row");
let row = &verify.rows[0];
log::debug!(
"Verification: Name: {:?}, Age: {:?}, Email: {:?}",
row.values.get("person.name"),
row.values.get("person.age"),
row.values.get("person.email")
);
if let Some(Value::String(name)) = row.values.get("person.name") {
assert_eq!(name, "John Doe");
} else {
panic!("person.name should be a string");
}
if let Some(Value::Number(age)) = row.values.get("person.age") {
assert_eq!(*age, 46.0);
} else {
panic!("person.age should be 46.0");
}
if let Some(Value::String(email)) = row.values.get("person.email") {
assert_eq!(email, "john.d@email.com");
} else {
panic!("person.email should be updated");
}
log::debug!("✅ Tutorial node SET example works correctly!");
}
#[test]
fn test_node_label_set_multiple_labels() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture
.query("INSERT (alice:Person {name: 'Alice Smith', age: 28})")
.expect("Insert should succeed");
let result =
fixture.query("MATCH (person:Person {name: 'Alice Smith'}) SET person:Manager:TeamLead");
assert!(result.is_ok(), "Multiple label SET query should succeed");
let result = result.unwrap();
log::debug!("Label SET result rows_affected: {}", result.rows_affected);
assert!(
result.rows_affected >= 2,
"Should add at least 2 labels (Manager and TeamLead)"
);
let verify = fixture.query("MATCH (person:Person {name: 'Alice Smith'}) RETURN person.name, LABELS(person) as all_labels").expect("Verify should succeed");
log::debug!("Verification returned {} rows", verify.rows.len());
for (i, row) in verify.rows.iter().enumerate() {
log::debug!(
"Row {}: Name: {:?}, Labels: {:?}",
i,
row.values.get("person.name"),
row.values.get("all_labels")
);
}
if !verify.rows.is_empty() {
let row = &verify.rows[0];
if let Some(Value::List(labels)) = row.values.get("all_labels") {
let label_strings: Vec<String> = labels
.iter()
.filter_map(|v| {
if let Value::String(s) = v {
Some(s.clone())
} else {
None
}
})
.collect();
log::debug!("Final labels: {:?}", label_strings);
if label_strings.contains(&"Manager".to_string())
&& label_strings.contains(&"TeamLead".to_string())
{
log::debug!("✅ Tutorial label SET example works correctly!");
} else {
log::debug!("❌ Labels not set correctly. Expected Manager and TeamLead");
}
} else {
log::debug!(
"❌ all_labels is not a list: {:?}",
row.values.get("all_labels")
);
}
} else {
log::debug!("❌ No rows returned from verification query");
}
}
#[test]
fn test_tutorial_remove_operations() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture.query("INSERT (john:Person {name: 'John Doe', age: 30, temporaryField: 'temp_value', oldEmail: 'old@example.com', email: 'current@example.com'})").expect("Insert John Doe should succeed");
fixture
.query("INSERT (alice:Person {name: 'Alice Smith', age: 28})")
.expect("Insert Alice Smith should succeed");
fixture
.query(
"MATCH (alice:Person {name: 'Alice Smith'}) SET alice:Manager:TeamLead:TemporaryRole",
)
.expect("Adding labels should succeed");
log::debug!("=== Testing Property REMOVE ===");
let remove_props_result = fixture.query(
"MATCH (person:Person {name: 'John Doe'}) REMOVE person.temporaryField, person.oldEmail",
);
log::debug!("Remove properties result: {:?}", remove_props_result);
assert!(
remove_props_result.is_ok(),
"Property REMOVE should succeed"
);
let verify_props = fixture.query("MATCH (person:Person {name: 'John Doe'}) RETURN person.name, person.temporaryField, person.oldEmail").expect("Property verification should succeed");
log::debug!("Property verification result:");
for row in &verify_props.rows {
log::debug!(
" Name: {:?}, temporaryField: {:?}, oldEmail: {:?}",
row.values.get("person.name"),
row.values.get("person.temporaryField"),
row.values.get("person.oldEmail")
);
}
if let Some(row) = verify_props.rows.first() {
assert_eq!(
row.values.get("person.temporaryField"),
Some(&Value::Null),
"temporaryField should be null after REMOVE"
);
assert_eq!(
row.values.get("person.oldEmail"),
Some(&Value::Null),
"oldEmail should be null after REMOVE"
);
assert_ne!(
row.values.get("person.name"),
Some(&Value::Null),
"name should still exist"
);
}
log::debug!("\n=== Testing Label REMOVE ===");
let remove_label_result =
fixture.query("MATCH (person:Person {name: 'Alice Smith'}) REMOVE person:TemporaryRole");
log::debug!("Remove label result: {:?}", remove_label_result);
assert!(remove_label_result.is_ok(), "Label REMOVE should succeed");
let verify_labels = fixture.query("MATCH (person:Person {name: 'Alice Smith'}) RETURN person.name, LABELS(person) as all_labels").expect("Label verification should succeed");
log::debug!("Label verification result:");
for row in &verify_labels.rows {
log::debug!(
" Name: {:?}, Labels: {:?}",
row.values.get("person.name"),
row.values.get("all_labels")
);
}
if let Some(row) = verify_labels.rows.first() {
if let Some(Value::List(labels)) = row.values.get("all_labels") {
let label_strings: Vec<String> = labels
.iter()
.filter_map(|v| {
if let Value::String(s) = v {
Some(s.clone())
} else {
None
}
})
.collect();
assert!(
!label_strings.contains(&"TemporaryRole".to_string()),
"TemporaryRole should be removed"
);
assert!(
label_strings.contains(&"Person".to_string()),
"Person label should remain"
);
assert!(
label_strings.contains(&"Manager".to_string()),
"Manager label should remain"
);
assert!(
label_strings.contains(&"TeamLead".to_string()),
"TeamLead label should remain"
);
}
}
log::debug!("\n✅ All tutorial REMOVE operations work correctly!");
}
#[test]
fn test_iso_gql_compliant_verification() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture.query("INSERT (john:Person {name: 'John Doe', age: 30, temporaryField: 'temp_value', oldEmail: 'old@example.com', email: 'current@example.com'})").expect("Insert John Doe should succeed");
fixture
.query("INSERT (alice:Person {name: 'Alice Smith', age: 28})")
.expect("Insert Alice Smith should succeed");
fixture
.query(
"MATCH (alice:Person {name: 'Alice Smith'}) SET alice:Manager:TeamLead:TemporaryRole",
)
.expect("Adding labels should succeed");
fixture.query("MATCH (person:Person {name: 'John Doe'}) REMOVE person.temporaryField, person.oldEmail").expect("Property REMOVE should succeed");
let verify_props = fixture.query("MATCH (person:Person {name: 'John Doe'}) RETURN person.name, person.temporaryField, person.oldEmail").expect("Property verification should succeed");
assert_eq!(verify_props.rows.len(), 1, "Should return exactly 1 row");
if let Some(row) = verify_props.rows.first() {
assert_eq!(
row.values.get("person.temporaryField"),
Some(&Value::Null),
"temporaryField should be null"
);
assert_eq!(
row.values.get("person.oldEmail"),
Some(&Value::Null),
"oldEmail should be null"
);
}
let verify_null = fixture.query("MATCH (person:Person {name: 'John Doe'}) WHERE person.temporaryField IS NULL AND person.oldEmail IS NULL RETURN person.name, 'Properties successfully removed' as status").expect("NULL verification should succeed");
assert_eq!(
verify_null.rows.len(),
1,
"Should confirm properties are null"
);
fixture
.query("MATCH (person:Person {name: 'Alice Smith'}) REMOVE person:TemporaryRole")
.expect("Label REMOVE should succeed");
let verify_labels = fixture.query("MATCH (person:Person {name: 'Alice Smith'}) RETURN person.name, LABELS(person) as all_labels").expect("Label verification should succeed");
assert!(
verify_labels.rows.len() >= 1,
"Should return at least 1 row"
);
let verify_no_temp = fixture.query("MATCH (person:Person {name: 'Alice Smith'}) WHERE NOT person:TemporaryRole RETURN person.name, 'TemporaryRole label successfully removed' as status").expect("Label removal verification should succeed");
log::debug!("WHERE NOT verification rows: {}", verify_no_temp.rows.len());
for (i, row) in verify_no_temp.rows.iter().enumerate() {
log::debug!(" Row {}: {:?}", i, row.values);
}
assert!(
verify_no_temp.rows.len() >= 1,
"Should confirm TemporaryRole was removed"
);
log::debug!("✅ All ISO GQL compliant verification methods work!");
}
#[test]
fn test_performance_with_large_result_sets() {
let fixture = create_tutorial_test_fixture().expect("Failed to create test fixture");
let query = "
MATCH (p:Person) RETURN p.name, p.age, p.salary
UNION ALL
MATCH (p:Person) RETURN p.name, p.age, p.salary
";
let start = std::time::Instant::now();
let result = fixture
.query(query)
.expect("Performance test query should succeed");
let duration = start.elapsed();
log::debug!(
"Performance test: {} rows in {:?}",
result.rows.len(),
duration
);
assert_eq!(
result.rows.len(),
12,
"UNION ALL should return 6 people * 2 = 12 rows"
);
assert!(
duration.as_millis() < 1000,
"Query should complete in under 1 second"
);
}
#[test]
fn test_detach_delete_with_relationships() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture
.query("INSERT (hub:Person {name: 'Hub User', role: 'coordinator'})")
.expect("Insert hub should succeed");
fixture
.query("INSERT (user1:Person {name: 'User One'})")
.expect("Insert user1 should succeed");
fixture
.query("INSERT (user2:Person {name: 'User Two'})")
.expect("Insert user2 should succeed");
fixture
.query("INSERT (project:Project {name: 'Test Project'})")
.expect("Insert project should succeed");
fixture.query("MATCH (hub:Person {name: 'Hub User'}), (user1:Person {name: 'User One'}) INSERT (hub)-[:KNOWS]->(user1)").expect("Create KNOWS relationship should succeed");
fixture.query("MATCH (hub:Person {name: 'Hub User'}), (user2:Person {name: 'User Two'}) INSERT (hub)-[:KNOWS]->(user2)").expect("Create KNOWS relationship should succeed");
fixture.query("MATCH (user2:Person {name: 'User Two'}), (hub:Person {name: 'Hub User'}) INSERT (user2)-[:REPORTS_TO]->(hub)").expect("Create REPORTS_TO relationship should succeed");
fixture.query("MATCH (hub:Person {name: 'Hub User'}), (project:Project {name: 'Test Project'}) INSERT (hub)-[:LEADS]->(project)").expect("Create LEADS relationship should succeed");
let result = fixture.query("MATCH (hub:Person {name: 'Hub User'})-[r]-(connected) RETURN hub.name, TYPE(r) as relationship_type, connected.name as connected_to").expect("Query should succeed");
assert_eq!(result.rows.len(), 4, "Hub User should have 4 relationships");
log::debug!("✅ Created hub node with 4 relationships");
let delete_result = fixture.query("MATCH (hub:Person {name: 'Hub User'}) DELETE hub");
assert!(
delete_result.is_err(),
"DELETE without DETACH should fail when node has relationships"
);
log::debug!("✅ DELETE without DETACH correctly failed for node with relationships");
let result = fixture
.query("MATCH (hub:Person {name: 'Hub User'}) RETURN hub.name")
.expect("Query should succeed");
assert_eq!(
result.rows.len(),
1,
"Hub User should still exist after failed DELETE"
);
let detach_result = fixture.query("MATCH (hub:Person {name: 'Hub User'}) DETACH DELETE hub");
assert!(
detach_result.is_ok(),
"DETACH DELETE should succeed even with relationships"
);
log::debug!("✅ DETACH DELETE succeeded for node with relationships");
let result = fixture
.query("MATCH (hub:Person {name: 'Hub User'}) RETURN hub.name")
.expect("Query should succeed");
assert_eq!(result.rows.len(), 0, "Hub User should be deleted");
let result = fixture
.query("MATCH (user1:Person {name: 'User One'}) RETURN user1.name")
.expect("Query should succeed");
assert_eq!(result.rows.len(), 1, "User One should still exist");
let result = fixture
.query("MATCH (user2:Person {name: 'User Two'}) RETURN user2.name")
.expect("Query should succeed");
assert_eq!(result.rows.len(), 1, "User Two should still exist");
let result = fixture
.query("MATCH (project:Project {name: 'Test Project'}) RETURN project.name")
.expect("Query should succeed");
assert_eq!(result.rows.len(), 1, "Test Project should still exist");
let result = fixture
.query("MATCH (user1:Person {name: 'User One'})-[r]-() RETURN r")
.expect("Query should succeed");
assert_eq!(
result.rows.len(),
0,
"User One should have no relationships"
);
let result = fixture
.query("MATCH (user2:Person {name: 'User Two'})-[r]-() RETURN r")
.expect("Query should succeed");
assert_eq!(
result.rows.len(),
0,
"User Two should have no relationships"
);
let result = fixture
.query("MATCH (project:Project {name: 'Test Project'})-[r]-() RETURN r")
.expect("Query should succeed");
assert_eq!(
result.rows.len(),
0,
"Test Project should have no relationships"
);
log::debug!("✅ All DETACH DELETE tests passed!");
}
#[test]
fn test_edge_deletion_with_pattern_matching() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture
.query("INSERT (alice:Person {name: 'Alice Test Edge'})")
.expect("Insert Alice");
fixture
.query("INSERT (bob:Person {name: 'Bob Test Edge'})")
.expect("Insert Bob");
fixture
.query("INSERT (charlie:Person {name: 'Charlie Test Edge'})")
.expect("Insert Charlie");
fixture.query("MATCH (alice:Person {name: 'Alice Test Edge'}), (bob:Person {name: 'Bob Test Edge'}) INSERT (alice)-[:KNOWS {since: 2021}]->(bob)").expect("Create Alice->Bob");
fixture.query("MATCH (alice:Person {name: 'Alice Test Edge'}), (charlie:Person {name: 'Charlie Test Edge'}) INSERT (alice)-[:KNOWS {since: 2020}]->(charlie)").expect("Create Alice->Charlie");
fixture.query("MATCH (bob:Person {name: 'Bob Test Edge'}), (charlie:Person {name: 'Charlie Test Edge'}) INSERT (bob)-[:KNOWS {since: 2019}]->(charlie)").expect("Create Bob->Charlie");
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name IN ['Alice Test Edge', 'Bob Test Edge'] AND b.name IN ['Bob Test Edge', 'Charlie Test Edge'] RETURN a.name, TYPE(r) as rel_type, b.name ORDER BY a.name, b.name").expect("Query should succeed");
assert_eq!(result.rows.len(), 3, "Should have 3 KNOWS relationships");
log::debug!("✅ Created 3 KNOWS relationships");
log::debug!("Testing delete with pattern matching...");
let delete_result = fixture.query("MATCH (alice:Person {name: 'Alice Test Edge'})-[r:KNOWS]->(charlie:Person {name: 'Charlie Test Edge'}) DELETE r");
assert!(
delete_result.is_ok(),
"Edge deletion with pattern matching should succeed"
);
log::debug!("✅ DELETE edge with pattern matching succeeded");
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice Test Edge' AND b.name = 'Charlie Test Edge' RETURN a.name, TYPE(r) as rel_type, b.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
0,
"Alice->Charlie relationship should be deleted"
);
let delete_result = fixture.query("MATCH (alice:Person {name: 'Alice Test Edge'})-[r:KNOWS]->(bob:Person {name: 'Bob Test Edge'}) DELETE r");
assert!(
delete_result.is_ok(),
"Second edge deletion with pattern matching should succeed"
);
log::debug!("✅ Second DELETE edge with pattern matching succeeded");
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice Test Edge' AND b.name = 'Bob Test Edge' RETURN a.name, TYPE(r) as rel_type, b.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
0,
"Alice->Bob relationship should be deleted"
);
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Bob Test Edge' AND b.name = 'Charlie Test Edge' RETURN a.name, TYPE(r) as rel_type, b.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
1,
"Bob->Charlie relationship should still exist"
);
let result = fixture.query("MATCH (p:Person) WHERE p.name IN ['Alice Test Edge', 'Bob Test Edge', 'Charlie Test Edge'] RETURN p.name ORDER BY p.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
3,
"All 3 test Person nodes should still exist"
);
log::debug!("✅ Edge deletion with pattern matching works correctly!");
}
#[test]
fn test_edge_deletion_with_where_clause_bug() {
let fixture = create_simple_test_fixture().expect("Failed to create test fixture");
fixture
.query("INSERT (alice:Person {name: 'Alice WHERE Test'})")
.expect("Insert Alice");
fixture
.query("INSERT (bob:Person {name: 'Bob WHERE Test'})")
.expect("Insert Bob");
fixture
.query("INSERT (charlie:Person {name: 'Charlie WHERE Test'})")
.expect("Insert Charlie");
fixture.query("MATCH (alice:Person {name: 'Alice WHERE Test'}), (bob:Person {name: 'Bob WHERE Test'}) INSERT (alice)-[:KNOWS {since: 2021}]->(bob)").expect("Create Alice->Bob");
fixture.query("MATCH (alice:Person {name: 'Alice WHERE Test'}), (charlie:Person {name: 'Charlie WHERE Test'}) INSERT (alice)-[:KNOWS {since: 2020}]->(charlie)").expect("Create Alice->Charlie");
fixture.query("MATCH (bob:Person {name: 'Bob WHERE Test'}), (charlie:Person {name: 'Charlie WHERE Test'}) INSERT (bob)-[:KNOWS {since: 2019}]->(charlie)").expect("Create Bob->Charlie");
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name IN ['Alice WHERE Test', 'Bob WHERE Test'] RETURN a.name, TYPE(r) as rel_type, b.name ORDER BY a.name, b.name").expect("Query should succeed");
assert_eq!(result.rows.len(), 3, "Should have 3 KNOWS relationships");
log::debug!("✅ Created 3 KNOWS relationships");
let delete_result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice WHERE Test' AND b.name = 'Charlie WHERE Test' DELETE r");
assert!(
delete_result.is_ok(),
"Edge deletion with WHERE clause should succeed"
);
log::debug!("✅ DELETE edge with WHERE clause succeeded");
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice WHERE Test' AND b.name = 'Charlie WHERE Test' RETURN a.name, TYPE(r) as rel_type, b.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
0,
"Alice->Charlie relationship should be deleted"
);
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Alice WHERE Test' AND b.name = 'Bob WHERE Test' RETURN a.name, TYPE(r) as rel_type, b.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
1,
"Alice->Bob relationship should still exist"
);
let result = fixture.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE a.name = 'Bob WHERE Test' AND b.name = 'Charlie WHERE Test' RETURN a.name, TYPE(r) as rel_type, b.name").expect("Query should succeed");
assert_eq!(
result.rows.len(),
1,
"Bob->Charlie relationship should still exist"
);
log::debug!("✅ Edge deletion with WHERE clause works correctly!");
}