mirror of https://gitee.com/bigwinds/arangodb
GraphViewer: Added a further abstraction layer for the adapters, makes it much easier to write adapters for different sources like FOXX
This commit is contained in:
parent
fb33638b91
commit
f719a32251
|
@ -124,10 +124,10 @@ function AbstractAdapter(nodes, edges) {
|
|||
source = findNode(data._from);
|
||||
target = findNode(data._to);
|
||||
if (!source) {
|
||||
throw "Unable to insert Edge, source node not existing " + edge._from;
|
||||
throw "Unable to insert Edge, source node not existing " + data._from;
|
||||
}
|
||||
if (!target) {
|
||||
throw "Unable to insert Edge, target node not existing " + edge._to;
|
||||
throw "Unable to insert Edge, target node not existing " + data._to;
|
||||
}
|
||||
edge.source = source;
|
||||
edge.target = target;
|
||||
|
@ -323,9 +323,8 @@ function AbstractAdapter(nodes, edges) {
|
|||
},
|
||||
|
||||
checkSizeOfInserted = function (inserted) {
|
||||
var buckets;
|
||||
if (_.size(inserted) > childLimit) {
|
||||
buckets = reducer.bucketNodes(_.values(inserted), childLimit);
|
||||
var buckets = reducer.bucketNodes(_.values(inserted), childLimit);
|
||||
_.each(buckets, function(b) {
|
||||
if (b.length > 1) {
|
||||
var ids = _.map(b, function(n) {
|
||||
|
|
|
@ -367,7 +367,7 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
processData: false,
|
||||
success: function() {
|
||||
absAdapter.removeEdgesForNode(nodeToRemove);
|
||||
permanentlyRemoveEdgesOfNode(nodeToRemove._id);
|
||||
permanentlyRemoveEdgesOfNode(nodeToRemove._id);
|
||||
absAdapter.removeNode(nodeToRemove);
|
||||
if (callback !== undefined && _.isFunction(callback)) {
|
||||
callback();
|
||||
|
|
|
@ -0,0 +1,725 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
|
||||
/*global beforeEach, afterEach */
|
||||
/*global describe, it, expect, jasmine */
|
||||
/*global runs, spyOn, waitsFor, waits */
|
||||
/*global window, eb, loadFixtures, document */
|
||||
/*global $, _, d3*/
|
||||
/*global describeInterface*/
|
||||
/*global AbstractAdapter*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Graph functionality
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Michael Hackstein
|
||||
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
describe('Abstract Adapter', function () {
|
||||
|
||||
var nodes, edges,
|
||||
|
||||
getCommunityNodes = function() {
|
||||
return _.filter(nodes, function(n) {
|
||||
return String(n._id).match(/^\*community/);
|
||||
});
|
||||
},
|
||||
|
||||
getCommunityNodesIds = function() {
|
||||
return _.pluck(getCommunityNodes(), "_id");
|
||||
},
|
||||
|
||||
nodeWithID = function(id) {
|
||||
return $.grep(nodes, function(e){
|
||||
return e._id === id;
|
||||
})[0];
|
||||
},
|
||||
edgeWithSourceAndTargetId = function(sourceId, targetId) {
|
||||
return $.grep(edges, function(e){
|
||||
return e.source._id === sourceId
|
||||
&& e.target._id === targetId;
|
||||
})[0];
|
||||
},
|
||||
existNode = function(id) {
|
||||
var node = nodeWithID(id);
|
||||
expect(node).toBeDefined();
|
||||
expect(node._id).toEqual(id);
|
||||
},
|
||||
|
||||
notExistNode = function(id) {
|
||||
var node = nodeWithID(id);
|
||||
expect(node).toBeUndefined();
|
||||
},
|
||||
|
||||
existEdge = function(source, target) {
|
||||
var edge = edgeWithSourceAndTargetId(source, target);
|
||||
expect(edge).toBeDefined();
|
||||
expect(edge.source._id).toEqual(source);
|
||||
expect(edge.target._id).toEqual(target);
|
||||
},
|
||||
|
||||
notExistEdge = function(source, target) {
|
||||
var edge = edgeWithSourceAndTargetId(source, target);
|
||||
expect(edge).toBeUndefined();
|
||||
},
|
||||
|
||||
existNodes = function(ids) {
|
||||
_.each(ids, existNode);
|
||||
},
|
||||
|
||||
notExistNodes = function(ids) {
|
||||
_.each(ids, notExistNode);
|
||||
};
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
nodes = [];
|
||||
edges = [];
|
||||
});
|
||||
|
||||
describe('setup process', function() {
|
||||
|
||||
it('should throw an error if nodes are not given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new AbstractAdapter();
|
||||
}
|
||||
).toThrow("The nodes have to be given.");
|
||||
});
|
||||
|
||||
it('should throw an error if edges are not given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new AbstractAdapter([]);
|
||||
}
|
||||
).toThrow("The edges have to be given.");
|
||||
});
|
||||
|
||||
it('should not throw an error if setup correctly', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new AbstractAdapter([], []);
|
||||
}
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('checking the interface', function() {
|
||||
var testee;
|
||||
|
||||
beforeEach(function() {
|
||||
testee = new AbstractAdapter([], []);
|
||||
this.addMatchers({
|
||||
toHaveFunction: function(func, argCounter) {
|
||||
var obj = this.actual;
|
||||
this.message = function(){
|
||||
return testee.constructor.name
|
||||
+ " should react to function "
|
||||
+ func
|
||||
+ " with at least "
|
||||
+ argCounter
|
||||
+ " arguments.";
|
||||
};
|
||||
if (typeof(obj[func]) !== "function") {
|
||||
return false;
|
||||
}
|
||||
if (obj[func].length < argCounter) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should offer a function to set the width', function() {
|
||||
expect(testee).toHaveFunction("setWidth", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to set the height', function() {
|
||||
expect(testee).toHaveFunction("setHeight", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to insert a node', function() {
|
||||
expect(testee).toHaveFunction("insertNode", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to insert an edge', function() {
|
||||
expect(testee).toHaveFunction("insertEdge", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to remove a node', function() {
|
||||
expect(testee).toHaveFunction("removeNode", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to remove an edge', function() {
|
||||
expect(testee).toHaveFunction("removeEdge", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to remove edges for a given node', function() {
|
||||
expect(testee).toHaveFunction("removeEdgesForNode", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to expand a community', function() {
|
||||
expect(testee).toHaveFunction("expandCommunity", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to set the node limit', function() {
|
||||
expect(testee).toHaveFunction("setNodeLimit", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to set the child limit', function() {
|
||||
expect(testee).toHaveFunction("setChildLimit", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to check the amount of freshly inserted nodes', function() {
|
||||
expect(testee).toHaveFunction("checkSizeOfInserted", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to check the overall amount of nodes', function() {
|
||||
expect(testee).toHaveFunction("checkNodeLimit", 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checking nodes', function() {
|
||||
|
||||
var adapter;
|
||||
|
||||
beforeEach(function() {
|
||||
adapter = new AbstractAdapter(nodes, []);
|
||||
});
|
||||
|
||||
it('should be possible to insert a node', function() {
|
||||
var newNode = {_id: 1};
|
||||
adapter.insertNode(newNode);
|
||||
existNode(1);
|
||||
expect(nodes.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not add the same node twice', function() {
|
||||
var newNode = {_id: 1};
|
||||
adapter.insertNode(newNode);
|
||||
adapter.insertNode(newNode);
|
||||
existNode(1);
|
||||
expect(nodes.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should add x and y coordinates', function() {
|
||||
var newNode = {_id: 1};
|
||||
adapter.insertNode(newNode);
|
||||
this.addMatchers({
|
||||
toHaveCorrectCoordinates: function() {
|
||||
var list = this.actual,
|
||||
evil;
|
||||
_.each(list, function(n) {
|
||||
if (isNaN(n.x) || isNaN(n.y)) {
|
||||
evil = n;
|
||||
}
|
||||
});
|
||||
this.message = function() {
|
||||
return "Expected " + JSON.stringify(evil) + " to contain Numbers as X and Y.";
|
||||
};
|
||||
return evil === undefined;
|
||||
}
|
||||
});
|
||||
expect(nodes).toHaveCorrectCoordinates();
|
||||
});
|
||||
|
||||
it('should set in- and outbound counters', function() {
|
||||
var newNode = adapter.insertNode({_id: 1});
|
||||
expect(newNode._outboundCounter).toEqual(0);
|
||||
expect(newNode._inboundCounter).toEqual(0);
|
||||
});
|
||||
|
||||
it('should set the x coordinate close to the center', function() {
|
||||
var width = 200,
|
||||
newNode;
|
||||
adapter.setWidth(width);
|
||||
newNode = adapter.insertNode({_id: 1});
|
||||
expect(newNode.x).toBeGreaterThan(width / 4);
|
||||
expect(newNode.x).toBeLessThan(3 * width / 4);
|
||||
});
|
||||
|
||||
it('should set the y coordinate close to the center', function() {
|
||||
var height = 200,
|
||||
newNode;
|
||||
adapter.setHeight(height);
|
||||
newNode = adapter.insertNode({_id: 1});
|
||||
expect(newNode.y).toBeGreaterThan(height / 4);
|
||||
expect(newNode.y).toBeLessThan(3 * height / 4);
|
||||
});
|
||||
|
||||
it('should be able to delete a node', function() {
|
||||
var toDelete = {_id: 1},
|
||||
nodeToDelete = adapter.insertNode(toDelete);
|
||||
adapter.removeNode(nodeToDelete);
|
||||
|
||||
notExistNode(1);
|
||||
expect(nodes.length).toEqual(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checking edges', function() {
|
||||
|
||||
var adapter,
|
||||
source,
|
||||
target,
|
||||
sourceid,
|
||||
targetid;
|
||||
|
||||
beforeEach(function() {
|
||||
adapter = new AbstractAdapter(nodes, edges);
|
||||
source = adapter.insertNode({_id: 1});
|
||||
target = adapter.insertNode({_id: 2});
|
||||
sourceid = source._id;
|
||||
targetid = target._id;
|
||||
});
|
||||
|
||||
it('should be able to insert an edge', function() {
|
||||
adapter.insertEdge({
|
||||
_id: "1-2",
|
||||
_from: sourceid,
|
||||
_to: targetid
|
||||
});
|
||||
existEdge(sourceid, targetid);
|
||||
expect(edges.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not insert the same edge twice', function() {
|
||||
var toInsert = {
|
||||
_id: "1-2",
|
||||
_from: sourceid,
|
||||
_to: targetid
|
||||
};
|
||||
adapter.insertEdge(toInsert);
|
||||
adapter.insertEdge(toInsert);
|
||||
existEdge(sourceid, targetid);
|
||||
expect(edges.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should throw an error if an edge is inserted with illeagal source', function() {
|
||||
expect(
|
||||
function() {
|
||||
adapter.insertEdge({
|
||||
_id: "1-3",
|
||||
_from: 3,
|
||||
_to: targetid
|
||||
});
|
||||
}
|
||||
).toThrow("Unable to insert Edge, source node not existing 3");
|
||||
|
||||
notExistEdge(3, targetid);
|
||||
expect(edges.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should throw an error if an edge is inserted with illeagal target', function() {
|
||||
expect(
|
||||
function() {
|
||||
adapter.insertEdge({
|
||||
_id: "1-3",
|
||||
_from: sourceid,
|
||||
_to: 3
|
||||
});
|
||||
}
|
||||
).toThrow("Unable to insert Edge, target node not existing 3");
|
||||
notExistEdge(sourceid, 3);
|
||||
expect(edges.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should change the in- and outbound counters accordingly', function() {
|
||||
var toInsert = {
|
||||
_id: "1-2",
|
||||
_from: sourceid,
|
||||
_to: targetid
|
||||
};
|
||||
adapter.insertEdge(toInsert);
|
||||
expect(source._outboundCounter).toEqual(1);
|
||||
expect(source._inboundCounter).toEqual(0);
|
||||
expect(target._outboundCounter).toEqual(0);
|
||||
expect(target._inboundCounter).toEqual(1);
|
||||
});
|
||||
|
||||
it('should be able to delete an edge', function() {
|
||||
var toDelete = {
|
||||
_id: "1-2",
|
||||
_from: sourceid,
|
||||
_to: targetid
|
||||
},
|
||||
edgeToDel = adapter.insertEdge(toDelete);
|
||||
adapter.removeEdge(edgeToDel);
|
||||
|
||||
notExistEdge(sourceid, targetid);
|
||||
expect(edges.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should be able to remove all edges of a node', function() {
|
||||
adapter.insertNode({_id: 3});
|
||||
adapter.insertEdge({
|
||||
_id: "3-1",
|
||||
_from: 3,
|
||||
_to: sourceid // This is nodes[0]
|
||||
});
|
||||
var edgeToKeep = adapter.insertEdge({
|
||||
_id: "2-3",
|
||||
_from: targetid, // This is nodes[1]
|
||||
_to: 3
|
||||
});
|
||||
|
||||
adapter.removeEdgesForNode(source);
|
||||
|
||||
existEdge(2, 3);
|
||||
notExistEdge(1, 2);
|
||||
notExistEdge(3, 1);
|
||||
expect(edges.length).toEqual(1);
|
||||
expect(edges[0]).toEqual(edgeToKeep);
|
||||
});
|
||||
|
||||
it('should maintain in- and outboundcounter '
|
||||
+ 'when removing all edges of a node', function() {
|
||||
var thirdNode = adapter.insertNode({_id: 3});
|
||||
adapter.insertEdge({
|
||||
_id: "3-1",
|
||||
_from: 3,
|
||||
_to: sourceid // This is nodes[0]
|
||||
});
|
||||
adapter.insertEdge({
|
||||
_id: "2-3",
|
||||
_from: targetid, // This is nodes[1]
|
||||
_to: 3
|
||||
});
|
||||
|
||||
adapter.removeEdgesForNode(source);
|
||||
expect(nodes.length).toEqual(3);
|
||||
existNode(1);
|
||||
existNode(2);
|
||||
existNode(3);
|
||||
expect(source._inboundCounter).toEqual(0);
|
||||
expect(source._outboundCounter).toEqual(0);
|
||||
expect(target._inboundCounter).toEqual(0);
|
||||
expect(target._outboundCounter).toEqual(1);
|
||||
expect(thirdNode._inboundCounter).toEqual(1);
|
||||
expect(thirdNode._outboundCounter).toEqual(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checking communities', function() {
|
||||
|
||||
var adapter, mockReducer;
|
||||
|
||||
beforeEach(function() {
|
||||
mockReducer = {};
|
||||
mockReducer.getCommunity = function() {};
|
||||
mockReducer.bucketNodes = function() {};
|
||||
spyOn(window, "NodeReducer").andCallFake(function(v, e) {
|
||||
return {
|
||||
getCommunity: function(limit, focus) {
|
||||
if (focus !== undefined) {
|
||||
return mockReducer.getCommunity(limit, focus);
|
||||
}
|
||||
return mockReducer.getCommunity(limit);
|
||||
},
|
||||
bucketNodes: function(toSort, numBuckets) {
|
||||
return mockReducer.bucketNodes(toSort, numBuckets);
|
||||
}
|
||||
};
|
||||
});
|
||||
adapter = new AbstractAdapter(nodes, edges);
|
||||
});
|
||||
|
||||
it('should not take any action if no limit is set', function() {
|
||||
spyOn(mockReducer, "getCommunity");
|
||||
adapter.insertNode({_id: 1});
|
||||
adapter.insertNode({_id: 2});
|
||||
adapter.insertNode({_id: 3});
|
||||
adapter.insertNode({_id: 4});
|
||||
adapter.insertNode({_id: 5});
|
||||
adapter.checkNodeLimit();
|
||||
expect(mockReducer.getCommunity).wasNotCalled();
|
||||
});
|
||||
|
||||
it('should take a given focus into account', function() {
|
||||
var n1, limit;
|
||||
spyOn(mockReducer, "getCommunity").andCallFake(function() {
|
||||
return [2, 4];
|
||||
});
|
||||
limit = 2;
|
||||
adapter.setNodeLimit(limit);
|
||||
n1 = adapter.insertNode({_id: 1});
|
||||
adapter.insertNode({_id: 2});
|
||||
adapter.insertNode({_id: 3});
|
||||
adapter.insertNode({_id: 4});
|
||||
adapter.insertNode({_id: 5});
|
||||
adapter.checkNodeLimit(n1);
|
||||
expect(mockReducer.getCommunity).toHaveBeenCalledWith(limit, n1);
|
||||
});
|
||||
|
||||
it('should create a community if too many nodes are added', function() {
|
||||
var n1, n2, commId;
|
||||
spyOn(mockReducer, "getCommunity").andCallFake(function() {
|
||||
return [1, 2];
|
||||
});
|
||||
adapter.setNodeLimit(2);
|
||||
n1 = adapter.insertNode({_id: 1});
|
||||
n2 = adapter.insertNode({_id: 2});
|
||||
adapter.insertNode({_id: 3});
|
||||
adapter.checkNodeLimit();
|
||||
expect(mockReducer.getCommunity).wasCalledWith(2);
|
||||
expect(getCommunityNodesIds().length).toEqual(1);
|
||||
notExistNode([1, 2]);
|
||||
existNode(3);
|
||||
});
|
||||
|
||||
it('should create a community if limit is set to small', function() {
|
||||
var n1, n2, commId;
|
||||
spyOn(mockReducer, "getCommunity").andCallFake(function() {
|
||||
return [1, 2];
|
||||
});
|
||||
n1 = adapter.insertNode({_id: 1});
|
||||
n2 = adapter.insertNode({_id: 2});
|
||||
adapter.insertNode({_id: 3});
|
||||
adapter.setNodeLimit(2);
|
||||
expect(mockReducer.getCommunity).wasCalledWith(2);
|
||||
expect(getCommunityNodesIds().length).toEqual(1);
|
||||
notExistNode([1, 2]);
|
||||
existNode(3);
|
||||
});
|
||||
|
||||
it('should connect edges to communities', function() {
|
||||
var n1, n2, comm, e1, e2, e3;
|
||||
spyOn(mockReducer, "getCommunity").andCallFake(function() {
|
||||
return [1, 2];
|
||||
});
|
||||
n1 = adapter.insertNode({_id: 1});
|
||||
n2 = adapter.insertNode({_id: 2});
|
||||
adapter.insertNode({_id: 3});
|
||||
e1 = adapter.insertEdge({
|
||||
_id: "1-2",
|
||||
_from: 1,
|
||||
_to: 2
|
||||
});
|
||||
e2 = adapter.insertEdge({
|
||||
_id: "2-3",
|
||||
_from: 2,
|
||||
_to: 3
|
||||
});
|
||||
e3 = adapter.insertEdge({
|
||||
_id: "3-1",
|
||||
_from: 3,
|
||||
_to: 1
|
||||
});
|
||||
adapter.setNodeLimit(2);
|
||||
|
||||
comm = getCommunityNodes()[0];
|
||||
notExistEdge(1, 2);
|
||||
expect(e2.source).toEqual(comm);
|
||||
expect(e3.target).toEqual(comm);
|
||||
});
|
||||
|
||||
describe('if a community allready exists', function() {
|
||||
|
||||
var n1, n2, n3, n4,
|
||||
e1, e2, e3, comm;
|
||||
|
||||
beforeEach(function() {
|
||||
mockReducer.getCommunity = function() {
|
||||
return [1, 2];
|
||||
};
|
||||
n1 = adapter.insertNode({_id: 1});
|
||||
n2 = adapter.insertNode({_id: 2});
|
||||
n3 = adapter.insertNode({_id: 3});
|
||||
n4 = adapter.insertNode({_id: 4});
|
||||
e1 = adapter.insertEdge({
|
||||
_id: "1-2",
|
||||
_from: 1,
|
||||
_to: 2
|
||||
});
|
||||
e2 = adapter.insertEdge({
|
||||
_id: "2-3",
|
||||
_from: 2,
|
||||
_to: 3
|
||||
});
|
||||
e3 = adapter.insertEdge({
|
||||
_id: "3-1",
|
||||
_from: 3,
|
||||
_to: 1
|
||||
});
|
||||
|
||||
adapter.setNodeLimit(3);
|
||||
comm = getCommunityNodes()[0];
|
||||
});
|
||||
|
||||
it('should be able to expand the community', function() {
|
||||
adapter.setNodeLimit(10);
|
||||
adapter.expandCommunity(comm);
|
||||
|
||||
expect(getCommunityNodes().length).toEqual(0);
|
||||
expect(nodes.length).toEqual(4);
|
||||
expect(edges.length).toEqual(3);
|
||||
existEdge(1, 2);
|
||||
existEdge(2, 3);
|
||||
existEdge(3, 1);
|
||||
existNodes([1,2,3,4]);
|
||||
});
|
||||
|
||||
it('should collapse another community if limit is to small', function() {
|
||||
spyOn(mockReducer, "getCommunity").andCallFake(function() {
|
||||
return [3, 4];
|
||||
});
|
||||
adapter.expandCommunity(comm);
|
||||
expect(getCommunityNodes().length).toEqual(1);
|
||||
var comm2 = getCommunityNodes()[0];
|
||||
expect(comm).not.toEqual(comm2);
|
||||
expect(mockReducer.getCommunity).wasCalled();
|
||||
expect(nodes.length).toEqual(3);
|
||||
existNodes([1, 2]);
|
||||
notExistNodes([3, 4]);
|
||||
});
|
||||
|
||||
it('should collapse another community if limit is further reduced', function() {
|
||||
spyOn(mockReducer, "getCommunity").andCallFake(function() {
|
||||
return [3, 4];
|
||||
});
|
||||
adapter.setNodeLimit(2);
|
||||
expect(getCommunityNodes().length).toEqual(2);
|
||||
var comm2 = getCommunityNodes()[1];
|
||||
expect(comm).not.toEqual(comm2);
|
||||
expect(mockReducer.getCommunity).wasCalled();
|
||||
expect(nodes.length).toEqual(2);
|
||||
notExistNodes([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checking many child nodes', function() {
|
||||
|
||||
var adapter, mockReducer;
|
||||
|
||||
beforeEach(function() {
|
||||
mockReducer = {};
|
||||
mockReducer.getCommunity = function() {};
|
||||
mockReducer.bucketNodes = function() {};
|
||||
spyOn(window, "NodeReducer").andCallFake(function(v, e) {
|
||||
return {
|
||||
getCommunity: function(limit, focus) {
|
||||
if (focus !== undefined) {
|
||||
return mockReducer.getCommunity(limit, focus);
|
||||
}
|
||||
return mockReducer.getCommunity(limit);
|
||||
},
|
||||
bucketNodes: function(toSort, numBuckets) {
|
||||
return mockReducer.bucketNodes(toSort, numBuckets);
|
||||
}
|
||||
};
|
||||
});
|
||||
adapter = new AbstractAdapter(nodes, edges);
|
||||
});
|
||||
|
||||
it('should not take any action if the limit is high enough', function() {
|
||||
adapter.setChildLimit(5);
|
||||
spyOn(mockReducer, "bucketNodes");
|
||||
|
||||
|
||||
var n1, n2, n3, n4, n5,
|
||||
inserted = {};
|
||||
n1 = adapter.insertNode({_id: 1 });
|
||||
n2 = adapter.insertNode({_id: 2 });
|
||||
n3 = adapter.insertNode({_id: 3 });
|
||||
n4 = adapter.insertNode({_id: 4 });
|
||||
n5 = adapter.insertNode({_id: 5 });
|
||||
_.each(nodes, function(n) {
|
||||
inserted[n._id] = n;
|
||||
});
|
||||
adapter.checkSizeOfInserted(inserted);
|
||||
expect(mockReducer.bucketNodes).wasNotCalled();
|
||||
});
|
||||
|
||||
it('should bucket nodes if limit is to small', function() {
|
||||
var n1, n2, n3, n4, n5,
|
||||
inserted = [],
|
||||
limit = 2;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
[n1, n2],
|
||||
[n3, n4, n5]
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
n1 = adapter.insertNode({_id: 1 });
|
||||
n2 = adapter.insertNode({_id: 2 });
|
||||
n3 = adapter.insertNode({_id: 3 });
|
||||
n4 = adapter.insertNode({_id: 4 });
|
||||
n5 = adapter.insertNode({_id: 5 });
|
||||
_.each(nodes, function(n) {
|
||||
inserted.push(n);
|
||||
});
|
||||
adapter.checkSizeOfInserted(inserted);
|
||||
|
||||
expect(mockReducer.bucketNodes).wasCalledWith(inserted, limit);
|
||||
|
||||
expect(nodes.length).toEqual(2);
|
||||
expect(getCommunityNodes().length).toEqual(2);
|
||||
notExistNodes([1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
it('should not display single nodes as buckets', function() {
|
||||
var n1, n2, n3, n4, n5,
|
||||
inserted = [],
|
||||
limit = 3;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
[n1],
|
||||
[n3, n4, n5],
|
||||
[n2]
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
n1 = adapter.insertNode({_id: 1 });
|
||||
n2 = adapter.insertNode({_id: 2 });
|
||||
n3 = adapter.insertNode({_id: 3 });
|
||||
n4 = adapter.insertNode({_id: 4 });
|
||||
n5 = adapter.insertNode({_id: 5 });
|
||||
_.each(nodes, function(n) {
|
||||
inserted.push(n);
|
||||
});
|
||||
adapter.checkSizeOfInserted(inserted);
|
||||
|
||||
expect(mockReducer.bucketNodes).wasCalledWith(inserted, limit);
|
||||
|
||||
expect(nodes.length).toEqual(3);
|
||||
expect(getCommunityNodes().length).toEqual(1);
|
||||
notExistNodes([3, 4, 5]);
|
||||
existNodes([1, 2]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}());
|
|
@ -5,7 +5,7 @@
|
|||
/*global window, eb, loadFixtures, document */
|
||||
/*global $, _, d3*/
|
||||
/*global describeInterface*/
|
||||
/*global ArangoAdapter*/
|
||||
/*global FoxxAdapter*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Graph functionality
|
||||
|
@ -40,19 +40,16 @@
|
|||
|
||||
describe('Foxx Adapter', function () {
|
||||
|
||||
describeInterface(new FoxxAdapter([], [], {
|
||||
nodeCollection: "",
|
||||
edgeCollection: ""
|
||||
}));
|
||||
describeInterface(new FoxxAdapter([], [], "foxx/route"));
|
||||
|
||||
describeIntegeration(new FoxxAdapter([], [], "foxx/route"));
|
||||
|
||||
var adapter,
|
||||
nodes,
|
||||
edges,
|
||||
arangodb = "http://localhost:8529",
|
||||
nodesCollection,
|
||||
altNodesCollection,
|
||||
edgesCollection,
|
||||
altEdgesCollection,
|
||||
mockCollection,
|
||||
callbackCheck,
|
||||
checkCallbackFunction = function() {
|
||||
|
@ -163,11 +160,6 @@
|
|||
beforeEach(function() {
|
||||
nodes = [];
|
||||
edges = [];
|
||||
mockCollection = {};
|
||||
nodesCollection = "TestNodes321";
|
||||
edgesCollection = "TestEdges321";
|
||||
altNodesCollection = "TestNodes654";
|
||||
altEdgesCollection = "TestEdges654";
|
||||
|
||||
this.addMatchers({
|
||||
toHaveCorrectCoordinates: function() {
|
||||
|
@ -193,7 +185,7 @@
|
|||
it('should throw an error if no nodes are given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new ArangoAdapter();
|
||||
var t = new FoxxAdapter();
|
||||
}
|
||||
).toThrow("The nodes have to be given.");
|
||||
});
|
||||
|
@ -201,90 +193,61 @@
|
|||
it('should throw an error if no edges are given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new ArangoAdapter([]);
|
||||
var t = new FoxxAdapter([]);
|
||||
}
|
||||
).toThrow("The edges have to be given.");
|
||||
});
|
||||
|
||||
it('should throw an error if no nodeCollection is given', function() {
|
||||
it('should throw an error if no route is given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new ArangoAdapter([], [], {
|
||||
var t = new FoxxAdapter([], [], {
|
||||
edgeCollection: ""
|
||||
});
|
||||
}
|
||||
).toThrow("The nodeCollection has to be given.");
|
||||
).toThrow("The route has to be given.");
|
||||
});
|
||||
|
||||
it('should throw an error if no edgeCollection is given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new ArangoAdapter([], [], {
|
||||
nodeCollection: ""
|
||||
});
|
||||
}
|
||||
).toThrow("The edgeCollection has to be given.");
|
||||
});
|
||||
|
||||
it('should not throw an error if everything is given', function() {
|
||||
expect(
|
||||
function() {
|
||||
var t = new ArangoAdapter([], [], {
|
||||
nodeCollection: "",
|
||||
edgeCollection: ""
|
||||
});
|
||||
}
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should automatically determine the host of not given', function() {
|
||||
var adapter = new ArangoAdapter(
|
||||
|
||||
it('should automatically determine the host of relative route is given', function() {
|
||||
var route = "foxx/route"
|
||||
adapter = new FoxxAdapter(
|
||||
nodes,
|
||||
edges,
|
||||
{
|
||||
nodeCollection: nodesCollection,
|
||||
edgeCollection: edgesCollection,
|
||||
width: 100,
|
||||
height: 40
|
||||
}
|
||||
route
|
||||
),
|
||||
args,
|
||||
host;
|
||||
spyOn($, "ajax");
|
||||
adapter.createNode({}, function() {});
|
||||
args = $.ajax.mostRecentCall.args[0];
|
||||
host = window.location.protocol + "//" + window.location.host;
|
||||
host = window.location.protocol + "//" + window.location.host + "/" + route;
|
||||
expect(args.url).toContain(host);
|
||||
});
|
||||
|
||||
it('should create a nodeReducer instance', function() {
|
||||
spyOn(window, "NodeReducer");
|
||||
var adapter = new ArangoAdapter(
|
||||
var adapter = new FoxxAdapter(
|
||||
nodes,
|
||||
edges,
|
||||
{
|
||||
nodeCollection: nodesCollection,
|
||||
edgeCollection: edgesCollection,
|
||||
width: 100,
|
||||
height: 40
|
||||
}
|
||||
"foxx/route"
|
||||
);
|
||||
expect(window.NodeReducer).wasCalledWith(nodes, edges);
|
||||
});
|
||||
|
||||
describe('setup correctly', function() {
|
||||
|
||||
var traversalQuery,
|
||||
filterQuery,
|
||||
childrenQuery,
|
||||
var edgeRoute,
|
||||
nodeRoute,
|
||||
queryRoute,
|
||||
loadGraph,
|
||||
requests;
|
||||
|
||||
beforeEach(function() {
|
||||
var self = this,
|
||||
host = window.location.protocol + "//" + window.location.host,
|
||||
apibase = host + "/_api/",
|
||||
apiCursor = apibase + 'cursor';
|
||||
route = "foxx/route"
|
||||
host = window.location.protocol + "//"
|
||||
+ window.location.host + "/"
|
||||
+ route;
|
||||
self.fakeReducerRequest = function() {};
|
||||
self.fakeReducerBucketRequest = function() {};
|
||||
spyOn(window, "NodeReducer").andCallFake(function(v, e) {
|
||||
|
@ -300,73 +263,23 @@
|
|||
}
|
||||
};
|
||||
});
|
||||
adapter = new ArangoAdapter(
|
||||
adapter = new FoxxAdapter(
|
||||
nodes,
|
||||
edges,
|
||||
{
|
||||
nodeCollection: nodesCollection,
|
||||
edgeCollection: edgesCollection,
|
||||
width: 100,
|
||||
height: 40
|
||||
}
|
||||
route
|
||||
);
|
||||
traversalQuery = function(id, nods, edgs, undirected) {
|
||||
var dir;
|
||||
if (undirected === true) {
|
||||
dir = "any";
|
||||
} else {
|
||||
dir = "outbound";
|
||||
}
|
||||
return JSON.stringify({
|
||||
query: "RETURN TRAVERSAL(@@nodes, @@edges, @id, @dir,"
|
||||
+ " {strategy: \"depthfirst\",maxDepth: 1,paths: true})",
|
||||
bindVars: {
|
||||
id: id,
|
||||
"@nodes": nods,
|
||||
dir: dir,
|
||||
"@edges": edgs
|
||||
}
|
||||
});
|
||||
};
|
||||
filterQuery = function(v, nods, edgs, undirected) {
|
||||
var dir;
|
||||
if (undirected === true) {
|
||||
dir = "any";
|
||||
} else {
|
||||
dir = "outbound";
|
||||
}
|
||||
return JSON.stringify({
|
||||
query: "FOR n IN @@nodes FILTER n.id == @value"
|
||||
+ " RETURN TRAVERSAL(@@nodes, @@edges, n._id, @dir,"
|
||||
+ " {strategy: \"depthfirst\",maxDepth: 1,paths: true})",
|
||||
bindVars: {
|
||||
value: v,
|
||||
"@nodes": nods,
|
||||
dir: dir,
|
||||
"@edges": edgs
|
||||
}
|
||||
});
|
||||
};
|
||||
childrenQuery = function(id, nods, edgs) {
|
||||
return JSON.stringify({
|
||||
query: "FOR u IN @@nodes FILTER u._id == @id"
|
||||
+ " LET g = ( FOR l in @@edges FILTER l._from == u._id RETURN 1 )"
|
||||
+ " RETURN length(g)",
|
||||
bindVars: {
|
||||
id: id,
|
||||
"@nodes": nods,
|
||||
"@edges": edgs
|
||||
}
|
||||
});
|
||||
};
|
||||
loadGraph = function(vars) {
|
||||
var nid = vars.id,
|
||||
ncol = vars["@nodes"],
|
||||
ecol = vars["@edges"],
|
||||
res = [],
|
||||
inner = [],
|
||||
first = {},
|
||||
node1 = readNode(ncol, nid);
|
||||
edgeRoute = host + "/edges";
|
||||
nodeRoute = host + "/nodes";
|
||||
queryRoute = host + "/query";
|
||||
|
||||
loadGraph = function(data) {
|
||||
var res = [],
|
||||
nid,
|
||||
ncol = nodesCollection,
|
||||
ecol = edgesCollection,
|
||||
inner = [],
|
||||
first = {},
|
||||
node1 = readNode(ncol, nid);
|
||||
res.push(inner);
|
||||
first.vertex = node1;
|
||||
first.path = {
|
||||
|
@ -386,10 +299,10 @@
|
|||
|
||||
|
||||
requests = {};
|
||||
requests.cursor = function(data) {
|
||||
requests.query = function(data) {
|
||||
return {
|
||||
type: 'POST',
|
||||
url: apiCursor,
|
||||
url: queryRoute,
|
||||
data: data,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
|
@ -398,98 +311,62 @@
|
|||
processData: false
|
||||
};
|
||||
};
|
||||
requests.node = function(col) {
|
||||
var read = apibase + "document?collection=" + col,
|
||||
write = apibase + "document/",
|
||||
base = {
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
success: jasmine.any(Function),
|
||||
error: jasmine.any(Function)
|
||||
};
|
||||
requests.node = function() {
|
||||
var base = {
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
success: jasmine.any(Function),
|
||||
error: jasmine.any(Function)
|
||||
};
|
||||
return {
|
||||
create: function(data) {
|
||||
return $.extend(base, {url: read, type: "POST", data: JSON.stringify(data)});
|
||||
return $.extend(base, {url: nodeRoute, type: "POST", data: JSON.stringify(data)});
|
||||
},
|
||||
patch: function(id, data) {
|
||||
return $.extend(base, {url: write + id, type: "PUT", data: JSON.stringify(data)});
|
||||
return $.extend(base, {url: nodeRoute + "/" + id, type: "PUT", data: JSON.stringify(data)});
|
||||
},
|
||||
del: function(id) {
|
||||
return $.extend(base, {url: write + id, type: "DELETE"});
|
||||
return $.extend(base, {url: nodeRoute + "/" + id, type: "DELETE"});
|
||||
}
|
||||
};
|
||||
};
|
||||
requests.edge = function(col) {
|
||||
var create = apibase + "edge?collection=" + col,
|
||||
base = {
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
success: jasmine.any(Function),
|
||||
error: jasmine.any(Function)
|
||||
};
|
||||
var base = {
|
||||
cache: false,
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
success: jasmine.any(Function),
|
||||
error: jasmine.any(Function)
|
||||
};
|
||||
return {
|
||||
create: function(from, to, data) {
|
||||
data = $.extend(data, {_from: from, _to: to});
|
||||
return $.extend(base, {
|
||||
url: create + "&from=" + from + "&to=" + to,
|
||||
url: edgeRoute,
|
||||
type: "POST",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
},
|
||||
patch: function(id, data) {
|
||||
return $.extend(base, {url: edgeRoute + "/" + id, type: "PUT", data: JSON.stringify(data)});
|
||||
},
|
||||
del: function(id) {
|
||||
return $.extend(base, {url: edgeRoute + "/" + id, type: "DELETE"});
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
it('should offer lists of available collections', function() {
|
||||
var collections = [],
|
||||
sys1 = {id: "1", name: "_sys1", status: 3, type: 2},
|
||||
sys2 = {id: "2", name: "_sys2", status: 2, type: 2},
|
||||
doc1 = {id: "3", name: "doc1", status: 3, type: 2},
|
||||
doc2 = {id: "4", name: "doc2", status: 2, type: 2},
|
||||
doc3 = {id: "5", name: "doc3", status: 3, type: 2},
|
||||
edge1 = {id: "6", name: "edge1", status: 3, type: 3},
|
||||
edge2 = {id: "7", name: "edge2", status: 2, type: 3};
|
||||
|
||||
collections.push(sys1);
|
||||
collections.push(sys2);
|
||||
collections.push(doc1);
|
||||
collections.push(doc2);
|
||||
collections.push(doc3);
|
||||
collections.push(edge1);
|
||||
collections.push(edge2);
|
||||
|
||||
spyOn($, "ajax").andCallFake(function(request) {
|
||||
request.success({collections: collections});
|
||||
});
|
||||
|
||||
adapter.getCollections(function(docs, edge) {
|
||||
expect(docs).toContain("doc1");
|
||||
expect(docs).toContain("doc2");
|
||||
expect(docs).toContain("doc3");
|
||||
|
||||
expect(docs.length).toEqual(3);
|
||||
|
||||
expect(edge).toContain("edge1");
|
||||
expect(edge).toContain("edge2");
|
||||
|
||||
expect(edge.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to load a tree node from '
|
||||
+ 'ArangoDB by internal _id attribute', function() {
|
||||
it('should be able to load by internal _id attribute', function() {
|
||||
|
||||
var c0, c1, c2, c3, c4;
|
||||
|
||||
runs(function() {
|
||||
spyOn($, "ajax").andCallFake(function(request) {
|
||||
var vars = JSON.parse(request.data).bindVars;
|
||||
if (vars !== undefined) {
|
||||
request.success({result: loadGraph(vars)});
|
||||
}
|
||||
request.success({result: loadGraph(JSON.parse(request.data))});
|
||||
});
|
||||
|
||||
c0 = insertNode(nodesCollection, 0);
|
||||
|
@ -504,7 +381,7 @@
|
|||
insertEdge(edgesCollection, c0, c4);
|
||||
|
||||
callbackCheck = false;
|
||||
adapter.loadNodeFromTreeById(c0, checkCallbackFunction);
|
||||
adapter.loadNode(c0, checkCallbackFunction);
|
||||
});
|
||||
|
||||
waitsFor(function() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
|
||||
/*global it, expect */
|
||||
/*global it, expect, describe*/
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -33,6 +33,7 @@
|
|||
var describeInterface = function (testee) {
|
||||
"use strict";
|
||||
|
||||
describe('checking the interface', function() {
|
||||
it('should comply to the Adapter Interface', function() {
|
||||
this.addMatchers({
|
||||
toHaveFunction: function(func, argCounter) {
|
||||
|
@ -69,7 +70,13 @@ var describeInterface = function (testee) {
|
|||
expect(testee).toHaveFunction("setNodeLimit", 2);
|
||||
expect(testee).toHaveFunction("setChildLimit", 1);
|
||||
expect(testee).toHaveFunction("expandCommunity", 2);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
var describeIntegeration = function(testee) {
|
||||
"use strict";
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue