flowquery 1.0.34 → 1.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/flowquery.min.js +1 -1
- package/dist/graph/database.d.ts +1 -0
- package/dist/graph/database.d.ts.map +1 -1
- package/dist/graph/database.js +43 -6
- package/dist/graph/database.js.map +1 -1
- package/dist/graph/relationship.d.ts +3 -1
- package/dist/graph/relationship.d.ts.map +1 -1
- package/dist/graph/relationship.js +12 -4
- package/dist/graph/relationship.js.map +1 -1
- package/dist/graph/relationship_data.js +1 -1
- package/dist/graph/relationship_data.js.map +1 -1
- package/dist/graph/relationship_match_collector.d.ts.map +1 -1
- package/dist/graph/relationship_match_collector.js +6 -3
- package/dist/graph/relationship_match_collector.js.map +1 -1
- package/dist/graph/relationship_reference.js +1 -1
- package/dist/graph/relationship_reference.js.map +1 -1
- package/dist/parsing/data_structures/lookup.d.ts.map +1 -1
- package/dist/parsing/data_structures/lookup.js +5 -1
- package/dist/parsing/data_structures/lookup.js.map +1 -1
- package/dist/parsing/functions/coalesce.d.ts +17 -0
- package/dist/parsing/functions/coalesce.d.ts.map +1 -0
- package/dist/parsing/functions/coalesce.js +61 -0
- package/dist/parsing/functions/coalesce.js.map +1 -0
- package/dist/parsing/functions/date.d.ts +22 -0
- package/dist/parsing/functions/date.d.ts.map +1 -0
- package/dist/parsing/functions/date.js +71 -0
- package/dist/parsing/functions/date.js.map +1 -0
- package/dist/parsing/functions/datetime.d.ts +22 -0
- package/dist/parsing/functions/datetime.d.ts.map +1 -0
- package/dist/parsing/functions/datetime.js +71 -0
- package/dist/parsing/functions/datetime.js.map +1 -0
- package/dist/parsing/functions/duration.d.ts +7 -0
- package/dist/parsing/functions/duration.d.ts.map +1 -0
- package/dist/parsing/functions/duration.js +145 -0
- package/dist/parsing/functions/duration.js.map +1 -0
- package/dist/parsing/functions/element_id.d.ts +7 -0
- package/dist/parsing/functions/element_id.d.ts.map +1 -0
- package/dist/parsing/functions/element_id.js +58 -0
- package/dist/parsing/functions/element_id.js.map +1 -0
- package/dist/parsing/functions/function_factory.d.ts +21 -0
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +21 -0
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/functions/head.d.ts +7 -0
- package/dist/parsing/functions/head.d.ts.map +1 -0
- package/dist/parsing/functions/head.js +53 -0
- package/dist/parsing/functions/head.js.map +1 -0
- package/dist/parsing/functions/id.d.ts +7 -0
- package/dist/parsing/functions/id.d.ts.map +1 -0
- package/dist/parsing/functions/id.js +58 -0
- package/dist/parsing/functions/id.js.map +1 -0
- package/dist/parsing/functions/last.d.ts +7 -0
- package/dist/parsing/functions/last.d.ts.map +1 -0
- package/dist/parsing/functions/last.js +53 -0
- package/dist/parsing/functions/last.js.map +1 -0
- package/dist/parsing/functions/localdatetime.d.ts +21 -0
- package/dist/parsing/functions/localdatetime.d.ts.map +1 -0
- package/dist/parsing/functions/localdatetime.js +71 -0
- package/dist/parsing/functions/localdatetime.js.map +1 -0
- package/dist/parsing/functions/localtime.d.ts +20 -0
- package/dist/parsing/functions/localtime.d.ts.map +1 -0
- package/dist/parsing/functions/localtime.js +67 -0
- package/dist/parsing/functions/localtime.js.map +1 -0
- package/dist/parsing/functions/max.d.ts +14 -0
- package/dist/parsing/functions/max.d.ts.map +1 -0
- package/dist/parsing/functions/max.js +51 -0
- package/dist/parsing/functions/max.js.map +1 -0
- package/dist/parsing/functions/min.d.ts +14 -0
- package/dist/parsing/functions/min.d.ts.map +1 -0
- package/dist/parsing/functions/min.js +51 -0
- package/dist/parsing/functions/min.js.map +1 -0
- package/dist/parsing/functions/nodes.d.ts +7 -0
- package/dist/parsing/functions/nodes.d.ts.map +1 -0
- package/dist/parsing/functions/nodes.js +63 -0
- package/dist/parsing/functions/nodes.js.map +1 -0
- package/dist/parsing/functions/predicate_sum.d.ts.map +1 -1
- package/dist/parsing/functions/predicate_sum.js +13 -10
- package/dist/parsing/functions/predicate_sum.js.map +1 -1
- package/dist/parsing/functions/properties.d.ts +7 -0
- package/dist/parsing/functions/properties.d.ts.map +1 -0
- package/dist/parsing/functions/properties.js +74 -0
- package/dist/parsing/functions/properties.js.map +1 -0
- package/dist/parsing/functions/relationships.d.ts +7 -0
- package/dist/parsing/functions/relationships.d.ts.map +1 -0
- package/dist/parsing/functions/relationships.js +61 -0
- package/dist/parsing/functions/relationships.js.map +1 -0
- package/dist/parsing/functions/schema.d.ts +5 -2
- package/dist/parsing/functions/schema.d.ts.map +1 -1
- package/dist/parsing/functions/schema.js +7 -4
- package/dist/parsing/functions/schema.js.map +1 -1
- package/dist/parsing/functions/tail.d.ts +7 -0
- package/dist/parsing/functions/tail.d.ts.map +1 -0
- package/dist/parsing/functions/tail.js +50 -0
- package/dist/parsing/functions/tail.js.map +1 -0
- package/dist/parsing/functions/temporal_utils.d.ts +39 -0
- package/dist/parsing/functions/temporal_utils.d.ts.map +1 -0
- package/dist/parsing/functions/temporal_utils.js +168 -0
- package/dist/parsing/functions/temporal_utils.js.map +1 -0
- package/dist/parsing/functions/time.d.ts +20 -0
- package/dist/parsing/functions/time.d.ts.map +1 -0
- package/dist/parsing/functions/time.js +67 -0
- package/dist/parsing/functions/time.js.map +1 -0
- package/dist/parsing/functions/timestamp.d.ts +17 -0
- package/dist/parsing/functions/timestamp.d.ts.map +1 -0
- package/dist/parsing/functions/timestamp.js +51 -0
- package/dist/parsing/functions/timestamp.js.map +1 -0
- package/dist/parsing/functions/to_float.d.ts +7 -0
- package/dist/parsing/functions/to_float.d.ts.map +1 -0
- package/dist/parsing/functions/to_float.js +61 -0
- package/dist/parsing/functions/to_float.js.map +1 -0
- package/dist/parsing/functions/to_integer.d.ts +7 -0
- package/dist/parsing/functions/to_integer.d.ts.map +1 -0
- package/dist/parsing/functions/to_integer.js +61 -0
- package/dist/parsing/functions/to_integer.js.map +1 -0
- package/dist/parsing/functions/trim.d.ts +7 -0
- package/dist/parsing/functions/trim.d.ts.map +1 -0
- package/dist/parsing/functions/trim.js +37 -0
- package/dist/parsing/functions/trim.js.map +1 -0
- package/dist/parsing/operations/group_by.d.ts.map +1 -1
- package/dist/parsing/operations/group_by.js +4 -2
- package/dist/parsing/operations/group_by.js.map +1 -1
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +15 -2
- package/dist/parsing/parser.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/pyproject.toml +1 -1
- package/flowquery-py/src/graph/database.py +44 -11
- package/flowquery-py/src/graph/relationship.py +11 -3
- package/flowquery-py/src/graph/relationship_data.py +2 -1
- package/flowquery-py/src/graph/relationship_match_collector.py +7 -1
- package/flowquery-py/src/graph/relationship_reference.py +2 -2
- package/flowquery-py/src/parsing/data_structures/lookup.py +2 -0
- package/flowquery-py/src/parsing/functions/__init__.py +42 -2
- package/flowquery-py/src/parsing/functions/coalesce.py +44 -0
- package/flowquery-py/src/parsing/functions/date_.py +63 -0
- package/flowquery-py/src/parsing/functions/datetime_.py +64 -0
- package/flowquery-py/src/parsing/functions/duration.py +159 -0
- package/flowquery-py/src/parsing/functions/element_id.py +50 -0
- package/flowquery-py/src/parsing/functions/head.py +39 -0
- package/flowquery-py/src/parsing/functions/id_.py +49 -0
- package/flowquery-py/src/parsing/functions/last.py +39 -0
- package/flowquery-py/src/parsing/functions/localdatetime.py +62 -0
- package/flowquery-py/src/parsing/functions/localtime.py +59 -0
- package/flowquery-py/src/parsing/functions/max_.py +49 -0
- package/flowquery-py/src/parsing/functions/min_.py +49 -0
- package/flowquery-py/src/parsing/functions/nodes.py +48 -0
- package/flowquery-py/src/parsing/functions/predicate_sum.py +3 -6
- package/flowquery-py/src/parsing/functions/properties.py +50 -0
- package/flowquery-py/src/parsing/functions/relationships.py +46 -0
- package/flowquery-py/src/parsing/functions/schema.py +9 -5
- package/flowquery-py/src/parsing/functions/tail.py +37 -0
- package/flowquery-py/src/parsing/functions/temporal_utils.py +186 -0
- package/flowquery-py/src/parsing/functions/time_.py +59 -0
- package/flowquery-py/src/parsing/functions/timestamp.py +39 -0
- package/flowquery-py/src/parsing/functions/to_float.py +46 -0
- package/flowquery-py/src/parsing/functions/to_integer.py +46 -0
- package/flowquery-py/src/parsing/functions/trim.py +35 -0
- package/flowquery-py/src/parsing/operations/group_by.py +2 -0
- package/flowquery-py/src/parsing/parser.py +12 -2
- package/flowquery-py/tests/compute/test_runner.py +1082 -4
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +1 -1
- package/src/graph/database.ts +42 -4
- package/src/graph/relationship.ts +12 -4
- package/src/graph/relationship_data.ts +1 -1
- package/src/graph/relationship_match_collector.ts +6 -2
- package/src/graph/relationship_reference.ts +1 -1
- package/src/parsing/data_structures/lookup.ts +8 -4
- package/src/parsing/functions/coalesce.ts +50 -0
- package/src/parsing/functions/date.ts +65 -0
- package/src/parsing/functions/datetime.ts +65 -0
- package/src/parsing/functions/duration.ts +143 -0
- package/src/parsing/functions/element_id.ts +51 -0
- package/src/parsing/functions/function_factory.ts +21 -0
- package/src/parsing/functions/head.ts +42 -0
- package/src/parsing/functions/id.ts +51 -0
- package/src/parsing/functions/last.ts +42 -0
- package/src/parsing/functions/localdatetime.ts +65 -0
- package/src/parsing/functions/localtime.ts +60 -0
- package/src/parsing/functions/max.ts +37 -0
- package/src/parsing/functions/min.ts +37 -0
- package/src/parsing/functions/nodes.ts +54 -0
- package/src/parsing/functions/predicate_sum.ts +17 -12
- package/src/parsing/functions/properties.ts +56 -0
- package/src/parsing/functions/relationships.ts +52 -0
- package/src/parsing/functions/schema.ts +7 -4
- package/src/parsing/functions/tail.ts +39 -0
- package/src/parsing/functions/temporal_utils.ts +180 -0
- package/src/parsing/functions/time.ts +60 -0
- package/src/parsing/functions/timestamp.ts +41 -0
- package/src/parsing/functions/to_float.ts +50 -0
- package/src/parsing/functions/to_integer.ts +50 -0
- package/src/parsing/functions/trim.ts +25 -0
- package/src/parsing/operations/group_by.ts +4 -1
- package/src/parsing/parser.ts +15 -2
- package/tests/compute/runner.test.ts +1005 -3
- package/tests/parsing/parser.test.ts +37 -0
|
@@ -216,6 +216,86 @@ test("Test avg with one value", async () => {
|
|
|
216
216
|
expect(results[0]).toEqual({ avg: 1 });
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
+
test("Test min", async () => {
|
|
220
|
+
const runner = new Runner("unwind [3, 1, 4, 1, 5, 9] as n return min(n) as minimum");
|
|
221
|
+
await runner.run();
|
|
222
|
+
const results = runner.results;
|
|
223
|
+
expect(results.length).toBe(1);
|
|
224
|
+
expect(results[0]).toEqual({ minimum: 1 });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("Test max", async () => {
|
|
228
|
+
const runner = new Runner("unwind [3, 1, 4, 1, 5, 9] as n return max(n) as maximum");
|
|
229
|
+
await runner.run();
|
|
230
|
+
const results = runner.results;
|
|
231
|
+
expect(results.length).toBe(1);
|
|
232
|
+
expect(results[0]).toEqual({ maximum: 9 });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("Test min with grouped values", async () => {
|
|
236
|
+
const runner = new Runner(
|
|
237
|
+
"unwind [1, 1, 2, 2] as i unwind [10, 20, 30, 40] as j return i, min(j) as minimum"
|
|
238
|
+
);
|
|
239
|
+
await runner.run();
|
|
240
|
+
const results = runner.results;
|
|
241
|
+
expect(results.length).toBe(2);
|
|
242
|
+
expect(results[0]).toEqual({ i: 1, minimum: 10 });
|
|
243
|
+
expect(results[1]).toEqual({ i: 2, minimum: 10 });
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("Test max with grouped values", async () => {
|
|
247
|
+
const runner = new Runner(
|
|
248
|
+
"unwind [1, 1, 2, 2] as i unwind [10, 20, 30, 40] as j return i, max(j) as maximum"
|
|
249
|
+
);
|
|
250
|
+
await runner.run();
|
|
251
|
+
const results = runner.results;
|
|
252
|
+
expect(results.length).toBe(2);
|
|
253
|
+
expect(results[0]).toEqual({ i: 1, maximum: 40 });
|
|
254
|
+
expect(results[1]).toEqual({ i: 2, maximum: 40 });
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("Test min with null", async () => {
|
|
258
|
+
const runner = new Runner("return min(null) as minimum");
|
|
259
|
+
await runner.run();
|
|
260
|
+
const results = runner.results;
|
|
261
|
+
expect(results.length).toBe(1);
|
|
262
|
+
expect(results[0]).toEqual({ minimum: null });
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("Test max with null", async () => {
|
|
266
|
+
const runner = new Runner("return max(null) as maximum");
|
|
267
|
+
await runner.run();
|
|
268
|
+
const results = runner.results;
|
|
269
|
+
expect(results.length).toBe(1);
|
|
270
|
+
expect(results[0]).toEqual({ maximum: null });
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("Test min with strings", async () => {
|
|
274
|
+
const runner = new Runner('unwind ["cherry", "apple", "banana"] as s return min(s) as minimum');
|
|
275
|
+
await runner.run();
|
|
276
|
+
const results = runner.results;
|
|
277
|
+
expect(results.length).toBe(1);
|
|
278
|
+
expect(results[0]).toEqual({ minimum: "apple" });
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("Test max with strings", async () => {
|
|
282
|
+
const runner = new Runner('unwind ["cherry", "apple", "banana"] as s return max(s) as maximum');
|
|
283
|
+
await runner.run();
|
|
284
|
+
const results = runner.results;
|
|
285
|
+
expect(results.length).toBe(1);
|
|
286
|
+
expect(results[0]).toEqual({ maximum: "cherry" });
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("Test min and max together", async () => {
|
|
290
|
+
const runner = new Runner(
|
|
291
|
+
"unwind [3, 1, 4, 1, 5, 9] as n return min(n) as minimum, max(n) as maximum"
|
|
292
|
+
);
|
|
293
|
+
await runner.run();
|
|
294
|
+
const results = runner.results;
|
|
295
|
+
expect(results.length).toBe(1);
|
|
296
|
+
expect(results[0]).toEqual({ minimum: 1, maximum: 9 });
|
|
297
|
+
});
|
|
298
|
+
|
|
219
299
|
test("Test with and return", async () => {
|
|
220
300
|
const runner = new Runner("with 1 as a return a");
|
|
221
301
|
await runner.run();
|
|
@@ -613,6 +693,38 @@ test("Test toLower function with all uppercase", async () => {
|
|
|
613
693
|
expect(results[0]).toEqual({ result: "foo bar" });
|
|
614
694
|
});
|
|
615
695
|
|
|
696
|
+
test("Test trim function", async () => {
|
|
697
|
+
const runner = new Runner('RETURN trim(" hello ") as result');
|
|
698
|
+
await runner.run();
|
|
699
|
+
const results = runner.results;
|
|
700
|
+
expect(results.length).toBe(1);
|
|
701
|
+
expect(results[0]).toEqual({ result: "hello" });
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
test("Test trim function with tabs and newlines", async () => {
|
|
705
|
+
const runner = new Runner('WITH "\tfoo\n" AS s RETURN trim(s) as result');
|
|
706
|
+
await runner.run();
|
|
707
|
+
const results = runner.results;
|
|
708
|
+
expect(results.length).toBe(1);
|
|
709
|
+
expect(results[0]).toEqual({ result: "foo" });
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
test("Test trim function with no whitespace", async () => {
|
|
713
|
+
const runner = new Runner('RETURN trim("hello") as result');
|
|
714
|
+
await runner.run();
|
|
715
|
+
const results = runner.results;
|
|
716
|
+
expect(results.length).toBe(1);
|
|
717
|
+
expect(results[0]).toEqual({ result: "hello" });
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
test("Test trim function with empty string", async () => {
|
|
721
|
+
const runner = new Runner('RETURN trim("") as result');
|
|
722
|
+
await runner.run();
|
|
723
|
+
const results = runner.results;
|
|
724
|
+
expect(results.length).toBe(1);
|
|
725
|
+
expect(results[0]).toEqual({ result: "" });
|
|
726
|
+
});
|
|
727
|
+
|
|
616
728
|
test("Test associative array with key which is keyword", async () => {
|
|
617
729
|
const runner = new Runner("RETURN {return: 1} as aa");
|
|
618
730
|
await runner.run();
|
|
@@ -790,6 +902,121 @@ test("Test keys function", async () => {
|
|
|
790
902
|
expect(results[0]).toEqual({ keys: ["name", "age"] });
|
|
791
903
|
});
|
|
792
904
|
|
|
905
|
+
test("Test properties function with map", async () => {
|
|
906
|
+
const runner = new Runner('RETURN properties({name: "Alice", age: 30}) as props');
|
|
907
|
+
await runner.run();
|
|
908
|
+
const results = runner.results;
|
|
909
|
+
expect(results.length).toBe(1);
|
|
910
|
+
expect(results[0]).toEqual({ props: { name: "Alice", age: 30 } });
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
test("Test properties function with node", async () => {
|
|
914
|
+
await new Runner(`
|
|
915
|
+
CREATE VIRTUAL (:Animal) AS {
|
|
916
|
+
UNWIND [
|
|
917
|
+
{id: 1, name: 'Dog', legs: 4},
|
|
918
|
+
{id: 2, name: 'Cat', legs: 4}
|
|
919
|
+
] AS record
|
|
920
|
+
RETURN record.id AS id, record.name AS name, record.legs AS legs
|
|
921
|
+
}
|
|
922
|
+
`).run();
|
|
923
|
+
const match = new Runner(`
|
|
924
|
+
MATCH (a:Animal)
|
|
925
|
+
RETURN properties(a) AS props
|
|
926
|
+
`);
|
|
927
|
+
await match.run();
|
|
928
|
+
const results = match.results;
|
|
929
|
+
expect(results.length).toBe(2);
|
|
930
|
+
expect(results[0]).toEqual({ props: { name: "Dog", legs: 4 } });
|
|
931
|
+
expect(results[1]).toEqual({ props: { name: "Cat", legs: 4 } });
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
test("Test properties function with null", async () => {
|
|
935
|
+
const runner = new Runner("RETURN properties(null) as props");
|
|
936
|
+
await runner.run();
|
|
937
|
+
const results = runner.results;
|
|
938
|
+
expect(results.length).toBe(1);
|
|
939
|
+
expect(results[0]).toEqual({ props: null });
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
test("Test nodes function", async () => {
|
|
943
|
+
await new Runner(`
|
|
944
|
+
CREATE VIRTUAL (:City) AS {
|
|
945
|
+
UNWIND [
|
|
946
|
+
{id: 1, name: 'New York'},
|
|
947
|
+
{id: 2, name: 'Boston'}
|
|
948
|
+
] AS record
|
|
949
|
+
RETURN record.id AS id, record.name AS name
|
|
950
|
+
}
|
|
951
|
+
`).run();
|
|
952
|
+
await new Runner(`
|
|
953
|
+
CREATE VIRTUAL (:City)-[:CONNECTED_TO]-(:City) AS {
|
|
954
|
+
UNWIND [
|
|
955
|
+
{left_id: 1, right_id: 2}
|
|
956
|
+
] AS record
|
|
957
|
+
RETURN record.left_id AS left_id, record.right_id AS right_id
|
|
958
|
+
}
|
|
959
|
+
`).run();
|
|
960
|
+
const match = new Runner(`
|
|
961
|
+
MATCH p=(:City)-[:CONNECTED_TO]-(:City)
|
|
962
|
+
RETURN nodes(p) AS cities
|
|
963
|
+
`);
|
|
964
|
+
await match.run();
|
|
965
|
+
const results = match.results;
|
|
966
|
+
expect(results.length).toBe(1);
|
|
967
|
+
expect(results[0].cities.length).toBe(2);
|
|
968
|
+
expect(results[0].cities[0].id).toBe(1);
|
|
969
|
+
expect(results[0].cities[0].name).toBe("New York");
|
|
970
|
+
expect(results[0].cities[1].id).toBe(2);
|
|
971
|
+
expect(results[0].cities[1].name).toBe("Boston");
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
test("Test relationships function", async () => {
|
|
975
|
+
await new Runner(`
|
|
976
|
+
CREATE VIRTUAL (:City) AS {
|
|
977
|
+
UNWIND [
|
|
978
|
+
{id: 1, name: 'New York'},
|
|
979
|
+
{id: 2, name: 'Boston'}
|
|
980
|
+
] AS record
|
|
981
|
+
RETURN record.id AS id, record.name AS name
|
|
982
|
+
}
|
|
983
|
+
`).run();
|
|
984
|
+
await new Runner(`
|
|
985
|
+
CREATE VIRTUAL (:City)-[:CONNECTED_TO]-(:City) AS {
|
|
986
|
+
UNWIND [
|
|
987
|
+
{left_id: 1, right_id: 2, distance: 190}
|
|
988
|
+
] AS record
|
|
989
|
+
RETURN record.left_id AS left_id, record.right_id AS right_id, record.distance AS distance
|
|
990
|
+
}
|
|
991
|
+
`).run();
|
|
992
|
+
const match = new Runner(`
|
|
993
|
+
MATCH p=(:City)-[:CONNECTED_TO]-(:City)
|
|
994
|
+
RETURN relationships(p) AS rels
|
|
995
|
+
`);
|
|
996
|
+
await match.run();
|
|
997
|
+
const results = match.results;
|
|
998
|
+
expect(results.length).toBe(1);
|
|
999
|
+
expect(results[0].rels.length).toBe(1);
|
|
1000
|
+
expect(results[0].rels[0].type).toBe("CONNECTED_TO");
|
|
1001
|
+
expect(results[0].rels[0].properties.distance).toBe(190);
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test("Test nodes function with null", async () => {
|
|
1005
|
+
const runner = new Runner("RETURN nodes(null) as n");
|
|
1006
|
+
await runner.run();
|
|
1007
|
+
const results = runner.results;
|
|
1008
|
+
expect(results.length).toBe(1);
|
|
1009
|
+
expect(results[0]).toEqual({ n: [] });
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
test("Test relationships function with null", async () => {
|
|
1013
|
+
const runner = new Runner("RETURN relationships(null) as r");
|
|
1014
|
+
await runner.run();
|
|
1015
|
+
const results = runner.results;
|
|
1016
|
+
expect(results.length).toBe(1);
|
|
1017
|
+
expect(results[0]).toEqual({ r: [] });
|
|
1018
|
+
});
|
|
1019
|
+
|
|
793
1020
|
test("Test type function", async () => {
|
|
794
1021
|
const runner = new Runner(`
|
|
795
1022
|
RETURN type(123) as type1,
|
|
@@ -1793,6 +2020,39 @@ test("Test optional match with no matching relationship", async () => {
|
|
|
1793
2020
|
expect(results[2].friend).toBeNull();
|
|
1794
2021
|
});
|
|
1795
2022
|
|
|
2023
|
+
test("Test optional match property access on null node returns null", async () => {
|
|
2024
|
+
await new Runner(`
|
|
2025
|
+
CREATE VIRTUAL (:Person) AS {
|
|
2026
|
+
unwind [
|
|
2027
|
+
{id: 1, name: 'Person 1'},
|
|
2028
|
+
{id: 2, name: 'Person 2'},
|
|
2029
|
+
{id: 3, name: 'Person 3'}
|
|
2030
|
+
] as record
|
|
2031
|
+
RETURN record.id as id, record.name as name
|
|
2032
|
+
}
|
|
2033
|
+
`).run();
|
|
2034
|
+
await new Runner(`
|
|
2035
|
+
CREATE VIRTUAL (:Person)-[:KNOWS]-(:Person) AS {
|
|
2036
|
+
unwind [
|
|
2037
|
+
{left_id: 1, right_id: 2}
|
|
2038
|
+
] as record
|
|
2039
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
2040
|
+
}
|
|
2041
|
+
`).run();
|
|
2042
|
+
// When accessing b.name and b is null (no match), should return null like Neo4j
|
|
2043
|
+
const match = new Runner(`
|
|
2044
|
+
MATCH (a:Person)
|
|
2045
|
+
OPTIONAL MATCH (a)-[:KNOWS]->(b:Person)
|
|
2046
|
+
RETURN a.name AS name, b.name AS friend_name
|
|
2047
|
+
`);
|
|
2048
|
+
await match.run();
|
|
2049
|
+
const results = match.results;
|
|
2050
|
+
expect(results.length).toBe(3);
|
|
2051
|
+
expect(results[0]).toEqual({ name: "Person 1", friend_name: "Person 2" });
|
|
2052
|
+
expect(results[1]).toEqual({ name: "Person 2", friend_name: null });
|
|
2053
|
+
expect(results[2]).toEqual({ name: "Person 3", friend_name: null });
|
|
2054
|
+
});
|
|
2055
|
+
|
|
1796
2056
|
test("Test optional match where all nodes match", async () => {
|
|
1797
2057
|
await new Runner(`
|
|
1798
2058
|
CREATE VIRTUAL (:Person) AS {
|
|
@@ -1964,20 +2224,24 @@ test("Test schema() returns nodes and relationships with sample data", async ()
|
|
|
1964
2224
|
`).run();
|
|
1965
2225
|
|
|
1966
2226
|
const runner = new Runner(
|
|
1967
|
-
"CALL schema() YIELD kind, label, type, sample RETURN kind, label, type, sample"
|
|
2227
|
+
"CALL schema() YIELD kind, label, type, from_label, to_label, properties, sample RETURN kind, label, type, from_label, to_label, properties, sample"
|
|
1968
2228
|
);
|
|
1969
2229
|
await runner.run();
|
|
1970
2230
|
const results = runner.results;
|
|
1971
2231
|
|
|
1972
|
-
const animal = results.find((r: any) => r.kind === "
|
|
2232
|
+
const animal = results.find((r: any) => r.kind === "Node" && r.label === "Animal");
|
|
1973
2233
|
expect(animal).toBeDefined();
|
|
2234
|
+
expect(animal.properties).toEqual(["species", "legs"]);
|
|
1974
2235
|
expect(animal.sample).toBeDefined();
|
|
1975
2236
|
expect(animal.sample).not.toHaveProperty("id");
|
|
1976
2237
|
expect(animal.sample).toHaveProperty("species");
|
|
1977
2238
|
expect(animal.sample).toHaveProperty("legs");
|
|
1978
2239
|
|
|
1979
|
-
const chases = results.find((r: any) => r.kind === "
|
|
2240
|
+
const chases = results.find((r: any) => r.kind === "Relationship" && r.type === "CHASES");
|
|
1980
2241
|
expect(chases).toBeDefined();
|
|
2242
|
+
expect(chases.from_label).toBe("Animal");
|
|
2243
|
+
expect(chases.to_label).toBe("Animal");
|
|
2244
|
+
expect(chases.properties).toEqual(["speed"]);
|
|
1981
2245
|
expect(chases.sample).toBeDefined();
|
|
1982
2246
|
expect(chases.sample).not.toHaveProperty("left_id");
|
|
1983
2247
|
expect(chases.sample).not.toHaveProperty("right_id");
|
|
@@ -2690,3 +2954,741 @@ test("Test UNION with empty right side", async () => {
|
|
|
2690
2954
|
expect(results.length).toBe(1);
|
|
2691
2955
|
expect(results).toEqual([{ x: 1 }]);
|
|
2692
2956
|
});
|
|
2957
|
+
|
|
2958
|
+
test("Test language name hits query with virtual graph", async () => {
|
|
2959
|
+
// Create Language nodes
|
|
2960
|
+
await new Runner(`
|
|
2961
|
+
CREATE VIRTUAL (:Language) AS {
|
|
2962
|
+
UNWIND [
|
|
2963
|
+
{id: 1, name: 'Python'},
|
|
2964
|
+
{id: 2, name: 'JavaScript'},
|
|
2965
|
+
{id: 3, name: 'TypeScript'}
|
|
2966
|
+
] AS record
|
|
2967
|
+
RETURN record.id AS id, record.name AS name
|
|
2968
|
+
}
|
|
2969
|
+
`).run();
|
|
2970
|
+
|
|
2971
|
+
// Create Chat nodes with messages
|
|
2972
|
+
await new Runner(`
|
|
2973
|
+
CREATE VIRTUAL (:Chat) AS {
|
|
2974
|
+
UNWIND [
|
|
2975
|
+
{id: 1, name: 'Dev Discussion', messages: [
|
|
2976
|
+
{From: 'Alice', SentDateTime: '2025-01-01T10:00:00', Content: 'I love Python and JavaScript'},
|
|
2977
|
+
{From: 'Bob', SentDateTime: '2025-01-01T10:05:00', Content: 'What languages do you prefer?'}
|
|
2978
|
+
]},
|
|
2979
|
+
{id: 2, name: 'General', messages: [
|
|
2980
|
+
{From: 'Charlie', SentDateTime: '2025-01-02T09:00:00', Content: 'The weather is nice today'},
|
|
2981
|
+
{From: 'Alice', SentDateTime: '2025-01-02T09:05:00', Content: 'TypeScript is great for language tooling'}
|
|
2982
|
+
]}
|
|
2983
|
+
] AS record
|
|
2984
|
+
RETURN record.id AS id, record.name AS name, record.messages AS messages
|
|
2985
|
+
}
|
|
2986
|
+
`).run();
|
|
2987
|
+
|
|
2988
|
+
// Create User nodes
|
|
2989
|
+
await new Runner(`
|
|
2990
|
+
CREATE VIRTUAL (:User) AS {
|
|
2991
|
+
UNWIND [
|
|
2992
|
+
{id: 1, displayName: 'Alice'},
|
|
2993
|
+
{id: 2, displayName: 'Bob'},
|
|
2994
|
+
{id: 3, displayName: 'Charlie'}
|
|
2995
|
+
] AS record
|
|
2996
|
+
RETURN record.id AS id, record.displayName AS displayName
|
|
2997
|
+
}
|
|
2998
|
+
`).run();
|
|
2999
|
+
|
|
3000
|
+
// Create PARTICIPATES_IN relationships
|
|
3001
|
+
await new Runner(`
|
|
3002
|
+
CREATE VIRTUAL (:User)-[:PARTICIPATES_IN]-(:Chat) AS {
|
|
3003
|
+
UNWIND [
|
|
3004
|
+
{left_id: 1, right_id: 1},
|
|
3005
|
+
{left_id: 2, right_id: 1},
|
|
3006
|
+
{left_id: 3, right_id: 2},
|
|
3007
|
+
{left_id: 1, right_id: 2}
|
|
3008
|
+
] AS record
|
|
3009
|
+
RETURN record.left_id AS left_id, record.right_id AS right_id
|
|
3010
|
+
}
|
|
3011
|
+
`).run();
|
|
3012
|
+
|
|
3013
|
+
// Run the original query (using 'sender' alias since 'from' is a reserved keyword)
|
|
3014
|
+
const runner = new Runner(`
|
|
3015
|
+
MATCH (l:Language)
|
|
3016
|
+
WITH collect(distinct l.name) AS langs
|
|
3017
|
+
MATCH (c:Chat)
|
|
3018
|
+
UNWIND c.messages AS msg
|
|
3019
|
+
WITH c, msg, langs,
|
|
3020
|
+
sum(lang IN langs | 1 where toLower(msg.Content) CONTAINS toLower(lang)) AS langNameHits
|
|
3021
|
+
WHERE toLower(msg.Content) CONTAINS "language"
|
|
3022
|
+
OR toLower(msg.Content) CONTAINS "languages"
|
|
3023
|
+
OR langNameHits > 0
|
|
3024
|
+
OPTIONAL MATCH (u:User)-[:PARTICIPATES_IN]->(c)
|
|
3025
|
+
RETURN
|
|
3026
|
+
c.name AS chat,
|
|
3027
|
+
collect(distinct u.displayName) AS participants,
|
|
3028
|
+
msg.From AS sender,
|
|
3029
|
+
msg.SentDateTime AS sentDateTime,
|
|
3030
|
+
msg.Content AS message
|
|
3031
|
+
`);
|
|
3032
|
+
await runner.run();
|
|
3033
|
+
const results = runner.results;
|
|
3034
|
+
|
|
3035
|
+
// Messages that mention a language name or the word "language(s)":
|
|
3036
|
+
// 1. "I love Python and JavaScript" - langNameHits=2 (matches Python and JavaScript)
|
|
3037
|
+
// 2. "What languages do you prefer?" - contains "languages"
|
|
3038
|
+
// 3. "TypeScript is great for language tooling" - langNameHits=1, also contains "language"
|
|
3039
|
+
expect(results.length).toBe(3);
|
|
3040
|
+
expect(results[0].chat).toBe("Dev Discussion");
|
|
3041
|
+
expect(results[0].message).toBe("I love Python and JavaScript");
|
|
3042
|
+
expect(results[0].sender).toBe("Alice");
|
|
3043
|
+
expect(results[1].chat).toBe("Dev Discussion");
|
|
3044
|
+
expect(results[1].message).toBe("What languages do you prefer?");
|
|
3045
|
+
expect(results[1].sender).toBe("Bob");
|
|
3046
|
+
expect(results[2].chat).toBe("General");
|
|
3047
|
+
expect(results[2].message).toBe("TypeScript is great for language tooling");
|
|
3048
|
+
expect(results[2].sender).toBe("Alice");
|
|
3049
|
+
});
|
|
3050
|
+
|
|
3051
|
+
test("Test sum with empty collected array", async () => {
|
|
3052
|
+
// Reproduces the original bug: collect on empty input should yield []
|
|
3053
|
+
// and sum over that empty array should return 0, not throw
|
|
3054
|
+
const runner = new Runner(`
|
|
3055
|
+
UNWIND [] AS lang
|
|
3056
|
+
WITH collect(distinct lang) AS langs
|
|
3057
|
+
UNWIND ['hello', 'world'] AS msg
|
|
3058
|
+
WITH msg, langs, sum(l IN langs | 1 where toLower(msg) CONTAINS toLower(l)) AS hits
|
|
3059
|
+
RETURN msg, hits
|
|
3060
|
+
`);
|
|
3061
|
+
await runner.run();
|
|
3062
|
+
const results = runner.results;
|
|
3063
|
+
expect(results.length).toBe(2);
|
|
3064
|
+
expect(results[0]).toEqual({ msg: "hello", hits: 0 });
|
|
3065
|
+
expect(results[1]).toEqual({ msg: "world", hits: 0 });
|
|
3066
|
+
});
|
|
3067
|
+
|
|
3068
|
+
test("Test sum where all elements filtered returns 0", async () => {
|
|
3069
|
+
const runner = new Runner("RETURN sum(n in [1, 2, 3] | n where n > 100) as sum");
|
|
3070
|
+
await runner.run();
|
|
3071
|
+
const results = runner.results;
|
|
3072
|
+
expect(results.length).toBe(1);
|
|
3073
|
+
expect(results[0]).toEqual({ sum: 0 });
|
|
3074
|
+
});
|
|
3075
|
+
|
|
3076
|
+
test("Test sum over empty array returns 0", async () => {
|
|
3077
|
+
const runner = new Runner("WITH [] AS arr RETURN sum(n in arr | n) as sum");
|
|
3078
|
+
await runner.run();
|
|
3079
|
+
const results = runner.results;
|
|
3080
|
+
expect(results.length).toBe(1);
|
|
3081
|
+
expect(results[0]).toEqual({ sum: 0 });
|
|
3082
|
+
});
|
|
3083
|
+
|
|
3084
|
+
test("Test match with ORed relationship types", async () => {
|
|
3085
|
+
await new Runner(`
|
|
3086
|
+
CREATE VIRTUAL (:Person) AS {
|
|
3087
|
+
unwind [
|
|
3088
|
+
{id: 1, name: 'Alice'},
|
|
3089
|
+
{id: 2, name: 'Bob'},
|
|
3090
|
+
{id: 3, name: 'Charlie'}
|
|
3091
|
+
] as record
|
|
3092
|
+
RETURN record.id as id, record.name as name
|
|
3093
|
+
}
|
|
3094
|
+
`).run();
|
|
3095
|
+
await new Runner(`
|
|
3096
|
+
CREATE VIRTUAL (:Person)-[:KNOWS]-(:Person) AS {
|
|
3097
|
+
unwind [
|
|
3098
|
+
{left_id: 1, right_id: 2}
|
|
3099
|
+
] as record
|
|
3100
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
3101
|
+
}
|
|
3102
|
+
`).run();
|
|
3103
|
+
await new Runner(`
|
|
3104
|
+
CREATE VIRTUAL (:Person)-[:FOLLOWS]-(:Person) AS {
|
|
3105
|
+
unwind [
|
|
3106
|
+
{left_id: 2, right_id: 3}
|
|
3107
|
+
] as record
|
|
3108
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
3109
|
+
}
|
|
3110
|
+
`).run();
|
|
3111
|
+
const match = new Runner(`
|
|
3112
|
+
MATCH (a:Person)-[:KNOWS|FOLLOWS]->(b:Person)
|
|
3113
|
+
RETURN a.name AS name1, b.name AS name2
|
|
3114
|
+
`);
|
|
3115
|
+
await match.run();
|
|
3116
|
+
const results = match.results;
|
|
3117
|
+
expect(results.length).toBe(2);
|
|
3118
|
+
expect(results[0]).toEqual({ name1: "Alice", name2: "Bob" });
|
|
3119
|
+
expect(results[1]).toEqual({ name1: "Bob", name2: "Charlie" });
|
|
3120
|
+
});
|
|
3121
|
+
|
|
3122
|
+
test("Test match with ORed relationship types with optional colon syntax", async () => {
|
|
3123
|
+
await new Runner(`
|
|
3124
|
+
CREATE VIRTUAL (:Animal) AS {
|
|
3125
|
+
unwind [
|
|
3126
|
+
{id: 1, name: 'Cat'},
|
|
3127
|
+
{id: 2, name: 'Dog'},
|
|
3128
|
+
{id: 3, name: 'Fish'}
|
|
3129
|
+
] as record
|
|
3130
|
+
RETURN record.id as id, record.name as name
|
|
3131
|
+
}
|
|
3132
|
+
`).run();
|
|
3133
|
+
await new Runner(`
|
|
3134
|
+
CREATE VIRTUAL (:Animal)-[:CHASES]-(:Animal) AS {
|
|
3135
|
+
unwind [
|
|
3136
|
+
{left_id: 1, right_id: 2}
|
|
3137
|
+
] as record
|
|
3138
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
3139
|
+
}
|
|
3140
|
+
`).run();
|
|
3141
|
+
await new Runner(`
|
|
3142
|
+
CREATE VIRTUAL (:Animal)-[:EATS]-(:Animal) AS {
|
|
3143
|
+
unwind [
|
|
3144
|
+
{left_id: 1, right_id: 3}
|
|
3145
|
+
] as record
|
|
3146
|
+
RETURN record.left_id as left_id, record.right_id as right_id
|
|
3147
|
+
}
|
|
3148
|
+
`).run();
|
|
3149
|
+
const match = new Runner(`
|
|
3150
|
+
MATCH (a:Animal)-[:CHASES|:EATS]->(b:Animal)
|
|
3151
|
+
RETURN a.name AS name1, b.name AS name2
|
|
3152
|
+
`);
|
|
3153
|
+
await match.run();
|
|
3154
|
+
const results = match.results;
|
|
3155
|
+
expect(results.length).toBe(2);
|
|
3156
|
+
expect(results[0]).toEqual({ name1: "Cat", name2: "Dog" });
|
|
3157
|
+
expect(results[1]).toEqual({ name1: "Cat", name2: "Fish" });
|
|
3158
|
+
});
|
|
3159
|
+
|
|
3160
|
+
test("Test match with ORed relationship types returns correct type in relationship variable", async () => {
|
|
3161
|
+
await new Runner(`
|
|
3162
|
+
CREATE VIRTUAL (:City) AS {
|
|
3163
|
+
unwind [
|
|
3164
|
+
{id: 1, name: 'NYC'},
|
|
3165
|
+
{id: 2, name: 'LA'},
|
|
3166
|
+
{id: 3, name: 'Chicago'}
|
|
3167
|
+
] as record
|
|
3168
|
+
RETURN record.id as id, record.name as name
|
|
3169
|
+
}
|
|
3170
|
+
`).run();
|
|
3171
|
+
await new Runner(`
|
|
3172
|
+
CREATE VIRTUAL (:City)-[:FLIGHT]-(:City) AS {
|
|
3173
|
+
unwind [
|
|
3174
|
+
{left_id: 1, right_id: 2, airline: 'Delta'}
|
|
3175
|
+
] as record
|
|
3176
|
+
RETURN record.left_id as left_id, record.right_id as right_id, record.airline as airline
|
|
3177
|
+
}
|
|
3178
|
+
`).run();
|
|
3179
|
+
await new Runner(`
|
|
3180
|
+
CREATE VIRTUAL (:City)-[:TRAIN]-(:City) AS {
|
|
3181
|
+
unwind [
|
|
3182
|
+
{left_id: 1, right_id: 3, line: 'Amtrak'}
|
|
3183
|
+
] as record
|
|
3184
|
+
RETURN record.left_id as left_id, record.right_id as right_id, record.line as line
|
|
3185
|
+
}
|
|
3186
|
+
`).run();
|
|
3187
|
+
const match = new Runner(`
|
|
3188
|
+
MATCH (a:City)-[r:FLIGHT|TRAIN]->(b:City)
|
|
3189
|
+
RETURN a.name AS from, b.name AS to, r.type AS type
|
|
3190
|
+
`);
|
|
3191
|
+
await match.run();
|
|
3192
|
+
const results = match.results;
|
|
3193
|
+
expect(results.length).toBe(2);
|
|
3194
|
+
expect(results[0]).toEqual({ from: "NYC", to: "LA", type: "FLIGHT" });
|
|
3195
|
+
expect(results[1]).toEqual({ from: "NYC", to: "Chicago", type: "TRAIN" });
|
|
3196
|
+
});
|
|
3197
|
+
|
|
3198
|
+
test("Test coalesce returns first non-null value", async () => {
|
|
3199
|
+
const runner = new Runner("RETURN coalesce(null, null, 'hello', 'world') as result");
|
|
3200
|
+
await runner.run();
|
|
3201
|
+
const results = runner.results;
|
|
3202
|
+
expect(results.length).toBe(1);
|
|
3203
|
+
expect(results[0]).toEqual({ result: "hello" });
|
|
3204
|
+
});
|
|
3205
|
+
|
|
3206
|
+
test("Test coalesce returns first argument when not null", async () => {
|
|
3207
|
+
const runner = new Runner("RETURN coalesce('first', 'second') as result");
|
|
3208
|
+
await runner.run();
|
|
3209
|
+
const results = runner.results;
|
|
3210
|
+
expect(results.length).toBe(1);
|
|
3211
|
+
expect(results[0]).toEqual({ result: "first" });
|
|
3212
|
+
});
|
|
3213
|
+
|
|
3214
|
+
test("Test coalesce returns null when all arguments are null", async () => {
|
|
3215
|
+
const runner = new Runner("RETURN coalesce(null, null, null) as result");
|
|
3216
|
+
await runner.run();
|
|
3217
|
+
const results = runner.results;
|
|
3218
|
+
expect(results.length).toBe(1);
|
|
3219
|
+
expect(results[0]).toEqual({ result: null });
|
|
3220
|
+
});
|
|
3221
|
+
|
|
3222
|
+
test("Test coalesce with single non-null argument", async () => {
|
|
3223
|
+
const runner = new Runner("RETURN coalesce(42) as result");
|
|
3224
|
+
await runner.run();
|
|
3225
|
+
const results = runner.results;
|
|
3226
|
+
expect(results.length).toBe(1);
|
|
3227
|
+
expect(results[0]).toEqual({ result: 42 });
|
|
3228
|
+
});
|
|
3229
|
+
|
|
3230
|
+
test("Test coalesce with mixed types", async () => {
|
|
3231
|
+
const runner = new Runner("RETURN coalesce(null, 42, 'hello') as result");
|
|
3232
|
+
await runner.run();
|
|
3233
|
+
const results = runner.results;
|
|
3234
|
+
expect(results.length).toBe(1);
|
|
3235
|
+
expect(results[0]).toEqual({ result: 42 });
|
|
3236
|
+
});
|
|
3237
|
+
|
|
3238
|
+
test("Test coalesce with property access", async () => {
|
|
3239
|
+
const runner = new Runner(
|
|
3240
|
+
"WITH {name: 'Alice'} AS person RETURN coalesce(person.nickname, person.name) as result"
|
|
3241
|
+
);
|
|
3242
|
+
await runner.run();
|
|
3243
|
+
const results = runner.results;
|
|
3244
|
+
expect(results.length).toBe(1);
|
|
3245
|
+
expect(results[0]).toEqual({ result: "Alice" });
|
|
3246
|
+
});
|
|
3247
|
+
|
|
3248
|
+
// ============================================================
|
|
3249
|
+
// Temporal / Time Functions (Neo4j-style)
|
|
3250
|
+
// ============================================================
|
|
3251
|
+
|
|
3252
|
+
test("Test datetime() returns current datetime object", async () => {
|
|
3253
|
+
const before = Date.now();
|
|
3254
|
+
const runner = new Runner("RETURN datetime() AS dt");
|
|
3255
|
+
await runner.run();
|
|
3256
|
+
const after = Date.now();
|
|
3257
|
+
const results = runner.results;
|
|
3258
|
+
expect(results.length).toBe(1);
|
|
3259
|
+
const dt = results[0].dt;
|
|
3260
|
+
expect(dt).toBeDefined();
|
|
3261
|
+
expect(typeof dt.year).toBe("number");
|
|
3262
|
+
expect(typeof dt.month).toBe("number");
|
|
3263
|
+
expect(typeof dt.day).toBe("number");
|
|
3264
|
+
expect(typeof dt.hour).toBe("number");
|
|
3265
|
+
expect(typeof dt.minute).toBe("number");
|
|
3266
|
+
expect(typeof dt.second).toBe("number");
|
|
3267
|
+
expect(typeof dt.millisecond).toBe("number");
|
|
3268
|
+
expect(typeof dt.epochMillis).toBe("number");
|
|
3269
|
+
expect(typeof dt.epochSeconds).toBe("number");
|
|
3270
|
+
expect(typeof dt.dayOfWeek).toBe("number");
|
|
3271
|
+
expect(typeof dt.dayOfYear).toBe("number");
|
|
3272
|
+
expect(typeof dt.quarter).toBe("number");
|
|
3273
|
+
expect(typeof dt.formatted).toBe("string");
|
|
3274
|
+
// epochMillis should be between before and after
|
|
3275
|
+
expect(dt.epochMillis).toBeGreaterThanOrEqual(before);
|
|
3276
|
+
expect(dt.epochMillis).toBeLessThanOrEqual(after);
|
|
3277
|
+
});
|
|
3278
|
+
|
|
3279
|
+
test("Test datetime() with ISO string argument", async () => {
|
|
3280
|
+
const runner = new Runner("RETURN datetime('2025-06-15T12:30:45.123Z') AS dt");
|
|
3281
|
+
await runner.run();
|
|
3282
|
+
const results = runner.results;
|
|
3283
|
+
expect(results.length).toBe(1);
|
|
3284
|
+
const dt = results[0].dt;
|
|
3285
|
+
expect(dt.year).toBe(2025);
|
|
3286
|
+
expect(dt.month).toBe(6);
|
|
3287
|
+
expect(dt.day).toBe(15);
|
|
3288
|
+
expect(dt.hour).toBe(12);
|
|
3289
|
+
expect(dt.minute).toBe(30);
|
|
3290
|
+
expect(dt.second).toBe(45);
|
|
3291
|
+
expect(dt.millisecond).toBe(123);
|
|
3292
|
+
expect(dt.formatted).toBe("2025-06-15T12:30:45.123Z");
|
|
3293
|
+
});
|
|
3294
|
+
|
|
3295
|
+
test("Test datetime() property access", async () => {
|
|
3296
|
+
const runner = new Runner(
|
|
3297
|
+
"WITH datetime('2025-06-15T12:30:45.123Z') AS dt RETURN dt.year AS year, dt.month AS month, dt.day AS day"
|
|
3298
|
+
);
|
|
3299
|
+
await runner.run();
|
|
3300
|
+
const results = runner.results;
|
|
3301
|
+
expect(results.length).toBe(1);
|
|
3302
|
+
expect(results[0]).toEqual({ year: 2025, month: 6, day: 15 });
|
|
3303
|
+
});
|
|
3304
|
+
|
|
3305
|
+
test("Test date() returns current date object", async () => {
|
|
3306
|
+
const runner = new Runner("RETURN date() AS d");
|
|
3307
|
+
await runner.run();
|
|
3308
|
+
const results = runner.results;
|
|
3309
|
+
expect(results.length).toBe(1);
|
|
3310
|
+
const d = results[0].d;
|
|
3311
|
+
expect(d).toBeDefined();
|
|
3312
|
+
expect(typeof d.year).toBe("number");
|
|
3313
|
+
expect(typeof d.month).toBe("number");
|
|
3314
|
+
expect(typeof d.day).toBe("number");
|
|
3315
|
+
expect(typeof d.epochMillis).toBe("number");
|
|
3316
|
+
expect(typeof d.dayOfWeek).toBe("number");
|
|
3317
|
+
expect(typeof d.dayOfYear).toBe("number");
|
|
3318
|
+
expect(typeof d.quarter).toBe("number");
|
|
3319
|
+
expect(typeof d.formatted).toBe("string");
|
|
3320
|
+
// Should not have time fields
|
|
3321
|
+
expect(d.hour).toBeUndefined();
|
|
3322
|
+
expect(d.minute).toBeUndefined();
|
|
3323
|
+
});
|
|
3324
|
+
|
|
3325
|
+
test("Test date() with ISO date string", async () => {
|
|
3326
|
+
const runner = new Runner("RETURN date('2025-06-15') AS d");
|
|
3327
|
+
await runner.run();
|
|
3328
|
+
const results = runner.results;
|
|
3329
|
+
expect(results.length).toBe(1);
|
|
3330
|
+
const d = results[0].d;
|
|
3331
|
+
expect(d.year).toBe(2025);
|
|
3332
|
+
expect(d.month).toBe(6);
|
|
3333
|
+
expect(d.day).toBe(15);
|
|
3334
|
+
expect(d.formatted).toBe("2025-06-15");
|
|
3335
|
+
});
|
|
3336
|
+
|
|
3337
|
+
test("Test date() dayOfWeek and quarter", async () => {
|
|
3338
|
+
// 2025-06-15 is a Sunday
|
|
3339
|
+
const runner = new Runner("RETURN date('2025-06-15') AS d");
|
|
3340
|
+
await runner.run();
|
|
3341
|
+
const d = runner.results[0].d;
|
|
3342
|
+
expect(d.dayOfWeek).toBe(7); // Sunday = 7 in ISO
|
|
3343
|
+
expect(d.quarter).toBe(2); // June = Q2
|
|
3344
|
+
});
|
|
3345
|
+
|
|
3346
|
+
test("Test time() returns current UTC time", async () => {
|
|
3347
|
+
const runner = new Runner("RETURN time() AS t");
|
|
3348
|
+
await runner.run();
|
|
3349
|
+
const results = runner.results;
|
|
3350
|
+
expect(results.length).toBe(1);
|
|
3351
|
+
const t = results[0].t;
|
|
3352
|
+
expect(typeof t.hour).toBe("number");
|
|
3353
|
+
expect(typeof t.minute).toBe("number");
|
|
3354
|
+
expect(typeof t.second).toBe("number");
|
|
3355
|
+
expect(typeof t.millisecond).toBe("number");
|
|
3356
|
+
expect(typeof t.formatted).toBe("string");
|
|
3357
|
+
expect(t.formatted).toMatch(/Z$/); // UTC time ends in Z
|
|
3358
|
+
});
|
|
3359
|
+
|
|
3360
|
+
test("Test localtime() returns current local time", async () => {
|
|
3361
|
+
const runner = new Runner("RETURN localtime() AS t");
|
|
3362
|
+
await runner.run();
|
|
3363
|
+
const results = runner.results;
|
|
3364
|
+
expect(results.length).toBe(1);
|
|
3365
|
+
const t = results[0].t;
|
|
3366
|
+
expect(typeof t.hour).toBe("number");
|
|
3367
|
+
expect(typeof t.minute).toBe("number");
|
|
3368
|
+
expect(typeof t.second).toBe("number");
|
|
3369
|
+
expect(typeof t.millisecond).toBe("number");
|
|
3370
|
+
expect(typeof t.formatted).toBe("string");
|
|
3371
|
+
expect(t.formatted).not.toMatch(/Z$/); // Local time does not end in Z
|
|
3372
|
+
});
|
|
3373
|
+
|
|
3374
|
+
test("Test localdatetime() returns current local datetime", async () => {
|
|
3375
|
+
const runner = new Runner("RETURN localdatetime() AS dt");
|
|
3376
|
+
await runner.run();
|
|
3377
|
+
const results = runner.results;
|
|
3378
|
+
expect(results.length).toBe(1);
|
|
3379
|
+
const dt = results[0].dt;
|
|
3380
|
+
expect(typeof dt.year).toBe("number");
|
|
3381
|
+
expect(typeof dt.month).toBe("number");
|
|
3382
|
+
expect(typeof dt.day).toBe("number");
|
|
3383
|
+
expect(typeof dt.hour).toBe("number");
|
|
3384
|
+
expect(typeof dt.minute).toBe("number");
|
|
3385
|
+
expect(typeof dt.second).toBe("number");
|
|
3386
|
+
expect(typeof dt.millisecond).toBe("number");
|
|
3387
|
+
expect(typeof dt.epochMillis).toBe("number");
|
|
3388
|
+
expect(typeof dt.formatted).toBe("string");
|
|
3389
|
+
expect(dt.formatted).not.toMatch(/Z$/); // Local datetime does not end in Z
|
|
3390
|
+
});
|
|
3391
|
+
|
|
3392
|
+
test("Test localdatetime() with string argument", async () => {
|
|
3393
|
+
const runner = new Runner("RETURN localdatetime('2025-01-20T08:15:30.500Z') AS dt");
|
|
3394
|
+
await runner.run();
|
|
3395
|
+
const dt = runner.results[0].dt;
|
|
3396
|
+
expect(typeof dt.year).toBe("number");
|
|
3397
|
+
expect(typeof dt.hour).toBe("number");
|
|
3398
|
+
expect(dt.epochMillis).toBeDefined();
|
|
3399
|
+
});
|
|
3400
|
+
|
|
3401
|
+
test("Test timestamp() returns epoch millis", async () => {
|
|
3402
|
+
const before = Date.now();
|
|
3403
|
+
const runner = new Runner("RETURN timestamp() AS ts");
|
|
3404
|
+
await runner.run();
|
|
3405
|
+
const after = Date.now();
|
|
3406
|
+
const results = runner.results;
|
|
3407
|
+
expect(results.length).toBe(1);
|
|
3408
|
+
const ts = results[0].ts;
|
|
3409
|
+
expect(typeof ts).toBe("number");
|
|
3410
|
+
expect(ts).toBeGreaterThanOrEqual(before);
|
|
3411
|
+
expect(ts).toBeLessThanOrEqual(after);
|
|
3412
|
+
});
|
|
3413
|
+
|
|
3414
|
+
test("Test datetime() epochMillis matches timestamp()", async () => {
|
|
3415
|
+
const runner = new Runner(
|
|
3416
|
+
"WITH datetime() AS dt, timestamp() AS ts RETURN dt.epochMillis AS dtMillis, ts AS tsMillis"
|
|
3417
|
+
);
|
|
3418
|
+
await runner.run();
|
|
3419
|
+
const results = runner.results;
|
|
3420
|
+
expect(results.length).toBe(1);
|
|
3421
|
+
// They should be very close (within a few ms)
|
|
3422
|
+
expect(Math.abs(results[0].dtMillis - results[0].tsMillis)).toBeLessThan(100);
|
|
3423
|
+
});
|
|
3424
|
+
|
|
3425
|
+
test("Test date() with property access in WHERE", async () => {
|
|
3426
|
+
const runner = new Runner(
|
|
3427
|
+
"UNWIND [1, 2, 3] AS x WITH x, date('2025-06-15') AS d WHERE d.quarter = 2 RETURN x"
|
|
3428
|
+
);
|
|
3429
|
+
await runner.run();
|
|
3430
|
+
const results = runner.results;
|
|
3431
|
+
expect(results.length).toBe(3); // All 3 pass through since Q2 = 2
|
|
3432
|
+
});
|
|
3433
|
+
|
|
3434
|
+
test("Test datetime() with map argument", async () => {
|
|
3435
|
+
const runner = new Runner(
|
|
3436
|
+
"RETURN datetime({year: 2024, month: 12, day: 25, hour: 10, minute: 30}) AS dt"
|
|
3437
|
+
);
|
|
3438
|
+
await runner.run();
|
|
3439
|
+
const dt = runner.results[0].dt;
|
|
3440
|
+
expect(dt.year).toBe(2024);
|
|
3441
|
+
expect(dt.month).toBe(12);
|
|
3442
|
+
expect(dt.day).toBe(25);
|
|
3443
|
+
expect(dt.quarter).toBe(4); // December = Q4
|
|
3444
|
+
});
|
|
3445
|
+
|
|
3446
|
+
test("Test date() with map argument", async () => {
|
|
3447
|
+
const runner = new Runner("RETURN date({year: 2025, month: 3, day: 1}) AS d");
|
|
3448
|
+
await runner.run();
|
|
3449
|
+
const d = runner.results[0].d;
|
|
3450
|
+
expect(d.year).toBe(2025);
|
|
3451
|
+
expect(d.month).toBe(3);
|
|
3452
|
+
expect(d.day).toBe(1);
|
|
3453
|
+
expect(d.quarter).toBe(1); // March = Q1
|
|
3454
|
+
});
|
|
3455
|
+
|
|
3456
|
+
test("Test id() function with node", async () => {
|
|
3457
|
+
await new Runner(`
|
|
3458
|
+
CREATE VIRTUAL (:Person) AS {
|
|
3459
|
+
UNWIND [
|
|
3460
|
+
{id: 1, name: 'Alice'},
|
|
3461
|
+
{id: 2, name: 'Bob'}
|
|
3462
|
+
] AS record
|
|
3463
|
+
RETURN record.id AS id, record.name AS name
|
|
3464
|
+
}
|
|
3465
|
+
`).run();
|
|
3466
|
+
const match = new Runner(`
|
|
3467
|
+
MATCH (n:Person)
|
|
3468
|
+
RETURN id(n) AS nodeId
|
|
3469
|
+
`);
|
|
3470
|
+
await match.run();
|
|
3471
|
+
const results = match.results;
|
|
3472
|
+
expect(results.length).toBe(2);
|
|
3473
|
+
expect(results[0]).toEqual({ nodeId: 1 });
|
|
3474
|
+
expect(results[1]).toEqual({ nodeId: 2 });
|
|
3475
|
+
});
|
|
3476
|
+
|
|
3477
|
+
test("Test id() function with null", async () => {
|
|
3478
|
+
const runner = new Runner("RETURN id(null) AS nodeId");
|
|
3479
|
+
await runner.run();
|
|
3480
|
+
const results = runner.results;
|
|
3481
|
+
expect(results.length).toBe(1);
|
|
3482
|
+
expect(results[0]).toEqual({ nodeId: null });
|
|
3483
|
+
});
|
|
3484
|
+
|
|
3485
|
+
test("Test id() function with relationship", async () => {
|
|
3486
|
+
await new Runner(`
|
|
3487
|
+
CREATE VIRTUAL (:City) AS {
|
|
3488
|
+
UNWIND [
|
|
3489
|
+
{id: 1, name: 'New York'},
|
|
3490
|
+
{id: 2, name: 'Boston'}
|
|
3491
|
+
] AS record
|
|
3492
|
+
RETURN record.id AS id, record.name AS name
|
|
3493
|
+
}
|
|
3494
|
+
`).run();
|
|
3495
|
+
await new Runner(`
|
|
3496
|
+
CREATE VIRTUAL (:City)-[:CONNECTED_TO]-(:City) AS {
|
|
3497
|
+
UNWIND [
|
|
3498
|
+
{left_id: 1, right_id: 2}
|
|
3499
|
+
] AS record
|
|
3500
|
+
RETURN record.left_id AS left_id, record.right_id AS right_id
|
|
3501
|
+
}
|
|
3502
|
+
`).run();
|
|
3503
|
+
const match = new Runner(`
|
|
3504
|
+
MATCH (a:City)-[r:CONNECTED_TO]->(b:City)
|
|
3505
|
+
RETURN id(r) AS relId
|
|
3506
|
+
`);
|
|
3507
|
+
await match.run();
|
|
3508
|
+
const results = match.results;
|
|
3509
|
+
expect(results.length).toBe(1);
|
|
3510
|
+
expect(results[0]).toEqual({ relId: "CONNECTED_TO" });
|
|
3511
|
+
});
|
|
3512
|
+
|
|
3513
|
+
test("Test elementId() function with node", async () => {
|
|
3514
|
+
await new Runner(`
|
|
3515
|
+
CREATE VIRTUAL (:Person) AS {
|
|
3516
|
+
UNWIND [
|
|
3517
|
+
{id: 1, name: 'Alice'},
|
|
3518
|
+
{id: 2, name: 'Bob'}
|
|
3519
|
+
] AS record
|
|
3520
|
+
RETURN record.id AS id, record.name AS name
|
|
3521
|
+
}
|
|
3522
|
+
`).run();
|
|
3523
|
+
const match = new Runner(`
|
|
3524
|
+
MATCH (n:Person)
|
|
3525
|
+
RETURN elementId(n) AS eid
|
|
3526
|
+
`);
|
|
3527
|
+
await match.run();
|
|
3528
|
+
const results = match.results;
|
|
3529
|
+
expect(results.length).toBe(2);
|
|
3530
|
+
expect(results[0]).toEqual({ eid: "1" });
|
|
3531
|
+
expect(results[1]).toEqual({ eid: "2" });
|
|
3532
|
+
});
|
|
3533
|
+
|
|
3534
|
+
test("Test elementId() function with null", async () => {
|
|
3535
|
+
const runner = new Runner("RETURN elementId(null) AS eid");
|
|
3536
|
+
await runner.run();
|
|
3537
|
+
const results = runner.results;
|
|
3538
|
+
expect(results.length).toBe(1);
|
|
3539
|
+
expect(results[0]).toEqual({ eid: null });
|
|
3540
|
+
});
|
|
3541
|
+
|
|
3542
|
+
test("Test head() function", async () => {
|
|
3543
|
+
const runner = new Runner("RETURN head([1, 2, 3]) AS h");
|
|
3544
|
+
await runner.run();
|
|
3545
|
+
expect(runner.results.length).toBe(1);
|
|
3546
|
+
expect(runner.results[0]).toEqual({ h: 1 });
|
|
3547
|
+
});
|
|
3548
|
+
|
|
3549
|
+
test("Test head() function with empty list", async () => {
|
|
3550
|
+
const runner = new Runner("RETURN head([]) AS h");
|
|
3551
|
+
await runner.run();
|
|
3552
|
+
expect(runner.results[0]).toEqual({ h: null });
|
|
3553
|
+
});
|
|
3554
|
+
|
|
3555
|
+
test("Test head() function with null", async () => {
|
|
3556
|
+
const runner = new Runner("RETURN head(null) AS h");
|
|
3557
|
+
await runner.run();
|
|
3558
|
+
expect(runner.results[0]).toEqual({ h: null });
|
|
3559
|
+
});
|
|
3560
|
+
|
|
3561
|
+
test("Test tail() function", async () => {
|
|
3562
|
+
const runner = new Runner("RETURN tail([1, 2, 3]) AS t");
|
|
3563
|
+
await runner.run();
|
|
3564
|
+
expect(runner.results.length).toBe(1);
|
|
3565
|
+
expect(runner.results[0]).toEqual({ t: [2, 3] });
|
|
3566
|
+
});
|
|
3567
|
+
|
|
3568
|
+
test("Test tail() function with single element", async () => {
|
|
3569
|
+
const runner = new Runner("RETURN tail([1]) AS t");
|
|
3570
|
+
await runner.run();
|
|
3571
|
+
expect(runner.results[0]).toEqual({ t: [] });
|
|
3572
|
+
});
|
|
3573
|
+
|
|
3574
|
+
test("Test tail() function with null", async () => {
|
|
3575
|
+
const runner = new Runner("RETURN tail(null) AS t");
|
|
3576
|
+
await runner.run();
|
|
3577
|
+
expect(runner.results[0]).toEqual({ t: null });
|
|
3578
|
+
});
|
|
3579
|
+
|
|
3580
|
+
test("Test last() function", async () => {
|
|
3581
|
+
const runner = new Runner("RETURN last([1, 2, 3]) AS l");
|
|
3582
|
+
await runner.run();
|
|
3583
|
+
expect(runner.results.length).toBe(1);
|
|
3584
|
+
expect(runner.results[0]).toEqual({ l: 3 });
|
|
3585
|
+
});
|
|
3586
|
+
|
|
3587
|
+
test("Test last() function with empty list", async () => {
|
|
3588
|
+
const runner = new Runner("RETURN last([]) AS l");
|
|
3589
|
+
await runner.run();
|
|
3590
|
+
expect(runner.results[0]).toEqual({ l: null });
|
|
3591
|
+
});
|
|
3592
|
+
|
|
3593
|
+
test("Test last() function with null", async () => {
|
|
3594
|
+
const runner = new Runner("RETURN last(null) AS l");
|
|
3595
|
+
await runner.run();
|
|
3596
|
+
expect(runner.results[0]).toEqual({ l: null });
|
|
3597
|
+
});
|
|
3598
|
+
|
|
3599
|
+
test("Test toInteger() function with string", async () => {
|
|
3600
|
+
const runner = new Runner('RETURN toInteger("42") AS i');
|
|
3601
|
+
await runner.run();
|
|
3602
|
+
expect(runner.results[0]).toEqual({ i: 42 });
|
|
3603
|
+
});
|
|
3604
|
+
|
|
3605
|
+
test("Test toInteger() function with float", async () => {
|
|
3606
|
+
const runner = new Runner("RETURN toInteger(3.14) AS i");
|
|
3607
|
+
await runner.run();
|
|
3608
|
+
expect(runner.results[0]).toEqual({ i: 3 });
|
|
3609
|
+
});
|
|
3610
|
+
|
|
3611
|
+
test("Test toInteger() function with boolean", async () => {
|
|
3612
|
+
const runner = new Runner("RETURN toInteger(true) AS i");
|
|
3613
|
+
await runner.run();
|
|
3614
|
+
expect(runner.results[0]).toEqual({ i: 1 });
|
|
3615
|
+
});
|
|
3616
|
+
|
|
3617
|
+
test("Test toInteger() function with null", async () => {
|
|
3618
|
+
const runner = new Runner("RETURN toInteger(null) AS i");
|
|
3619
|
+
await runner.run();
|
|
3620
|
+
expect(runner.results[0]).toEqual({ i: null });
|
|
3621
|
+
});
|
|
3622
|
+
|
|
3623
|
+
test("Test toFloat() function with string", async () => {
|
|
3624
|
+
const runner = new Runner('RETURN toFloat("3.14") AS f');
|
|
3625
|
+
await runner.run();
|
|
3626
|
+
expect(runner.results[0]).toEqual({ f: 3.14 });
|
|
3627
|
+
});
|
|
3628
|
+
|
|
3629
|
+
test("Test toFloat() function with integer", async () => {
|
|
3630
|
+
const runner = new Runner("RETURN toFloat(42) AS f");
|
|
3631
|
+
await runner.run();
|
|
3632
|
+
expect(runner.results[0]).toEqual({ f: 42 });
|
|
3633
|
+
});
|
|
3634
|
+
|
|
3635
|
+
test("Test toFloat() function with boolean", async () => {
|
|
3636
|
+
const runner = new Runner("RETURN toFloat(true) AS f");
|
|
3637
|
+
await runner.run();
|
|
3638
|
+
expect(runner.results[0]).toEqual({ f: 1.0 });
|
|
3639
|
+
});
|
|
3640
|
+
|
|
3641
|
+
test("Test toFloat() function with null", async () => {
|
|
3642
|
+
const runner = new Runner("RETURN toFloat(null) AS f");
|
|
3643
|
+
await runner.run();
|
|
3644
|
+
expect(runner.results[0]).toEqual({ f: null });
|
|
3645
|
+
});
|
|
3646
|
+
|
|
3647
|
+
test("Test duration() with ISO 8601 string", async () => {
|
|
3648
|
+
const runner = new Runner("RETURN duration('P1Y2M3DT4H5M6S') AS d");
|
|
3649
|
+
await runner.run();
|
|
3650
|
+
const d = runner.results[0].d;
|
|
3651
|
+
expect(d.years).toBe(1);
|
|
3652
|
+
expect(d.months).toBe(2);
|
|
3653
|
+
expect(d.days).toBe(3);
|
|
3654
|
+
expect(d.hours).toBe(4);
|
|
3655
|
+
expect(d.minutes).toBe(5);
|
|
3656
|
+
expect(d.seconds).toBe(6);
|
|
3657
|
+
expect(d.totalMonths).toBe(14);
|
|
3658
|
+
expect(d.formatted).toBe("P1Y2M3DT4H5M6S");
|
|
3659
|
+
});
|
|
3660
|
+
|
|
3661
|
+
test("Test duration() with map argument", async () => {
|
|
3662
|
+
const runner = new Runner("RETURN duration({days: 14, hours: 16}) AS d");
|
|
3663
|
+
await runner.run();
|
|
3664
|
+
const d = runner.results[0].d;
|
|
3665
|
+
expect(d.days).toBe(14);
|
|
3666
|
+
expect(d.hours).toBe(16);
|
|
3667
|
+
expect(d.totalDays).toBe(14);
|
|
3668
|
+
expect(d.totalSeconds).toBe(57600);
|
|
3669
|
+
});
|
|
3670
|
+
|
|
3671
|
+
test("Test duration() with weeks", async () => {
|
|
3672
|
+
const runner = new Runner("RETURN duration('P2W') AS d");
|
|
3673
|
+
await runner.run();
|
|
3674
|
+
const d = runner.results[0].d;
|
|
3675
|
+
expect(d.weeks).toBe(2);
|
|
3676
|
+
expect(d.days).toBe(14);
|
|
3677
|
+
expect(d.totalDays).toBe(14);
|
|
3678
|
+
});
|
|
3679
|
+
|
|
3680
|
+
test("Test duration() with null", async () => {
|
|
3681
|
+
const runner = new Runner("RETURN duration(null) AS d");
|
|
3682
|
+
await runner.run();
|
|
3683
|
+
expect(runner.results[0]).toEqual({ d: null });
|
|
3684
|
+
});
|
|
3685
|
+
|
|
3686
|
+
test("Test duration() with time only", async () => {
|
|
3687
|
+
const runner = new Runner("RETURN duration('PT2H30M') AS d");
|
|
3688
|
+
await runner.run();
|
|
3689
|
+
const d = runner.results[0].d;
|
|
3690
|
+
expect(d.hours).toBe(2);
|
|
3691
|
+
expect(d.minutes).toBe(30);
|
|
3692
|
+
expect(d.totalSeconds).toBe(9000);
|
|
3693
|
+
expect(d.formatted).toBe("PT2H30M");
|
|
3694
|
+
});
|