1
0
Fork 0
arangodb/tests/js/client/http/api-gharial-spec.js

1476 lines
54 KiB
JavaScript

/* jshint globalstrict:false, strict:false, maxlen: 5000 */
/* global describe, beforeEach, afterEach, it */
'use strict';
// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2016 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
// /
// / @author Michael Hackstein
// / @author Heiko Kernbach
// //////////////////////////////////////////////////////////////////////////////
const chai = require('chai');
const Joi = require('joi');
const expect = chai.expect;
chai.Assertion.addProperty('does', function () {
return this;
});
const arangodb = require('@arangodb');
const arango = arangodb.arango;
const ERRORS = arangodb.errors;
const db = arangodb.db;
const internal = require('internal');
const wait = internal.wait;
const url = '/_api/gharial';
describe('_api/gharial', () => {
const graphName = 'UnitTestGraph';
const vColName = 'UnitTestVertices';
const eColName = 'UnitTestRelations';
const oColName = 'UnitTestOrphans';
const oColName2 = 'UnitTestOrphansSecond';
const cleanup = () => {
try {
db._drop(vColName);
} catch (e) {
}
try {
db._drop(eColName);
} catch (e) {
}
try {
db._drop(oColName);
} catch (e) {
}
try {
db._drop(oColName2);
} catch (e) {
}
try {
db._graphs.remove(graphName);
} catch (e) {
}
try {
db._graphs.remove('knows_graph');
} catch (e) {
}
try {
db._drop('persons');
} catch (e) {
}
try {
db._drop('knows');
} catch (e) {
}
try {
db._drop('knows_2');
} catch (e) {
}
};
const validateGraphFormat = (graph) => {
const edgeDefinition = Joi.object({
collection: Joi.string().required(),
from: Joi.array().items(Joi.string()).required(),
to: Joi.array().items(Joi.string()).required()
});
const schema = Joi.object({
"_key": Joi.string().required(),
"_rev": Joi.string().required(),
"_id": Joi.string().required(),
name: Joi.string().required(),
numberOfShards: Joi.number().integer().min(1).required(),
replicationFactor: Joi.number().integer().min(1).required(),
minReplicationFactor: Joi.number().integer().min(1).required(),
isSmart: Joi.boolean().required(),
orphanCollections: Joi.array().items(Joi.string()).required(),
edgeDefinitions: Joi.array().items(edgeDefinition).required()
});
const res = schema.validate(graph);
expect(res.error).to.be.null;
};
beforeEach(cleanup);
afterEach(cleanup);
describe('graph creation', () => {
it('should create a graph without orphans', () => {
const graphDef = {
"name": graphName,
"edgeDefinitions": [{
"collection": eColName,
"from": [vColName],
"to": [vColName]
}
],
"isSmart": false
};
expect(db._collection(eColName)).to.be.null;
expect(db._collection(vColName)).to.be.null;
let req = arango.POST(url, graphDef);
expect(req).to.have.keys("error", "code", "graph");
expect(req.code).to.equal(202);
expect(req.error).to.be.false;
validateGraphFormat(req.graph);
// This is all async give it some time
do {
wait(0.1);
req = arango.GET(url + "/" + graphName);
} while (req.code !== 200);
expect(db._collection(eColName)).to.not.be.null;
expect(db._collection(vColName)).to.not.be.null;
expect(req).to.have.keys("error", "code", "graph");
expect(req.code).to.equal(200);
expect(req.error).to.be.false;
validateGraphFormat(req.graph);
});
it('should create a graph with orphans', () => {
const graphDef = {
"name": graphName,
"edgeDefinitions": [{
"collection": eColName,
"from": [vColName],
"to": [vColName]
}
],
"orphanCollections": [
oColName,
oColName2
],
"isSmart": false
};
expect(db._collection(eColName)).to.be.null;
expect(db._collection(vColName)).to.be.null;
expect(db._collection(oColName)).to.be.null;
expect(db._collection(oColName2)).to.be.null;
let req = arango.POST(url, graphDef);
expect(req).to.have.keys("error", "code", "graph");
expect(req.code).to.equal(202);
expect(req.error).to.be.false;
validateGraphFormat(req.graph);
// This is all async give it some time
do {
wait(0.1);
req = arango.GET(url + "/" + graphName);
} while (req.code !== 200);
expect(db._collection(eColName)).to.not.be.null;
expect(db._collection(vColName)).to.not.be.null;
expect(db._collection(oColName)).to.not.be.null;
expect(db._collection(oColName2)).to.not.be.null;
expect(req).to.have.keys("error", "code", "graph");
expect(req.code).to.equal(200);
expect(req.error).to.be.false;
validateGraphFormat(req.graph);
});
});
describe('graph modification test suite', function () {
const vertexUrl = `${url}/${graphName}/vertex`;
const edgeUrl = `${url}/${graphName}/edge`;
beforeEach(() => {
const graphDef = {
"name": graphName,
"edgeDefinitions": [{
"collection": eColName,
"from": [vColName],
"to": [vColName]
}
],
"isSmart": false
};
expect(db._collection(eColName)).to.be.null;
expect(db._collection(vColName)).to.be.null;
let req = arango.POST(url, graphDef);
// Just make sure the graph exists
do {
wait(0.1);
req = arango.GET(url + "/" + graphName);
} while (req.code !== 200);
});
it('should list all graphs in correct format', () => {
const res = arango.GET(url);
expect(res).to.have.keys("error", "code", "graphs");
expect(res.code).to.equal(200);
expect(res.error).to.be.false;
res.graphs.map(validateGraphFormat);
});
it('should be able to add an orphan', () => {
const res = arango.POST(vertexUrl, {collection: oColName});
expect(res).to.have.keys("error", "code", "graph");
expect(res.code).to.equal(202);
expect(res.error).to.be.false;
validateGraphFormat(res.graph);
expect(db._collection(oColName)).to.not.be.null;
});
it('should be able to modify edge definition', () => {
const res = arango.PUT(`${edgeUrl}/${eColName}`, {
"collection": eColName,
"from": [vColName, oColName],
"to": [vColName]
});
expect(res).to.have.keys("error", "code", "graph");
expect(res.code).to.equal(202);
expect(res.error).to.be.false;
validateGraphFormat(res.graph);
expect(db._collection(oColName)).to.not.be.null;
});
});
describe('edge modification test suite', function () {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
describe('edge creation (POST)', function () {
describe('valid positives', function () {
it('_from and _to vertices are existent and valid', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/bob',
_to: 'persons/charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef );
expect(req.code).to.equal(202);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
});
describe('false positives', function () {
it('_from is set but vertex is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/notavailable',
_to: 'persons/charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to is set but vertex is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/bob',
_to: 'persons/notavailable'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from and _to are set but are not available documents', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/notavailable',
_to: 'persons/notavailable'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from definition is valid, but collection is not available', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'xxx/peter',
_to: 'persons/charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from definition is valid, but document is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows/', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to definition is valid, but document is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_to: 'persons/peterNotExisting',
_from: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows/', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from && _to definitions are valid, but documents are not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlieNotExisting'
};
// get a (any) valid key of an existing edge document
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows/', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to definition is valid, but collection is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/bob',
_to: 'xxx/charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from && _to definitions are valid, but colletions are not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'xxx/peter',
_to: 'xxx/charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'peter',
_to: 'persons/charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peter',
_to: 'charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from && _to attributes are invalid (strings)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'peter',
_to: 'charlie'
};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('empty edge definition', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {};
let req = arango.POST(url + '/' + exampleGraphName + '/edge/knows', edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
});
});
describe('edge updates (PATCH)', function () {
describe('valid positives', function () {
it('_to attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
// get a (any) valid key of an existing edge document
// and modify it's from's and to's
const edgeDef = db.knows.any();
edgeDef._from = 'persons/charlie';
edgeDef._to = 'persons/charlie';
let res = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + edgeDef._key, edgeDef);
// 202 without waitForSync (default)
expect(res.code).to.equal(202);
expect(res.error).to.equal(false);
expect(res.edge._key).to.equal(edgeDef._key);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
});
describe('false positives', function () {
it('_to attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/charlie',
_to: 'charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to vertex collection not part of graph definition', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/charlie',
_to: 'personsNot/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from vertex collection not part of graph definition', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_to: 'persons/charlie',
_from: 'personsNot/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from document is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to document is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_to: 'persons/peterNotExisting',
_from: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from && _to documents are not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlieNotExisting'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'charlie',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
});
});
describe('edge replacements (PUT)', function () {
describe('valid positives', function () {
it('should check that edges can be replaced', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const e = db.knows.any();
const newEdge = Object.assign({}, e);
newEdge.newAttribute = 'new value';
const res = arango.PUT(
`${url}/${exampleGraphName}/edge/${eName}/${e._key}`, newEdge);
// 202 without waitForSync (default)
expect(res.code).to.equal(202);
expect(res.error).to.equal(false);
expect(res.edge._key).to.equal(e._key);
expect(db.knows.document(e._key))
.to.be.an('object')
.that.has.property('newAttribute')
.which.equals('new value');
});
});
describe('false positives', function () {
it('should check that edges can NOT be replaced if their _from or _to' +
' attribute is missing', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const e = db.knows.any();
let newEdge;
let newEdges = {};
newEdge = newEdges['_from missing'] = Object.assign({new: "new"}, e);
delete newEdge._from;
newEdge = newEdges['_to missing'] = Object.assign({new: "new"}, e);
delete newEdge._to;
newEdge = newEdges['_from and _to missing'] = Object.assign({new: "new"}, e);
delete newEdge._from;
delete newEdge._to;
for (let key in newEdges) {
if (!newEdges.hasOwnProperty(key)) {
continue;
}
const description = key;
const newEdge = newEdges[key];
const res = arango.PUT(
`${url}/${exampleGraphName}/edge/${eName}/${e._key}`,
newEdge);
expect(res.code, description).to.equal(400);
expect(res.errorNum, description)
.to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
}
expect(db.knows.document(e._key))
.to.be.an('object')
.that.does.not.have.property('new');
});
it('_to attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/charlie',
_to: 'charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to vertex collection not part of graph definition', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/charlie',
_to: 'personsNot/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from vertex collection not part of graph definition', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_to: 'persons/charlie',
_from: 'personsNot/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from document is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to document is not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_to: 'persons/peterNotExisting',
_from: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from && _to documents are not available', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlieNotExisting'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_from attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'charlie',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PATCH(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(400);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
it('_to attribute is invalid (string)', () => {
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
const edgeDef = {
_from: 'persons/peterNotExisting',
_to: 'persons/charlie'
};
// get a (any) valid key of an existing edge document
const _key = db.knows.any()._key;
let req = arango.PUT(url + '/' + exampleGraphName + '/edge/knows/' + _key, edgeDef);
expect(req.code).to.equal(404);
expect(req.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
});
});
});
describe('edge deletion (DELETE)', function () {
it('should check if incident edges are deleted with a vertex', () => {
// vertices
const alice = 'alice';
const bob = 'bob';
const eve = 'eve';
expect(db._collection(eName)).to.be.null;
expect(db._collection(vName)).to.be.null;
// load graph
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
// pre-check that the expected edges are there
expect(db[eName].all().toArray().length).to.equal(5);
// delete vertex bob
const res = arango.DELETE(
`${url}/${exampleGraphName}/vertex/${vName}/${bob}`
);
// check response
// 202 without waitForSync (default)
expect(res).to.eql({
error: false,
code: 202,
removed: true
});
// check that all edges incident to bob were removed as well
expect(db[eName].all().toArray().length).to.equal(1);
// check that the remaining edge is the expected one
const eveKnowsAlice = db[eName].all().toArray()[0];
expect(eveKnowsAlice).to.have.all.keys(
['_key', '_id', '_rev', '_from', '_to', 'vertex']
);
expect(eveKnowsAlice).to.include({
_from: `${vName}/${eve}`,
_to: `${vName}/${alice}`,
vertex: eve
});
});
it('should check that non-graph incident edges are not deleted with a' +
' vertex', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
// vertices
const alice = 'alice';
const bob = 'bob';
const charlie = 'charlie';
const dave = 'dave';
const eve = 'eve';
expect(db._collection(eName)).to.be.null;
expect(db._collection(vName)).to.be.null;
// load graph
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const ngEdges = db._create(eColName);
ngEdges.insert({
_from: `${vName}/${bob}`,
_to: `${vName}/${charlie}`,
name: 'bob->charlie'
});
ngEdges.insert({
_from: `${vName}/${dave}`,
_to: `${vName}/${bob}`,
name: 'dave->bob'
});
// pre-check that the expected edges are there
expect(db[eName].all().toArray().length).to.equal(5);
// delete vertex bob
const res = arango.DELETE(
`${url}/${exampleGraphName}/vertex/${vName}/${bob}`
);
// check response
// 202 without waitForSync (default)
expect(res).to.eql({
error: false,
code: 202,
removed: true
});
// check that the edges outside of g are still there
let remainingEdges = ngEdges.all().toArray();
expect(remainingEdges.length).to.equal(2);
expect(remainingEdges.map(x => x.name))
.to.have.members(['bob->charlie', 'dave->bob']);
});
});
describe('edge to edge suite', function () {
describe('valid positives', function () {
it('create edge to another edge, then update, replace & delete', function () {
// testing gharial api for direct edge to edge linking
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
const eName_2 = 'knows_2';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
// add a new edge definition to the knows graph pointing from knows -> knows (edge to edge)
const G = require('@arangodb/general-graph');
const graph = G._graph(exampleGraphName);
graph._extendEdgeDefinitions({collection: "knows_2", from: ["knows_2"], to: ["knows_2"]});
// vertices
const alice = 'alice'; // edge from alice to bob exists
const bob = 'bob';
// ( A --> B )
// /\
// |
// ( A <-- B )
const edgeDefBToA = {
_from: vName + "/" + bob,
_to: vName + "/" + alice
};
const edgeDefAToB = {
_to: vName + "/" + bob,
_from: vName + "/" + alice
};
// create the actual edge pointing from bob to alice
let reqB = arango.POST(url + '/' + exampleGraphName + '/edge/' + eName_2, edgeDefBToA);
expect(reqB.code).to.equal(202);
let bobToAlice = reqB.edge;
// create the actual edge pointing from alice to bob
let reqA = arango.POST(url + '/' + exampleGraphName + '/edge/' + eName_2, edgeDefAToB);
expect(reqA.code).to.equal(202);
let aliceToBob = reqA.edge;
// now create a new edge between the edges from A->B and B->A
const edgeLinkDef = {
_from: aliceToBob._id,
_to: bobToAlice._id
};
let reqx = arango.POST(url + '/' + exampleGraphName + '/edge/' + eName_2, edgeLinkDef);
let newEdge = reqx.edge;
expect(reqx.code).to.equal(202);
const updateEdgeLinkDef = {
_to: aliceToBob._id,
_from: bobToAlice._id
};
// UPDATE that edge
reqx = arango.PATCH(url + '/' + exampleGraphName + '/edge/' + eName_2 + "/" + newEdge._key, updateEdgeLinkDef);
newEdge = reqx.edge;
expect(reqx.code).to.equal(202);
// REPLACE that edge
reqx = arango.PUT(url + '/' + exampleGraphName + '/edge/' + eName_2 + "/" + newEdge._key, edgeLinkDef);
newEdge = reqx.edge;
expect(reqx.code).to.equal(202);
// DELETE that edge
reqx = arango.DELETE(url + '/' + exampleGraphName + '/edge/' + eName_2 + "/" + newEdge._key, {
});
expect(reqx.code).to.equal(202);
});
});
describe('false positives', function () {
it('create edge to another edge, but not defined within edge definition', function () {
// testing gharial api for direct edge to edge linking
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
// vertices
const alice = 'alice'; // edge from alice to bob exists
const bob = 'bob';
// get the edge from alice to bob
const aliceToBob = db.knows.byExample({_from: vName + "/" + alice, _to: vName + "/" + bob}).toArray()[0];
// ( A --> B )
// /\
// |
// ( A <-- B )
const edgeDef = {
_from: vName + "/" + bob,
_to: vName + "/" + alice
};
// create the actual edge pointing from bob to alice
let req = arango.POST(url + '/' + exampleGraphName + '/edge/' + eName, edgeDef);
expect(req.code).to.equal(202);
let bobToAlice = req.edge;
// now create a new edge between the edges from A->B and B->A
const edgeLinkDef = {
_from: aliceToBob._id,
_to: bobToAlice._id
};
let reqx = arango.POST(url + '/' + exampleGraphName + '/edge/' + eName, edgeLinkDef);
expect(reqx.code).to.equal(404);
expect(reqx.errorNum).to.equal(ERRORS.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code);
expect(reqx.error).to.equal(true);
});
});
});
describe('header match verification', function () {
it('should check if the if-match header is working - positive', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const key = 'bob';
const doc = db[vName].document(key);
const revision = doc._rev; // get a valid revision
let req = arango.GET(url + '/' + exampleGraphName + '/edge/' + vName + '/' + key, {
headers: {
'if-match': revision
}
});
expect(req.code).to.equal(200);
expect(req.edge).to.deep.equal(doc);
});
it('should check if the if-match header is working - negative', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const key = 'bob';
const doc = db[vName].document(key);
let revision = doc._rev; // get a valid revision
revision = revision + 'x';
const revisions = [null, undefined, true, false, revision];
revisions.forEach(function (rev) {
let req = arango.GET(url + '/' + exampleGraphName + '/edge/' + vName + '/' + key, {
'if-match': rev
});
expect(req.error).to.equal(true);
expect(req.code).to.equal(ERRORS.ERROR_HTTP_PRECONDITION_FAILED.code);
expect(req.errorMessage).to.equal(ERRORS.ERROR_HTTP_PRECONDITION_FAILED.message);
});
});
it('should check if the if-none-match header is working - positive', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const key = 'bob';
const doc = db[vName].document(key);
const revision = doc._rev; // get a valid revision
let req = arango.GET(url + '/' + exampleGraphName + '/edge/' + vName + '/' + key, {
'if-none-match': revision
});
expect(req.code).to.equal(304);
});
it('should check if the if-none-match header is working - negative', () => {
const examples = require('@arangodb/graph-examples/example-graph');
const exampleGraphName = 'knows_graph';
const vName = 'persons';
const eName = 'knows';
expect(db._collection(eName)).to.be.null; // edgec
expect(db._collection(vName)).to.be.null; // vertexc
const g = examples.loadGraph(exampleGraphName);
expect(g).to.not.be.null;
expect(db._collection(eName)).to.not.be.null;
expect(db._collection(vName)).to.not.be.null;
const key = 'bob';
const doc = db[vName].document(key);
let revision = doc._rev; // get a valid revision
revision = revision + 'x';
const revisions = [null, undefined, true, false, revision];
revisions.forEach(function (rev) {
let req = arango.GET(url + '/' + exampleGraphName + '/edge/' + vName + '/' + key, {
'if-none-match': rev
});
expect(req.code).to.equal(200);
expect(req.edge).to.deep.equal(doc);
});
});
});
describe('_api/gharial testing cross-graph deletes', () => {
var graph = require("@arangodb/general-graph");
const cleanup = () => {
try {
graph._drop("firstGraph", true);
} catch (e) {
}
try {
graph._drop("secondGraph", true);
} catch (e) {
}
};
beforeEach(cleanup);
afterEach(cleanup);
it('should also remove an edge in a second graph if connection in first graph is removed', () => {
// G1 = X ---- e ----> Y
// G2 = A --^ // A points to e in G1
// A ---- b ----> B // A points to B in G2
// G1
var g1 = graph._create("firstGraph",
graph._edgeDefinitions(
graph._relation("firstEdge", ["firstFrom"], ["firstTo"])
)
);
var vertexFrom1 = db["firstFrom"].save({});
var vertexIDFrom1 = vertexFrom1._id;
var vertexTo1 = db["firstTo"].save({});
var vertexIDTo1 = vertexTo1._id;
var edge1 = db["firstEdge"].save(vertexIDTo1, vertexIDFrom1, {_key: "1"});
var edgeID1 = edge1._id;
// G2
var g2 = graph._create("secondGraph",
graph._edgeDefinitions(
graph._relation("secondEdge", ["secondFrom"], ["firstEdge"])
)
);
var vertexFrom2 = db["secondFrom"].save({});
var vertexIDFrom2 = vertexFrom2._id;
// create edge from G2 to G1 using HTTP API
const edgeDef = {
_from: vertexIDFrom2,
_to: edgeID1
};
// create edge pointing from g2 to g1 (edge)
let req = arango.POST(url + '/' + 'secondGraph' + '/edge/secondEdge', edgeDef);
expect(req.code).to.equal(202);
let toBeRemovedEdgeID = req.edge._id;
// now delete the target edge of g1
let req2 = arango.DELETE(url + '/' + 'firstGraph' + '/edge/' + edgeID1);
expect(req2.code).to.equal(202);
var deleted = false;
try {
let deletedEdge = db._document(toBeRemovedEdgeID);
} catch (e) {
expect(e.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
deleted = true;
}
expect(deleted).to.equal(true);
});
it('should also remove an edge in a second graph if connected vertex in first graph is removed', () => {
// G1 = X ---- e ----> Y
// G2 = A -------------^ // A points to Y in G1
// A ---- b ----> B // A points to B in G2
// G1
var g1 = graph._create("firstGraph",
graph._edgeDefinitions(
graph._relation("firstEdge", ["firstFrom"], ["firstTo"])
)
);
var vertexFrom1 = db["firstFrom"].save({});
var vertexIDFrom1 = vertexFrom1._id;
var vertexTo1 = db["firstTo"].save({});
var vertexIDTo1 = vertexTo1._id;
var edge1 = db["firstEdge"].save(vertexIDTo1, vertexIDFrom1, {_key: "1"});
var edgeID1 = edge1._id;
// G2
var g2 = graph._create("secondGraph",
graph._edgeDefinitions(
graph._relation("secondEdge", ["secondFrom"], ["firstTo"])
)
);
var vertexFrom2 = db["secondFrom"].save({});
var vertexIDFrom2 = vertexFrom2._id;
// create edge from G2 to G1 using HTTP API
const edgeDef = {
_from: vertexIDFrom2,
_to: vertexIDTo1
};
// create edge pointing from g2 to g1 (edge)
let req = arango.POST(url + '/' + 'secondGraph' + '/edge/secondEdge', edgeDef);
expect(req.code).to.equal(202);
let toBeRemovedEdgeID = req.edge._id;
// now delete the target edge of g1 (a vertex)
let req2 = arango.DELETE(url + '/' + 'firstGraph' + '/vertex/' + vertexIDTo1);
expect(req2.code).to.equal(202);
var deleted = false;
try {
let deletedEdge = db._document(toBeRemovedEdgeID);
} catch (e) {
expect(e.errorNum).to.equal(ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code);
deleted = true;
}
expect(deleted).to.equal(true);
});
it('should not create multiple orphan collection entries', () => {
var graph = require("@arangodb/general-graph");
// create an empty graph
var gName = "firstGraph";
var collection = "firstEdge";
var from = "firstTo";
var to = "firstTo";
var g1 = graph._create("firstGraph");
// create a simple edge definition
const edgeDef = {
collection: collection,
from: [from],
to: [to]
};
var createAndDropEdgeDefinition = function () {
let req = arango.POST(url + '/' + gName + '/edge', edgeDef);
expect(req.code).to.equal(202);
// now delete the created edge definition
let req2 = arango.DELETE(url + '/' + gName + '/edge/' + collection + '?dropCollections=true');
expect(req2.code).to.equal(202);
};
createAndDropEdgeDefinition();
createAndDropEdgeDefinition();
createAndDropEdgeDefinition();
g1 = graph._graph(gName);
expect(g1.__orphanCollections).to.deep.equal([from]);
expect(g1.__orphanCollections.length).to.equal(1);
graph._drop(gName, true);
});
});
});
});