blob: f73a29086bba570b1bac6f2920c18a572ec5f86e [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 { END_OF_STREAM } from '../../../lib/structure/io/binary/internals/MarkerSerializer.js';
/**
* Round-trip tests: serialize a value with the existing sync serializer,
* then deserialize it with the new async StreamReader-based deserializer.
*/
describe('Async deserialization round-trip', () => {
async function roundTrip(serializer, value) {
const buf = serializer.serialize(value);
const reader = StreamReader.fromBuffer(buf);
return ioc.anySerializer.deserialize(reader);
}
describe('primitives via AnySerializer', () => {
it('Int', async () => {
assert.equal(await roundTrip(ioc.intSerializer, 42), 42);
});
it('Int negative', async () => {
assert.equal(await roundTrip(ioc.intSerializer, -2147483648), -2147483648);
});
it('Int null', async () => {
assert.isNull(await roundTrip(ioc.intSerializer, null));
});
it('Long', async () => {
assert.equal(await roundTrip(ioc.longSerializer, 9007199254740991), 9007199254740991);
});
it('Short', async () => {
assert.equal(await roundTrip(ioc.shortSerializer, 32767), 32767);
});
it('Byte', async () => {
assert.equal(await roundTrip(ioc.byteSerializer, -128), -128);
});
it('Float', async () => {
const result = await roundTrip(ioc.floatSerializer, 3.14);
assert.closeTo(result, 3.14, 0.001);
});
it('Double', async () => {
assert.equal(await roundTrip(ioc.doubleSerializer, 3.141592653589793), 3.141592653589793);
});
it('Double NaN', async () => {
assert.isNaN(await roundTrip(ioc.doubleSerializer, NaN));
});
it('Double Infinity', async () => {
assert.equal(await roundTrip(ioc.doubleSerializer, Infinity), Infinity);
});
it('Boolean true', async () => {
assert.equal(await roundTrip(ioc.booleanSerializer, true), true);
});
it('Boolean false', async () => {
assert.equal(await roundTrip(ioc.booleanSerializer, false), false);
});
it('String', async () => {
assert.equal(await roundTrip(ioc.stringSerializer, 'hello world'), 'hello world');
});
it('String empty', async () => {
assert.equal(await roundTrip(ioc.stringSerializer, ''), '');
});
it('String null', async () => {
assert.isNull(await roundTrip(ioc.stringSerializer, null));
});
it('UUID', async () => {
const uuid = '41d2e28a-20a4-4ab0-b379-d810dede3786';
assert.equal(await roundTrip(ioc.uuidSerializer, uuid), uuid);
});
it('DateTime', async () => {
const date = new Date('2023-06-15T10:30:00.000Z');
const result = await roundTrip(ioc.dateTimeSerializer, date);
assert.equal(result.getTime(), date.getTime());
});
it('BigInteger', async () => {
assert.equal(await roundTrip(ioc.bigIntegerSerializer, 123456789012345678901234567890n), 123456789012345678901234567890n);
});
it('BigInteger negative', async () => {
assert.equal(await roundTrip(ioc.bigIntegerSerializer, -42n), -42n);
});
it('Binary', async () => {
const buf = Buffer.from([0x01, 0x02, 0x03]);
const result = await roundTrip(ioc.binarySerializer, buf);
assert.deepEqual([...result], [0x01, 0x02, 0x03]);
});
it('null (UnspecifiedNull)', async () => {
assert.isNull(await roundTrip(ioc.unspecifiedNullSerializer, null));
});
});
describe('Marker', () => {
it('deserializes EndOfStream marker', async () => {
// Marker wire format: type_code=0xFD, value_flag=0x00, value=0x00
const buf = Buffer.from([0xFD, 0x00, 0x00]);
const reader = StreamReader.fromBuffer(buf);
const result = await ioc.anySerializer.deserialize(reader);
assert.equal(result, END_OF_STREAM);
});
});
describe('Enum', () => {
it('Direction.OUT', async () => {
const { direction } = await import('../../../lib/process/traversal.js');
const result = await roundTrip(ioc.enumSerializer, direction.out);
assert.equal(result.typeName, 'Direction');
assert.equal(result.elementName, 'OUT');
});
it('T.id', async () => {
const { t } = await import('../../../lib/process/traversal.js');
const result = await roundTrip(ioc.enumSerializer, t.id);
assert.equal(result.typeName, 'T');
assert.equal(result.elementName, 'id');
});
});
describe('direct serializer.deserialize(reader)', () => {
it('IntSerializer.deserialize reads fully-qualified', async () => {
const buf = ioc.intSerializer.serialize(99);
const reader = StreamReader.fromBuffer(buf);
const result = await ioc.intSerializer.deserialize(reader);
assert.equal(result, 99);
});
it('StringSerializer.deserialize reads fully-qualified', async () => {
const buf = ioc.stringSerializer.serialize('test');
const reader = StreamReader.fromBuffer(buf);
const result = await ioc.stringSerializer.deserialize(reader);
assert.equal(result, 'test');
});
});
describe('streaming (chunked) deserialization', () => {
it('deserializes Int from 1-byte chunks', async () => {
const buf = ioc.intSerializer.serialize(42);
const chunks = [];
for (let i = 0; i < buf.length; i++) {
chunks.push(buf.subarray(i, i + 1));
}
const stream = new ReadableStream({
pull(controller) {
if (chunks.length > 0) {
controller.enqueue(new Uint8Array(chunks.shift()));
} else {
controller.close();
}
},
});
const reader = StreamReader.fromReadableStream(stream);
const result = await ioc.anySerializer.deserialize(reader);
assert.equal(result, 42);
});
it('deserializes String from 2-byte chunks', async () => {
const buf = ioc.stringSerializer.serialize('hello');
const chunks = [];
for (let i = 0; i < buf.length; i += 2) {
chunks.push(buf.subarray(i, Math.min(i + 2, buf.length)));
}
const stream = new ReadableStream({
pull(controller) {
if (chunks.length > 0) {
controller.enqueue(new Uint8Array(chunks.shift()));
} else {
controller.close();
}
},
});
const reader = StreamReader.fromReadableStream(stream);
const result = await ioc.anySerializer.deserialize(reader);
assert.equal(result, 'hello');
});
});
});
describe('Async deserialization round-trip — compound types', () => {
async function roundTrip(serializer, value) {
const buf = serializer.serialize(value);
const reader = (await import('../../../lib/structure/io/binary/internals/StreamReader.js')).default.fromBuffer(buf);
return ioc.anySerializer.deserialize(reader);
}
it('List of ints', async () => {
const result = await roundTrip(ioc.listSerializer, [1, 2, 3]);
assert.deepEqual(result, [1, 2, 3]);
});
it('List empty', async () => {
const result = await roundTrip(ioc.listSerializer, []);
assert.deepEqual(result, []);
});
it('List null', async () => {
assert.isNull(await roundTrip(ioc.listSerializer, null));
});
it('Map', async () => {
const map = new Map([['a', 1], ['b', 2]]);
const result = await roundTrip(ioc.mapSerializer, map);
assert.equal(result.get('a'), 1);
assert.equal(result.get('b'), 2);
});
it('Map empty', async () => {
const result = await roundTrip(ioc.mapSerializer, new Map());
assert.equal(result.size, 0);
});
it('Nested list of strings', async () => {
const result = await roundTrip(ioc.listSerializer, ['hello', 'world']);
assert.deepEqual(result, ['hello', 'world']);
});
it('Vertex', async () => {
const { Vertex } = await import('../../../lib/structure/graph.js');
const v = new Vertex(1, 'person', []);
const result = await roundTrip(ioc.vertexSerializer, v);
assert.equal(result.id, 1);
assert.equal(result.label, 'person');
});
it('Edge', async () => {
const { Vertex, Edge } = await import('../../../lib/structure/graph.js');
const outV = new Vertex(1, 'person', []);
const inV = new Vertex(2, 'person', []);
const e = new Edge(10, outV, 'knows', inV, []);
const result = await roundTrip(ioc.edgeSerializer, e);
assert.equal(result.id, 10);
assert.equal(result.label, 'knows');
});
it('Path', async () => {
const { Path } = await import('../../../lib/structure/graph.js');
const p = new Path([['a'], ['b']], [1, 2]);
const result = await roundTrip(ioc.pathSerializer, p);
assert.deepEqual(result.labels, [['a'], ['b']]);
assert.deepEqual(result.objects, [1, 2]);
});
it('Property', async () => {
const { Property } = await import('../../../lib/structure/graph.js');
const prop = new Property('name', 'marko');
const result = await roundTrip(ioc.propertySerializer, prop);
assert.equal(result.key, 'name');
assert.equal(result.value, 'marko');
});
});