mod test_helpers;
use test_helpers::TestDb;
#[test]
fn list_positive_index() {
assert_eq!(TestDb::new().scalar("RETURN [10, 20, 30][0]"), 10);
}
#[test]
fn list_last_index() {
assert_eq!(TestDb::new().scalar("RETURN [10, 20, 30][2]"), 30);
}
#[test]
fn list_negative_index() {
assert_eq!(TestDb::new().scalar("RETURN [10, 20, 30][-1]"), 30);
}
#[test]
fn list_negative_index_first() {
assert_eq!(TestDb::new().scalar("RETURN [10, 20, 30][-3]"), 10);
}
#[test]
fn list_out_of_bounds_returns_null() {
assert!(TestDb::new().scalar("RETURN [1, 2, 3][10]").is_null());
}
#[test]
fn list_index_on_empty_list() {
assert!(TestDb::new().scalar("RETURN [][0]").is_null());
}
#[test]
fn list_slice_middle() {
let v = TestDb::new().scalar("RETURN [1, 2, 3, 4, 5][1..3]");
assert_eq!(v.as_array().unwrap(), &[2, 3]);
}
#[test]
fn list_slice_from_start() {
let v = TestDb::new().scalar("RETURN [1, 2, 3, 4, 5][..2]");
assert_eq!(v.as_array().unwrap(), &[1, 2]);
}
#[test]
fn list_slice_to_end() {
let v = TestDb::new().scalar("RETURN [1, 2, 3, 4, 5][3..]");
assert_eq!(v.as_array().unwrap(), &[4, 5]);
}
#[test]
fn list_slice_entire() {
let v = TestDb::new().scalar("RETURN [1, 2, 3][0..3]");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3]);
}
#[test]
fn list_slice_empty_range() {
let v = TestDb::new().scalar("RETURN [1, 2, 3][2..2]");
assert_eq!(v.as_array().unwrap().len(), 0);
}
#[test]
fn list_slice_beyond_bounds_clamps() {
let v = TestDb::new().scalar("RETURN [1, 2, 3][0..100]");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3]);
}
#[test]
fn list_concatenation() {
let v = TestDb::new().scalar("RETURN [1, 2] + [3, 4]");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3, 4]);
}
#[test]
fn list_concat_empty_left() {
let v = TestDb::new().scalar("RETURN [] + [1, 2]");
assert_eq!(v.as_array().unwrap(), &[1, 2]);
}
#[test]
fn list_concat_empty_right() {
let v = TestDb::new().scalar("RETURN [1, 2] + []");
assert_eq!(v.as_array().unwrap(), &[1, 2]);
}
#[test]
fn list_equality_same() {
assert_eq!(TestDb::new().scalar("RETURN [1, 2, 3] = [1, 2, 3]"), true);
}
#[test]
fn list_equality_different_length() {
assert_eq!(TestDb::new().scalar("RETURN [1, 2] = [1, 2, 3]"), false);
}
#[test]
fn list_equality_different_values() {
assert_eq!(TestDb::new().scalar("RETURN [1, 2, 3] = [1, 2, 4]"), false);
}
#[test]
fn list_inequality() {
assert_eq!(TestDb::new().scalar("RETURN [1, 2] <> [3, 4]"), true);
}
#[test]
fn empty_list_equality() {
assert_eq!(TestDb::new().scalar("RETURN [] = []"), true);
}
#[test]
fn in_list_found() {
assert_eq!(TestDb::new().scalar("RETURN 2 IN [1, 2, 3]"), true);
}
#[test]
fn in_list_not_found() {
assert_eq!(TestDb::new().scalar("RETURN 5 IN [1, 2, 3]"), false);
}
#[test]
fn in_empty_list() {
assert_eq!(TestDb::new().scalar("RETURN 1 IN []"), false);
}
#[test]
fn string_in_list() {
assert_eq!(TestDb::new().scalar("RETURN 'b' IN ['a', 'b', 'c']"), true);
}
#[test]
fn null_in_list_returns_null() {
assert!(TestDb::new().scalar("RETURN null IN [1, 2, 3]").is_null());
}
#[test]
fn value_in_null_returns_null() {
assert!(TestDb::new().scalar("RETURN 1 IN null").is_null());
}
#[test]
fn nested_list_literal() {
let v = TestDb::new().scalar("RETURN [[1, 2], [3, 4]]");
let outer = v.as_array().unwrap();
assert_eq!(outer.len(), 2);
assert_eq!(outer[0].as_array().unwrap(), &[1, 2]);
assert_eq!(outer[1].as_array().unwrap(), &[3, 4]);
}
#[test]
fn nested_list_index() {
assert_eq!(
TestDb::new().scalar("RETURN [[10, 20], [30, 40]][1][0]"),
30
);
}
#[test]
fn list_of_mixed_types() {
let v = TestDb::new().scalar("RETURN [1, 'two', true, null]");
let arr = v.as_array().unwrap();
assert_eq!(arr.len(), 4);
assert_eq!(arr[0], 1);
assert_eq!(arr[1], "two");
assert_eq!(arr[2], true);
assert!(arr[3].is_null());
}
#[test]
fn head_of_list() {
assert_eq!(TestDb::new().scalar("RETURN head([10, 20, 30])"), 10);
}
#[test]
fn tail_of_list() {
let v = TestDb::new().scalar("RETURN tail([10, 20, 30])");
assert_eq!(v.as_array().unwrap(), &[20, 30]);
}
#[test]
fn tail_of_single_element() {
let v = TestDb::new().scalar("RETURN tail([42])");
assert_eq!(v.as_array().unwrap().len(), 0);
}
#[test]
fn tail_of_empty_list_is_null() {
assert!(TestDb::new().scalar("RETURN tail([])").is_null());
}
#[test]
fn reverse_list_values() {
let v = TestDb::new().scalar("RETURN reverse([1, 2, 3])");
assert_eq!(v.as_array().unwrap(), &[3, 2, 1]);
}
#[test]
fn reverse_empty_list() {
let v = TestDb::new().scalar("RETURN reverse([])");
assert_eq!(v.as_array().unwrap().len(), 0);
}
#[test]
fn size_of_nested_list() {
assert_eq!(TestDb::new().scalar("RETURN size([[1,2], [3,4], [5]])"), 3);
}
#[test]
fn range_ascending() {
let v = TestDb::new().scalar("RETURN range(1, 5)");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3, 4, 5]);
}
#[test]
fn range_with_step() {
let v = TestDb::new().scalar("RETURN range(0, 10, 3)");
assert_eq!(v.as_array().unwrap(), &[0, 3, 6, 9]);
}
#[test]
fn range_descending() {
let v = TestDb::new().scalar("RETURN range(5, 1, -1)");
assert_eq!(v.as_array().unwrap(), &[5, 4, 3, 2, 1]);
}
#[test]
fn range_single_element() {
let v = TestDb::new().scalar("RETURN range(3, 3)");
assert_eq!(v.as_array().unwrap(), &[3]);
}
#[test]
fn range_zero_step_returns_null() {
assert!(TestDb::new().scalar("RETURN range(1, 5, 0)").is_null());
}
#[test]
fn map_literal_access_by_key() {
assert_eq!(
TestDb::new().scalar("RETURN {name: 'Alice', age: 30}.name"),
"Alice"
);
}
#[test]
fn map_access_integer_value() {
assert_eq!(TestDb::new().scalar("RETURN {x: 42}.x"), 42);
}
#[test]
fn map_access_missing_key_returns_null() {
assert!(TestDb::new().scalar("RETURN {name: 'Alice'}.age").is_null());
}
#[test]
fn map_equality() {
assert_eq!(
TestDb::new().scalar("RETURN {a: 1, b: 2} = {a: 1, b: 2}"),
true
);
}
#[test]
fn map_inequality_different_values() {
assert_eq!(
TestDb::new().scalar("RETURN {a: 1, b: 2} = {a: 1, b: 3}"),
false
);
}
#[test]
fn map_inequality_different_keys() {
assert_eq!(TestDb::new().scalar("RETURN {a: 1} = {b: 1}"), false);
}
#[test]
fn empty_map_equality() {
assert_eq!(TestDb::new().scalar("RETURN {} = {}"), true);
}
#[test]
fn nested_map() {
let v = TestDb::new().scalar("RETURN {outer: {inner: 42}}.outer.inner");
assert_eq!(v, 42);
}
#[test]
fn map_with_list_value() {
let v = TestDb::new().scalar("RETURN {items: [1, 2, 3]}.items");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3]);
}
#[test]
fn keys_of_map() {
let v = TestDb::new().scalar("RETURN keys({name: 'Alice', age: 30})");
let keys = v.as_array().unwrap();
assert_eq!(keys.len(), 2);
}
#[test]
fn map_bracket_access() {
assert_eq!(
TestDb::new().scalar("RETURN {name: 'Alice'}['name']"),
"Alice"
);
}
#[test]
fn map_bracket_missing_key() {
assert!(TestDb::new()
.scalar("RETURN {name: 'Alice'}['missing']")
.is_null());
}
#[test]
fn null_equals_null_returns_null() {
let v = TestDb::new().scalar("RETURN null = null");
assert!(v.is_null() || v == true);
}
#[test]
fn null_not_equals_null() {
let v = TestDb::new().scalar("RETURN null <> null");
assert!(v.is_null() || v == false);
}
#[test]
fn null_is_null() {
assert_eq!(TestDb::new().scalar("RETURN null IS NULL"), true);
}
#[test]
fn value_is_not_null() {
assert_eq!(TestDb::new().scalar("RETURN 42 IS NOT NULL"), true);
}
#[test]
fn null_is_not_null() {
assert_eq!(TestDb::new().scalar("RETURN null IS NOT NULL"), false);
}
#[test]
fn null_and_true() {
assert!(TestDb::new().scalar("RETURN null AND true").is_null());
}
#[test]
fn null_and_false() {
assert_eq!(TestDb::new().scalar("RETURN null AND false"), false);
}
#[test]
fn null_or_true() {
assert_eq!(TestDb::new().scalar("RETURN null OR true"), true);
}
#[test]
fn null_or_false() {
assert!(TestDb::new().scalar("RETURN null OR false").is_null());
}
#[test]
fn not_null_returns_null() {
assert!(TestDb::new().scalar("RETURN NOT null").is_null());
}
#[test]
fn null_plus_value() {
assert!(TestDb::new().scalar("RETURN null + 1").is_null());
}
#[test]
fn null_comparison_lt() {
assert!(TestDb::new().scalar("RETURN null < 1").is_null());
}
#[test]
fn null_comparison_gt() {
assert!(TestDb::new().scalar("RETURN null > 1").is_null());
}
#[test]
fn list_with_null_elements() {
let v = TestDb::new().scalar("RETURN [1, null, 3]");
let arr = v.as_array().unwrap();
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], 1);
assert!(arr[1].is_null());
assert_eq!(arr[2], 3);
}
#[test]
fn map_with_null_value() {
let v = TestDb::new().scalar("RETURN {a: 1, b: null}");
let obj = v.as_object().unwrap();
assert_eq!(obj["a"], 1);
assert!(obj["b"].is_null());
}
#[test]
fn head_of_null_list() {
assert!(TestDb::new().scalar("RETURN head(null)").is_null());
}
#[test]
fn size_of_null() {
assert!(TestDb::new().scalar("RETURN size(null)").is_null());
}
#[test]
fn keys_of_null() {
assert!(TestDb::new().scalar("RETURN keys(null)").is_null());
}
#[test]
fn int_equals_float_same_value() {
assert_eq!(TestDb::new().scalar("RETURN 1 = 1.0"), true);
}
#[test]
fn int_not_equals_float_different_value() {
assert_eq!(TestDb::new().scalar("RETURN 1 = 1.5"), false);
}
#[test]
fn int_less_than_float() {
assert_eq!(TestDb::new().scalar("RETURN 1 < 1.5"), true);
}
#[test]
fn float_greater_than_int() {
assert_eq!(TestDb::new().scalar("RETURN 2.5 > 2"), true);
}
#[test]
fn string_not_equal_to_int() {
assert_eq!(TestDb::new().scalar("RETURN '1' = 1"), false);
}
#[test]
fn bool_not_equal_to_int() {
assert_eq!(TestDb::new().scalar("RETURN true = 1"), false);
}
#[test]
fn starts_with_true() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello world' STARTS WITH 'hello'"),
true
);
}
#[test]
fn starts_with_false() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello world' STARTS WITH 'world'"),
false
);
}
#[test]
fn ends_with_true() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello world' ENDS WITH 'world'"),
true
);
}
#[test]
fn ends_with_false() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello world' ENDS WITH 'hello'"),
false
);
}
#[test]
fn contains_true() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello world' CONTAINS 'lo wo'"),
true
);
}
#[test]
fn contains_false() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello world' CONTAINS 'xyz'"),
false
);
}
#[test]
fn starts_with_null_returns_null() {
assert!(TestDb::new()
.scalar("RETURN null STARTS WITH 'x'")
.is_null());
}
#[test]
fn contains_null_rhs_returns_null() {
assert!(TestDb::new()
.scalar("RETURN 'hello' CONTAINS null")
.is_null());
}
#[test]
fn regex_match_simple() {
assert_eq!(
TestDb::new().scalar("RETURN 'hello123' =~ 'hello[0-9]+'"),
true
);
}
#[test]
fn regex_no_match() {
assert_eq!(TestDb::new().scalar("RETURN 'hello' =~ '[0-9]+'"), false);
}
#[test]
fn regex_null_returns_null() {
assert!(TestDb::new().scalar("RETURN null =~ 'pattern'").is_null());
}
#[test]
fn properties_of_node() {
let db = TestDb::new();
db.run("CREATE (:Item {name: 'Widget', price: 42})");
let rows = db.run("MATCH (i:Item) RETURN properties(i) AS props");
let props = rows[0]["props"].as_object().unwrap();
assert_eq!(props["name"], "Widget");
assert_eq!(props["price"], 42);
}
#[test]
fn properties_of_map() {
let v = TestDb::new().scalar("RETURN properties({a: 1, b: 2})");
let obj = v.as_object().unwrap();
assert_eq!(obj["a"], 1);
assert_eq!(obj["b"], 2);
}
#[test]
fn list_comprehension_filter_and_transform() {
let v = TestDb::new().scalar("RETURN [x IN range(1, 10) WHERE x % 3 = 0 | x * x] AS squares");
assert_eq!(v.as_array().unwrap(), &[9, 36, 81]);
}
#[test]
fn list_comprehension_identity() {
let v = TestDb::new().scalar("RETURN [x IN [1, 2, 3]] AS copy");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3]);
}
#[test]
fn list_comprehension_with_strings() {
let v = TestDb::new().scalar("RETURN [x IN ['hello', 'world'] | toUpper(x)] AS upper");
let arr = v.as_array().unwrap();
assert_eq!(arr[0], "HELLO");
assert_eq!(arr[1], "WORLD");
}
#[test]
fn reduce_sum() {
let v =
TestDb::new().scalar("RETURN reduce(total = 0, x IN [1, 2, 3, 4, 5] | total + x) AS sum");
assert_eq!(v, 15);
}
#[test]
fn reduce_string_concat() {
let v = TestDb::new().scalar("RETURN reduce(s = '', x IN ['a', 'b', 'c'] | s + x) AS joined");
assert_eq!(v, "abc");
}
#[test]
fn reduce_empty_list() {
let v = TestDb::new().scalar("RETURN reduce(total = 0, x IN [] | total + x) AS sum");
assert_eq!(v, 0);
}
#[test]
fn any_predicate_true() {
assert_eq!(
TestDb::new().scalar("RETURN any(x IN [1, 2, 3] WHERE x > 2)"),
true
);
}
#[test]
fn any_predicate_false() {
assert_eq!(
TestDb::new().scalar("RETURN any(x IN [1, 2, 3] WHERE x > 5)"),
false
);
}
#[test]
fn all_predicate_true() {
assert_eq!(
TestDb::new().scalar("RETURN all(x IN [2, 4, 6] WHERE x % 2 = 0)"),
true
);
}
#[test]
fn all_predicate_false() {
assert_eq!(
TestDb::new().scalar("RETURN all(x IN [1, 2, 3] WHERE x > 1)"),
false
);
}
#[test]
fn none_predicate_true() {
assert_eq!(
TestDb::new().scalar("RETURN none(x IN [1, 2, 3] WHERE x > 5)"),
true
);
}
#[test]
fn none_predicate_false() {
assert_eq!(
TestDb::new().scalar("RETURN none(x IN [1, 2, 3] WHERE x > 2)"),
false
);
}
#[test]
fn single_predicate_true() {
assert_eq!(
TestDb::new().scalar("RETURN single(x IN [1, 2, 3] WHERE x = 2)"),
true
);
}
#[test]
fn single_predicate_false_none() {
assert_eq!(
TestDb::new().scalar("RETURN single(x IN [1, 2, 3] WHERE x = 5)"),
false
);
}
#[test]
fn single_predicate_false_multiple() {
assert_eq!(
TestDb::new().scalar("RETURN single(x IN [1, 2, 2] WHERE x = 2)"),
false
);
}
#[test]
fn any_on_empty_list() {
assert_eq!(
TestDb::new().scalar("RETURN any(x IN [] WHERE x > 0)"),
false
);
}
#[test]
fn all_on_empty_list() {
assert_eq!(
TestDb::new().scalar("RETURN all(x IN [] WHERE x > 0)"),
true
);
}
#[test]
fn any_on_null_list() {
assert!(TestDb::new()
.scalar("RETURN any(x IN null WHERE x > 0)")
.is_null());
}
#[test]
fn unwind_with_index() {
let db = TestDb::new();
let rows = db.run(
"WITH ['a', 'b', 'c'] AS items \
UNWIND range(0, size(items) - 1) AS i \
RETURN i, items[i] AS val",
);
assert_eq!(rows.len(), 3);
assert_eq!(rows[0]["val"], "a");
assert_eq!(rows[1]["val"], "b");
assert_eq!(rows[2]["val"], "c");
}
#[test]
fn unwind_then_collect() {
let v =
TestDb::new().scalar("UNWIND [3, 1, 2] AS x WITH x ORDER BY x RETURN collect(x) AS sorted");
assert_eq!(v.as_array().unwrap(), &[1, 2, 3]);
}
#[test]
fn type_name_of_integer() {
let v = TestDb::new().scalar("RETURN valueType(42)");
assert_eq!(v, "INTEGER");
}
#[test]
fn type_name_of_string() {
let v = TestDb::new().scalar("RETURN valueType('hello')");
assert_eq!(v, "STRING");
}
#[test]
fn type_name_of_list() {
let v = TestDb::new().scalar("RETURN valueType([1, 2, 3])");
assert_eq!(v, "LIST<INTEGER>");
}
#[test]
fn type_name_of_null() {
let v = TestDb::new().scalar("RETURN valueType(null)");
assert_eq!(v, "NULL");
}
#[test]
fn type_name_of_boolean() {
let v = TestDb::new().scalar("RETURN valueType(true)");
assert_eq!(v, "BOOLEAN");
}
#[test]
fn type_name_of_float() {
let v = TestDb::new().scalar("RETURN valueType(3.14)");
assert_eq!(v, "FLOAT");
}
#[test]
fn tointeger_from_bool_true() {
let v = TestDb::new().scalar("RETURN toInteger(1)");
assert_eq!(v, 1);
}
#[test]
fn tofloat_from_int() {
let v = TestDb::new().scalar("RETURN toFloat(42)").as_f64().unwrap();
assert!((v - 42.0).abs() < 0.001);
}
#[test]
fn tointeger_invalid_string_returns_null() {
assert!(TestDb::new()
.scalar("RETURN toInteger('not_a_number')")
.is_null());
}
#[test]
fn tofloat_invalid_string_returns_null() {
assert!(TestDb::new()
.scalar("RETURN toFloat('not_a_number')")
.is_null());
}
#[test]
fn tostring_of_null() {
assert!(TestDb::new().scalar("RETURN toString(null)").is_null());
}
#[test]
fn toboolean_from_string_true() {
let v = TestDb::new().scalar("RETURN toBoolean('true')");
assert_eq!(v, true);
}
#[test]
fn toboolean_from_string_false() {
let v = TestDb::new().scalar("RETURN toBoolean('false')");
assert_eq!(v, false);
}
#[test]
fn toboolean_invalid_returns_null() {
assert!(TestDb::new().scalar("RETURN toBoolean('maybe')").is_null());
}
#[test]
fn toboolean_from_integer() {
assert_eq!(TestDb::new().scalar("RETURN toBoolean(1)"), true);
assert_eq!(TestDb::new().scalar("RETURN toBoolean(0)"), false);
}
#[test]
fn node_map_projection_dot_star() {
let db = TestDb::new();
db.run("CREATE (:Item {name: 'Widget', price: 42})");
let rows = db.run("MATCH (i:Item) RETURN i {.*} AS m");
let m = rows[0]["m"].as_object().unwrap();
assert_eq!(m["name"], "Widget");
assert_eq!(m["price"], 42);
}
#[test]
fn node_map_projection_specific_keys() {
let db = TestDb::new();
db.run("CREATE (:Item {name: 'Widget', price: 42, stock: 100})");
let rows = db.run("MATCH (i:Item) RETURN i {.name, .price} AS m");
let m = rows[0]["m"].as_object().unwrap();
assert_eq!(m["name"], "Widget");
assert_eq!(m["price"], 42);
assert!(m.get("stock").is_none());
}
#[test]
fn node_map_projection_with_computed() {
let db = TestDb::new();
db.run("CREATE (:Item {name: 'Widget', price: 42})");
let rows = db.run("MATCH (i:Item) RETURN i {.name, total: i.price * 2} AS m");
let m = rows[0]["m"].as_object().unwrap();
assert_eq!(m["name"], "Widget");
assert_eq!(m["total"], 84);
}
#[test]
fn create_node_with_list_property() {
let db = TestDb::new();
db.run("CREATE (:Tag {name: 'multi', values: [1, 2, 3]})");
let rows = db.run("MATCH (t:Tag {name: 'multi'}) RETURN t.values AS vals");
assert_eq!(rows[0]["vals"].as_array().unwrap(), &[1, 2, 3]);
}
#[test]
fn create_node_with_string_list_property() {
let db = TestDb::new();
db.run("CREATE (:Tag {name: 'langs', items: ['Rust', 'Go', 'Python']})");
let rows = db.run("MATCH (t:Tag) RETURN t.items AS items");
let items = rows[0]["items"].as_array().unwrap();
assert_eq!(items.len(), 3);
assert_eq!(items[0], "Rust");
}
#[test]
fn filter_using_in_with_list_property() {
let db = TestDb::new();
db.run("CREATE (:Item {name: 'A', tags: ['red', 'blue']})");
db.run("CREATE (:Item {name: 'B', tags: ['green', 'blue']})");
db.run("CREATE (:Item {name: 'C', tags: ['red', 'green']})");
let rows = db.run("MATCH (i:Item) WHERE 'red' IN i.tags RETURN i.name AS name ORDER BY name");
assert_eq!(rows.len(), 2);
assert_eq!(rows[0]["name"], "A");
assert_eq!(rows[1]["name"], "C");
}
#[test]
fn create_node_with_map_property() {
let db = TestDb::new();
db.run("CREATE (:Config {name: 'app', settings: {debug: true, level: 3}})");
let rows = db.run("MATCH (c:Config) RETURN c.settings AS s");
let s = rows[0]["s"].as_object().unwrap();
assert_eq!(s["debug"], true);
assert_eq!(s["level"], 3);
}
#[test]
fn distinct_integers() {
let v = TestDb::new().scalar("UNWIND [1, 2, 2, 3, 3, 3] AS x RETURN count(DISTINCT x) AS cnt");
assert_eq!(v, 3);
}
#[test]
fn distinct_strings() {
let v =
TestDb::new().scalar("UNWIND ['a', 'b', 'a', 'c'] AS x RETURN count(DISTINCT x) AS cnt");
assert_eq!(v, 3);
}
#[test]
fn distinct_mixed_null() {
let v =
TestDb::new().scalar("UNWIND [1, null, 2, null, 3] AS x RETURN count(DISTINCT x) AS cnt");
assert_eq!(v, 3);
}
#[test]
fn collect_distinct() {
let v = TestDb::new().scalar("UNWIND [1, 2, 2, 3, 1] AS x RETURN collect(DISTINCT x) AS items");
let items = v.as_array().unwrap();
assert_eq!(items.len(), 3);
}
#[test]
fn coalesce_chain() {
assert_eq!(
TestDb::new().scalar("RETURN coalesce(null, null, null, 'found')"),
"found"
);
}
#[test]
fn coalesce_first_non_null_wins() {
assert_eq!(
TestDb::new().scalar("RETURN coalesce('first', 'second', 'third')"),
"first"
);
}
#[test]
fn coalesce_all_null() {
assert!(TestDb::new()
.scalar("RETURN coalesce(null, null)")
.is_null());
}
#[test]
fn xor_true_false() {
assert_eq!(TestDb::new().scalar("RETURN true XOR false"), true);
}
#[test]
fn xor_true_true() {
assert_eq!(TestDb::new().scalar("RETURN true XOR true"), false);
}
#[test]
fn xor_false_false() {
assert_eq!(TestDb::new().scalar("RETURN false XOR false"), false);
}
#[test]
fn xor_null() {
assert!(TestDb::new().scalar("RETURN true XOR null").is_null());
}
#[test]
fn point_2d_creation() {
let v = TestDb::new().scalar("RETURN point({x: 3.0, y: 4.0}) AS p");
assert!(!v.is_null());
}
#[test]
fn point_geographic_creation() {
let v = TestDb::new().scalar("RETURN point({latitude: 52.37, longitude: 4.89}) AS p");
assert!(!v.is_null());
}
#[test]
fn distance_between_points() {
let v =
TestDb::new().scalar("RETURN distance(point({x: 0.0, y: 0.0}), point({x: 3.0, y: 4.0}))");
let d = v.as_f64().unwrap();
assert!((d - 5.0).abs() < 0.001);
}
#[test]
fn point_property_access() {
let v = TestDb::new().scalar("RETURN point({x: 3.0, y: 4.0}).x");
let x = v.as_f64().unwrap();
assert!((x - 3.0).abs() < 0.001);
}
#[test]
fn point_equality() {
assert_eq!(
TestDb::new().scalar("RETURN point({x: 1.0, y: 2.0}) = point({x: 1.0, y: 2.0})"),
true
);
}
#[test]
fn create_node_with_point_property() {
let db = TestDb::new();
db.run("CREATE (:Location {name: 'HQ', pos: point({latitude: 52.37, longitude: 4.89})})");
let rows = db.run("MATCH (l:Location) RETURN l.pos AS pos");
assert_eq!(rows.len(), 1);
assert!(!rows[0]["pos"].is_null());
}
#[test]
fn filter_by_distance() {
let db = TestDb::new();
db.run("CREATE (:Place {name: 'A', pos: point({latitude: 52.37, longitude: 4.89})})");
db.run("CREATE (:Place {name: 'B', pos: point({latitude: 48.85, longitude: 2.35})})");
let rows = db.run(
"WITH point({latitude: 52.0, longitude: 4.5}) AS origin \
MATCH (p:Place) \
WHERE distance(p.pos, origin) < 100000 \
RETURN p.name AS name",
);
assert_eq!(rows.len(), 1);
assert_eq!(rows[0]["name"], "A");
}
#[test]
fn point_y_property_access() {
let v = TestDb::new().scalar("RETURN point({x: 3.0, y: 4.0}).y");
let y = v.as_f64().unwrap();
assert!((y - 4.0).abs() < 0.001);
}
#[test]
fn point_latitude_longitude_access() {
let db = TestDb::new();
let lat = db.scalar("RETURN point({latitude: 52.37, longitude: 4.89}).latitude");
let lon = db.scalar("RETURN point({latitude: 52.37, longitude: 4.89}).longitude");
assert!((lat.as_f64().unwrap() - 52.37).abs() < 0.001);
assert!((lon.as_f64().unwrap() - 4.89).abs() < 0.001);
}
#[test]
fn point_inequality() {
assert_eq!(
TestDb::new().scalar("RETURN point({x: 1.0, y: 2.0}) = point({x: 3.0, y: 4.0})"),
false
);
}
#[test]
fn distance_zero_same_point() {
let v =
TestDb::new().scalar("RETURN distance(point({x: 5.0, y: 5.0}), point({x: 5.0, y: 5.0}))");
let d = v.as_f64().unwrap();
assert!(d.abs() < 0.001);
}
#[test]
fn distance_geographic_known_value() {
let v = TestDb::new().scalar(
"RETURN distance(point({latitude: 52.37, longitude: 4.89}), \
point({latitude: 48.85, longitude: 2.35}))",
);
let d = v.as_f64().unwrap();
assert!(d > 400_000.0 && d < 460_000.0, "distance was {d}");
}
#[test]
fn point_in_where_with_comparison() {
let db = TestDb::new();
db.run("CREATE (:Spot {name: 'A', loc: point({x: 1.0, y: 1.0})})");
db.run("CREATE (:Spot {name: 'B', loc: point({x: 10.0, y: 10.0})})");
let rows = db.run(
"MATCH (s:Spot) \
WHERE distance(s.loc, point({x: 0.0, y: 0.0})) < 5.0 \
RETURN s.name AS name",
);
assert_eq!(rows.len(), 1);
assert_eq!(rows[0]["name"], "A");
}
#[test]
fn point_srid_property() {
let db = TestDb::new();
let srid_cart = db.scalar("RETURN point({x: 1.0, y: 2.0}).srid");
let srid_geo = db.scalar("RETURN point({latitude: 52.0, longitude: 4.0}).srid");
assert_eq!(srid_cart, 7203);
assert_eq!(srid_geo, 4326);
}
#[test]
fn point_valuetype() {
let v = TestDb::new().scalar("RETURN valueType(point({x: 1.0, y: 2.0}))");
assert_eq!(v, "POINT");
}
#[test]
fn point_collect_in_list() {
let db = TestDb::new();
db.run("CREATE (:Pin {loc: point({x: 1.0, y: 1.0})})");
db.run("CREATE (:Pin {loc: point({x: 2.0, y: 2.0})})");
let rows = db.run("MATCH (p:Pin) RETURN collect(p.loc) AS locs");
let locs = rows[0]["locs"].as_array().unwrap();
assert_eq!(locs.len(), 2);
}
#[test]
fn point_order_by_distance() {
let db = TestDb::new();
db.run("CREATE (:City {name: 'Far', loc: point({x: 100.0, y: 100.0})})");
db.run("CREATE (:City {name: 'Near', loc: point({x: 1.0, y: 1.0})})");
db.run("CREATE (:City {name: 'Mid', loc: point({x: 50.0, y: 50.0})})");
let rows = db.run(
"MATCH (c:City) \
RETURN c.name AS name \
ORDER BY distance(c.loc, point({x: 0.0, y: 0.0})) ASC",
);
assert_eq!(rows[0]["name"], "Near");
assert_eq!(rows[1]["name"], "Mid");
assert_eq!(rows[2]["name"], "Far");
}