/*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, document, setTimeout */ /*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, descendant, getCommunityNodes = function() { return _.filter(nodes, function(n) { return n._isCommunity; }); }, 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 = []; descendant = { loadNode: function(){} }; }); 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 throw an error if no inheriting class is given', function() { expect( function() { var t = new AbstractAdapter([], []); } ).toThrow("An inheriting class has to be given."); }); it('should not throw an error if setup correctly', function() { expect( function() { var t = new AbstractAdapter([], [], descendant); } ).not.toThrow(); }); it('should create a NodeReducer instance', function() { spyOn(window, "NodeReducer"); var nodes = [], edges = [], t = new AbstractAdapter(nodes, edges, descendant); expect(window.NodeReducer).wasCalledWith(); }); it('should send the nodeReducer the configuration if given', function() { spyOn(window, "NodeReducer"); var nodes = [], edges = [], config = { prioList: ["foo", "bar", "baz"] }, t = new AbstractAdapter(nodes, edges, descendant, config); expect(window.NodeReducer).wasCalledWith(["foo", "bar", "baz"]); }); it('should create a ModularityJoiner worker', function() { spyOn(window, "WebWorkerWrapper"); var nodes = [], edges = [], t = new AbstractAdapter(nodes, edges, descendant); expect(window.WebWorkerWrapper).wasCalledWith( window.ModularityJoiner, jasmine.any(Function) ); }); }); describe('checking the interface', function() { var testee; beforeEach(function() { testee = new AbstractAdapter([], [], descendant); 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); }); it('should offer a function to change to a different configuration', function() { expect(testee).toHaveFunction("changeTo", 1); }); it('should offer a function to get the current priority list', function() { expect(testee).toHaveFunction("getPrioList", 0); }); }); describe('checking nodes', function() { var adapter; beforeEach(function() { adapter = new AbstractAdapter(nodes, [], descendant); }); 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 encapsulate all attributes in _data', function() { var data = { _id: 1, name: "Alice", age: 42 }, newNode = adapter.insertNode(data); expect(newNode).toBeDefined(); expect(newNode._data).toEqual(data); }); 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, descendant); 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 encapsulate all attributes in _data', function() { var data = { _id: "1-2", _from: sourceid, _to: targetid, label: "MyLabel", color: "black" }, edge = adapter.insertEdge(data); expect(edge._data).toBeDefined(); expect(edge._data).toEqual(data); }); 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 node exploration', function() { var adapter, mockReducer, mockWrapper, workerCB; beforeEach(function() { mockWrapper = {}; mockWrapper.call = function() {}; mockReducer = {}; mockReducer.getCommunity = function() {}; mockReducer.bucketNodes = function() {}; spyOn(window, "NodeReducer").andCallFake(function() { return { bucketNodes: function(toSort, numBuckets) { return mockReducer.bucketNodes(toSort, numBuckets); } }; }); spyOn(window, "WebWorkerWrapper").andCallFake(function(c, cb) { workerCB = cb; return { call: function() { mockWrapper.call.apply( mockWrapper, Array.prototype.slice.call(arguments) ); } }; }); adapter = new AbstractAdapter(nodes, edges, descendant); }); it('should expand a collapsed node', function() { var node = { _id: "0" }, loaded = 0, loadedNodes = []; adapter.insertNode(node); spyOn(descendant, "loadNode").andCallFake(function(node) { loaded++; loadedNodes.push(node); }); adapter.explore(node); expect(descendant.loadNode).wasCalled(); expect(node._expanded).toBeTruthy(); expect(loaded).toEqual(1); expect(loadedNodes.length).toEqual(1); expect(loadedNodes[0]).toEqual(node._id); }); it('should collapse an expanded node', function() { var node = { _id: "0" }; node = adapter.insertNode(node); node._expanded = true; spyOn(descendant, "loadNode"); adapter.explore(node); expect(node._expanded).toBeFalsy(); expect(descendant.loadNode).wasNotCalled(); }); it('should collapse a tree', function() { var root = { _id: "0" }, c1 = { _id: "1" }, c2 = { _id: "2" }, e1 = { _id: "0-1", _from: "0", _to: "1" }, e2 = { _id: "0-2", _from: "0", _to: "2" }; root = adapter.insertNode(root); // Fake expansion of node root._expanded = true; adapter.insertNode(c1); adapter.insertNode(c2); adapter.insertEdge(e1); adapter.insertEdge(e2); spyOn(descendant, "loadNode"); adapter.explore(root); expect(root._expanded).toBeFalsy(); expect(descendant.loadNode).wasNotCalled(); expect(nodes.length).toEqual(1); expect(edges.length).toEqual(0); }); it('should not remove referenced nodes on collapsing ', function() { var root = { _id: "0" }, c1 = { _id: "1" }, c2 = { _id: "2" }, c3 = { _id: "3" }, e1 = { _id: "0-1", _from: "0", _to: "1" }, e2 = { _id: "0-3", _from: "0", _to: "3" }, e3 = { _id: "2-1", _from: "2", _to: "1" }; root = adapter.insertNode(root); root._expanded = true; c1 = adapter.insertNode(c1); c2 = adapter.insertNode(c2); adapter.insertNode(c3); adapter.insertEdge(e1); adapter.insertEdge(e2); adapter.insertEdge(e3); spyOn(descendant, "loadNode"); adapter.explore(root); expect(root._expanded).toBeFalsy(); expect(descendant.loadNode).wasNotCalled(); expect(nodes.length).toEqual(3); expect(edges.length).toEqual(1); expect(root._outboundCounter).toEqual(0); expect(c1._inboundCounter).toEqual(1); expect(c2._outboundCounter).toEqual(1); }); describe('with community nodes', function() { it('should expand a community node properly', function() { // Hack for a CommunityNode var comm = { _id: "*community_1" }; comm = adapter.insertNode(comm); comm._isCommunity = true; spyOn(adapter, "expandCommunity"); adapter.explore(comm, function() {}); expect(adapter.expandCommunity).toHaveBeenCalledWith(comm, jasmine.any(Function)); }); it('should remove a community if last pointer to it is collapsed', function() { runs(function() { var c0 = { _id: "0" }, internal = { _id: "internal" }, internal2 = { _id: "internal2" }, c1 = { _id: "1" }, c2 = { _id: "2" }, e0 = { _id: "0-1", _from: "0", _to: "1" }, e1 = { _id: "1-c", _from: "1", _to: "internal" }, e2 = { _id: "c-2", _from: "internal", _to: "2" }, commNode; c0 = adapter.insertNode(c0); c1 = adapter.insertNode(c1); c1._expanded = true; adapter.insertNode(internal); adapter.insertNode(internal2); c2 = adapter.insertNode(c2); e0 = adapter.insertEdge(e0); e1 = adapter.insertEdge(e1); e2 = adapter.insertEdge(e2); spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: ["internal", "internal2"] } }); }); adapter.setNodeLimit(3); commNode = getCommunityNodes()[0]; expect(nodes).toEqual([c0, c1, c2, commNode]); expect(edges).toEqual([e0, e1, e2]); adapter.explore(c1); expect(nodes.length).toEqual(2); expect(edges.length).toEqual(1); expect(nodes).toEqual([c0, c1]); expect(edges).toEqual([e0]); }); }); it('should not remove a community if a pointer to it still exists', function() { runs(function() { var c0 = { _id: "0" }, c1 = { _id: "1" }, internal = { _id: "internal" }, internal2 = { _id: "internal2" }, e0 = { _id: "0-1", _from: "0", _to: "1" }, e1 = { _id: "0-c", _from: "0", _to: "internal" }, e2 = { _id: "1-c", _from: "1", _to: "internal" }, comm; c0 = adapter.insertNode(c0); c1 = adapter.insertNode(c1); c1._expanded = true; adapter.insertNode(internal); adapter.insertNode(internal2); e0 = adapter.insertEdge(e0); e1 = adapter.insertEdge(e1); adapter.insertEdge(e2); spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: ["internal", "internal2"] } }); }); adapter.setNodeLimit(3); comm = getCommunityNodes()[0]; adapter.explore(c1); expect(nodes).toEqual([c0, c1, comm]); expect(edges).toEqual([e0, e1]); }); }); it('should be possible to explore a node, collapse it and explore it again', function() { var id1 = "1", id2 = "2", id3 = "3", id4 = "4", id5 = "5", id6 = "6", ids = "start", n1 = {_id: id1}, n2 = {_id: id2}, n3 = {_id: id3}, n4 = {_id: id4}, n5 = {_id: id5}, n6 = {_id: id6}, ns = {_id: ids}, intS, int1, int2, int3, int4, int5, int6, es1 = { _id: ids + "-" + id1, _from: ids, _to: id1 }, es2 = { _id: ids + "-" + id2, _from: ids, _to: id2 }, es3 = { _id: ids + "-" + id3, _from: ids, _to: id3 }, es4 = { _id: ids + "-" + id4, _from: ids, _to: id4 }, es5 = { _id: ids + "-" + id5, _from: ids, _to: id5 }, es6 = { _id: ids + "-" + id6, _from: ids, _to: id6 }; spyOn(mockReducer, "bucketNodes").andCallFake(function(list) { return [ { reason: { type: "similar" }, nodes: [int1, int2, int3] }, { reason: { type: "similar" }, nodes: [int4, int5, int6] } ]; }); spyOn(descendant, "loadNode").andCallFake(function() { var inserted = {}; int1 = adapter.insertNode(n1); int2 = adapter.insertNode(n2); int3 = adapter.insertNode(n3); int4 = adapter.insertNode(n4); int5 = adapter.insertNode(n5); int6 = adapter.insertNode(n6); inserted.id1 = int1; inserted.id2 = int2; inserted.id3 = int3; inserted.id4 = int4; inserted.id5 = int5; inserted.id6 = int6; adapter.insertEdge(es1); adapter.insertEdge(es2); adapter.insertEdge(es3); adapter.insertEdge(es4); adapter.insertEdge(es5); adapter.insertEdge(es6); adapter.checkSizeOfInserted(inserted); }); adapter.setChildLimit(2); expect(nodes).toEqual([]); intS = adapter.insertNode(ns); expect(nodes).toEqual([intS]); adapter.explore(intS); expect(nodes.length).toEqual(3); expect(edges.length).toEqual(6); expect(mockReducer.bucketNodes).wasCalledWith( [int1, int2, int3, int4, int5, int6], 2 ); expect(intS._expanded).toBeTruthy(); adapter.explore(intS); expect(nodes.length).toEqual(1); expect(edges.length).toEqual(0); expect(intS._expanded).toBeFalsy(); adapter.setChildLimit(3); adapter.explore(intS); expect(intS._expanded).toBeTruthy(); expect(nodes.length).toEqual(3); expect(edges.length).toEqual(6); expect(mockReducer.bucketNodes).wasCalledWith( [int1, int2, int3, int4, int5, int6], 3 ); }); it('should be possible to propagate collapsing through a community', function() { var id1 = "1", id2 = "2", id3 = "3", id4 = "4", id5 = "5", ids = "start", n1 = {_id: id1}, n2 = {_id: id2}, n3 = {_id: id3}, n4 = {_id: id4}, n5 = {_id: id5}, ns = {_id: ids}, intS, int1, int2, int3, int4, int5, es1 = { _id: ids + "-" + id1, _from: ids, _to: id1 }, es2 = { _id: ids + "-" + id2, _from: ids, _to: id2 }, e13 = { _id: id1 + "-" + id3, _from: id1, _to: id3 }, e24 = { _id: id2 + "-" + id4, _from: id2, _to: id4 }, e35 = { _id: id3 + "-" + id5, _from: id3, _to: id5 }; spyOn(mockWrapper, "call").andCallFake(function(name) { if (name === "getCommunity") { workerCB({ data: { cmd: name, result: [id2, id3] } }); } }); spyOn(descendant, "loadNode").andCallFake(function(n) { if (n === ids) { int1 = adapter.insertNode(n1); int2 = adapter.insertNode(n2); adapter.insertEdge(es1); adapter.insertEdge(es2); return; } if (n === id1) { int3 = adapter.insertNode(n3); adapter.insertEdge(e13); return; } if (n === id2) { int4 = adapter.insertNode(n4); adapter.insertEdge(e24); return; } if (n === id3) { int5 = adapter.insertNode(n5); adapter.insertEdge(e35); return; } }); adapter.setChildLimit(2); expect(nodes).toEqual([]); intS = adapter.insertNode(ns); expect(nodes).toEqual([intS]); adapter.explore(intS); expect(nodes).toEqual([intS, int1, int2]); adapter.explore(int1); expect(nodes).toEqual([intS, int1, int2, int3]); adapter.explore(int2); adapter.explore(int3); adapter.setNodeLimit(5); expect(mockWrapper.call).wasCalledWith("getCommunity", 5); expect(nodes.length).toEqual(5); expect(edges.length).toEqual(5); expect(int1._expanded).toBeTruthy(); adapter.explore(int1); expect(int1._expanded).toBeFalsy(); expect(nodes.length).toEqual(4); expect(edges.length).toEqual(3); }); }); }); describe('checking communities', function() { var adapter, mockReducer, mockWrapper, workerCB; beforeEach(function() { mockWrapper = {}; mockWrapper.call = function() {}; mockReducer = {}; mockReducer.getCommunity = function() {}; mockReducer.bucketNodes = function() {}; spyOn(window, "NodeReducer").andCallFake(function() { 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); } }; }); spyOn(window, "WebWorkerWrapper").andCallFake(function(c, cb) { workerCB = cb; return { call: function() { mockWrapper.call.apply( mockWrapper, Array.prototype.slice.call(arguments) ); } }; }); adapter = new AbstractAdapter(nodes, edges, descendant); }); /* it('should not take any action if no limit is set', function() { spyOn(mockWrapper, "call"); adapter.insertNode({_id: 1}); adapter.insertNode({_id: 2}); adapter.insertNode({_id: 3}); adapter.insertNode({_id: 4}); adapter.insertNode({_id: 5}); adapter.checkNodeLimit(); expect(mockWrapper.call).wasNotCalled(); }); it('should take a given focus into account', function() { var n1, limit; spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: [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(mockWrapper.call).toHaveBeenCalledWith("getCommunity", limit, n1._id); }); it('should create a community if too many nodes are added', function() { var n1, n2, commId; spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: [1, 2] } }); }); adapter.setNodeLimit(2); n1 = adapter.insertNode({_id: 1}); n2 = adapter.insertNode({_id: 2}); adapter.insertNode({_id: 3}); adapter.checkNodeLimit(); expect(mockWrapper.call).wasCalledWith("getCommunity", 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(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: [1, 2] } }); }); n1 = adapter.insertNode({_id: 1}); n2 = adapter.insertNode({_id: 2}); adapter.insertNode({_id: 3}); adapter.setNodeLimit(2); expect(mockWrapper.call).wasCalledWith("getCommunity", 2); expect(getCommunityNodesIds().length).toEqual(1); notExistNode([1, 2]); existNode(3); }); it('should create proper attributes for the community', function() { var n1, n2, n3, n4, n5, com; spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: [1, 2, 3, 4, 5] } }); }); 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}); adapter.setNodeLimit(2); com = getCommunityNodes()[0]; expect(com.x).toBeDefined(); expect(com.y).toBeDefined(); expect(com._size).toEqual(5); }); it('should not trigger getCommunity multiple times', function() { spyOn(mockWrapper, "call").andCallFake(function(name) { setTimeout(function() { workerCB({ data: { cmd: name, result: [1, 2] } }); }, 200); }); adapter.insertNode({_id: 1}); adapter.insertNode({_id: 2}); adapter.insertNode({_id: 3}); adapter.insertNode({_id: 4}); adapter.insertNode({_id: 5}); adapter.insertNode({_id: 6}); adapter.setNodeLimit(5); adapter.setNodeLimit(4); adapter.setNodeLimit(3); adapter.setNodeLimit(2); adapter.setNodeLimit(1); expect(mockWrapper.call).wasCalledWith("getCommunity", 5); expect(mockWrapper.call.calls.length).toEqual(1); }); it('should be able to trigger getCommunity after it returns', function() { var send; runs(function() { send = false; spyOn(mockWrapper, "call").andCallFake(function(name) { if (!send) { setTimeout(function() { send = true; workerCB({ data: { cmd: name, result: [1, 2] } }); }, 200); } }); adapter.insertNode({_id: 1}); adapter.insertNode({_id: 2}); adapter.insertNode({_id: 3}); adapter.insertNode({_id: 4}); adapter.insertNode({_id: 5}); adapter.insertNode({_id: 6}); adapter.setNodeLimit(5); adapter.setNodeLimit(4); expect(mockWrapper.call).wasCalledWith("getCommunity", 5); expect(mockWrapper.call.calls.length).toEqual(1); }); waitsFor(function() { return send; }); runs(function() { adapter.setNodeLimit(3); expect(mockWrapper.call).wasCalledWith("getCommunity", 3); expect(mockWrapper.call.calls.length).toEqual(2); }); }); it('should connect edges to communities', function() { var n1, n2, comm, e1, e2, e3; spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: [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, fakeData; beforeEach(function() { fakeData = [1, 2]; spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: fakeData } }); }); 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(1); expect(comm._expanded).toBeTruthy(); expect(nodes.length).toEqual(3); expect(edges.length).toEqual(2); //existEdge(1, 2); existEdge(comm._id, 3); existEdge(3, comm._id); existNodes([comm._id, 3, 4]); }); */ it('should collapse another community if limit is to small', function() { fakeData = [3, 4]; adapter.expandCommunity(comm); expect(getCommunityNodes().length).toEqual(2); var comm2 = getCommunityNodes()[1]; expect(comm).not.toEqual(comm2); expect(mockWrapper.call).wasCalled(); expect(nodes.length).toEqual(2); existNodes([comm._id, comm2._id]); expect(comm._expanded).toBeTruthy(); expect(comm2._expanded).toBeFalsy(); notExistNodes([1, 2, 3, 4]); }); /* it('should collapse another community if limit is further reduced', function() { fakeData = [3, 4]; adapter.setNodeLimit(2); expect(getCommunityNodes().length).toEqual(2); var comm2 = getCommunityNodes()[1]; expect(comm).not.toEqual(comm2); expect(mockWrapper.call).wasCalled(); expect(nodes.length).toEqual(2); notExistNodes([1, 2, 3, 4]); }); it('should be possible to insert an edge targeting a node in the community', function() { var e = adapter.insertEdge({ _id: "4-2", _from: 4, _to: 2 }); expect(e.source).toEqual(n4); expect(e.target).toEqual(comm); expect(e._target).toEqual(n2); expect(comm.getDissolveInfo().edges.inbound).toEqual([e3, e]); }); it('should be possible to insert an edge starting' + ' from a node in the community', function() { var e = adapter.insertEdge({ _id: "2-4", _from: 2, _to: 4 }); expect(e.source).toEqual(comm); expect(e._source).toEqual(n2); expect(e.target).toEqual(n4); expect(comm.getDissolveInfo().edges.outbound).toEqual([e2, e]); }); */ }); }); describe('checking information of modularity joiner', function() { var adapter, source, target, sourceid, targetid, mockWrapper, mockReducer, workerCB; beforeEach(function() { mockReducer = {}; mockReducer.bucketNodes = function() {}; spyOn(window, "NodeReducer").andCallFake(function(v, e) { return { bucketNodes: function(toSort, numBuckets) { return mockReducer.bucketNodes(toSort, numBuckets); } }; }); mockWrapper = {}; mockWrapper.call = function() {}; spyOn(window, "WebWorkerWrapper").andCallFake(function(c, cb) { workerCB = cb; return { call: function() { mockWrapper.call.apply( mockWrapper, Array.prototype.slice.call(arguments) ); } }; }); adapter = new AbstractAdapter(nodes, edges, descendant); source = adapter.insertNode({_id: "1"}); target = adapter.insertNode({_id: "2"}); sourceid = source._id; targetid = target._id; }); it('should be informed if an edge is inserted', function() { spyOn(mockWrapper, "call"); adapter.insertEdge({ _id: "1-2", _from: sourceid, _to: targetid }); expect(mockWrapper.call).wasCalledWith("insertEdge", sourceid, targetid); }); it('should be informed if an edge is removed', function() { var toDelete = { _id: "1-2", _from: sourceid, _to: targetid }, edgeToDel = adapter.insertEdge(toDelete); spyOn(mockWrapper, "call"); adapter.removeEdge(edgeToDel); expect(mockWrapper.call).wasCalledWith("deleteEdge", sourceid, targetid); }); it('should be informed if all edges for a node are removed', function() { var toDelete1 = { _id: "1-2", _from: sourceid, _to: targetid }, toDelete2 = { _id: "2-1", _from: targetid, _to: sourceid }; adapter.insertEdge(toDelete1); adapter.insertEdge(toDelete2); spyOn(mockWrapper, "call"); adapter.removeEdgesForNode(source); expect(mockWrapper.call).wasCalledWith("deleteEdge", sourceid, targetid); expect(mockWrapper.call).wasCalledWith("deleteEdge", targetid, sourceid); }); it('should remove all edges of a community if it is joined', function() { var n1, n2, n3, n4, e1, e2, e3, e4, eout, ein; n1 = { _id: "n1" }; n2 = { _id: "n2" }; n3 = { _id: "n3" }; n4 = { _id: "n4" }; e1 = { _id: "n1-n2", _from: n1._id, _to: n2._id }; e2 = { _id: "n2-n3", _from: n2._id, _to: n3._id }; e3 = { _id: "n3-n4", _from: n3._id, _to: n4._id }; e4 = { _id: "n4-n1", _from: n4._id, _to: n1._id }; eout = { _id: "n1-1", _from: n1._id, _to: source._id }; ein = { _id: "2-n1", _from: target._id, _to: n1._id }; adapter.insertNode(n1); adapter.insertNode(n2); adapter.insertNode(n3); adapter.insertNode(n4); adapter.insertEdge(e1); adapter.insertEdge(e2); adapter.insertEdge(e3); adapter.insertEdge(e4); adapter.insertEdge(eout); adapter.insertEdge(ein); spyOn(mockWrapper, "call").andCallFake(function(name) { if (name === "getCommunity") { workerCB({ data: { cmd: name, result: [n1._id, n2._id, n3._id, n4._id] } }); } }); adapter.setNodeLimit(3); expect(mockWrapper.call).wasCalledWith("deleteEdge", n1._id, n2._id); expect(mockWrapper.call).wasCalledWith("deleteEdge", n2._id, n3._id); expect(mockWrapper.call).wasCalledWith("deleteEdge", n3._id, n4._id); expect(mockWrapper.call).wasCalledWith("deleteEdge", n4._id, n1._id); expect(mockWrapper.call).wasCalledWith("deleteEdge", n1._id, source._id); expect(mockWrapper.call).wasCalledWith("deleteEdge", target._id, n1._id); // 1 time getCommunity, 7 times deleteEdge expect(mockWrapper.call.calls.length).toEqual(7); }); it('should not be informed of edges connected to communities', function() { var n1, n2, n3, e1, e2; n1 = { _id: "n1" }; n2 = { _id: "n2" }; n3 = { _id: "n3" }; e1 = { _id: "n1-n2", _from: n1._id, _to: n2._id }; e2 = { _id: "n2-n3", _from: n2._id, _to: n3._id }; adapter.insertNode(n1); adapter.insertNode(n2); adapter.insertNode(n3); adapter.insertEdge(e1); spyOn(mockWrapper, "call").andCallFake(function(name) { if (name === "getCommunity") { workerCB({ data: { cmd: name, result: [n1._id, n2._id] } }); } }); adapter.setNodeLimit(3); adapter.insertEdge(e2); expect(mockWrapper.call).not.wasCalledWith("insertEdge", n2._id, n3._id); }); it('should be informed if a community is dissolved', function() { var n1, n2, n3, e1, e2; n1 = { _id: "n1" }; n2 = { _id: "n2" }; n3 = { _id: "n3" }; e1 = { _id: "n1-n2", _from: n1._id, _to: n2._id }; e2 = { _id: "n2-n3", _from: n2._id, _to: n3._id }; adapter.insertNode(n1); adapter.insertNode(n2); adapter.insertNode(n3); adapter.insertEdge(e1); adapter.insertEdge(e2); spyOn(mockWrapper, "call").andCallFake(function(name) { if (name === "getCommunity") { workerCB({ data: { cmd: name, result: [n1._id, n2._id, n3._id] } }); } }); adapter.setNodeLimit(3); expect(mockWrapper.call).wasCalledWith("deleteEdge", n1._id, n2._id); expect(mockWrapper.call).wasCalledWith("deleteEdge", n2._id, n3._id); adapter.setNodeLimit(100); adapter.dissolveCommunity(getCommunityNodes()[0]); expect(mockWrapper.call).wasCalledWith("insertEdge", n1._id, n2._id); expect(mockWrapper.call).wasCalledWith("insertEdge", n2._id, n3._id); }); it('should be informed to remove edges if nodes are added to a bucket', function() { var centroid = { _id: "center" }, s1 = { _id: "s_1" }, s2 = { _id: "s_2" }, s3 = { _id: "s_3" }, s4 = { _id: "s_4" }, s5 = { _id: "s_5" }, s6 = { _id: "s_6" }, s7 = { _id: "s_7" }, s8 = { _id: "s_8" }, ec1 = { _id: "c1", _from: "center", _to: "s_1" }, ec2 = { _id: "c2", _from: "center", _to: "s_2" }, ec3 = { _id: "c3", _from: "center", _to: "s_3" }, ec4 = { _id: "c4", _from: "center", _to: "s_4" }, ec5 = { _id: "c5", _from: "center", _to: "s_5" }, ec6 = { _id: "c6", _from: "center", _to: "s_6" }, ec7 = { _id: "c7", _from: "center", _to: "s_7" }, ec8 = { _id: "c8", _from: "center", _to: "s_8" }, inserted = [s1, s2, s3, s4, s5, s6, s7, s8], limit = 3; spyOn(mockReducer, "bucketNodes").andCallFake(function() { return [ { reason: { type: "similar", example: s1 }, nodes: [s1, s2] }, { reason: { type: "similar", example: s3 }, nodes: [s3, s4, s5, s6, s7] }, { reason: { type: "similar", example: s8 }, nodes: [s8] } ]; }); adapter.setChildLimit(limit); adapter.insertNode(centroid); adapter.insertNode(s1); adapter.insertNode(s2); adapter.insertNode(s3); adapter.insertNode(s4); adapter.insertNode(s5); adapter.insertNode(s6); adapter.insertNode(s7); adapter.insertNode(s8); adapter.insertEdge(ec1); adapter.insertEdge(ec2); adapter.insertEdge(ec3); adapter.insertEdge(ec4); adapter.insertEdge(ec5); adapter.insertEdge(ec6); adapter.insertEdge(ec7); adapter.insertEdge(ec8); spyOn(mockWrapper, "call"); adapter.checkSizeOfInserted(inserted); expect(mockReducer.bucketNodes).wasCalledWith(inserted, limit); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_1"); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_2"); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_3"); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_4"); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_5"); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_6"); expect(mockWrapper.call).toHaveBeenCalledWith("deleteEdge", "center", "s_7"); expect(mockWrapper.call.calls.length).toEqual(7); }); it('should not be informed about edges targeting at communities', function() { var centroid = { _id: "center" }, s1 = { _id: "s_1" }, s2 = { _id: "s_2" }, ec1 = { _id: "c1", _from: "center", _to: "s_1" }, ec2 = { _id: "c2", _from: "center", _to: "s_2" }, comSource = { _id: "comsource", _from: "s_1", _to: "1" }, comTarget = { _id: "comtarget", _from: "1", _to: "s_1" }, inserted = [s1, s2], limit = 1; spyOn(mockReducer, "bucketNodes").andCallFake(function() { return [ { reason: { type: "similar", example: s1 }, nodes: [s1, s2] } ]; }); adapter.setChildLimit(limit); adapter.insertNode(centroid); adapter.insertNode(s1); adapter.insertNode(s2); adapter.insertEdge(ec1); adapter.insertEdge(ec2); adapter.checkSizeOfInserted(inserted); expect(mockReducer.bucketNodes).wasCalledWith(inserted, limit); spyOn(mockWrapper, "call"); adapter.insertEdge(comSource); adapter.insertEdge(comTarget); expect(mockWrapper.call).wasNotCalled(); }); it('should never be informed about edges concerning communities', function() { var fakeData, n1, n2, n3, n4, e1, e2, e3, comm; adapter.removeNode(source); adapter.removeNode(target); expect(nodes.length).toEqual(0); fakeData = [1, 2]; spyOn(mockWrapper, "call").andCallFake(function(name) { if (name === "getCommunity") { workerCB({ data: { cmd: name, result: fakeData } }); } }); 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 }); expect(mockWrapper.call).wasCalledWith("insertEdge", 1, 2); expect(mockWrapper.call).wasCalledWith("insertEdge", 2, 3); expect(mockWrapper.call).wasCalledWith("insertEdge", 3, 1); expect(mockWrapper.call.calls.length).toEqual(3); adapter.setNodeLimit(3); comm = getCommunityNodes()[0]; expect(mockWrapper.call).wasCalledWith("getCommunity", 3); expect(mockWrapper.call).wasCalledWith("deleteEdge", 1, 2); expect(mockWrapper.call).wasCalledWith("deleteEdge", 2, 3); expect(mockWrapper.call).wasCalledWith("deleteEdge", 3, 1); expect(mockWrapper.call.calls.length).toEqual(7); fakeData = [3, 4]; adapter.setNodeLimit(2); expect(mockWrapper.call).wasCalledWith("getCommunity", 2); expect(mockWrapper.call.calls.length).toEqual(8); }); it('should be informed properly if a community is removed during collapsing', function() { runs(function() { var c0 = { _id: "0" }, internal = { _id: "internal" }, internal2 = { _id: "internal2" }, c1 = { _id: "1" }, c2 = { _id: "2" }, e0 = { _id: "0-1", _from: "0", _to: "1" }, e1 = { _id: "1-c", _from: "1", _to: "internal" }, e2 = { _id: "c-2", _from: "internal", _to: "2" }, commNode; spyOn(mockWrapper, "call").andCallFake(function(name) { workerCB({ data: { cmd: name, result: ["internal", "internal2"] } }); }); adapter.insertNode(c0); c1 = adapter.insertNode(c1); c1._expanded = true; adapter.insertNode(internal); adapter.insertNode(internal2); adapter.insertNode(c2); adapter.insertEdge(e0); adapter.insertEdge(e1); adapter.insertEdge(e2); expect(mockWrapper.call).wasCalledWith("insertEdge", e0._from, e0._to); expect(mockWrapper.call).wasCalledWith("insertEdge", e1._from, e1._to); expect(mockWrapper.call).wasCalledWith("insertEdge", e2._from, e2._to); expect(mockWrapper.call.calls.length).toEqual(3); adapter.setNodeLimit(3); expect(mockWrapper.call).wasCalledWith("getCommunity", 3); expect(mockWrapper.call).wasCalledWith("deleteEdge", e1._from, e1._to); expect(mockWrapper.call).wasCalledWith("deleteEdge", e2._from, e2._to); expect(mockWrapper.call.calls.length).toEqual(6); adapter.explore(c1); expect(mockWrapper.call.calls.length).toEqual(6); }); }); it('should be informed if a node is explored, collapsed and explored again', function() { var id1 = "1", id2 = "2", id3 = "3", id4 = "4", id5 = "5", id6 = "6", ids = "start", n1 = {_id: id1}, n2 = {_id: id2}, n3 = {_id: id3}, n4 = {_id: id4}, n5 = {_id: id5}, n6 = {_id: id6}, ns = {_id: ids}, intS, int1, int2, int3, int4, int5, int6, es1 = { _id: ids + "-" + id1, _from: ids, _to: id1 }, es2 = { _id: ids + "-" + id2, _from: ids, _to: id2 }, es3 = { _id: ids + "-" + id3, _from: ids, _to: id3 }, es4 = { _id: ids + "-" + id4, _from: ids, _to: id4 }, es5 = { _id: ids + "-" + id5, _from: ids, _to: id5 }, es6 = { _id: ids + "-" + id6, _from: ids, _to: id6 }; spyOn(mockWrapper, "call"); spyOn(mockReducer, "bucketNodes").andCallFake(function(list) { return [ { reason: { type: "similar" }, nodes: [int1, int2, int3] }, { reason: { type: "similar" }, nodes: [int4, int5, int6] } ]; }); spyOn(descendant, "loadNode").andCallFake(function() { var inserted = {}; int1 = adapter.insertNode(n1); int2 = adapter.insertNode(n2); int3 = adapter.insertNode(n3); int4 = adapter.insertNode(n4); int5 = adapter.insertNode(n5); int6 = adapter.insertNode(n6); inserted.id1 = int1; inserted.id2 = int2; inserted.id3 = int3; inserted.id4 = int4; inserted.id5 = int5; inserted.id6 = int6; adapter.insertEdge(es1); adapter.insertEdge(es2); adapter.insertEdge(es3); adapter.insertEdge(es4); adapter.insertEdge(es5); adapter.insertEdge(es6); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id1); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id2); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id3); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id4); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id5); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id6); expect(mockWrapper.call.calls.length).toEqual(6); mockWrapper.call.calls.length = 0; adapter.checkSizeOfInserted(inserted); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id1); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id2); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id3); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id4); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id5); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id6); expect(mockWrapper.call.calls.length).toEqual(6); mockWrapper.call.calls.length = 0; }); adapter.setChildLimit(2); intS = adapter.insertNode(ns); expect(mockWrapper.call.calls.length).toEqual(0); adapter.explore(intS); expect(edges.length).toEqual(6); adapter.explore(intS); expect(edges.length).toEqual(0); expect(mockWrapper.call.calls.length).toEqual(0); mockWrapper.call.calls.length = 0; adapter.explore(intS); expect(edges.length).toEqual(6); }); it('should be informed properly about propagating' + ' a collapse through a community', function() { var id1 = "1", id2 = "2", id3 = "3", id4 = "4", id5 = "5", ids = "start", n1 = {_id: id1}, n2 = {_id: id2}, n3 = {_id: id3}, n4 = {_id: id4}, n5 = {_id: id5}, ns = {_id: ids}, intS, int1, int2, int3, int4, int5, es1 = { _id: ids + "-" + id1, _from: ids, _to: id1 }, es2 = { _id: ids + "-" + id2, _from: ids, _to: id2 }, e13 = { _id: id1 + "-" + id3, _from: id1, _to: id3 }, e24 = { _id: id2 + "-" + id4, _from: id2, _to: id4 }, e35 = { _id: id3 + "-" + id5, _from: id3, _to: id5 }; spyOn(mockWrapper, "call").andCallFake(function(name) { if (name === "getCommunity") { workerCB({ data: { cmd: name, result: [id2, id3] } }); } }); spyOn(descendant, "loadNode").andCallFake(function(n) { if (n === ids) { int1 = adapter.insertNode(n1); int2 = adapter.insertNode(n2); adapter.insertEdge(es1); adapter.insertEdge(es2); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id1); expect(mockWrapper.call).wasCalledWith("insertEdge", ids, id2); expect(mockWrapper.call.calls.length).toEqual(2); mockWrapper.call.calls.length = 0; return; } if (n === id1) { int3 = adapter.insertNode(n3); adapter.insertEdge(e13); expect(mockWrapper.call).wasCalledWith("insertEdge", id1, id3); expect(mockWrapper.call.calls.length).toEqual(1); mockWrapper.call.calls.length = 0; return; } if (n === id2) { int4 = adapter.insertNode(n4); adapter.insertEdge(e24); expect(mockWrapper.call).wasCalledWith("insertEdge", id2, id4); expect(mockWrapper.call.calls.length).toEqual(1); mockWrapper.call.calls.length = 0; return; } if (n === id3) { int5 = adapter.insertNode(n5); adapter.insertEdge(e35); expect(mockWrapper.call).wasCalledWith("insertEdge", id3, id5); expect(mockWrapper.call.calls.length).toEqual(1); mockWrapper.call.calls.length = 0; return; } }); adapter.setChildLimit(2); intS = adapter.insertNode(ns); adapter.explore(intS); adapter.explore(int1); adapter.explore(int2); adapter.explore(int3); adapter.setNodeLimit(5); expect(mockWrapper.call).wasCalledWith("getCommunity", 5); expect(mockWrapper.call).wasCalledWith("deleteEdge", ids, id2); expect(mockWrapper.call).wasCalledWith("deleteEdge", id1, id3); expect(mockWrapper.call).wasCalledWith("deleteEdge", id2, id4); expect(mockWrapper.call).wasCalledWith("deleteEdge", id3, id5); expect(mockWrapper.call.calls.length).toEqual(5); mockWrapper.call.calls.length = 0; expect(int1._expanded).toBeTruthy(); adapter.explore(int1); expect(int1._expanded).toBeFalsy(); expect(mockWrapper.call.calls.length).toEqual(0); }); }); describe('checking many child nodes', function() { var adapter, mockReducer; beforeEach(function() { mockReducer = {}; mockReducer.getPrioList = function() {}; mockReducer.changePrioList = function() {}; mockReducer.bucketNodes = function() {}; spyOn(window, "NodeReducer").andCallFake(function(v, e) { return { bucketNodes: function(toSort, numBuckets) { return mockReducer.bucketNodes(toSort, numBuckets); }, changePrioList: function(list) { return mockReducer.changePrioList(list); }, getPrioList: function() { return mockReducer.getPrioList(); } }; }); adapter = new AbstractAdapter(nodes, edges, descendant); }); it('should be able to change the reducer to a new prioList', function() { spyOn(mockReducer,"changePrioList"); var list = ["a", "b", "c"], config = { prioList: list }; adapter.changeTo(config); expect(mockReducer.changePrioList).wasCalledWith(list); }); it('should be able to return the current prioList', function() { spyOn(mockReducer,"getPrioList"); adapter.getPrioList(); expect(mockReducer.getPrioList).wasCalled(); }); 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 [ { reason: { type: "similar", example: n1 }, nodes: [n1, n2] }, { reason: { type: "similar", example: n3 }, nodes: [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 [ { reason: { type: "similar", example: n1 }, nodes: [n1] }, { reason: { type: "similar", example: n3 }, nodes: [n3, n4, n5] }, { reason: { type: "similar", example: n2 }, nodes: [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"]); }); it('should display the reason why a community has been joined', function() { var n1, n2, n3, n4, n5, com1, com2, inserted = [], limit = 3; spyOn(mockReducer, "bucketNodes").andCallFake(function() { return [ { reason: { type: "similar", example: n1 }, nodes: [n1, n2] }, { reason: { key: "type", value: "example" }, nodes: [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"]); _.each(getCommunityNodes(), function(c) { if (c._size === 2) { com1 = c; return; } if (c._size === 3) { com2 = c; return; } // Should never be reached expect(true).toBeFalsy(); }); expect(com1._reason).toEqual({ type: "similar", example: n1 }); expect(com2._reason).toEqual({ key: "type", value: "example" }); }); }); }); }());