blob: a7297cf995b6df03f44b81b9da117d8bc4244733 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import { assert } from 'chai';
import { Buffer } from 'buffer';
import ioc from '../../lib/structure/io/binary/GraphBinary.js';
import StreamReader from '../../lib/structure/io/binary/internals/StreamReader.js';
import { Graph, Vertex, Edge, VertexProperty, Property } from '../../lib/structure/graph.js';
/**
* Round-trips a value through serialize + StreamReader -> anySerializer.deserialize.
* Mirrors the pattern used in test/unit/graphbinary/async-deserialize-test.js.
*/
async function roundTripAny(value) {
const buf = ioc.anySerializer.serialize(value);
const reader = StreamReader.fromBuffer(buf);
return ioc.anySerializer.deserialize(reader);
}
describe('Graph', () => {
describe('toString()', () => {
it('renders an empty graph with zero counts', () => {
const graph = new Graph();
assert.equal(graph.toString(), 'graph[vertices:0 edges:0]');
});
it('renders the counts of vertices and edges', () => {
const graph = new Graph();
const v1 = new Vertex(1, 'person', []);
const v2 = new Vertex(2, 'person', []);
graph.vertices.set(1, v1);
graph.vertices.set(2, v2);
graph.edges.set(3, new Edge(3, v1, 'knows', v2, []));
assert.equal(graph.toString(), 'graph[vertices:2 edges:1]');
});
});
});
describe('GraphSerializer', () => {
describe('round-trip', () => {
it('preserves vertices, edges, vertex-properties, and properties', async () => {
const graph = new Graph();
const v1 = new Vertex(1, 'person', []);
const v2 = new Vertex(2, 'person', []);
graph.vertices.set(1, v1);
graph.vertices.set(2, v2);
// VertexProperty on v1 with one meta-property
const vp1 = new VertexProperty(4, 'name', 'marko', [new Property('acl', 'public')]);
v1.properties.push(vp1);
// Edge v1 -knows-> v2 with weight property
const e1 = new Edge(3, v1, 'knows', v2, [new Property('weight', 0.5)]);
graph.edges.set(3, e1);
const output = await roundTripAny(graph);
assert.instanceOf(output, Graph);
assert.equal(output.vertices.size, 2);
assert.equal(output.edges.size, 1);
const rv1 = output.vertices.get(1);
assert.instanceOf(rv1, Vertex);
assert.equal(rv1.label, 'person');
assert.equal(rv1.properties.length, 1);
const rvp1 = rv1.properties[0];
assert.instanceOf(rvp1, VertexProperty);
assert.equal(rvp1.value, 'marko');
assert.equal(rvp1.label, 'name');
assert.equal(rvp1.properties.length, 1);
assert.equal(rvp1.properties[0].key, 'acl');
assert.equal(rvp1.properties[0].value, 'public');
const rv2 = output.vertices.get(2);
assert.instanceOf(rv2, Vertex);
assert.equal(rv2.label, 'person');
const re1 = output.edges.get(3);
assert.instanceOf(re1, Edge);
assert.equal(re1.label, 'knows');
assert.equal(re1.outV.id, 1);
assert.equal(re1.inV.id, 2);
assert.equal(re1.properties.length, 1);
assert.equal(re1.properties[0].key, 'weight');
assert.equal(re1.properties[0].value, 0.5);
});
it('handles an empty graph', async () => {
const graph = new Graph();
const output = await roundTripAny(graph);
assert.instanceOf(output, Graph);
assert.equal(output.vertices.size, 0);
assert.equal(output.edges.size, 0);
});
it('handles vertices with no properties and edges with no properties', async () => {
const graph = new Graph();
const v1 = new Vertex(10, 'person', []);
const v2 = new Vertex(20, 'software', []);
graph.vertices.set(10, v1);
graph.vertices.set(20, v2);
graph.edges.set(30, new Edge(30, v1, 'created', v2, []));
const output = await roundTripAny(graph);
assert.instanceOf(output, Graph);
assert.equal(output.vertices.size, 2);
assert.equal(output.edges.size, 1);
const re = output.edges.get(30);
assert.equal(re.label, 'created');
assert.equal(re.outV.id, 10);
assert.equal(re.inV.id, 20);
// Edge points at the same Vertex instance we already deserialized into the graph
assert.strictEqual(re.outV, output.vertices.get(10));
assert.strictEqual(re.inV, output.vertices.get(20));
assert.equal(re.properties.length, 0);
});
it('handles string ids', async () => {
const graph = new Graph();
const v1 = new Vertex('a', 'person', []);
const v2 = new Vertex('b', 'person', []);
graph.vertices.set('a', v1);
graph.vertices.set('b', v2);
graph.edges.set('e1', new Edge('e1', v1, 'knows', v2, []));
const output = await roundTripAny(graph);
assert.equal(output.vertices.size, 2);
assert.equal(output.edges.size, 1);
assert.equal(output.vertices.get('a').label, 'person');
assert.equal(output.edges.get('e1').outV.id, 'a');
assert.equal(output.edges.get('e1').inV.id, 'b');
});
});
describe('null handling', () => {
it('serializes null as fully-qualified null bytes', () => {
const buf = ioc.graphSerializer.serialize(null, true);
assert.deepEqual([...buf], [ioc.DataType.GRAPH, 0x01]);
});
it('deserialize() returns null for value_flag=0x01', async () => {
const buf = Buffer.from([ioc.DataType.GRAPH, 0x01]);
const reader = StreamReader.fromBuffer(buf);
const result = await ioc.graphSerializer.deserialize(reader);
assert.isNull(result);
});
it('rejects an unexpected type_code', async () => {
const buf = Buffer.from([ioc.DataType.VERTEX, 0x00]);
const reader = StreamReader.fromBuffer(buf);
try {
await ioc.graphSerializer.deserialize(reader);
assert.fail('should have thrown');
} catch (err) {
assert.match(err.message, /unexpected \{type_code\}/);
}
});
});
describe('AnySerializer routing', () => {
it('selects GraphSerializer for a Graph instance', () => {
const graph = new Graph();
const serializer = ioc.anySerializer.getSerializerCanBeUsedFor(graph);
assert.strictEqual(serializer, ioc.graphSerializer);
});
it('serializes a Graph via anySerializer with the GRAPH type code', () => {
const graph = new Graph();
const buf = ioc.anySerializer.serialize(graph);
assert.equal(buf[0], ioc.DataType.GRAPH);
assert.equal(buf[1], 0x00);
});
});
});