#[path = "testutils/mod.rs"]
mod testutils;
use graphlite::Value; use std::collections::HashMap;
use testutils::test_fixture::{FixtureType, TestCase, TestFixture, TestSuite};
#[test]
fn test_basic_aggregation_functions() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_basic_aggregation_functions")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
fixture.assert_first_value(
"MATCH (a:Account) RETURN count(a) as account_count",
"account_count",
Value::Number(50.0),
);
fixture.assert_first_value(
"MATCH ()-[t:Transaction]->() RETURN count(t) as transaction_count",
"transaction_count",
Value::Number(100.0), );
let result = fixture.assert_query_succeeds(
"MATCH (a:Account) RETURN count(DISTINCT a.account_type) as distinct_types",
);
assert!(!result.rows.is_empty());
let result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->()
RETURN sum(t.amount) as total_amount,
avg(t.amount) as avg_amount,
count(t) as transaction_count",
);
assert!(!result.rows.is_empty());
let total = match result.rows[0].values.get("total_amount").unwrap() {
Value::Number(n) => *n,
_ => panic!("Expected number for total_amount"),
};
let avg = match result.rows[0].values.get("avg_amount").unwrap() {
Value::Number(n) => *n,
_ => panic!("Expected number for avg_amount"),
};
assert!(
total > 5_000.0,
"Total transaction amount should be > 5K, got: {}",
total
);
assert!(total < 10_000.0, "Total transaction amount should be < 10K");
assert!(avg > 50.0, "Average transaction should be > 50");
assert!(avg < 100.0, "Average transaction should be < 100");
let balance_result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN min(a.balance) as min_balance,
max(a.balance) as max_balance",
);
assert!(!balance_result.rows.is_empty());
let min_balance = match balance_result.rows[0].values.get("min_balance").unwrap() {
Value::Number(n) => *n,
_ => panic!("Expected number for min_balance"),
};
let max_balance = match balance_result.rows[0].values.get("max_balance").unwrap() {
Value::Number(n) => *n,
_ => panic!("Expected number for max_balance"),
};
assert!(min_balance > 50.0, "Min balance should be > 50");
assert!(
max_balance > min_balance,
"Max balance should be > min balance"
);
let avg_result =
fixture.assert_query_succeeds("MATCH (a:Account) RETURN avg(a.balance) as avg_balance");
assert!(!avg_result.rows.is_empty());
let avg_balance = match avg_result.rows[0].values.get("avg_balance").unwrap() {
Value::Number(n) => *n,
_ => panic!("Expected number for avg_balance"),
};
assert!(
avg_balance > min_balance,
"Avg balance should be > min balance"
);
assert!(
avg_balance < max_balance,
"Avg balance should be < max balance"
);
}
#[test]
fn test_group_by_operations() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_group_by_operations")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_type, count(a) as count
GROUP BY a.account_type
ORDER BY count DESC",
);
assert_eq!(result.rows.len(), 4, "Should have exactly 4 account types");
for row in &result.rows {
let count = match row.values.get("count").unwrap() {
Value::Number(n) => *n as usize,
_ => panic!("Expected number for count"),
};
assert!(
count >= 12 && count <= 13,
"Each account type should have 12-13 accounts, got: {}",
count
);
}
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_type, a.account_status, count(a) as count
GROUP BY a.account_type, a.account_status
ORDER BY a.account_type, a.account_status",
);
assert!(!result.rows.is_empty());
let result = fixture.assert_query_succeeds(
"MATCH (m:Merchant)<-[t:Transaction]-()
RETURN m.category,
count(t) as transaction_count,
sum(t.amount) as total_volume,
avg(t.amount) as avg_transaction_size,
min(t.amount) as min_transaction,
max(t.amount) as max_transaction
GROUP BY m.category
ORDER BY total_volume DESC",
);
assert!(!result.rows.is_empty());
let result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->()
RETURN CASE
WHEN t.amount < 1000 THEN 'small'
WHEN t.amount < 10000 THEN 'medium'
WHEN t.amount < 50000 THEN 'large'
ELSE 'very_large'
END as amount_category,
count(t) as transaction_count,
sum(t.amount) as total_amount
GROUP BY amount_category
ORDER BY total_amount DESC",
);
assert!(!result.rows.is_empty());
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->()
WITH a, count(t) as transaction_count, sum(t.amount) as total_spent
RETURN CASE
WHEN transaction_count > 10 THEN 'high_activity'
WHEN transaction_count > 5 THEN 'medium_activity'
ELSE 'low_activity'
END as activity_level,
count(a) as account_count,
avg(total_spent) as avg_total_spent
GROUP BY activity_level
ORDER BY avg_total_spent DESC",
);
assert!(!result.rows.is_empty());
}
#[test]
fn test_having_clause_operations() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_having_clause_operations")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_type, count(a) as count
GROUP BY a.account_type
HAVING count(a) > 10
ORDER BY count DESC",
);
assert!(!result.rows.is_empty());
let _ = fixture.assert_query_succeeds(
"MATCH (m:Merchant)<-[t:Transaction]-()
RETURN m.name,
count(t) as transaction_count,
sum(t.amount) as total_volume
GROUP BY m.name
HAVING count(t) > 20 AND sum(t.amount) > 50000
ORDER BY total_volume DESC
LIMIT 10",
);
let _ = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->()
RETURN a.account_number,
count(t) as transaction_count,
avg(t.amount) as avg_amount,
sum(t.amount) as total_spent
GROUP BY a.account_number
HAVING avg(t.amount) > 5000 AND count(t) > 3
ORDER BY total_spent DESC
LIMIT 20",
);
let result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->(m:Merchant)
RETURN m.category,
count(t) as txn_count,
avg(t.amount) as avg_amount,
sum(t.amount) as total_volume
GROUP BY m.category
HAVING sum(t.amount) / count(t) > 50 -- avg > 50, adjusted for smaller dataset
ORDER BY total_volume DESC",
);
assert!(!result.rows.is_empty());
}
#[test]
fn test_collect_and_list_aggregations() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_collect_and_list_aggregations")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_type, collect(a.account_number) as account_numbers
GROUP BY a.account_type
LIMIT 2",
);
assert!(result.rows.len() <= 2);
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->(m:Merchant)
RETURN a.account_number,
collect(DISTINCT m.category) as merchant_categories,
count(DISTINCT m.category) as category_count
GROUP BY a.account_number
ORDER BY category_count DESC
LIMIT 10",
);
assert!(result.rows.len() <= 10);
let result = fixture.assert_query_succeeds(
"MATCH (m:Merchant)<-[t:Transaction]-(a:Account)
RETURN m.name,
collect(t.amount) as transaction_amounts,
collect(a.account_number) as customer_accounts
GROUP BY m.name
LIMIT 5",
);
assert!(result.rows.len() <= 5);
}
#[test]
fn test_statistical_aggregations() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_statistical_aggregations")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->()
RETURN count(t) as total_transactions,
sum(t.amount) as total_amount,
avg(t.amount) as mean_amount,
min(t.amount) as min_amount,
max(t.amount) as max_amount,
sum(t.amount * t.amount) / count(t) - (avg(t.amount) * avg(t.amount)) as variance_approx"
);
assert!(!result.rows.is_empty());
let result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->()
RETURN count(t.amount) as total_count,
min(t.amount) as min_amount,
max(t.amount) as max_amount,
avg(t.amount) as avg_amount",
);
assert!(!result.rows.is_empty());
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_type, count(a) as frequency
GROUP BY a.account_type
ORDER BY frequency DESC
LIMIT 1",
);
assert_eq!(result.rows.len(), 1);
}
#[test]
fn test_window_function_like_operations() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_window_function_like_operations")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_number, a.balance
ORDER BY a.balance DESC
LIMIT 10",
);
assert!(result.rows.len() <= 10);
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->()
RETURN a.account_number,
count(t) as transaction_count,
sum(t.amount) as total_spent,
avg(t.amount) as avg_transaction_amount
GROUP BY a.account_number
ORDER BY total_spent DESC
LIMIT 20",
);
assert!(!result.rows.is_empty());
let top_result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->()
RETURN t.amount
ORDER BY t.amount DESC
LIMIT 5",
);
assert!(!top_result.rows.is_empty());
let bottom_result = fixture.assert_query_succeeds(
"MATCH ()-[t:Transaction]->()
RETURN t.amount
ORDER BY t.amount ASC
LIMIT 5",
);
assert!(!bottom_result.rows.is_empty());
}
#[test]
fn test_aggregation_data_driven_cases() {
let test_suite = TestSuite {
name: "Aggregation Functions Test Suite".to_string(),
fixture_type: FixtureType::Fraud,
test_cases: vec![
TestCase {
name: "count_all_accounts".to_string(),
description: "Count total number of accounts".to_string(),
query: "MATCH (a:Account) RETURN count(a) as total".to_string(),
expected_rows: Some(1),
expected_values: Some(HashMap::from([("total".to_string(), Value::Number(50.0))])),
expected_error: None,
},
TestCase {
name: "sum_all_balances".to_string(),
description: "Sum all account balances".to_string(),
query: "MATCH (a:Account) RETURN sum(a.balance) as total_balance".to_string(),
expected_rows: Some(1),
expected_values: None, expected_error: None,
},
TestCase {
name: "avg_transaction_amount".to_string(),
description: "Calculate average transaction amount".to_string(),
query: "MATCH ()-[t:Transaction]->() RETURN avg(t.amount) as avg_amount".to_string(),
expected_rows: Some(1),
expected_values: None,
expected_error: None,
},
TestCase {
name: "group_by_account_type".to_string(),
description: "Group accounts by type".to_string(),
query: "MATCH (a:Account) RETURN a.account_type, count(a) as count GROUP BY a.account_type".to_string(),
expected_rows: Some(4), expected_values: None,
expected_error: None,
},
TestCase {
name: "group_by_merchant_category".to_string(),
description: "Group transactions by merchant category".to_string(),
query: "MATCH ()-[t:Transaction]->(m:Merchant) RETURN m.category, count(t) as txn_count GROUP BY m.category".to_string(),
expected_rows: Some(10), expected_values: None,
expected_error: None,
},
TestCase {
name: "having_large_groups".to_string(),
description: "Filter groups with HAVING clause".to_string(),
query: "MATCH (a:Account) RETURN a.account_type, count(a) as count GROUP BY a.account_type HAVING count(a) > 10".to_string(),
expected_rows: None, expected_values: None,
expected_error: None,
},
],
};
let results = test_suite
.run()
.expect("Failed to run aggregation test suite");
results.print_summary();
assert!(
results.passed >= 5,
"Should have at least 5 passing aggregation tests"
);
}
#[test]
fn test_complex_aggregation_scenarios() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_complex_aggregation_scenarios")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->(m:Merchant)
RETURN count(DISTINCT a) as unique_accounts,
count(DISTINCT m.category) as unique_categories,
count(t) as total_transactions,
sum(t.amount) as total_volume,
avg(t.amount) as avg_transaction_amount",
);
assert!(!result.rows.is_empty());
let unique_accounts = match result.rows[0].values.get("unique_accounts").unwrap() {
Value::Number(n) => *n as usize,
_ => panic!("Expected number for unique_accounts"),
};
assert!(
unique_accounts > 0,
"Should have at least some unique accounts"
);
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->()
RETURN CASE
WHEN a.balance < 10000 THEN 'low'
WHEN a.balance < 100000 THEN 'medium'
ELSE 'high'
END as balance_tier,
count(a) as accounts,
count(t) as total_transactions,
avg(t.amount) as avg_transaction_amount
GROUP BY CASE
WHEN a.balance < 10000 THEN 'low'
WHEN a.balance < 100000 THEN 'medium'
ELSE 'high'
END
ORDER BY balance_tier",
);
assert!(!result.rows.is_empty());
let _ = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->()
RETURN a.account_type,
count(t) as transaction_count,
sum(t.amount) as total_volume,
avg(t.amount) as avg_amount
GROUP BY a.account_type
ORDER BY a.account_type
LIMIT 10",
);
let _ = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->(m:Merchant)
WHERE a.balance > 50000
RETURN count(DISTINCT a) as high_balance_accounts,
count(t) as transactions,
sum(t.amount) as total_volume,
count(DISTINCT m) as unique_merchants,
count(DISTINCT m.category) as unique_categories,
avg(t.amount) as avg_transaction_amount",
);
}
#[test]
fn test_aggregation_performance() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_aggregation_performance")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let start = std::time::Instant::now();
let result = fixture.assert_query_succeeds(
"MATCH (a:Account)-[t:Transaction]->(m:Merchant)
RETURN a.account_type,
m.category,
count(t) as transaction_count,
sum(t.amount) as total_amount,
avg(t.amount) as avg_amount,
min(t.amount) as min_amount,
max(t.amount) as max_amount,
count(DISTINCT a) as unique_accounts,
count(DISTINCT m) as unique_merchants
GROUP BY a.account_type, m.category
ORDER BY total_amount DESC",
);
let duration = start.elapsed();
assert!(
duration.as_secs() < 15,
"Complex aggregation should complete within 15 seconds"
);
assert!(!result.rows.is_empty());
let start = std::time::Instant::now();
fixture.assert_query_succeeds(
"MATCH (a:Account)
RETURN a.account_number,
a.balance,
a.risk_score,
a.account_status
GROUP BY a.account_number, a.balance, a.risk_score, a.account_status
ORDER BY a.balance DESC",
);
let duration = start.elapsed();
assert!(
duration.as_secs() < 10,
"Large GROUP BY should complete within 10 seconds"
);
}
#[test]
fn test_aggregation_edge_cases() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_aggregation_edge_cases")
.expect("Failed to setup graph");
fixture
.insert_simple_data()
.expect("Failed to insert simple data");
fixture.assert_first_value(
"MATCH (n:NonExistent) RETURN count(n) as count",
"count",
Value::Number(0.0),
);
let result = fixture.assert_query_succeeds("MATCH (n:NonExistent) RETURN sum(n.value) as sum");
assert!(matches!(
result.rows[0].values.get("sum"),
Some(Value::Null)
));
fixture.assert_query_succeeds(
"INSERT (test:AggTest {value: 10}), (test2:AggTest {value: null}), (test3:AggTest {value: 20})"
);
let result = fixture.assert_query_succeeds(
"MATCH (t:AggTest)
RETURN count(t) as total_nodes,
count(t.value) as non_null_values,
sum(t.value) as sum_values,
avg(t.value) as avg_values",
);
assert_eq!(result.rows.len(), 1);
fixture.assert_query_succeeds("INSERT (big:BigValue {value: 999999999999999})");
fixture.assert_query_succeeds("MATCH (b:BigValue) RETURN sum(b.value) as big_sum");
fixture.assert_query_succeeds("INSERT (small:SmallValue {value: 0.000001})");
fixture.assert_query_succeeds("MATCH (s:SmallValue) RETURN sum(s.value) as small_sum");
}
#[test]
fn test_aggregation_column_order() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_aggregation_column_order")
.expect("Failed to setup graph");
fixture
.insert_simple_data()
.expect("Failed to insert simple data");
let query = r#"
MATCH (n)
RETURN LABELS(n) AS node_labels, COUNT(n) AS count
GROUP BY LABELS(n)
"#;
let result = fixture.assert_query_succeeds(query);
assert_eq!(result.variables.len(), 2, "Should have 2 variables");
assert_eq!(
result.variables[0], "LABELS(...)",
"First variable should be LABELS(...)"
);
assert_eq!(
result.variables[1], "count",
"Second variable should be count"
);
assert!(!result.rows.is_empty(), "Should have some results");
for row in &result.rows {
assert!(
row.values.contains_key("LABELS(...)"),
"Should have LABELS(...) column"
);
assert!(row.values.contains_key("count"), "Should have count column");
if let Some(Value::Number(count)) = row.values.get("count") {
assert!(*count > 0.0, "Count should be positive");
} else {
panic!("Count should be a number");
}
}
}
#[test]
fn test_labels_function_in_aggregation() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_labels_function_in_aggregation")
.expect("Failed to setup graph");
fixture
.insert_fraud_data()
.expect("Failed to insert fraud data");
let simple_query = "MATCH (n:Account) RETURN LABELS(n) AS labels LIMIT 1";
let simple_result = fixture.assert_query_succeeds(simple_query);
let query = r#"
MATCH (n:Account)
RETURN LABELS(n) AS node_labels, COUNT(n) AS count
GROUP BY LABELS(n)
"#;
let result = fixture.assert_query_succeeds(query);
assert!(!result.rows.is_empty(), "Should have aggregation results");
for (i, row) in result.rows.iter().enumerate() {}
let mut found_non_empty_labels = false;
for row in &result.rows {
let node_labels_value = row
.values
.get("node_labels")
.or_else(|| row.values.get("LABELS(...)"));
if let Some(node_labels_value) = node_labels_value {
match node_labels_value {
Value::Array(labels) | Value::List(labels) => {
if !labels.is_empty() {
found_non_empty_labels = true;
for label in labels {
assert!(
matches!(label, Value::String(_)),
"Labels should be strings"
);
}
}
}
_ => {
panic!(
"node_labels should be an array or list, got: {:?}",
node_labels_value
);
}
}
} else {
panic!("node_labels column not found in row");
}
}
if !found_non_empty_labels {}
}
#[test]
fn test_labels_aggregation_with_multiple_labels_and_order_by() {
let fixture = TestFixture::new().expect("Failed to create test fixture");
fixture
.setup_graph("test_labels_aggregation_with_multiple_labels_and_order_by")
.expect("Failed to setup graph");
fixture
.insert_simple_data()
.expect("Failed to insert simple data");
fixture.assert_query_succeeds(&format!(
"CREATE GRAPH /{}/multi_label_test_graph",
fixture.schema_name()
));
fixture.assert_query_succeeds(&format!(
"SESSION SET GRAPH /{}/multi_label_test_graph",
fixture.schema_name()
));
fixture.assert_query_succeeds(
r#"
INSERT
(a:Person {name: "Alice"}),
(b:Person:Employee {name: "Bob", id: 1}),
(c:Person:Employee:Manager {name: "Carol", id: 2}),
(d:Employee {name: "Dave", id: 3}),
(e:Employee:Manager {name: "Eve", id: 4}),
(f:Manager {name: "Frank", id: 5}),
(g:Person {name: "Grace"}),
(h:Person:Employee {name: "Henry", id: 6}),
(i:Company {name: "TechCorp"})
"#,
);
let result = fixture.assert_query_succeeds(
"MATCH (n) RETURN LABELS(n) AS node_labels, COUNT(n) AS count GROUP BY LABELS(n) ORDER BY node_labels"
);
for (i, row) in result.rows.iter().enumerate() {
if let Some(node_labels_value) = row
.get_value("node_labels")
.or_else(|| row.get_value("LABELS(...)"))
{
if let Some(count_value) = row.get_value("count") {}
}
}
assert!(
result.rows.len() >= 6,
"Expected at least 6 different label combinations, got {}",
result.rows.len()
);
let mut previous_labels: Option<String> = None;
for row in &result.rows {
if let Some(node_labels_value) = row
.get_value("node_labels")
.or_else(|| row.get_value("LABELS(...)"))
{
let current_labels = format!("{:?}", node_labels_value);
previous_labels = Some(current_labels);
}
}
let mut found_single_person = false;
let mut found_person_employee = false;
let mut found_person_employee_manager = false;
let mut found_employee_manager = false;
for row in &result.rows {
if let Some(node_labels_value) = row
.get_value("node_labels")
.or_else(|| row.get_value("LABELS(...)"))
{
match node_labels_value {
Value::List(labels) => {
let label_strings: Vec<String> = labels
.iter()
.filter_map(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
.collect();
match label_strings.len() {
1 if label_strings.contains(&"Person".to_string()) => {
found_single_person = true
}
2 if label_strings.contains(&"Person".to_string())
&& label_strings.contains(&"Employee".to_string()) =>
{
found_person_employee = true
}
2 if label_strings.contains(&"Employee".to_string())
&& label_strings.contains(&"Manager".to_string()) =>
{
found_employee_manager = true
}
3 if label_strings.contains(&"Person".to_string())
&& label_strings.contains(&"Employee".to_string())
&& label_strings.contains(&"Manager".to_string()) =>
{
found_person_employee_manager = true
}
_ => {}
}
}
Value::Array(labels) => {
let label_strings: Vec<String> = labels
.iter()
.filter_map(|v| match v {
Value::String(s) => Some(s.clone()),
_ => None,
})
.collect();
match label_strings.len() {
1 if label_strings.contains(&"Person".to_string()) => {
found_single_person = true
}
2 if label_strings.contains(&"Person".to_string())
&& label_strings.contains(&"Employee".to_string()) =>
{
found_person_employee = true
}
2 if label_strings.contains(&"Employee".to_string())
&& label_strings.contains(&"Manager".to_string()) =>
{
found_employee_manager = true
}
3 if label_strings.contains(&"Person".to_string())
&& label_strings.contains(&"Employee".to_string())
&& label_strings.contains(&"Manager".to_string()) =>
{
found_person_employee_manager = true
}
_ => {}
}
}
_ => {}
}
}
}
assert!(
found_single_person,
"Should find nodes with only Person label"
);
assert!(
found_person_employee,
"Should find nodes with Person and Employee labels"
);
assert!(
found_person_employee_manager,
"Should find nodes with Person, Employee, and Manager labels"
);
assert!(
found_employee_manager,
"Should find nodes with Employee and Manager labels"
);
for row in &result.rows {
if let (Some(labels), Some(count)) = (
row.get_value("node_labels")
.or_else(|| row.get_value("LABELS(...)")),
row.get_value("count"),
) {
match count {
Value::Number(n) => {
assert!(*n >= 1.0, "Count should be at least 1, got {}", n);
}
_ => panic!("Count should be a number, got {:?}", count),
}
}
}
}