From a9a8f1644d46556abf0da39bc8e2eb9a497732c9 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Mon, 30 Nov 2015 17:35:48 +0100 Subject: [PATCH] Use jsunity for graph traverser tests, jasmine report is simply useless in our framework --- js/server/tests/aql-graph-traverser-spec.js | 1294 ++++++++++--------- 1 file changed, 649 insertions(+), 645 deletions(-) diff --git a/js/server/tests/aql-graph-traverser-spec.js b/js/server/tests/aql-graph-traverser-spec.js index 0ada604654..821d0b8e64 100644 --- a/js/server/tests/aql-graph-traverser-spec.js +++ b/js/server/tests/aql-graph-traverser-spec.js @@ -1,5 +1,5 @@ /*jshint esnext: true */ -/*global describe, beforeEach, it, expect, afterEach, fail*/ +/*global assertEqual, fail*/ //////////////////////////////////////////////////////////////////////////////// /// @brief Spec for the AQL FOR x IN GRAPH name statement @@ -31,21 +31,43 @@ (function() { "use strict"; + const jsunity = require("jsunity"); + const internal = require("internal"); const db = internal.db; const errors = require("org/arangodb").errors; const gm = require("org/arangodb/general-graph"); const vn = "UnitTestVertexCollection"; const en = "UnitTestEdgeCollection"; - const vertex = {}; - const edge = {}; + let vertex = {}; + let edge = {}; let cleanup = function () { db._drop(vn); db._drop(en); + vertex = {}; + edge = {}; }; - describe("The FOR x IN GRAPH statement", function () { + let createBaseGraph = function () { + let vc = db._create(vn, {numberOfShards: 4}); + let ec = db._createEdgeCollection(en, {numberOfShards: 4}); + vertex.A = vc.save({_key: "A"})._id; + vertex.B = vc.save({_key: "B"})._id; + vertex.C = vc.save({_key: "C"})._id; + vertex.D = vc.save({_key: "D"})._id; + vertex.E = vc.save({_key: "E"})._id; + vertex.F = vc.save({_key: "F"})._id; + + edge.AB = ec.save(vertex.A, vertex.B, {})._id; + edge.BC = ec.save(vertex.B, vertex.C, {})._id; + edge.CD = ec.save(vertex.C, vertex.D, {})._id; + edge.CF = ec.save(vertex.C, vertex.F, {})._id; + edge.EB = ec.save(vertex.E, vertex.B, {})._id; + edge.FE = ec.save(vertex.F, vertex.E, {})._id; + }; + + let namedGraphSuite = function() { /*********************************************************************** * Graph under test: @@ -68,179 +90,145 @@ * ***********************************************************************/ - beforeEach(function() { - cleanup(); - let vc = db._create(vn); - let ec = db._createEdgeCollection(en); - vertex.A = vc.save({_key: "A"})._id; - vertex.B = vc.save({_key: "B"})._id; - vertex.C = vc.save({_key: "C"})._id; - vertex.D = vc.save({_key: "D"})._id; - vertex.E = vc.save({_key: "E"})._id; - vertex.F = vc.save({_key: "F"})._id; + let g; + const gn = "UnitTestGraph"; - edge.AB = ec.save(vertex.A, vertex.B, {})._id; - edge.BC = ec.save(vertex.B, vertex.C, {})._id; - edge.CD = ec.save(vertex.C, vertex.D, {})._id; - edge.CF = ec.save(vertex.C, vertex.F, {})._id; - edge.EB = ec.save(vertex.E, vertex.B, {})._id; - edge.FE = ec.save(vertex.F, vertex.E, {})._id; - }); + return { - afterEach(function() { - cleanup(); - }); - - describe("with a named graph", function () { - let g; - const gn = "UnitTestGraph"; - - beforeEach(function() { + setup: function() { + cleanup(); + createBaseGraph(); try { gm._drop(gn); } catch (e) { // It is expected that this graph does not exist. } g = gm._create(gn, [gm._relation(en, vn, vn)]); - }); + }, - afterEach(function() { + tearDown: function () { gm._drop(gn); - }); + cleanup(); + }, - describe("return format", function() { + firstEntryIsVertexTest: function () { + let query = "FOR x IN OUTBOUND @startId GRAPH @graph RETURN x"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + assertEqual(result[0]._id, vertex.C); + }, - it("should return the vertex as first entry", function () { - let query = "FOR x IN OUTBOUND @startId GRAPH @graph RETURN x"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(vertex.C); - }); + secondEntryIsEdgeTest: function () { + let query = "FOR x, e IN OUTBOUND @startId GRAPH @graph RETURN e"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + assertEqual(result[0]._id, edge.BC); + }, - it("should return the edge as second entry", function () { - let query = "FOR x, e IN OUTBOUND @startId GRAPH @graph RETURN e"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(edge.BC); - }); + thirdEntryIsPathTest: function () { + let query = "FOR x, e, p IN OUTBOUND @startId GRAPH @graph RETURN p"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + let entry = result[0]; + assertEqual(entry.vertices.length, 2); + assertEqual(entry.vertices[0]._id, vertex.B); + assertEqual(entry.vertices[1]._id, vertex.C); + assertEqual(entry.edges.length, 1); + assertEqual(entry.edges[0]._id, edge.BC); + }, - it("should return the path as third entry", function () { - let query = "FOR x, e, p IN OUTBOUND @startId GRAPH @graph RETURN p"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - let entry = result[0]; - expect(entry.vertices.length).toEqual(2); - expect(entry.vertices[0]._id).toEqual(vertex.B); - expect(entry.vertices[1]._id).toEqual(vertex.C); - expect(entry.edges.length).toEqual(1); - expect(entry.edges[0]._id).toEqual(edge.BC); - }); + outboundDirectionTest: function () { + let query = "FOR x IN OUTBOUND @startId GRAPH @graph RETURN x._id"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + let entry = result[0]; + assertEqual(entry, vertex.C); + }, - }); + inboundDirectionTest: function () { + let query = "FOR x IN INBOUND @startId GRAPH @graph RETURN x._id"; + let bindVars = { + graph: gn, + startId: vertex.C + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + let entry = result[0]; + assertEqual(entry, vertex.B); + }, - describe("direction", function() { + anyDirectionTest: function () { + let query = "FOR x IN ANY @startId GRAPH @graph SORT x._id ASC RETURN x._id"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 3); + let entry = result[0]; + assertEqual(entry, vertex.A); + entry = result[1]; + assertEqual(entry, vertex.C); + entry = result[2]; + assertEqual(entry, vertex.E); + }, - it("can use outbound direction", function () { - let query = "FOR x IN OUTBOUND @startId GRAPH @graph RETURN x._id"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - let entry = result[0]; - expect(entry).toEqual(vertex.C); - }); + exactNumberStepsTest: function () { + let query = "FOR x IN 2 OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 2); - it("can use inbound direction", function () { - let query = "FOR x IN INBOUND @startId GRAPH @graph RETURN x._id"; - let bindVars = { - graph: gn, - startId: vertex.C - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - let entry = result[0]; - expect(entry).toEqual(vertex.B); - }); + assertEqual(result[0], vertex.D); + assertEqual(result[1], vertex.F); + }, - it("can use any direction", function () { - let query = "FOR x IN ANY @startId GRAPH @graph SORT x._id ASC RETURN x._id"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(3); + rangeNumberStepsTest: function () { + let query = "FOR x IN 2..3 OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 3); - let entry = result[0]; - expect(entry).toEqual(vertex.A); - entry = result[1]; - expect(entry).toEqual(vertex.C); - entry = result[2]; - expect(entry).toEqual(vertex.E); - }); + assertEqual(result[0], vertex.D); + assertEqual(result[1], vertex.E); + assertEqual(result[2], vertex.F); + }, - }); + computedNumberStepsTest: function () { + let query = "FOR x IN LENGTH([1,2]) OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; + let bindVars = { + graph: gn, + startId: vertex.B + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 2); - describe("steps", function () { + assertEqual(result[0], vertex.D); + }, - it("can use an exact number of steps", function () { - let query = "FOR x IN 2 OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); - - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.F); - }); - - it("can use a range of steps", function () { - let query = "FOR x IN 2..3 OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(3); - - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.E); - expect(result[2]).toEqual(vertex.F); - }); - - it("can use a computed function of steps", function () { - let query = "FOR x IN LENGTH([1,2]) OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; - let bindVars = { - graph: gn, - startId: vertex.B - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); - - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.F); - }); - - }); - - describe("sorting", function () { - it("should be able to sort the result", function () { + sortTest: function () { let query = "FOR x IN OUTBOUND @startId GRAPH @graph SORT x._id ASC RETURN x._id"; let bindVars = { graph: gn, @@ -248,110 +236,106 @@ }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.F); + assertEqual(result.length, 2); + assertEqual(result[0], vertex.D); + assertEqual(result[1], vertex.F); // Reverse ordering query = "FOR x IN OUTBOUND @startId GRAPH @graph SORT x._id DESC RETURN x._id"; result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); - expect(result[0]).toEqual(vertex.F); - expect(result[1]).toEqual(vertex.D); - }); + assertEqual(result.length, 2); + assertEqual(result[0], vertex.F); + assertEqual(result[1], vertex.D); + } - }); - }); + }; + }; - describe("with a multi-collection graph", function () { + let multiCollectionGraphSuite = function () { - /*********************************************************************** - * Graph under test: - * - * A -> B -> C -> D <-E2- V2:G - * /|\ \|/ - * E <- F - * - * - * - ***********************************************************************/ + /*********************************************************************** + * Graph under test: + * + * A -> B -> C -> D <-E2- V2:G + * /|\ \|/ + * E <- F + * + * + * + ***********************************************************************/ let g; const gn = "UnitTestGraph"; const vn2 = "UnitTestVertexCollection2"; const en2 = "UnitTestEdgeCollection2"; - beforeEach(function() { - try { - gm._drop(gn); - } catch (e) { - // It is expected that this graph does not exist. - } - db._drop(vn2); - db._drop(en2); - g = gm._create(gn, [gm._relation(en, vn, vn), gm._relation(en2, vn2, vn)]); - db[vn2].save({_key: "G"}); - db[en2].save(vn2 + "/G", vn + "/D", {}); - }); + // We always use the same query, the result should be identical. + let validateResult = function (result) { + assertEqual(result.length, 1); + let entry = result[0]; + assertEqual(entry.vertex._id, vertex.C); + assertEqual(entry.path.vertices.length, 2); + assertEqual(entry.path.vertices[0]._id, vertex.B); + assertEqual(entry.path.vertices[1]._id, vertex.C); + assertEqual(entry.path.edges.length, 1); + assertEqual(entry.path.edges[0]._id, edge.BC); + }; - afterEach(function() { - gm._drop(gn); - db._drop(vn2); - db._drop(en2); - }); + return { - - }); - - describe("with a collection", function () { - - describe("bind parameter positions", function () { - - // We always use the same query, the result should be identical. - let validateResult = function (result) { - expect(result.length).toEqual(1); + setup: function() { + cleanup(); try { - let entry = result[0]; - expect(entry.vertex._id).toEqual(vertex.C); - expect(entry.path.vertices.length).toEqual(2); - expect(entry.path.vertices[0]._id).toEqual(vertex.B); - expect(entry.path.vertices[1]._id).toEqual(vertex.C); - expect(entry.path.edges.length).toEqual(1); - expect(entry.path.edges[0]._id).toEqual(edge.BC); - } catch (e) {} - }; + gm._drop(gn); + } catch (e) { + // It is expected that this graph does not exist. + } + db._drop(vn2); + db._drop(en2); + createBaseGraph(); + g = gm._create(gn, [gm._relation(en, vn, vn), gm._relation(en2, vn2, vn)]); + db[vn2].save({_key: "G"}); + db[en2].save(vn2 + "/G", vn + "/D", {}); + }, - it("should be able to use no bind parameters", function () { + tearDown: function() { + gm._drop(gn); + db._drop(vn2); + db._drop(en2); + cleanup(); + }, + + noBindParameterTest: function () { let query = "FOR x, p IN OUTBOUND '" + vertex.B + "' " + en + " RETURN {vertex: x, path: p}"; validateResult(db._query(query).toArray()); - }); + }, - it("should be able to bind the start point", function () { + startBindParameterTest: function () { let query = "FOR x, p IN OUTBOUND @startId " + en + " RETURN {vertex: x, path: p}"; let bindVars = { startId: vertex.B }; validateResult(db._query(query, bindVars).toArray()); - }); + }, - it("should be able to bind the edge collection", function () { + edgeCollectionBindParameterTest: function () { let query = "FOR x, p IN OUTBOUND '" + vertex.B + "' @@eCol RETURN {vertex: x, path: p}"; let bindVars = { "@eCol": en }; validateResult(db._query(query, bindVars).toArray()); - }); + }, - it("should be able to bind the steps", function () { + stepsBindParameterTest: function () { let query = "FOR x, p IN @steps OUTBOUND '" + vertex.B + "' " + en + " RETURN {vertex: x, path: p}"; let bindVars = { steps: 1 }; validateResult(db._query(query, bindVars).toArray()); - }); + }, - it("should be able to bind the steps as range with two values", function () { + stepsRangeBindParameterTest: function () { let query = "FOR x, p IN @lsteps..@rsteps OUTBOUND '" + vertex.B + "' " + en + " RETURN {vertex: x, path: p}"; let bindVars = { @@ -359,154 +343,126 @@ rsteps: 1 }; validateResult(db._query(query, bindVars).toArray()); - }); + }, - /* TODO: Should we support this? - it("should be able to bind the steps as range in one value", function () { - let query = "FOR x IN TRAVERSE FROM '" + vertex.B + "' GRAPH " + en + ", " - + vn + " @steps STEPS RETURN x"; - let bindVars = { - "steps": "1..1", - }; - validateResult(db._query(query, bindVars).toArray()); - }); - */ - - }); - - describe("return format", function() { - - it("should return the vertex as first entry", function () { + firstEntryIsVertexTest: function () { let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(vertex.C); - }); + assertEqual(result.length, 1); + assertEqual(result[0]._id, vertex.C); + }, - it("should return the edge as second entry", function () { + secondEntryIsEdgeTest: function () { let query = "FOR x, e IN OUTBOUND @startId @@eCol RETURN e"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(edge.BC); - }); + assertEqual(result.length, 1); + assertEqual(result[0]._id, edge.BC); + }, - it("should return the path as third entry", function () { + thirdEntryIsPathTest: function () { let query = "FOR x, e, p IN OUTBOUND @startId @@eCol RETURN p"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); + assertEqual(result.length, 1); let entry = result[0]; - expect(entry.vertices.length).toEqual(2); - expect(entry.vertices[0]._id).toEqual(vertex.B); - expect(entry.vertices[1]._id).toEqual(vertex.C); - expect(entry.edges.length).toEqual(1); - expect(entry.edges[0]._id).toEqual(edge.BC); - }); + assertEqual(entry.vertices.length, 2); + assertEqual(entry.vertices[0]._id, vertex.B); + assertEqual(entry.vertices[1]._id, vertex.C); + assertEqual(entry.edges.length, 1); + assertEqual(entry.edges[0]._id, edge.BC); + }, - }); - - describe("direction", function() { - - it("can use outbound direction", function () { + outboundDirectionTest: function () { let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x._id"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); + assertEqual(result.length, 1); let entry = result[0]; - expect(entry).toEqual(vertex.C); - }); + assertEqual(entry, vertex.C); + }, - it("can use inbound direction", function () { + inboundDirectionTest: function () { let query = "FOR x IN INBOUND @startId @@eCol RETURN x._id"; let bindVars = { "@eCol": en, startId: vertex.C }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); + assertEqual(result.length, 1); let entry = result[0]; - expect(entry).toEqual(vertex.B); - }); + assertEqual(entry, vertex.B); + }, - it("can use any direction", function () { + anyDirectionTest: function () { let query = "FOR x IN ANY @startId @@eCol SORT x._id ASC RETURN x._id"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(3); - + assertEqual(result.length, 3); let entry = result[0]; - expect(entry).toEqual(vertex.A); + assertEqual(entry, vertex.A); entry = result[1]; - expect(entry).toEqual(vertex.C); + assertEqual(entry, vertex.C); entry = result[2]; - expect(entry).toEqual(vertex.E); - }); + assertEqual(entry, vertex.E); + }, - }); - - describe("steps", function () { - - it("can use an exact number of steps", function () { - let query = "FOR x IN 2 OUTBOUND @startId @@eCol SORT x._id ASC RETURN x._id"; + exactNumberStepsTest: function () { + let query = "FOR x IN 2 OUTBOUND @startId @@eCol SORT x._id ASC RETURN x._id"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); + assertEqual(result.length, 2); - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.F); - }); + assertEqual(result[0], vertex.D); + assertEqual(result[1], vertex.F); + }, - it("can use a range of steps", function () { + rangeNumberStepsTest: function () { let query = "FOR x IN 2..3 OUTBOUND @startId @@eCol SORT x._id ASC RETURN x._id"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(3); + assertEqual(result.length, 3); - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.E); - expect(result[2]).toEqual(vertex.F); - }); + assertEqual(result[0], vertex.D); + assertEqual(result[1], vertex.E); + assertEqual(result[2], vertex.F); + }, - it("can use a computed function of steps", function () { + computedNumberStepsTest: function () { let query = "FOR x IN LENGTH([1,2]) OUTBOUND @startId @@eCol SORT x._id ASC RETURN x._id"; let bindVars = { "@eCol": en, startId: vertex.B }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); + assertEqual(result.length, 2); - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.F); - }); + assertEqual(result[0], vertex.D); + }, - }); - - describe("sorting", function () { - it("should be able to sort the result", function () { + sortTest: function () { let query = "FOR x IN OUTBOUND @startId @@eCol SORT x._id ASC RETURN x._id"; let bindVars = { "@eCol": en, @@ -514,24 +470,20 @@ }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); - expect(result[0]).toEqual(vertex.D); - expect(result[1]).toEqual(vertex.F); + assertEqual(result.length, 2); + assertEqual(result[0], vertex.D); + assertEqual(result[1], vertex.F); // Reverse ordering - query = "FOR x IN OUTBOUND @startId @@eCol SORT x._id DESC RETURN x._id"; + query = "FOR x IN OUTBOUND @startId GRAPH @graph SORT x._id DESC RETURN x._id"; result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(2); - expect(result[0]).toEqual(vertex.F); - expect(result[1]).toEqual(vertex.D); - }); + assertEqual(result.length, 2); + assertEqual(result[0], vertex.F); + assertEqual(result[1], vertex.D); + }, - }); - - describe("document input" , function () { - - it("should be able to use a document from a further iteration as input", function () { + singleDocumentInputTest: function () { let query = "FOR y IN @@vCol FILTER y._id == @startId " + "FOR x IN OUTBOUND y @@eCol RETURN x"; let bindVars = { @@ -540,11 +492,11 @@ "@vCol": vn }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(vertex.C); - }); + assertEqual(result.length, 1); + assertEqual(result[0]._id, vertex.C); + }, - it("should be able to use a list of documents as input", function () { + listDocumentInputTest: function () { let query = "FOR y IN @@vCol " + "FOR x IN OUTBOUND y @@eCol SORT x._id ASC RETURN x._id"; let bindVars = { @@ -552,354 +504,406 @@ "@vCol": vn }; let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(6); - expect(result[0]).toEqual(vertex.B); - expect(result[1]).toEqual(vertex.B); - expect(result[2]).toEqual(vertex.C); - expect(result[3]).toEqual(vertex.D); - expect(result[4]).toEqual(vertex.E); - expect(result[5]).toEqual(vertex.F); - }); + assertEqual(result.length).toEqual(6); + assertEqual(result[0], vertex.B); + assertEqual(result[1], vertex.B); + assertEqual(result[2], vertex.C); + assertEqual(result[3], vertex.D); + assertEqual(result[4], vertex.E); + assertEqual(result[5], vertex.F); + }, - - }); + }; + }; - describe("filter optimization", function () { - - it("should be able to prune early on vertex conditions", function () { - let query = "FOR v, e, p IN 100 OUTBOUND @start @@eCol FILTER p.vertices[1]._key == 'wrong' RETURN v"; - let bindVars = { - "@eCol": en, - "start": vertex.A - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(0); - }); - - it("should be able to prune early on vertex conditions for start vertex", function () { - let query = "FOR v, e, p IN 100 OUTBOUND @start @@eCol FILTER p.vertices[0]._key == 'wrong' RETURN v"; - let bindVars = { - "@eCol": en, - "start": vertex.A - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(0); - }); - - it("should be able to prune early on edge conditions", function () { - let query = "FOR v, e, p IN 100 OUTBOUND @start @@eCol FILTER p.edges[0]._key == 'wrong' RETURN v"; - let bindVars = { - "@eCol": en, - "start": vertex.A - }; - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(0); - }); - - }); - - }); - - - }); - - describe("Potential errors", function () { - - let vc, ec; - - beforeEach(function () { - cleanup(); - vc = db._create(vn); - ec = db._createEdgeCollection(en); - }); - - afterEach(function () { - cleanup(); - }); - - describe("Malformed AQL", function () { - - it("should not allow non-integer numbers of steps", function () { - let query = "FOR x IN 2.5 OUTBOUND @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - }); - - it("should not allow non numbers of steps", function () { - let query = "FOR x IN 'invalid' OUTBOUND @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - }); - - it("should not allow more than one direction", function () { - let query = "FOR x IN OUTBOUND ANY @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - }); - - it("should not allow an empty collection list", function () { - let query = "FOR x IN OUTBOUND @startId RETURN x"; - let bindVars = { - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - }); - - it("should not allow a query without a start vertex", function () { - let query = "FOR x IN OUTBOUND @@eCol RETURN x"; - let bindVars = { - "@eCol": en - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - }); - - it("should not allow more than three output parameters", function () { - let query = "FOR x, y, z, f IN OUTBOUND @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - }); - - it("should not allow to use a vertex collection for traversing", function () { - let query = "FOR x IN OUTBOUND @startId @@eCol, @@vCol RETURN x"; - let bindVars = { - "@eCol": en, - "@vCol": vn, - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - fail(query + " should not be allowed"); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_ARANGO_COLLECTION_TYPE_INVALID.code); - } - }); - - it("should not allow starting with a subquery", function () { - let query = "FOR x IN OUTBOUND (FOR y IN @@vCol SORT y._id LIMIT 3 RETURN y) @@eCol SORT x._id RETURN x"; - let bindVars = { - "@eCol": en, - "@vCol": vn - }; - try { - db._query(query, bindVars).toArray(); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - /* - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(4); - expect(result[0]._id).toEqual(vertex.B); - expect(result[1]._id).toEqual(vertex.C); - expect(result[2]._id).toEqual(vertex.D); - expect(result[3]._id).toEqual(vertex.F); - */ - }); - - it("should not allow to determine the steps with a subquery", function () { - let query = "FOR x IN (FOR y IN 1..1 RETURN y) OUTBOUND @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vertex.A - }; - try { - db._query(query, bindVars).toArray(); - } catch (e) { - expect(e.errorNum).toEqual(errors.ERROR_QUERY_PARSE.code); - } - /* - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(vertex.B); - */ - }); - - - }); - - describe("queries with complex interna", function () { + let potentialErrorsSuite = function () { let vc, ec; - beforeEach(function() { - cleanup(); - vc = db._create(vn); - ec = db._createEdgeCollection(en); - vertex.A = vc.save({_key: "A"})._id; - vertex.B = vc.save({_key: "B"})._id; - vertex.C = vc.save({_key: "C"})._id; - vertex.D = vc.save({_key: "D"})._id; - vertex.E = vc.save({_key: "E"})._id; - vertex.F = vc.save({_key: "F"})._id; + return { - edge.AB = ec.save(vertex.A, vertex.B, {})._id; - edge.BC = ec.save(vertex.B, vertex.C, {})._id; - edge.CD = ec.save(vertex.C, vertex.D, {})._id; - edge.CF = ec.save(vertex.C, vertex.F, {})._id; - edge.EB = ec.save(vertex.E, vertex.B, {})._id; - edge.FE = ec.save(vertex.F, vertex.E, {})._id; - }); + setup: function () { + cleanup(); + vc = db._create(vn); + ec = db._createEdgeCollection(en); + vertex.A = vn + "/unknown"; + }, - afterEach(function() { - cleanup(); - }); + tearDown: cleanup, - it("should return documents from unknown vertex collections", function () { - const vn2 = "UnitTestVertexCollectionOther"; - db._drop(vn2); - const vc2 = db._create(vn2); - vc.save({_key: "1"}); - vc2.save({_key: "1"}); - ec.save(vn + "/1", vn2 + "/1", {}); - let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vn + "/1" - }; - // NOTE: vn2 is not explicitly named in AQL - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(vn2 + "/1"); - db._drop(vn2); - }); - - it("should use the steps from let", function () { - let query = "LET s = 1 FOR x IN s OUTBOUND @startId @@eCol RETURN x"; - let bindVars = { - "@eCol": en, - "startId": vertex.A - }; - - let result = db._query(query, bindVars).toArray(); - expect(result.length).toEqual(1); - expect(result[0]._id).toEqual(vertex.B); - }); - - it("should be able to return a vast amount of results", function () { - let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x"; - let amount = 10000; - let startId = vn + "/test"; - let bindVars = { - "@eCol": en, - "startId": startId - }; - vc.save({_key: startId.split("/")[1]}); - - // Insert amount many edges and vertices into the collections. - for (let i = 0; i < amount; ++i) { - let tmp = vc.save({_key: "" + i})._id; - ec.save(startId, tmp, {}); - } - - // Check that we can get all of them out again. - let result = db._query(query, bindVars).toArray(); - // Internally: The Query selects elements in chunks, check that nothing is lost. - expect(result.length).toEqual(amount); - }); - - it("should be able to skip some results", function () { - let query = "FOR x, e, p IN 1..2 OUTBOUND @startId @@eCol LIMIT 4, 100 RETURN p.vertices[1]._key"; - let startId = vn + "/test"; - let bindVars = { - "@eCol": en, - "startId": startId - }; - vc.save({_key: startId.split("/")[1]}); - - // Insert amount many edges and vertices into the collections. - for (let i = 0; i < 3; ++i) { - let tmp = vc.save({_key: "" + i})._id; - ec.save(startId, tmp, {}); - for (let k = 0; k < 3; ++k) { - let tmp2 = vc.save({_key: "" + i + "_" + k})._id; - ec.save(tmp, tmp2, {}); + testNonIntegerSteps: function () { + let query = "FOR x IN 2.5 OUTBOUND @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + fail(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); } - } + }, - // Check that we can get all of them out again. - let result = db._query(query, bindVars).toArray(); - // Internally: The Query selects elements in chunks, check that nothing is lost. - expect(result.length).toEqual(8); - - // Each of the 3 parts of this graph contains of 4 nodes, one connected to the start. - // And 3 connected to the first one. As we do a depth first traversal we expect to skip - // exactly one sub-tree. Therefor we expect exactly two sub-trees to be traversed. - let seen = {}; - for (let r of result) { - if (!seen.hasOwnProperty(r)) { - seen[r] = true; + testNonNumberSteps: function () { + let query = "FOR x IN 'invalid' OUTBOUND @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + fail(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); } - } - expect(Object.keys(seen).length).toEqual(2); - }); + }, - it("should be able to handle many results per step", function () { - let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x._key"; - let startId = vn + "/many"; + testMultiDirections: function () { + let query = "FOR x IN OUTBOUND ANY @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + fail(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); + } + }, + + testNoCollections: function () { + let query = "FOR x IN OUTBOUND @startId RETURN x"; + let bindVars = { + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + fail(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); + } + }, + + testNoStartVertex: function () { + let query = "FOR x IN OUTBOUND @@eCol RETURN x"; + let bindVars = { + "@eCol": en + }; + try { + db._query(query, bindVars).toArray(); + fail(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); + } + }, + + testTooManyOutputParameters: function () { + let query = "FOR x, y, z, f IN OUTBOUND @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + fail(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); + } + }, + + testTraverseVertexCollection: function () { + let query = "FOR x IN OUTBOUND @startId @@eCol, @@vCol RETURN x"; + let bindVars = { + "@eCol": en, + "@vCol": vn, + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + fail(query + " should not be allowed"); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_ARANGO_COLLECTION_TYPE_INVALID.code); + } + }, + + testStartWithSubquery: function () { + let query = "FOR x IN OUTBOUND (FOR y IN @@vCol SORT y._id LIMIT 3 RETURN y) @@eCol SORT x._id RETURN x"; + let bindVars = { + "@eCol": en, + "@vCol": vn + }; + try { + db._query(query, bindVars).toArray(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); + } + /* + let result = db._query(query, bindVars).toArray(); + expect(result.length).toEqual(4); + expect(result[0]._id).toEqual(vertex.B); + expect(result[1]._id).toEqual(vertex.C); + expect(result[2]._id).toEqual(vertex.D); + expect(result[3]._id).toEqual(vertex.F); + */ + }, + + testStepsSubquery: function() { + let query = "FOR x IN (FOR y IN 1..1 RETURN y) OUTBOUND @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vertex.A + }; + try { + db._query(query, bindVars).toArray(); + } catch (e) { + assertEqual(e.errorNum, errors.ERROR_QUERY_PARSE.code); + } + /* + let result = db._query(query, bindVars).toArray(); + expect(result.length).toEqual(1); + expect(result[0]._id).toEqual(vertex.B); + */ + } + + }; + }; + + let complexInternaSuite = function () { + let vc, ec; + + return { + + setup: function () { + cleanup(); + createBaseGraph(); + }, + + tearDown: cleanup, + + testUnknownVertexCollection: function () { + const vn2 = "UnitTestVertexCollectionOther"; + db._drop(vn2); + const vc2 = db._create(vn2); + vc.save({_key: "1"}); + vc2.save({_key: "1"}); + ec.save(vn + "/1", vn2 + "/1", {}); + let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vn + "/1" + }; + // NOTE: vn2 is not explicitly named in AQL + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + assertEqual(result[0]._id, vn2 + "/1"); + db._drop(vn2); + }, + + testStepsFromLet: function () { + let query = "LET s = 1 FOR x IN s OUTBOUND @startId @@eCol RETURN x"; + let bindVars = { + "@eCol": en, + "startId": vertex.A + }; + + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 1); + assertEqual(result[0]._id, vertex.B); + }, + + testMultipleBlocksResult: function () { + let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x"; + let amount = 10000; + let startId = vn + "/test"; + let bindVars = { + "@eCol": en, + "startId": startId + }; + vc.save({_key: startId.split("/")[1]}); + + // Insert amount many edges and vertices into the collections. + for (let i = 0; i < amount; ++i) { + let tmp = vc.save({_key: "" + i})._id; + ec.save(startId, tmp, {}); + } + + // Check that we can get all of them out again. + let result = db._query(query, bindVars).toArray(); + // Internally: The Query selects elements in chunks, check that nothing is lost. + assertEqual(result.length, amount); + }, + + testSkipSome: function () { + let query = "FOR x, e, p IN 1..2 OUTBOUND @startId @@eCol LIMIT 4, 100 RETURN p.vertices[1]._key"; + let startId = vn + "/test"; + let bindVars = { + "@eCol": en, + "startId": startId + }; + vc.save({_key: startId.split("/")[1]}); + + // Insert amount many edges and vertices into the collections. + for (let i = 0; i < 3; ++i) { + let tmp = vc.save({_key: "" + i})._id; + ec.save(startId, tmp, {}); + for (let k = 0; k < 3; ++k) { + let tmp2 = vc.save({_key: "" + i + "_" + k})._id; + ec.save(tmp, tmp2, {}); + } + } + + // Check that we can get all of them out again. + let result = db._query(query, bindVars).toArray(); + // Internally: The Query selects elements in chunks, check that nothing is lost. + assertEqual(result.length, 8); + + // Each of the 3 parts of this graph contains of 4 nodes, one connected to the start. + // And 3 connected to the first one. As we do a depth first traversal we expect to skip + // exactly one sub-tree. Therefor we expect exactly two sub-trees to be traversed. + let seen = {}; + for (let r of result) { + if (!seen.hasOwnProperty(r)) { + seen[r] = true; + } + } + assertEqual(Object.keys(seen).length, 2); + }, + + testManyResults: function () { + let query = "FOR x IN OUTBOUND @startId @@eCol RETURN x._key"; + let startId = vn + "/many"; + let bindVars = { + "@eCol": en, + "startId": startId + }; + vc.save({_key: startId.split("/")[1]}); + let amount = 10000; + for (let i = 0; i < amount; ++i) { + let _id = vc.save({}); + ec.save(startId, _id, {}); + } + let result = db._query(query, bindVars); + let found = 0; + // Count has to be correct + assertEqual(result.count(), amount); + while (result.hasNext()) { + result.next(); + ++found; + } + // All elements must be enumerated + assertEqual(found, amount); + } + + }; + + }; + + let complexFilteringSuite = function() { + + /*********************************************************************** + * Graph under test: + * + * C <- B <- A -> D -> E + * F <--| |--> G + * + * + * + * + * + * Tri1 --> Tri2 + * ^ | + * |--Tri3<-| + * + * + ***********************************************************************/ + + return { + setup: function() { + cleanup(); + let vc = db._create(vn, {numberOfShards: 4}); + let ec = db._createEdgeCollection(en, {numberOfShards: 4}); + vertex.A = vc.save({_key: "A", left: false, right: false})._id; + vertex.B = vc.save({_key: "B", left: true, right: false})._id; + vertex.C = vc.save({_key: "C", left: true, right: false})._id; + vertex.D = vc.save({_key: "D", left: false, right: true})._id; + vertex.E = vc.save({_key: "E", left: false, right: true})._id; + vertex.F = vc.save({_key: "F", left: true, right: false})._id; + vertex.G = vc.save({_key: "G", left: false, right: true})._id; + + edge.AB = ec.save(vertex.A, vertex.B, {left: true, right: false})._id; + edge.BC = ec.save(vertex.B, vertex.C, {left: true, right: false})._id; + edge.AD = ec.save(vertex.A, vertex.D, {left: false, right: true})._id; + edge.DE = ec.save(vertex.D, vertex.E, {left: false, right: true})._id; + edge.BF = ec.save(vertex.B, vertex.F, {left: true, right: false})._id; + edge.DG = ec.save(vertex.D, vertex.G, {left: false, right: true})._id; + + vertex.Tri1 = vc.save({_key: "Tri1", isLoop: true})._id; + vertex.Tri2 = vc.save({_key: "Tri2", isLoop: true})._id; + vertex.Tri3 = vc.save({_key: "Tri3", isLoop: true})._id; + + edge.Tri12 = ec.save(vertex.Tri1, vertex.Tri2, {isLoop: true})._id; + edge.Tri23 = ec.save(vertex.Tri2, vertex.Tri3, {isLoop: true})._id; + edge.Tri31 = ec.save(vertex.Tri3, vertex.Tri1, {isLoop: true, lateLoop: true})._id; + }, + + tearDown: cleanup, + + testVertexEarlyPruneHighDepth: function () { + let query = "FOR v, e, p IN 100 OUTBOUND @start @@eCol FILTER p.vertices[1]._key == 'wrong' RETURN v"; let bindVars = { "@eCol": en, - "startId": startId + "start": vertex.A }; - vc.save({_key: startId.split("/")[1]}); - let amount = 10000; - for (let i = 0; i < amount; ++i) { - let _id = vc.save({}); - ec.save(startId, _id, {}); - } - let result = db._query(query, bindVars); - let found = 0; - // Count has to be correct - expect(result.count()).toEqual(amount); - while (result.hasNext()) { - result.next(); - ++found; - } - // All elements must be enumerated - expect(found).toEqual(amount); - }); + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 0); + }, - }); + testStartVertexEarlyPruneHighDepth: function () { + let query = "FOR v, e, p IN 100 OUTBOUND @start @@eCol FILTER p.vertices[0]._key == 'wrong' RETURN v"; + let bindVars = { + "@eCol": en, + "start": vertex.A + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 0); + }, - }); + testEdgesEarlyPruneHighDepth: function () { + let query = "FOR v, e, p IN 100 OUTBOUND @start @@eCol FILTER p.edges[0]._key == 'wrong' RETURN v"; + let bindVars = { + "@eCol": en, + "start": vertex.A + }; + let result = db._query(query, bindVars).toArray(); + assertEqual(result.length, 0); + }, + + testVertexLevel0: function () { + let query = `FOR v, e, p IN 1..2 OUTBOUND @start @@ecol + FILTER p.vertices[0].left == true + RETURN v`; + let bindVars = { + "@ecol": en, + start: vertex.A + }; + let cursor = db._query(query, bindVars); + assertEqual(cursor.count(), 0); + assertEqual(cursor.getExtra().stats.scannedFull, 0); + assertEqual(cursor.getExtra().stats.scannedIndex, 2); + assertEqual(cursor.getExtra().stats.filtered, 1); + } + + }; + + }; + + jsunity.run(namedGraphSuite); + jsunity.run(multiCollectionGraphSuite); + jsunity.run(potentialErrorsSuite); + jsunity.run(complexInternaSuite); + jsunity.run(complexFilteringSuite); + + return jsunity.done(); }());