diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index 55eec22e8..ccb9704e9 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -23,7 +23,7 @@ import { } from './bolt-protocol-util' // eslint-disable-next-line no-unused-vars import { Chunker } from '../channel' -import { v1 } from '../packstream' +import { structure, v1 } from '../packstream' import RequestMessage from './request-message' import { LoginObserver, @@ -33,6 +33,8 @@ import { StreamObserver } from './stream-observers' import { internal } from 'neo4j-driver-core' +import transformersFactories from './bolt-protocol-v1.transformer' +import Transformer from './transformer' const { bookmarks: { Bookmarks }, @@ -80,6 +82,14 @@ export default class BoltProtocol { this._onProtocolError = onProtocolError this._fatalError = null this._lastMessageSignature = null + this._config = { disableLosslessIntegers, useBigInt } + } + + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer } /** @@ -97,6 +107,15 @@ export default class BoltProtocol { return this._packer } + /** + * Creates a packable function out of the provided value + * @param x the value to pack + * @returns Function + */ + packable (x) { + return this._packer.packable(x, this.transformer.toStructure) + } + /** * Get the unpacker. * @return {Unpacker} the protocol's unpacker. @@ -105,6 +124,15 @@ export default class BoltProtocol { return this._unpacker } + /** + * Unpack a buffer + * @param {Buffer} buf + * @returns {any|null} The unpacked value + */ + unpack (buf) { + return this._unpacker.unpack(buf, this.transformer.fromStructure) + } + /** * Transform metadata received in SUCCESS message before it is passed to the handler. * @param {Object} metadata the received metadata. @@ -358,11 +386,9 @@ export default class BoltProtocol { } this._lastMessageSignature = message.signature + const messageStruct = new structure.Structure(message.signature, message.fields) - this.packer().packStruct( - message.signature, - message.fields.map(field => this.packer().packable(field)) - ) + this.packable(messageStruct)() this._chunker.messageBoundary() diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.transformer.js new file mode 100644 index 000000000..bc95ee555 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.transformer.js @@ -0,0 +1,187 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import { + Node, + newError, + error, + Relationship, + UnboundRelationship, + Path, + toNumber, + PathSegment +} from 'neo4j-driver-core' + +import { structure } from '../packstream' +import { TypeTransformer } from './transformer' + +const { PROTOCOL_ERROR } = error + +const NODE = 0x4e +const NODE_STRUCT_SIZE = 3 + +const RELATIONSHIP = 0x52 +const RELATIONSHIP_STRUCT_SIZE = 5 + +const UNBOUND_RELATIONSHIP = 0x72 +const UNBOUND_RELATIONSHIP_STRUCT_SIZE = 3 + +const PATH = 0x50 +const PATH_STRUCT_SIZE = 3 + +/** + * Creates the Node Transformer + * @returns {TypeTransformer} + */ +function createNodeTransformer () { + return new TypeTransformer({ + signature: NODE, + isTypeInstance: object => object instanceof Node, + toStructure: object => { + throw newError( + `It is not allowed to pass nodes in query parameters, given: ${object}`, + PROTOCOL_ERROR + ) + }, + fromStructure: struct => { + structure.verifyStructSize('Node', NODE_STRUCT_SIZE, struct.size) + + const [identity, labels, properties] = struct.fields + + return new Node(identity, labels, properties) + } + }) +} + +/** + * Creates the Relationship Transformer + * @returns {TypeTransformer} + */ +function createRelationshipTransformer () { + return new TypeTransformer({ + signature: RELATIONSHIP, + isTypeInstance: object => object instanceof Relationship, + toStructure: object => { + throw newError( + `It is not allowed to pass relationships in query parameters, given: ${object}`, + PROTOCOL_ERROR + ) + }, + fromStructure: struct => { + structure.verifyStructSize('Relationship', RELATIONSHIP_STRUCT_SIZE, struct.size) + + const [identity, startNodeIdentity, endNodeIdentity, type, properties] = struct.fields + + return new Relationship(identity, startNodeIdentity, endNodeIdentity, type, properties) + } + }) +} + +/** + * Creates the Unbound Relationship Transformer + * @returns {TypeTransformer} + */ +function createUnboundRelationshipTransformer () { + return new TypeTransformer({ + signature: UNBOUND_RELATIONSHIP, + isTypeInstance: object => object instanceof UnboundRelationship, + toStructure: object => { + throw newError( + `It is not allowed to pass unbound relationships in query parameters, given: ${object}`, + PROTOCOL_ERROR + ) + }, + fromStructure: struct => { + structure.verifyStructSize( + 'UnboundRelationship', + UNBOUND_RELATIONSHIP_STRUCT_SIZE, + struct.size + ) + + const [identity, type, properties] = struct.fields + + return new UnboundRelationship(identity, type, properties) + } + }) +} + +/** + * Creates the Path Transformer + * @returns {TypeTransformer} + */ +function createPathTransformer () { + return new TypeTransformer({ + signature: PATH, + isTypeInstance: object => object instanceof Path, + toStructure: object => { + throw newError( + `It is not allowed to pass paths in query parameters, given: ${object}`, + PROTOCOL_ERROR + ) + }, + fromStructure: struct => { + structure.verifyStructSize('Path', PATH_STRUCT_SIZE, struct.size) + + const [nodes, rels, sequence] = struct.fields + + const segments = [] + let prevNode = nodes[0] + + for (let i = 0; i < sequence.length; i += 2) { + const nextNode = nodes[sequence[i + 1]] + const relIndex = toNumber(sequence[i]) + let rel + + if (relIndex > 0) { + rel = rels[relIndex - 1] + if (rel instanceof UnboundRelationship) { + // To avoid duplication, relationships in a path do not contain + // information about their start and end nodes, that's instead + // inferred from the path sequence. This is us inferring (and, + // for performance reasons remembering) the start/end of a rel. + rels[relIndex - 1] = rel = rel.bindTo( + prevNode, + nextNode + ) + } + } else { + rel = rels[-relIndex - 1] + if (rel instanceof UnboundRelationship) { + // See above + rels[-relIndex - 1] = rel = rel.bindTo( + nextNode, + prevNode + ) + } + } + // Done hydrating one path segment. + segments.push(new PathSegment(prevNode, rel, nextNode)) + prevNode = nextNode + } + return new Path(nodes[0], nodes[nodes.length - 1], segments) + } + }) +} + +export default { + createNodeTransformer, + createRelationshipTransformer, + createUnboundRelationshipTransformer, + createPathTransformer +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v2.js index 13d007d87..4abec0941 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v2.js @@ -19,6 +19,8 @@ import BoltProtocolV1 from './bolt-protocol-v1' import v2 from '../packstream' import { internal } from 'neo4j-driver-core' +import transformersFactories from './bolt-protocol-v2.transformer' +import Transformer from './transformer' const { constants: { BOLT_PROTOCOL_V2 } @@ -33,6 +35,13 @@ export default class BoltProtocol extends BoltProtocolV1 { return new v2.Unpacker(disableLosslessIntegers, useBigInt) } + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } + get version () { return BOLT_PROTOCOL_V2 } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v2.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v2.transformer.js new file mode 100644 index 000000000..d598925f0 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v2.transformer.js @@ -0,0 +1,432 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import { + isPoint, + int, + isDuration, + Duration, + isLocalDateTime, + isLocalTime, + internal, + isTime, + Time, + isDate, + isDateTime, + DateTime, + Point, + isInt +} from 'neo4j-driver-core' + +import { structure } from '../packstream' +import { TypeTransformer } from './transformer' + +import { + epochDayToDate, + nanoOfDayToLocalTime, + epochSecondAndNanoToLocalDateTime +} from './temporal-factory' + +import v1 from './bolt-protocol-v1.transformer' + +const { + temporalUtil: { + dateToEpochDay, + localDateTimeToEpochSecond, + localTimeToNanoOfDay + } +} = internal + +const POINT_2D = 0x58 +const POINT_2D_STRUCT_SIZE = 3 + +const POINT_3D = 0x59 +const POINT_3D_STRUCT_SIZE = 4 + +const DURATION = 0x45 +const DURATION_STRUCT_SIZE = 4 + +const LOCAL_TIME = 0x74 +const LOCAL_TIME_STRUCT_SIZE = 1 + +const TIME = 0x54 +const TIME_STRUCT_SIZE = 2 + +const DATE = 0x44 +const DATE_STRUCT_SIZE = 1 + +const LOCAL_DATE_TIME = 0x64 +const LOCAL_DATE_TIME_STRUCT_SIZE = 2 + +const DATE_TIME_WITH_ZONE_OFFSET = 0x46 +const DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE = 3 + +const DATE_TIME_WITH_ZONE_ID = 0x66 +const DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE = 3 + +/** + * Creates the Point2D Transformer + * @returns {TypeTransformer} + */ +function createPoint2DTransformer () { + return new TypeTransformer({ + signature: POINT_2D, + isTypeInstance: point => isPoint(point) && (point.z === null || point.z === undefined), + toStructure: point => new structure.Structure(POINT_2D, [ + int(point.srid), + point.x, + point.y + ]), + fromStructure: struct => { + structure.verifyStructSize('Point2D', POINT_2D_STRUCT_SIZE, struct.size) + + const [srid, x, y] = struct.fields + return new Point( + srid, + x, + y, + undefined // z + ) + } + }) +} + +/** + * Creates the Point3D Transformer + * @returns {TypeTransformer} + */ +function createPoint3DTransformer () { + return new TypeTransformer({ + signature: POINT_3D, + isTypeInstance: point => isPoint(point) && point.z !== null && point.z !== undefined, + toStructure: point => new structure.Structure(POINT_3D, [ + int(point.srid), + point.x, + point.y, + point.z + ]), + fromStructure: struct => { + structure.verifyStructSize('Point3D', POINT_3D_STRUCT_SIZE, struct.size) + + const [srid, x, y, z] = struct.fields + return new Point( + srid, + x, + y, + z + ) + } + }) +} + +/** + * Creates the Duration Transformer + * @returns {TypeTransformer} + */ +function createDurationTransformer () { + return new TypeTransformer({ + signature: DURATION, + isTypeInstance: isDuration, + toStructure: value => { + const months = int(value.months) + const days = int(value.days) + const seconds = int(value.seconds) + const nanoseconds = int(value.nanoseconds) + + return new structure.Structure(DURATION, [months, days, seconds, nanoseconds]) + }, + fromStructure: struct => { + structure.verifyStructSize('Duration', DURATION_STRUCT_SIZE, struct.size) + + const [months, days, seconds, nanoseconds] = struct.fields + + return new Duration(months, days, seconds, nanoseconds) + } + }) +} + +/** + * Creates the LocalTime Transformer + * @param {Object} param + * @param {boolean} param.disableLosslessIntegers Disables lossless integers + * @param {boolean} param.useBigInt Uses BigInt instead of number or Integer + * @returns {TypeTransformer} + */ +function createLocalTimeTransformer ({ disableLosslessIntegers, useBigInt }) { + return new TypeTransformer({ + signature: LOCAL_TIME, + isTypeInstance: isLocalTime, + toStructure: value => { + const nanoOfDay = localTimeToNanoOfDay( + value.hour, + value.minute, + value.second, + value.nanosecond + ) + + return new structure.Structure(LOCAL_TIME, [nanoOfDay]) + }, + fromStructure: struct => { + structure.verifyStructSize('LocalTime', LOCAL_TIME_STRUCT_SIZE, struct.size) + + const [nanoOfDay] = struct.fields + const result = nanoOfDayToLocalTime(nanoOfDay) + return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) + } + }) +} + +/** + * Creates the Time Transformer + * @param {Object} param + * @param {boolean} param.disableLosslessIntegers Disables lossless integers + * @param {boolean} param.useBigInt Uses BigInt instead of number or Integer + * @returns {TypeTransformer} + */ +function createTimeTransformer ({ disableLosslessIntegers, useBigInt }) { + return new TypeTransformer({ + signature: TIME, + isTypeInstance: isTime, + toStructure: value => { + const nanoOfDay = localTimeToNanoOfDay( + value.hour, + value.minute, + value.second, + value.nanosecond + ) + const offsetSeconds = int(value.timeZoneOffsetSeconds) + + return new structure.Structure(TIME, [nanoOfDay, offsetSeconds]) + }, + fromStructure: struct => { + structure.verifyStructSize('Time', TIME_STRUCT_SIZE, struct.size) + + const [nanoOfDay, offsetSeconds] = struct.fields + const localTime = nanoOfDayToLocalTime(nanoOfDay) + const result = new Time( + localTime.hour, + localTime.minute, + localTime.second, + localTime.nanosecond, + offsetSeconds + ) + return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) + } + }) +} + +/** + * Creates the Date Transformer + * @param {Object} param + * @param {boolean} param.disableLosslessIntegers Disables lossless integers + * @param {boolean} param.useBigInt Uses BigInt instead of number or Integer + * @returns {TypeTransformer} + */ +function createDateTransformer ({ disableLosslessIntegers, useBigInt }) { + return new TypeTransformer({ + signature: DATE, + isTypeInstance: isDate, + toStructure: value => { + const epochDay = dateToEpochDay(value.year, value.month, value.day) + + return new structure.Structure(DATE, [epochDay]) + }, + fromStructure: struct => { + structure.verifyStructSize('Date', DATE_STRUCT_SIZE, struct.size) + + const [epochDay] = struct.fields + const result = epochDayToDate(epochDay) + return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) + } + }) +} + +/** + * Creates the LocalDateTime Transformer + * @param {Object} param + * @param {boolean} param.disableLosslessIntegers Disables lossless integers + * @param {boolean} param.useBigInt Uses BigInt instead of number or Integer + * @returns {TypeTransformer} + */ +function createLocalDateTimeTransformer ({ disableLosslessIntegers, useBigInt }) { + return new TypeTransformer({ + signature: LOCAL_DATE_TIME, + isTypeInstance: isLocalDateTime, + toStructure: value => { + const epochSecond = localDateTimeToEpochSecond( + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.nanosecond + ) + const nano = int(value.nanosecond) + + return new structure.Structure(LOCAL_DATE_TIME, [epochSecond, nano]) + }, + fromStructure: struct => { + structure.verifyStructSize( + 'LocalDateTime', + LOCAL_DATE_TIME_STRUCT_SIZE, + struct.size + ) + + const [epochSecond, nano] = struct.fields + const result = epochSecondAndNanoToLocalDateTime(epochSecond, nano) + return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) + } + }) +} + +/** + * Creates the DateTime with ZoneId Transformer + * @param {Object} param + * @param {boolean} param.disableLosslessIntegers Disables lossless integers + * @param {boolean} param.useBigInt Uses BigInt instead of number or Integer + * @returns {TypeTransformer} + */ +function createDateTimeWithZoneIdTransformer ({ disableLosslessIntegers, useBigInt }) { + return new TypeTransformer({ + signature: DATE_TIME_WITH_ZONE_ID, + isTypeInstance: object => isDateTime(object) && object.timeZoneId != null, + toStructure: value => { + const epochSecond = localDateTimeToEpochSecond( + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.nanosecond + ) + const nano = int(value.nanosecond) + const timeZoneId = value.timeZoneId + + return new structure.Structure(DATE_TIME_WITH_ZONE_ID, [epochSecond, nano, timeZoneId]) + }, + fromStructure: struct => { + structure.verifyStructSize( + 'DateTimeWithZoneId', + DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE, + struct.size + ) + + const [epochSecond, nano, timeZoneId] = struct.fields + + const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano) + const result = new DateTime( + localDateTime.year, + localDateTime.month, + localDateTime.day, + localDateTime.hour, + localDateTime.minute, + localDateTime.second, + localDateTime.nanosecond, + null, + timeZoneId + ) + return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) + } + }) +} + +/** + * Creates the DateTime with Offset Transformer + * @param {Object} param + * @param {boolean} param.disableLosslessIntegers Disables lossless integers + * @param {boolean} param.useBigInt Uses BigInt instead of number or Integer + * @returns {TypeTransformer} + */ +function createDateTimeWithOffsetTransformer ({ disableLosslessIntegers, useBigInt }) { + return new TypeTransformer({ + signature: DATE_TIME_WITH_ZONE_OFFSET, + isTypeInstance: object => isDateTime(object) && object.timeZoneId == null, + toStructure: value => { + const epochSecond = localDateTimeToEpochSecond( + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.nanosecond + ) + const nano = int(value.nanosecond) + const timeZoneOffsetSeconds = int(value.timeZoneOffsetSeconds) + return new structure.Structure(DATE_TIME_WITH_ZONE_OFFSET, [epochSecond, nano, timeZoneOffsetSeconds]) + }, + fromStructure: struct => { + structure.verifyStructSize( + 'DateTimeWithZoneOffset', + DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE, + struct.size + ) + + const [epochSecond, nano, timeZoneOffsetSeconds] = struct.fields + + const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano) + const result = new DateTime( + localDateTime.year, + localDateTime.month, + localDateTime.day, + localDateTime.hour, + localDateTime.minute, + localDateTime.second, + localDateTime.nanosecond, + timeZoneOffsetSeconds, + null + ) + return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) + } + }) +} + +function convertIntegerPropsIfNeeded (obj, disableLosslessIntegers, useBigInt) { + if (!disableLosslessIntegers && !useBigInt) { + return obj + } + + const convert = value => + useBigInt ? value.toBigInt() : value.toNumberOrInfinity() + + const clone = Object.create(Object.getPrototypeOf(obj)) + for (const prop in obj) { + if (Object.prototype.hasOwnProperty.call(obj, prop) === true) { + const value = obj[prop] + clone[prop] = isInt(value) ? convert(value) : value + } + } + Object.freeze(clone) + return clone +} + +export default { + ...v1, + createPoint2DTransformer, + createPoint3DTransformer, + createDurationTransformer, + createLocalTimeTransformer, + createTimeTransformer, + createDateTransformer, + createLocalDateTimeTransformer, + createDateTimeWithZoneIdTransformer, + createDateTimeWithOffsetTransformer +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js index a4fbe3d66..32f0d664b 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js @@ -25,6 +25,8 @@ import { ResultStreamObserver, ProcedureRouteObserver } from './stream-observers' +import transformersFactories from './bolt-protocol-v3.transformer' +import Transformer from './transformer' import { internal } from 'neo4j-driver-core' const { @@ -44,6 +46,13 @@ export default class BoltProtocol extends BoltProtocolV2 { return BOLT_PROTOCOL_V3 } + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } + transformMetadata (metadata) { if ('t_first' in metadata) { // Bolt V3 uses shorter key 't_first' to represent 'result_available_after' diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v3.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v3.transformer.js new file mode 100644 index 000000000..979bde7ed --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.transformer.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import v2 from './bolt-protocol-v2.transformer' + +export default { + ...v2 +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js index 834faf415..102034d51 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js @@ -23,6 +23,8 @@ import { ResultStreamObserver, ProcedureRouteObserver } from './stream-observers' +import transformersFactories from './bolt-protocol-v4x0.transformer' +import Transformer from './transformer' import { internal } from 'neo4j-driver-core' @@ -42,6 +44,13 @@ export default class BoltProtocol extends BoltProtocolV3 { return BOLT_PROTOCOL_V4_0 } + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } + beginTransaction ({ bookmarks, txConfig, diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.transformer.js new file mode 100644 index 000000000..d78652665 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.transformer.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import v3 from './bolt-protocol-v3.transformer' + +export default { + ...v3 +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js index 55ad99c8f..7ae475765 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js @@ -21,6 +21,9 @@ import RequestMessage from './request-message' import { LoginObserver } from './stream-observers' import { internal } from 'neo4j-driver-core' +import transformersFactories from './bolt-protocol-v4x1.transformer' +import Transformer from './transformer' + const { constants: { BOLT_PROTOCOL_V4_1 } } = internal @@ -62,6 +65,13 @@ export default class BoltProtocol extends BoltProtocolV4 { return BOLT_PROTOCOL_V4_1 } + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } + initialize ({ userAgent, authToken, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.transformer.js new file mode 100644 index 000000000..fe770bc40 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.transformer.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import v4x0 from './bolt-protocol-v4x0.transformer' + +export default { + ...v4x0 +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js index 3f1fbd70b..07378b53a 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.js @@ -20,6 +20,9 @@ import BoltProtocolV41 from './bolt-protocol-v4x1' import { internal } from 'neo4j-driver-core' +import transformersFactories from './bolt-protocol-v4x2.transformer' +import Transformer from './transformer' + const { constants: { BOLT_PROTOCOL_V4_2 } } = internal @@ -28,4 +31,11 @@ export default class BoltProtocol extends BoltProtocolV41 { get version () { return BOLT_PROTOCOL_V4_2 } + + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.transformer.js new file mode 100644 index 000000000..620807597 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x2.transformer.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import v4x1 from './bolt-protocol-v4x1.transformer' + +export default { + ...v4x1 +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js index 379a69249..0897c6826 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js @@ -20,6 +20,9 @@ import BoltProtocolV42 from './bolt-protocol-v4x2' import RequestMessage from './request-message' import { RouteObserver } from './stream-observers' +import transformersFactories from './bolt-protocol-v4x3.transformer' +import Transformer from './transformer' + import { internal } from 'neo4j-driver-core' const { @@ -32,6 +35,13 @@ export default class BoltProtocol extends BoltProtocolV42 { return BOLT_PROTOCOL_V4_3 } + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } + /** * Request routing information * diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.transformer.js new file mode 100644 index 000000000..e65b50cf8 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.transformer.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import v4x2 from './bolt-protocol-v4x2.transformer' + +export default { + ...v4x2 +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js index 634edbf82..6c6e6af6d 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js @@ -22,6 +22,9 @@ import { internal } from 'neo4j-driver-core' import RequestMessage from './request-message' import { RouteObserver, ResultStreamObserver } from './stream-observers' +import transformersFactories from './bolt-protocol-v4x4.transformer' +import Transformer from './transformer' + const { constants: { BOLT_PROTOCOL_V4_4, FETCH_ALL }, bookmarks: { Bookmarks } @@ -32,6 +35,13 @@ export default class BoltProtocol extends BoltProtocolV43 { return BOLT_PROTOCOL_V4_4 } + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer + } + /** * Request routing information * diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.transformer.js new file mode 100644 index 000000000..09bb8de90 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.transformer.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import v4x3 from './bolt-protocol-v4x3.transformer' + +export default { + ...v4x3 +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js index c4e3e4d93..f60c43d89 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js @@ -17,7 +17,9 @@ * limitations under the License. */ import BoltProtocolV44 from './bolt-protocol-v4x4' -import { v5 } from '../packstream' + +import transformersFactories from './bolt-protocol-v5x0.transformer' +import Transformer from './transformer' import { internal } from 'neo4j-driver-core' @@ -30,11 +32,10 @@ export default class BoltProtocol extends BoltProtocolV44 { return BOLT_PROTOCOL_V5_0 } - _createPacker (chunker) { - return new v5.Packer(chunker) - } - - _createUnpacker (disableLosslessIntegers, useBigInt) { - return new v5.Unpacker(disableLosslessIntegers, useBigInt) + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config))) + } + return this._transformer } } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.transformer.js new file mode 100644 index 000000000..148958c08 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.transformer.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import { structure } from '../packstream' +import { Node, Relationship, UnboundRelationship } from 'neo4j-driver-core' +import v4x4 from './bolt-protocol-v4x4.transformer' + +const NODE_STRUCT_SIZE = 4 +const RELATIONSHIP_STRUCT_SIZE = 8 +const UNBOUND_RELATIONSHIP_STRUCT_SIZE = 4 + +/** + * Create an extend Node transformer with support to elementId + * @param {any} config + * @returns {TypeTransformer} + */ +function createNodeTransformer (config) { + const node4x4Transformer = v4x4.createNodeTransformer(config) + return node4x4Transformer.extendsWith({ + fromStructure: struct => { + structure.verifyStructSize('Node', NODE_STRUCT_SIZE, struct.size) + + const [identity, lables, properties, elementId] = struct.fields + + return new Node( + identity, + lables, + properties, + elementId + ) + } + }) +} + +/** + * Create an extend Relationship transformer with support to elementId + * @param {any} config + * @returns {TypeTransformer} + */ +function createRelationshipTransformer (config) { + const relationship4x4Transformer = v4x4.createRelationshipTransformer(config) + return relationship4x4Transformer.extendsWith({ + fromStructure: struct => { + structure.verifyStructSize('Relationship', RELATIONSHIP_STRUCT_SIZE, struct.size) + + const [ + identity, + startNodeIdentity, + endNodeIdentity, + type, + properties, + elementId, + startNodeElementId, + endNodeElementId + ] = struct.fields + + return new Relationship( + identity, + startNodeIdentity, + endNodeIdentity, + type, + properties, + elementId, + startNodeElementId, + endNodeElementId + ) + } + }) +} + +/** + * Create an extend Unbound Relationship transformer with support to elementId + * @param {any} config + * @returns {TypeTransformer} + */ +function createUnboundRelationshipTransformer (config) { + const unboundRelationshipTransformer = v4x4.createUnboundRelationshipTransformer(config) + return unboundRelationshipTransformer.extendsWith({ + fromStructure: struct => { + structure.verifyStructSize( + 'UnboundRelationship', + UNBOUND_RELATIONSHIP_STRUCT_SIZE, + struct.size + ) + + const [ + identity, + type, + properties, + elementId + ] = struct.fields + + return new UnboundRelationship( + identity, + type, + properties, + elementId + ) + } + }) +} + +export default { + ...v4x4, + createNodeTransformer, + createRelationshipTransformer, + createUnboundRelationshipTransformer +} diff --git a/packages/bolt-connection/src/bolt/create.js b/packages/bolt-connection/src/bolt/create.js index c1e53d006..a3a323cd9 100644 --- a/packages/bolt-connection/src/bolt/create.js +++ b/packages/bolt-connection/src/bolt/create.js @@ -72,7 +72,7 @@ export default function create ({ // setup dechunker to dechunk messages and forward them to the message handler dechunker.onmessage = buf => { - responseHandler.handleResponse(protocol.unpacker().unpack(buf)) + responseHandler.handleResponse(protocol.unpack(buf)) } return responseHandler diff --git a/packages/bolt-connection/src/packstream/temporal-factory.js b/packages/bolt-connection/src/bolt/temporal-factory.js similarity index 100% rename from packages/bolt-connection/src/packstream/temporal-factory.js rename to packages/bolt-connection/src/bolt/temporal-factory.js diff --git a/packages/bolt-connection/src/bolt/transformer.js b/packages/bolt-connection/src/bolt/transformer.js new file mode 100644 index 000000000..485c157dd --- /dev/null +++ b/packages/bolt-connection/src/bolt/transformer.js @@ -0,0 +1,123 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import { structure } from '../packstream' + +/** + * Class responsible for applying the expected {@link TypeTransformer} to + * transform the driver types from and to {@link struct.Structure} + */ +export default class Transformer { + /** + * Constructor + * @param {TypeTransformer[]} transformers The type transformers + */ + constructor (transformers) { + this._transformers = transformers + this._transformersPerSignature = new Map(transformers.map(typeTransformer => [typeTransformer.signature, typeTransformer])) + this.fromStructure = this.fromStructure.bind(this) + this.toStructure = this.toStructure.bind(this) + Object.freeze(this) + } + + /** + * Transform from structure to specific object + * + * @param {struct.Structure} struct The structure + * @returns {|structure.Structure} The driver object or the structure if the transformer was not found. + */ + fromStructure (struct) { + if (struct instanceof structure.Structure && this._transformersPerSignature.has(struct.signature)) { + const { fromStructure } = this._transformersPerSignature.get(struct.signature) + return fromStructure(struct) + } + return struct + } + + /** + * Transform from object to structure + * @param {} type The object to be transoformed in structure + * @returns {|structure.Structure} The structure or the object, if any transformer was found + */ + toStructure (type) { + const transformer = this._transformers.find(({ isTypeInstance }) => isTypeInstance(type)) + if (transformer !== undefined) { + return transformer.toStructure(type) + } + return type + } +} + +/** + * @callback isTypeInstanceFunction + * @param {any} object The object + * @return {boolean} is instance of + */ + +/** + * @callback toStructureFunction + * @param {any} object The object + * @return {structure.Structure} The structure + */ + +/** + * @callback fromStructureFunction + * @param {structure.Structure} struct The structure + * @return {any} The object + */ + +/** + * Class responsible for grouping the properties of a TypeTransformer + */ +export class TypeTransformer { + /** + * @param {Object} param + * @param {number} param.signature The signature of the structure + * @param {isTypeInstanceFunction} param.isTypeInstance The function which checks if object is + * instance of the type described by the TypeTransformer + * @param {toStructureFunction} param.toStructure The function which gets the object and converts to structure + * @param {fromStructureFunction} param.fromStructure The function which get the structure and covnverts to object + */ + constructor ({ signature, fromStructure, toStructure, isTypeInstance }) { + this.signature = signature + this.isTypeInstance = isTypeInstance + this.fromStructure = fromStructure + this.toStructure = toStructure + + Object.freeze(this) + } + + /** + * @param {Object} param + * @param {number} [param.signature] The signature of the structure + * @param {isTypeInstanceFunction} [param.isTypeInstance] The function which checks if object is + * instance of the type described by the TypeTransformer + * @param {toStructureFunction} [param.toStructure] The function which gets the object and converts to structure + * @param {fromStructureFunction} pparam.fromStructure] The function which get the structure and covnverts to object + * @returns {TypeTransformer} A new type transform extends with new methods + */ + extendsWith ({ signature, fromStructure, toStructure, isTypeInstance }) { + return new TypeTransformer({ + signature: signature || this.signature, + fromStructure: fromStructure || this.fromStructure, + toStructure: toStructure || this.toStructure, + isTypeInstance: isTypeInstance || this.isTypeInstance + }) + } +} diff --git a/packages/bolt-connection/src/lang/functional.js b/packages/bolt-connection/src/lang/functional.js new file mode 100644 index 000000000..24aa87184 --- /dev/null +++ b/packages/bolt-connection/src/lang/functional.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +/** + * Identity function. + * + * Identity functions are function which returns the input as output. + * + * @param {any} x + * @returns {any} the x + */ +export function identity (x) { + return x +} diff --git a/packages/bolt-connection/src/lang/index.js b/packages/bolt-connection/src/lang/index.js new file mode 100644 index 000000000..9d565fe8f --- /dev/null +++ b/packages/bolt-connection/src/lang/index.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +export * as functional from './functional' diff --git a/packages/bolt-connection/src/packstream/index.js b/packages/bolt-connection/src/packstream/index.js index 564c32001..d1ef5caf4 100644 --- a/packages/bolt-connection/src/packstream/index.js +++ b/packages/bolt-connection/src/packstream/index.js @@ -19,8 +19,8 @@ import * as v1 from './packstream-v1' import * as v2 from './packstream-v2' -import * as v5 from './packstream-v5' +import * as structure from './structure' -export { v1, v2, v5 } +export { v1, v2, structure } export default v2 diff --git a/packages/bolt-connection/src/packstream/packstream-v1.js b/packages/bolt-connection/src/packstream/packstream-v1.js index 1b77f096a..b71ef43f0 100644 --- a/packages/bolt-connection/src/packstream/packstream-v1.js +++ b/packages/bolt-connection/src/packstream/packstream-v1.js @@ -17,18 +17,14 @@ * limitations under the License. */ import { utf8 } from '../channel' +import { functional } from '../lang' +import { Structure } from './structure' import { newError, error, int, isInt, - Integer, - toNumber, - Node, - Path, - PathSegment, - Relationship, - UnboundRelationship + Integer } from 'neo4j-driver-core' const { PROTOCOL_ERROR } = error @@ -60,43 +56,6 @@ const MAP_32 = 0xda const STRUCT_8 = 0xdc const STRUCT_16 = 0xdd -const NODE = 0x4e -const NODE_STRUCT_SIZE = 3 - -const RELATIONSHIP = 0x52 -const RELATIONSHIP_STRUCT_SIZE = 5 - -const UNBOUND_RELATIONSHIP = 0x72 -const UNBOUND_RELATIONSHIP_STRUCT_SIZE = 3 - -const PATH = 0x50 -const PATH_STRUCT_SIZE = 3 - -/** - * A Structure have a signature and fields. - * @access private - */ -class Structure { - /** - * Create new instance - */ - constructor (signature, fields) { - this.signature = signature - this.fields = fields - } - - toString () { - let fieldStr = '' - for (let i = 0; i < this.fields.length; i++) { - if (i > 0) { - fieldStr += ', ' - } - fieldStr += this.fields[i] - } - return 'Structure(' + this.signature + ', [' + fieldStr + '])' - } -} - /** * Class to pack * @access private @@ -116,7 +75,13 @@ class Packer { * @param x the value to pack * @returns Function */ - packable (x) { + packable (x, dehydrateStruct = functional.identity) { + try { + x = dehydrateStruct(x) + } catch (ex) { + return () => { throw ex } + } + if (x === null) { return () => this._ch.writeUInt8(NULL) } else if (x === true) { @@ -137,27 +102,15 @@ class Packer { return () => { this.packListHeader(x.length) for (let i = 0; i < x.length; i++) { - this.packable(x[i] === undefined ? null : x[i])() + this.packable(x[i] === undefined ? null : x[i], dehydrateStruct)() } } } else if (isIterable(x)) { - return this.packableIterable(x) - } else if (x instanceof Node) { - return this._nonPackableValue( - `It is not allowed to pass nodes in query parameters, given: ${x}` - ) - } else if (x instanceof Relationship) { - return this._nonPackableValue( - `It is not allowed to pass relationships in query parameters, given: ${x}` - ) - } else if (x instanceof Path) { - return this._nonPackableValue( - `It is not allowed to pass paths in query parameters, given: ${x}` - ) + return this.packableIterable(x, dehydrateStruct) } else if (x instanceof Structure) { const packableFields = [] for (let i = 0; i < x.fields.length; i++) { - packableFields[i] = this.packable(x.fields[i]) + packableFields[i] = this.packable(x.fields[i], dehydrateStruct) } return () => this.packStruct(x.signature, packableFields) } else if (typeof x === 'object') { @@ -175,7 +128,7 @@ class Packer { const key = keys[i] if (x[key] !== undefined) { this.packString(key) - this.packable(x[key])() + this.packable(x[key], dehydrateStruct)() } } } @@ -184,10 +137,10 @@ class Packer { } } - packableIterable (iterable) { + packableIterable (iterable, dehydrateStruct) { try { const array = Array.from(iterable) - return this.packable(array) + return this.packable(array, dehydrateStruct) } catch (e) { // handle errors from iterable to array conversion throw newError(`Cannot pack given iterable, ${e.message}: ${iterable}`) @@ -378,7 +331,7 @@ class Unpacker { this._useBigInt = useBigInt } - unpack (buffer) { + unpack (buffer, hydrateStructure = functional.identity) { const marker = buffer.readUInt8() const markerHigh = marker & 0xf0 const markerLow = marker & 0x0f @@ -409,7 +362,7 @@ class Unpacker { return string } - const list = this._unpackList(marker, markerHigh, markerLow, buffer) + const list = this._unpackList(marker, markerHigh, markerLow, buffer, hydrateStructure) if (list !== null) { return list } @@ -419,12 +372,12 @@ class Unpacker { return byteArray } - const map = this._unpackMap(marker, markerHigh, markerLow, buffer) + const map = this._unpackMap(marker, markerHigh, markerLow, buffer, hydrateStructure) if (map !== null) { return map } - const struct = this._unpackStruct(marker, markerHigh, markerLow, buffer) + const struct = this._unpackStruct(marker, markerHigh, markerLow, buffer, hydrateStructure) if (struct !== null) { return struct } @@ -496,24 +449,24 @@ class Unpacker { } } - _unpackList (marker, markerHigh, markerLow, buffer) { + _unpackList (marker, markerHigh, markerLow, buffer, hydrateStructure) { if (markerHigh === TINY_LIST) { - return this._unpackListWithSize(markerLow, buffer) + return this._unpackListWithSize(markerLow, buffer, hydrateStructure) } else if (marker === LIST_8) { - return this._unpackListWithSize(buffer.readUInt8(), buffer) + return this._unpackListWithSize(buffer.readUInt8(), buffer, hydrateStructure) } else if (marker === LIST_16) { - return this._unpackListWithSize(buffer.readUInt16(), buffer) + return this._unpackListWithSize(buffer.readUInt16(), buffer, hydrateStructure) } else if (marker === LIST_32) { - return this._unpackListWithSize(buffer.readUInt32(), buffer) + return this._unpackListWithSize(buffer.readUInt32(), buffer, hydrateStructure) } else { return null } } - _unpackListWithSize (size, buffer) { + _unpackListWithSize (size, buffer, hydrateStructure) { const value = [] for (let i = 0; i < size; i++) { - value.push(this.unpack(buffer)) + value.push(this.unpack(buffer, hydrateStructure)) } return value } @@ -538,151 +491,49 @@ class Unpacker { return value } - _unpackMap (marker, markerHigh, markerLow, buffer) { + _unpackMap (marker, markerHigh, markerLow, buffer, hydrateStructure) { if (markerHigh === TINY_MAP) { - return this._unpackMapWithSize(markerLow, buffer) + return this._unpackMapWithSize(markerLow, buffer, hydrateStructure) } else if (marker === MAP_8) { - return this._unpackMapWithSize(buffer.readUInt8(), buffer) + return this._unpackMapWithSize(buffer.readUInt8(), buffer, hydrateStructure) } else if (marker === MAP_16) { - return this._unpackMapWithSize(buffer.readUInt16(), buffer) + return this._unpackMapWithSize(buffer.readUInt16(), buffer, hydrateStructure) } else if (marker === MAP_32) { - return this._unpackMapWithSize(buffer.readUInt32(), buffer) + return this._unpackMapWithSize(buffer.readUInt32(), buffer, hydrateStructure) } else { return null } } - _unpackMapWithSize (size, buffer) { + _unpackMapWithSize (size, buffer, hydrateStructure) { const value = {} for (let i = 0; i < size; i++) { - const key = this.unpack(buffer) - value[key] = this.unpack(buffer) + const key = this.unpack(buffer, hydrateStructure) + value[key] = this.unpack(buffer, hydrateStructure) } return value } - _unpackStruct (marker, markerHigh, markerLow, buffer) { + _unpackStruct (marker, markerHigh, markerLow, buffer, hydrateStructure) { if (markerHigh === TINY_STRUCT) { - return this._unpackStructWithSize(markerLow, buffer) + return this._unpackStructWithSize(markerLow, buffer, hydrateStructure) } else if (marker === STRUCT_8) { - return this._unpackStructWithSize(buffer.readUInt8(), buffer) + return this._unpackStructWithSize(buffer.readUInt8(), buffer, hydrateStructure) } else if (marker === STRUCT_16) { - return this._unpackStructWithSize(buffer.readUInt16(), buffer) + return this._unpackStructWithSize(buffer.readUInt16(), buffer, hydrateStructure) } else { return null } } - _unpackStructWithSize (structSize, buffer) { + _unpackStructWithSize (structSize, buffer, hydrateStructure) { const signature = buffer.readUInt8() - if (signature === NODE) { - return this._unpackNode(structSize, buffer) - } else if (signature === RELATIONSHIP) { - return this._unpackRelationship(structSize, buffer) - } else if (signature === UNBOUND_RELATIONSHIP) { - return this._unpackUnboundRelationship(structSize, buffer) - } else if (signature === PATH) { - return this._unpackPath(structSize, buffer) - } else { - return this._unpackUnknownStruct(signature, structSize, buffer) - } - } - - _unpackNode (structSize, buffer) { - this._verifyStructSize('Node', NODE_STRUCT_SIZE, structSize) - - return new Node( - this.unpack(buffer), // Identity - this.unpack(buffer), // Labels - this.unpack(buffer) // Properties - ) - } - - _unpackRelationship (structSize, buffer) { - this._verifyStructSize('Relationship', RELATIONSHIP_STRUCT_SIZE, structSize) - - return new Relationship( - this.unpack(buffer), // Identity - this.unpack(buffer), // Start Node Identity - this.unpack(buffer), // End Node Identity - this.unpack(buffer), // Type - this.unpack(buffer) // Properties - ) - } - - _unpackUnboundRelationship (structSize, buffer) { - this._verifyStructSize( - 'UnboundRelationship', - UNBOUND_RELATIONSHIP_STRUCT_SIZE, - structSize - ) - - return new UnboundRelationship( - this.unpack(buffer), // Identity - this.unpack(buffer), // Type - this.unpack(buffer) // Properties - ) - } - - _unpackPath (structSize, buffer) { - this._verifyStructSize('Path', PATH_STRUCT_SIZE, structSize) - - const nodes = this.unpack(buffer) - const rels = this.unpack(buffer) - const sequence = this.unpack(buffer) - - const segments = [] - let prevNode = nodes[0] - - for (let i = 0; i < sequence.length; i += 2) { - const nextNode = nodes[sequence[i + 1]] - const relIndex = toNumber(sequence[i]) - let rel - - if (relIndex > 0) { - rel = rels[relIndex - 1] - if (rel instanceof UnboundRelationship) { - // To avoid duplication, relationships in a path do not contain - // information about their start and end nodes, that's instead - // inferred from the path sequence. This is us inferring (and, - // for performance reasons remembering) the start/end of a rel. - rels[relIndex - 1] = rel = rel.bindTo( - prevNode, - nextNode - ) - } - } else { - rel = rels[-relIndex - 1] - if (rel instanceof UnboundRelationship) { - // See above - rels[-relIndex - 1] = rel = rel.bindTo( - nextNode, - prevNode - ) - } - } - // Done hydrating one path segment. - segments.push(new PathSegment(prevNode, rel, nextNode)) - prevNode = nextNode - } - return new Path(nodes[0], nodes[nodes.length - 1], segments) - } - - _unpackUnknownStruct (signature, structSize, buffer) { - const result = new Structure(signature, []) + const structure = new Structure(signature, []) for (let i = 0; i < structSize; i++) { - result.fields.push(this.unpack(buffer)) + structure.fields.push(this.unpack(buffer, hydrateStructure)) } - return result - } - _verifyStructSize (structName, expectedSize, actualSize) { - if (expectedSize !== actualSize) { - throw newError( - `Wrong struct size for ${structName}, expected ${expectedSize} but was ${actualSize}`, - PROTOCOL_ERROR - ) - } + return hydrateStructure(structure) } } @@ -693,4 +544,4 @@ function isIterable (obj) { return typeof obj[Symbol.iterator] === 'function' } -export { Packer, Unpacker, Structure } +export { Packer, Unpacker } diff --git a/packages/bolt-connection/src/packstream/packstream-v2.js b/packages/bolt-connection/src/packstream/packstream-v2.js index 7a1f49607..bbd3f0da5 100644 --- a/packages/bolt-connection/src/packstream/packstream-v2.js +++ b/packages/bolt-connection/src/packstream/packstream-v2.js @@ -18,88 +18,11 @@ */ import * as v1 from './packstream-v1' -import { - int, - isInt, - isPoint, - Point, - DateTime, - Duration, - isDate, - isDateTime, - isDuration, - isLocalDateTime, - isLocalTime, - isTime, - Time, - internal -} from 'neo4j-driver-core' - -import { - epochDayToDate, - epochSecondAndNanoToLocalDateTime, - nanoOfDayToLocalTime -} from './temporal-factory' - -const { - temporalUtil: { - dateToEpochDay, - localDateTimeToEpochSecond, - localTimeToNanoOfDay - } -} = internal - -const POINT_2D = 0x58 -const POINT_2D_STRUCT_SIZE = 3 - -const POINT_3D = 0x59 -const POINT_3D_STRUCT_SIZE = 4 - -const DURATION = 0x45 -const DURATION_STRUCT_SIZE = 4 - -const LOCAL_TIME = 0x74 -const LOCAL_TIME_STRUCT_SIZE = 1 - -const TIME = 0x54 -const TIME_STRUCT_SIZE = 2 - -const DATE = 0x44 -const DATE_STRUCT_SIZE = 1 - -const LOCAL_DATE_TIME = 0x64 -const LOCAL_DATE_TIME_STRUCT_SIZE = 2 - -const DATE_TIME_WITH_ZONE_OFFSET = 0x46 -const DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE = 3 - -const DATE_TIME_WITH_ZONE_ID = 0x66 -const DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE = 3 export class Packer extends v1.Packer { disableByteArrays () { throw new Error('Bolt V2 should always support byte arrays') } - - packable (obj) { - if (isPoint(obj)) { - return () => packPoint(obj, this) - } else if (isDuration(obj)) { - return () => packDuration(obj, this) - } else if (isLocalTime(obj)) { - return () => packLocalTime(obj, this) - } else if (isTime(obj)) { - return () => packTime(obj, this) - } else if (isDate(obj)) { - return () => packDate(obj, this) - } else if (isLocalDateTime(obj)) { - return () => packLocalDateTime(obj, this) - } else if (isDateTime(obj)) { - return () => packDateTime(obj, this) - } else { - return super.packable(obj) - } - } } export class Unpacker extends v1.Unpacker { @@ -111,526 +34,4 @@ export class Unpacker extends v1.Unpacker { constructor (disableLosslessIntegers = false, useBigInt = false) { super(disableLosslessIntegers, useBigInt) } - - _unpackUnknownStruct (signature, structSize, buffer) { - if (signature === POINT_2D) { - return unpackPoint2D(this, structSize, buffer) - } else if (signature === POINT_3D) { - return unpackPoint3D(this, structSize, buffer) - } else if (signature === DURATION) { - return unpackDuration(this, structSize, buffer) - } else if (signature === LOCAL_TIME) { - return unpackLocalTime( - this, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } else if (signature === TIME) { - return unpackTime( - this, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } else if (signature === DATE) { - return unpackDate( - this, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } else if (signature === LOCAL_DATE_TIME) { - return unpackLocalDateTime( - this, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } else if (signature === DATE_TIME_WITH_ZONE_OFFSET) { - return unpackDateTimeWithZoneOffset( - this, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } else if (signature === DATE_TIME_WITH_ZONE_ID) { - return unpackDateTimeWithZoneId( - this, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } else { - return super._unpackUnknownStruct( - signature, - structSize, - buffer, - this._disableLosslessIntegers, - this._useBigInt - ) - } - } -} - -/** - * Pack given 2D or 3D point. - * @param {Point} point the point value to pack. - * @param {Packer} packer the packer to use. - */ -function packPoint (point, packer) { - const is2DPoint = point.z === null || point.z === undefined - if (is2DPoint) { - packPoint2D(point, packer) - } else { - packPoint3D(point, packer) - } -} - -/** - * Pack given 2D point. - * @param {Point} point the point value to pack. - * @param {Packer} packer the packer to use. - */ -function packPoint2D (point, packer) { - const packableStructFields = [ - packer.packable(int(point.srid)), - packer.packable(point.x), - packer.packable(point.y) - ] - packer.packStruct(POINT_2D, packableStructFields) -} - -/** - * Pack given 3D point. - * @param {Point} point the point value to pack. - * @param {Packer} packer the packer to use. - */ -function packPoint3D (point, packer) { - const packableStructFields = [ - packer.packable(int(point.srid)), - packer.packable(point.x), - packer.packable(point.y), - packer.packable(point.z) - ] - packer.packStruct(POINT_3D, packableStructFields) -} - -/** - * Unpack 2D point value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @return {Point} the unpacked 2D point value. - */ -function unpackPoint2D (unpacker, structSize, buffer) { - unpacker._verifyStructSize('Point2D', POINT_2D_STRUCT_SIZE, structSize) - - return new Point( - unpacker.unpack(buffer), // srid - unpacker.unpack(buffer), // x - unpacker.unpack(buffer), // y - undefined // z - ) -} - -/** - * Unpack 3D point value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @return {Point} the unpacked 3D point value. - */ -function unpackPoint3D (unpacker, structSize, buffer) { - unpacker._verifyStructSize('Point3D', POINT_3D_STRUCT_SIZE, structSize) - - return new Point( - unpacker.unpack(buffer), // srid - unpacker.unpack(buffer), // x - unpacker.unpack(buffer), // y - unpacker.unpack(buffer) // z - ) -} - -/** - * Pack given duration. - * @param {Duration} value the duration value to pack. - * @param {Packer} packer the packer to use. - */ -function packDuration (value, packer) { - const months = int(value.months) - const days = int(value.days) - const seconds = int(value.seconds) - const nanoseconds = int(value.nanoseconds) - - const packableStructFields = [ - packer.packable(months), - packer.packable(days), - packer.packable(seconds), - packer.packable(nanoseconds) - ] - packer.packStruct(DURATION, packableStructFields) -} - -/** - * Unpack duration value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @return {Duration} the unpacked duration value. - */ -function unpackDuration (unpacker, structSize, buffer) { - unpacker._verifyStructSize('Duration', DURATION_STRUCT_SIZE, structSize) - - const months = unpacker.unpack(buffer) - const days = unpacker.unpack(buffer) - const seconds = unpacker.unpack(buffer) - const nanoseconds = unpacker.unpack(buffer) - - return new Duration(months, days, seconds, nanoseconds) -} - -/** - * Pack given local time. - * @param {LocalTime} value the local time value to pack. - * @param {Packer} packer the packer to use. - */ -function packLocalTime (value, packer) { - const nanoOfDay = localTimeToNanoOfDay( - value.hour, - value.minute, - value.second, - value.nanosecond - ) - - const packableStructFields = [packer.packable(nanoOfDay)] - packer.packStruct(LOCAL_TIME, packableStructFields) -} - -/** - * Unpack local time value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @param {boolean} disableLosslessIntegers if integer properties in the result local time should be native JS numbers. - * @return {LocalTime} the unpacked local time value. - */ -function unpackLocalTime ( - unpacker, - structSize, - buffer, - disableLosslessIntegers -) { - unpacker._verifyStructSize('LocalTime', LOCAL_TIME_STRUCT_SIZE, structSize) - - const nanoOfDay = unpacker.unpackInteger(buffer) - const result = nanoOfDayToLocalTime(nanoOfDay) - return convertIntegerPropsIfNeeded(result, disableLosslessIntegers) -} - -/** - * Pack given time. - * @param {Time} value the time value to pack. - * @param {Packer} packer the packer to use. - */ -function packTime (value, packer) { - const nanoOfDay = localTimeToNanoOfDay( - value.hour, - value.minute, - value.second, - value.nanosecond - ) - const offsetSeconds = int(value.timeZoneOffsetSeconds) - - const packableStructFields = [ - packer.packable(nanoOfDay), - packer.packable(offsetSeconds) - ] - packer.packStruct(TIME, packableStructFields) -} - -/** - * Unpack time value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @param {boolean} disableLosslessIntegers if integer properties in the result time should be native JS numbers. - * @return {Time} the unpacked time value. - */ -function unpackTime ( - unpacker, - structSize, - buffer, - disableLosslessIntegers, - useBigInt -) { - unpacker._verifyStructSize('Time', TIME_STRUCT_SIZE, structSize) - - const nanoOfDay = unpacker.unpackInteger(buffer) - const offsetSeconds = unpacker.unpackInteger(buffer) - - const localTime = nanoOfDayToLocalTime(nanoOfDay) - const result = new Time( - localTime.hour, - localTime.minute, - localTime.second, - localTime.nanosecond, - offsetSeconds - ) - return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) -} - -/** - * Pack given neo4j date. - * @param {Date} value the date value to pack. - * @param {Packer} packer the packer to use. - */ -function packDate (value, packer) { - const epochDay = dateToEpochDay(value.year, value.month, value.day) - - const packableStructFields = [packer.packable(epochDay)] - packer.packStruct(DATE, packableStructFields) -} - -/** - * Unpack neo4j date value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @param {boolean} disableLosslessIntegers if integer properties in the result date should be native JS numbers. - * @return {Date} the unpacked neo4j date value. - */ -function unpackDate ( - unpacker, - structSize, - buffer, - disableLosslessIntegers, - useBigInt -) { - unpacker._verifyStructSize('Date', DATE_STRUCT_SIZE, structSize) - - const epochDay = unpacker.unpackInteger(buffer) - const result = epochDayToDate(epochDay) - return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) -} - -/** - * Pack given local date time. - * @param {LocalDateTime} value the local date time value to pack. - * @param {Packer} packer the packer to use. - */ -function packLocalDateTime (value, packer) { - const epochSecond = localDateTimeToEpochSecond( - value.year, - value.month, - value.day, - value.hour, - value.minute, - value.second, - value.nanosecond - ) - const nano = int(value.nanosecond) - - const packableStructFields = [ - packer.packable(epochSecond), - packer.packable(nano) - ] - packer.packStruct(LOCAL_DATE_TIME, packableStructFields) -} - -/** - * Unpack local date time value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @param {boolean} disableLosslessIntegers if integer properties in the result local date-time should be native JS numbers. - * @return {LocalDateTime} the unpacked local date time value. - */ -function unpackLocalDateTime ( - unpacker, - structSize, - buffer, - disableLosslessIntegers, - useBigInt -) { - unpacker._verifyStructSize( - 'LocalDateTime', - LOCAL_DATE_TIME_STRUCT_SIZE, - structSize - ) - - const epochSecond = unpacker.unpackInteger(buffer) - const nano = unpacker.unpackInteger(buffer) - const result = epochSecondAndNanoToLocalDateTime(epochSecond, nano) - return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) -} - -/** - * Pack given date time. - * @param {DateTime} value the date time value to pack. - * @param {Packer} packer the packer to use. - */ -function packDateTime (value, packer) { - if (value.timeZoneId) { - packDateTimeWithZoneId(value, packer) - } else { - packDateTimeWithZoneOffset(value, packer) - } -} - -/** - * Pack given date time with zone offset. - * @param {DateTime} value the date time value to pack. - * @param {Packer} packer the packer to use. - */ -function packDateTimeWithZoneOffset (value, packer) { - const epochSecond = localDateTimeToEpochSecond( - value.year, - value.month, - value.day, - value.hour, - value.minute, - value.second, - value.nanosecond - ) - const nano = int(value.nanosecond) - const timeZoneOffsetSeconds = int(value.timeZoneOffsetSeconds) - - const packableStructFields = [ - packer.packable(epochSecond), - packer.packable(nano), - packer.packable(timeZoneOffsetSeconds) - ] - packer.packStruct(DATE_TIME_WITH_ZONE_OFFSET, packableStructFields) -} - -/** - * Unpack date time with zone offset value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @param {boolean} disableLosslessIntegers if integer properties in the result date-time should be native JS numbers. - * @return {DateTime} the unpacked date time with zone offset value. - */ -function unpackDateTimeWithZoneOffset ( - unpacker, - structSize, - buffer, - disableLosslessIntegers, - useBigInt -) { - unpacker._verifyStructSize( - 'DateTimeWithZoneOffset', - DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE, - structSize - ) - - const epochSecond = unpacker.unpackInteger(buffer) - const nano = unpacker.unpackInteger(buffer) - const timeZoneOffsetSeconds = unpacker.unpackInteger(buffer) - - const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano) - const result = new DateTime( - localDateTime.year, - localDateTime.month, - localDateTime.day, - localDateTime.hour, - localDateTime.minute, - localDateTime.second, - localDateTime.nanosecond, - timeZoneOffsetSeconds, - null - ) - return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) -} - -/** - * Pack given date time with zone id. - * @param {DateTime} value the date time value to pack. - * @param {Packer} packer the packer to use. - */ -function packDateTimeWithZoneId (value, packer) { - const epochSecond = localDateTimeToEpochSecond( - value.year, - value.month, - value.day, - value.hour, - value.minute, - value.second, - value.nanosecond - ) - const nano = int(value.nanosecond) - const timeZoneId = value.timeZoneId - - const packableStructFields = [ - packer.packable(epochSecond), - packer.packable(nano), - packer.packable(timeZoneId) - ] - packer.packStruct(DATE_TIME_WITH_ZONE_ID, packableStructFields) -} - -/** - * Unpack date time with zone id value using the given unpacker. - * @param {Unpacker} unpacker the unpacker to use. - * @param {number} structSize the retrieved struct size. - * @param {BaseBuffer} buffer the buffer to unpack from. - * @param {boolean} disableLosslessIntegers if integer properties in the result date-time should be native JS numbers. - * @return {DateTime} the unpacked date time with zone id value. - */ -function unpackDateTimeWithZoneId ( - unpacker, - structSize, - buffer, - disableLosslessIntegers, - useBigInt -) { - unpacker._verifyStructSize( - 'DateTimeWithZoneId', - DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE, - structSize - ) - - const epochSecond = unpacker.unpackInteger(buffer) - const nano = unpacker.unpackInteger(buffer) - const timeZoneId = unpacker.unpack(buffer) - - const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano) - const result = new DateTime( - localDateTime.year, - localDateTime.month, - localDateTime.day, - localDateTime.hour, - localDateTime.minute, - localDateTime.second, - localDateTime.nanosecond, - null, - timeZoneId - ) - return convertIntegerPropsIfNeeded(result, disableLosslessIntegers, useBigInt) -} - -function convertIntegerPropsIfNeeded (obj, disableLosslessIntegers, useBigInt) { - if (!disableLosslessIntegers && !useBigInt) { - return obj - } - - const convert = value => - useBigInt ? value.toBigInt() : value.toNumberOrInfinity() - - const clone = Object.create(Object.getPrototypeOf(obj)) - for (const prop in obj) { - if (Object.prototype.hasOwnProperty.call(obj, prop) === true) { - const value = obj[prop] - clone[prop] = isInt(value) ? convert(value) : value - } - } - Object.freeze(clone) - return clone } diff --git a/packages/bolt-connection/src/packstream/packstream-v5.js b/packages/bolt-connection/src/packstream/packstream-v5.js deleted file mode 100644 index e99731336..000000000 --- a/packages/bolt-connection/src/packstream/packstream-v5.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * 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. - */ - -import * as v2 from './packstream-v2' -import { - Node, - Relationship, - UnboundRelationship, - int -} from 'neo4j-driver-core' - -const NODE_STRUCT_SIZE = 4 -const RELATIONSHIP_STRUCT_SIZE = 8 -const UNBOUND_RELATIONSHIP_STRUCT_SIZE = 4 - -export class Packer extends v2.Packer { - // This implementation is the same -} - -export class Unpacker extends v2.Unpacker { - /** - * @constructor - * @param {boolean} disableLosslessIntegers if this unpacker should convert all received integers to native JS numbers. - * @param {boolean} useBigInt if this unpacker should convert all received integers to Bigint - */ - constructor (disableLosslessIntegers = false, useBigInt = false) { - super(disableLosslessIntegers, useBigInt) - this._defaultIdentity = this._getDefaultIdentity() - } - - _getDefaultIdentity () { - if (this._useBigInt) { - return BigInt(-1) - } else if (this._disableLosslessIntegers) { - return -1 - } else { - return int(-1) - } - } - - _unpackNode (structSize, buffer) { - this._verifyStructSize('Node', NODE_STRUCT_SIZE, structSize) - - return new Node( - _valueOrDefault(this.unpack(buffer), this._defaultIdentity), // Identity - this.unpack(buffer), // Labels - this.unpack(buffer), // Properties, - this.unpack(buffer) // ElementId - ) - } - - _unpackRelationship (structSize, buffer) { - this._verifyStructSize('Relationship', RELATIONSHIP_STRUCT_SIZE, structSize) - - return new Relationship( - _valueOrDefault(this.unpack(buffer), this._defaultIdentity), // Identity - _valueOrDefault(this.unpack(buffer), this._defaultIdentity), // Start Node Identity - _valueOrDefault(this.unpack(buffer), this._defaultIdentity), // End Node Identity - this.unpack(buffer), // Type - this.unpack(buffer), // Properties, - this.unpack(buffer), // ElementId - this.unpack(buffer), // Start Node Element Id - this.unpack(buffer) // End Node Element Id - ) - } - - _unpackUnboundRelationship (structSize, buffer) { - this._verifyStructSize( - 'UnboundRelationship', - UNBOUND_RELATIONSHIP_STRUCT_SIZE, - structSize - ) - - return new UnboundRelationship( - _valueOrDefault(this.unpack(buffer), this._defaultIdentity), // Identity - this.unpack(buffer), // Type - this.unpack(buffer), // Properties - this.unpack(buffer) // ElementId - ) - } -} - -function _valueOrDefault (value, defaultValue) { - return value === null ? defaultValue : value -} diff --git a/packages/bolt-connection/src/packstream/structure.js b/packages/bolt-connection/src/packstream/structure.js new file mode 100644 index 000000000..4fff7e19d --- /dev/null +++ b/packages/bolt-connection/src/packstream/structure.js @@ -0,0 +1,63 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import { newError, error } from 'neo4j-driver-core' + +const { + PROTOCOL_ERROR +} = error + +/** + * A Structure have a signature and fields. + */ +export class Structure { + /** + * Create new instance + */ + constructor (signature, fields) { + this.signature = signature + this.fields = fields + } + + get size () { + return this.fields.length + } + + toString () { + let fieldStr = '' + for (let i = 0; i < this.fields.length; i++) { + if (i > 0) { + fieldStr += ', ' + } + fieldStr += this.fields[i] + } + return 'Structure(' + this.signature + ', [' + fieldStr + '])' + } +} + +export function verifyStructSize (structName, expectedSize, actualSize) { + if (expectedSize !== actualSize) { + throw newError( + `Wrong struct size for ${structName}, expected ${expectedSize} but was ${actualSize}`, + PROTOCOL_ERROR + ) + } +} + +export default Structure diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v1.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v1.test.js.snap new file mode 100644 index 000000000..960a5e254 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v1.test.js.snap @@ -0,0 +1,109 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV1 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV1 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV1 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV1 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (Date) 1`] = ` +Object { + "day": 1, + "month": 1, + "year": 1, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (DateTime) 1`] = ` +Object { + "day": 1, + "hour": 1, + "minute": 1, + "month": 1, + "nanosecond": 1, + "second": 1, + "timeZoneOffsetSeconds": 1, + "year": 1, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (Duration) 1`] = ` +Object { + "days": 1, + "months": 1, + "nanoseconds": Integer { + "high": 0, + "low": 1, + }, + "seconds": Integer { + "high": 0, + "low": 1, + }, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (LocalDateTime) 1`] = ` +Object { + "day": 1, + "hour": 1, + "minute": 1, + "month": 1, + "nanosecond": 1, + "second": 1, + "year": 1, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (LocalTime) 1`] = ` +Object { + "hour": 1, + "minute": 1, + "nanosecond": 1, + "second": 1, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (Point2D) 1`] = ` +Object { + "srid": 1, + "x": 1, + "y": 1, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (Point3D) 1`] = ` +Object { + "srid": 1, + "x": 1, + "y": 1, + "z": 1, +} +`; + +exports[`#unit BoltProtocolV1 .packable() should pack types introduced afterwards as Map (Time) 1`] = ` +Object { + "hour": 1, + "minute": 1, + "nanosecond": 1, + "second": 1, + "timeZoneOffsetSeconds": 1, +} +`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV1 .unpack() should not unpack graph types with wrong size(UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v2.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v2.test.js.snap new file mode 100644 index 000000000..906b29ed0 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v2.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV2 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV2 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV2 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV2 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV2 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v3.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v3.test.js.snap new file mode 100644 index 000000000..966e99a67 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v3.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV3 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV3 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV3 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV3 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV3 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x0.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x0.test.js.snap new file mode 100644 index 000000000..2372c840a --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x0.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV4x0 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV4x0 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV4x0 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV4x0 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x0 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x1.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x1.test.js.snap new file mode 100644 index 000000000..e541c059f --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x1.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV4x1 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV4x1 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV4x1 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV4x1 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x1 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x2.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x2.test.js.snap new file mode 100644 index 000000000..24528c606 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x2.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV4x2 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV4x2 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV4x2 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV4x2 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x2 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x3.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x3.test.js.snap new file mode 100644 index 000000000..f1c9a01ed --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x3.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV4x3 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV4x3 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV4x3 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV4x3 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x3 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x4.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x4.test.js.snap new file mode 100644 index 000000000..4068caf7d --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v4x4.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV4x4 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV4x4 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV4x4 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV4x4 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 4"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 5 but was 8"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV4x4 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 3 but was 4"`; diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x0.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x0.test.js.snap new file mode 100644 index 000000000..4d3877736 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x0.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV5x0 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV5x0 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV5x0 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV5x0 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Node, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Node, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 8 but was 5"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 8 but was 9"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 4 but was 5"`; diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js index b11757ed7..533f0023b 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js @@ -19,9 +19,25 @@ import BoltProtocolV1 from '../../src/bolt/bolt-protocol-v1' import RequestMessage from '../../src/bolt/request-message' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + internal, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node +} from 'neo4j-driver-core' import utils from '../test-utils' import { LoginObserver } from '../../src/bolt/stream-observers' +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' const WRITE = 'WRITE' @@ -360,4 +376,227 @@ describe('#unit BoltProtocolV1', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV1( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack types introduced afterwards as Map (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV1( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toBeInstanceOf(Object) + expect(unpacked).toMatchSnapshot() + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV1( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ] + ])('should not unpack graph types with wrong size(%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV1( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Point', new structure.Structure(0x58, [1, 2, 3])], + ['Point3D', new structure.Structure(0x59, [1, 2, 3, 4])], + ['Duration', new structure.Structure(0x45, [1, 2, 3, 4])], + ['LocalTime', new structure.Structure(0x74, [1])], + ['Time', new structure.Structure(0x54, [1, 2])], + ['Date', new structure.Structure(0x44, [1])], + ['LocalDateTime', new structure.Structure(0x64, [1, 2])], + ['DateTimeWithZoneOffset', new structure.Structure(0x46, [1, 2, 3])], + ['DateTimeWithZoneId', new structure.Structure(0x66, [1, 2, 'America/Sao Paulo'])] + ])('should unpack future structs as structs (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV1( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(struct) + }) + }) }) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js index 99eb55a28..94575ea0a 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js @@ -20,6 +20,24 @@ import BoltProtocolV2 from '../../src/bolt/bolt-protocol-v2' import utils from '../test-utils' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' + describe('#unit BoltProtocolV2', () => { beforeEach(() => { expect.extend(utils.matchers) @@ -111,4 +129,340 @@ describe('#unit BoltProtocolV2', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV2( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV2( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV2( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV2( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV2( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js index 205ced51a..f8a6e914a 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js @@ -20,12 +20,30 @@ import BoltProtocolV3 from '../../src/bolt/bolt-protocol-v3' import RequestMessage from '../../src/bolt/request-message' import utils from '../test-utils' -import { internal } from 'neo4j-driver-core' import { ProcedureRouteObserver, ResultStreamObserver } from '../../src/bolt/stream-observers' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' + const { bookmarks: { Bookmarks }, txConfig: { TxConfig } @@ -316,6 +334,342 @@ describe('#unit BoltProtocolV3', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV3( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV3( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV3( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) class SpiedBoltProtocolV3 extends BoltProtocolV3 { diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js index 819eaa932..d92f36330 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js @@ -25,7 +25,24 @@ import { ResultStreamObserver } from '../../src/bolt/stream-observers' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' const WRITE = 'WRITE' @@ -235,6 +252,342 @@ describe('#unit BoltProtocolV4x0', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV4x0( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x0( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x0( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x0( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x0( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) class SpiedBoltProtocolV4x0 extends BoltProtocolV4x0 { diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js index 545ea97dd..48f8ca724 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js @@ -19,7 +19,24 @@ import BoltProtocolV4x1 from '../../src/bolt/bolt-protocol-v4x1' import utils from '../test-utils' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' const { txConfig: { TxConfig }, @@ -109,4 +126,340 @@ describe('#unit BoltProtocolV4x1', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV4x1( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x1( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x1( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x1( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x1( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js index 6a7604bda..2000283a3 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js @@ -19,7 +19,24 @@ import BoltProtocolV4x2 from '../../src/bolt/bolt-protocol-v4x2' import utils from '../test-utils' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' const { txConfig: { TxConfig }, @@ -108,4 +125,340 @@ describe('#unit BoltProtocolV4x2', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV4x2( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x2( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x2( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x2( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x2( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js index fc3b846b8..84982df25 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js @@ -21,7 +21,24 @@ import BoltProtocolV4x3 from '../../src/bolt/bolt-protocol-v4x3' import RequestMessage from '../../src/bolt/request-message' import utils from '../test-utils' import { RouteObserver } from '../../src/bolt/stream-observers' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' const WRITE = 'WRITE' @@ -320,4 +337,340 @@ describe('#unit BoltProtocolV4x3', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV4x3( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x3( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x3( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js index d4f779b16..80019513f 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js @@ -21,7 +21,24 @@ import BoltProtocolV4x4 from '../../src/bolt/bolt-protocol-v4x4' import RequestMessage from '../../src/bolt/request-message' import utils from '../test-utils' import { RouteObserver } from '../../src/bolt/stream-observers' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { structure } from '../../src/packstream' const WRITE = 'WRITE' @@ -353,4 +370,340 @@ describe('#unit BoltProtocolV4x4', () => { expect(observer._highRecordWatermark).toEqual(200) }) }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV4x4( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x4( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]), + new Node(1, ['a'], { c: 'd' }) + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]), + new Relationship(1, 2, 3, '4', { 5: 6 }) + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }]), + new UnboundRelationship(1, '2', { 3: 4 }) + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }), + new Node(2, ['3'], { 4: '5' }), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }), + new Relationship(3, 1, 4, 'rel1', { 4: '5' }), + new Node(4, ['5'], { 6: 7 }) + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }), + new Relationship(5, 4, 2, 'rel2', { 6: 7 }), + new Node(2, ['3'], { 4: '5' }) + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x4( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a']]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4']) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2']) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x4( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV4x4( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) }) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js index 512918bd3..a8e22faf3 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js @@ -19,10 +19,26 @@ import BoltProtocolV5x0 from '../../src/bolt/bolt-protocol-v5x0' import RequestMessage from '../../src/bolt/request-message' -import { v5 } from '../../src/packstream' +import { v2, structure } from '../../src/packstream' import utils from '../test-utils' import { RouteObserver } from '../../src/bolt/stream-observers' -import { internal } from 'neo4j-driver-core' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' const WRITE = 'WRITE' @@ -356,14 +372,350 @@ describe('#unit BoltProtocolV5x0', () => { }) describe('packstream', () => { - it('should configure v5 packer', () => { + it('should configure v2 packer', () => { const protocol = new BoltProtocolV5x0(null, null, false) - expect(protocol.packer()).toBeInstanceOf(v5.Packer) + expect(protocol.packer()).toBeInstanceOf(v2.Packer) }) - it('should configure v5 unpacker', () => { + it('should configure v2 unpacker', () => { const protocol = new BoltProtocolV5x0(null, null, false) - expect(protocol.unpacker()).toBeInstanceOf(v5.Unpacker) + expect(protocol.unpacker()).toBeInstanceOf(v2.Unpacker) + }) + }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV5x0( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['DateTime', new DateTime(1, 1, 1, 1, 1, 1, 1, 1)], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x0( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, 'elementId']), + new Node(1, ['a'], { c: 'd' }, 'elementId') + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, 'elementId', 'node1', 'node2']), + new Relationship(1, 2, 3, '4', { 5: 6 }, 'elementId', 'node1', 'node2') + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }, 'elementId']), + new UnboundRelationship(1, '2', { 3: 4 }, 'elementId') + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }, 'node1']), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }, 'node2']), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }, 'node3']) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'reltype1', { 4: '5' }, 'rel1', 'node1', 'node2']), + new structure.Structure(0x52, [5, 4, 2, 'reltype2', { 6: 7 }, 'rel2', 'node2', 'node3']) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }, 'node1'), + new Node(2, ['3'], { 4: '5' }, 'node3'), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }, 'node1'), + new Relationship(3, 1, 4, 'reltype1', { 4: '5' }, 'rel1', 'node1', 'node2'), + new Node(4, ['5'], { 6: 7 }, 'node2') + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }, 'node2'), + new Relationship(5, 4, 2, 'reltype2', { 6: 7 }, 'rel2', 'node2', 'node3'), + new Node(2, ['3'], { 4: '5' }, 'node3') + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x0( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1', 'b']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3', '4']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }]) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1', '2']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x46, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x46, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x66, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x0( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x46, [1, 2, 3]), + new DateTime(1970, 1, 1, 0, 0, 1, 2, 3) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x0( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) }) }) }) diff --git a/packages/bolt-connection/test/bolt/index.test.js b/packages/bolt-connection/test/bolt/index.test.js index e1bc0fd94..cbed93f74 100644 --- a/packages/bolt-connection/test/bolt/index.test.js +++ b/packages/bolt-connection/test/bolt/index.test.js @@ -266,7 +266,7 @@ describe('#unit Bolt', () => { protocol.packer().packStruct( expectedMessage.signature, - expectedMessage.fields.map(field => protocol.packer().packable(field)) + expectedMessage.fields.map(field => protocol.packable(field)) ) params.chunker.messageBoundary() params.chunker.flush() diff --git a/packages/bolt-connection/test/bolt/transformer.test.js b/packages/bolt-connection/test/bolt/transformer.test.js new file mode 100644 index 000000000..6858577b4 --- /dev/null +++ b/packages/bolt-connection/test/bolt/transformer.test.js @@ -0,0 +1,100 @@ +/** + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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. + */ + +import { TypeTransformer } from '../../src/bolt/transformer.js' + +describe('TypeTransformer', () => { + const defaultSignature = 123 + const defaultFromStructure = (struct) => struct + const defaultToStructure = (obj) => obj + const defaultIsTypeInstance = () => true + const createDefaultTransformer = () => { + return new TypeTransformer({ + signature: defaultSignature, + isTypeInstance: defaultIsTypeInstance, + fromStructure: defaultFromStructure, + toStructure: defaultToStructure + }) + } + + describe('constructor', () => { + it('should create the complete object', () => { + const typeTransformer = createDefaultTransformer() + + expect(typeTransformer.signature).toBe(defaultSignature) + expect(typeTransformer.isTypeInstance).toBe(defaultIsTypeInstance) + expect(typeTransformer.fromStructure).toBe(defaultFromStructure) + expect(typeTransformer.toStructure).toBe(defaultToStructure) + }) + }) + + describe('.extendsWith()', () => { + it('should override signature and keep rest of param intact', () => { + const expectedSignature = 124 + + const typeTransformer = createDefaultTransformer() + + const extended = typeTransformer.extendsWith({ signature: expectedSignature }) + + expect(extended.signature).toBe(expectedSignature) + expect(extended.isTypeInstance).toBe(typeTransformer.isTypeInstance) + expect(extended.fromStructure).toBe(typeTransformer.fromStructure) + expect(extended.toStructure).toBe(typeTransformer.toStructure) + }) + + it('should override isTypeInstance and keep rest of param intact', () => { + const expectedIsTypeInstance = () => false + + const typeTransformer = createDefaultTransformer() + + const extended = typeTransformer.extendsWith({ isTypeInstance: expectedIsTypeInstance }) + + expect(extended.isTypeInstance).toBe(expectedIsTypeInstance) + expect(extended.signature).toEqual(typeTransformer.signature) + expect(extended.fromStructure).toBe(typeTransformer.fromStructure) + expect(extended.toStructure).toBe(typeTransformer.toStructure) + }) + + it('should override fromStructure and keep rest of param intact', () => { + const expectedFromStructure = () => false + + const typeTransformer = createDefaultTransformer() + + const extended = typeTransformer.extendsWith({ fromStructure: expectedFromStructure }) + + expect(extended.fromStructure).toBe(expectedFromStructure) + expect(extended.signature).toEqual(typeTransformer.signature) + expect(extended.isTypeInstance).toBe(typeTransformer.isTypeInstance) + expect(extended.toStructure).toBe(typeTransformer.toStructure) + }) + + it('should override toStructure and keep rest of param intact', () => { + const expectedToStructure = () => false + + const typeTransformer = createDefaultTransformer() + + const extended = typeTransformer.extendsWith({ toStructure: expectedToStructure }) + + expect(extended.toStructure).toBe(expectedToStructure) + expect(extended.signature).toEqual(typeTransformer.signature) + expect(extended.fromStructure).toBe(typeTransformer.fromStructure) + expect(extended.isTypeInstance).toBe(typeTransformer.isTypeInstance) + }) + }) +}) diff --git a/packages/bolt-connection/test/packstream/packstream-v1.test.js b/packages/bolt-connection/test/packstream/packstream-v1.test.js index e8e8590b3..038c49bbf 100644 --- a/packages/bolt-connection/test/packstream/packstream-v1.test.js +++ b/packages/bolt-connection/test/packstream/packstream-v1.test.js @@ -19,7 +19,8 @@ import { int, Integer } from 'neo4j-driver-core' import { alloc } from '../../src/channel' -import { Packer, Structure, Unpacker } from '../../src/packstream/packstream-v1' +import { Packer, Unpacker } from '../../src/packstream/packstream-v1' +import { Structure } from '../../src/packstream/structure' describe('#unit PackStreamV1', () => { it('should pack integers with small numbers', () => { diff --git a/packages/bolt-connection/test/packstream/packstream-v5.test.js b/packages/bolt-connection/test/packstream/packstream-v5.test.js deleted file mode 100644 index ffb9df042..000000000 --- a/packages/bolt-connection/test/packstream/packstream-v5.test.js +++ /dev/null @@ -1,549 +0,0 @@ -/** - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * 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. - */ - -import { int, Integer, Node, Relationship, UnboundRelationship } from 'neo4j-driver-core' -import { alloc } from '../../src/channel' -import { Packer, Unpacker } from '../../src/packstream/packstream-v5' -import { Structure } from '../../src/packstream/packstream-v1' - -describe('#unit PackStreamV5', () => { - it('should pack integers with small numbers', () => { - let n, i - // test small numbers - for (n = -999; n <= 999; n += 1) { - i = int(n) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - expect( - packAndUnpack(i, { disableLosslessIntegers: true }).toString() - ).toBe(i.toString()) - expect(packAndUnpack(i, { useBigInt: true }).toString()).toBe( - i.toString() - ) - } - }) - - it('should pack integers with small numbers created with Integer', () => { - let n, i - // test small numbers - for (n = -10; n <= 10; n += 1) { - i = new Integer(n, 0) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - expect( - packAndUnpack(i, { disableLosslessIntegers: true }).toString() - ).toBe(i.toString()) - expect(packAndUnpack(i, { useBigInt: true }).toString()).toBe( - i.toString() - ) - } - }) - - it('should pack integers with positive numbers', () => { - let n, i - // positive numbers - for (n = 16; n <= 16; n += 1) { - i = int(Math.pow(2, n)) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - - const unpackedLossyInteger = packAndUnpack(i, { - disableLosslessIntegers: true - }) - expect(typeof unpackedLossyInteger).toBe('number') - expect(unpackedLossyInteger.toString()).toBe( - i.inSafeRange() ? i.toString() : 'Infinity' - ) - - const bigint = packAndUnpack(i, { useBigInt: true }) - expect(typeof bigint).toBe('bigint') - expect(bigint.toString()).toBe(i.toString()) - } - }) - - it('should pack integer with negative numbers', () => { - let n, i - // negative numbers - for (n = 0; n <= 63; n += 1) { - i = int(-Math.pow(2, n)) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - - const unpackedLossyInteger = packAndUnpack(i, { - disableLosslessIntegers: true - }) - expect(typeof unpackedLossyInteger).toBe('number') - expect(unpackedLossyInteger.toString()).toBe( - i.inSafeRange() ? i.toString() : '-Infinity' - ) - - const bigint = packAndUnpack(i, { useBigInt: true }) - expect(typeof bigint).toBe('bigint') - expect(bigint.toString()).toBe(i.toString()) - } - }) - - it('should pack BigInt with small numbers', () => { - let n, i - // test small numbers - for (n = -999; n <= 999; n += 1) { - i = BigInt(n) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - expect( - packAndUnpack(i, { disableLosslessIntegers: true }).toString() - ).toBe(i.toString()) - expect(packAndUnpack(i, { useBigInt: true }).toString()).toBe( - i.toString() - ) - } - }) - - it('should pack BigInt with positive numbers', () => { - let n, i - // positive numbers - for (n = 16; n <= 16; n += 1) { - i = BigInt(Math.pow(2, n)) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - - const unpackedLossyInteger = packAndUnpack(i, { - disableLosslessIntegers: true - }) - expect(typeof unpackedLossyInteger).toBe('number') - expect(unpackedLossyInteger.toString()).toBe( - int(i).inSafeRange() ? i.toString() : 'Infinity' - ) - - const bigint = packAndUnpack(i, { useBigInt: true }) - expect(typeof bigint).toBe('bigint') - expect(bigint.toString()).toBe(i.toString()) - } - }) - - it('should pack BigInt with negative numbers', () => { - let n, i - // negative numbers - for (n = 0; n <= 63; n += 1) { - i = BigInt(-Math.pow(2, n)) - expect(packAndUnpack(i).toString()).toBe(i.toString()) - - const unpackedLossyInteger = packAndUnpack(i, { - disableLosslessIntegers: true - }) - expect(typeof unpackedLossyInteger).toBe('number') - expect(unpackedLossyInteger.toString()).toBe( - int(i).inSafeRange() ? i.toString() : '-Infinity' - ) - - const bigint = packAndUnpack(i, { useBigInt: true }) - expect(typeof bigint).toBe('bigint') - expect(bigint.toString()).toBe(i.toString()) - } - }) - - it('should pack strings', () => { - expect(packAndUnpack('')).toBe('') - expect(packAndUnpack('abcdefg123567')).toBe('abcdefg123567') - const str = Array(65536 + 1).join('a') // 2 ^ 16 + 1 - expect(packAndUnpack(str, { bufferSize: str.length + 8 })).toBe(str) - }) - - it('should pack structures', () => { - expect(packAndUnpack(new Structure(1, ['Hello, world!!!'])).fields[0]).toBe( - 'Hello, world!!!' - ) - }) - - it('should pack lists', () => { - const list = ['a', 'b'] - const unpacked = packAndUnpack(list) - expect(unpacked[0]).toBe(list[0]) - expect(unpacked[1]).toBe(list[1]) - }) - - it('should pack long lists', () => { - const listLength = 256 - const list = [] - for (let i = 0; i < listLength; i++) { - list.push(null) - } - const unpacked = packAndUnpack(list, { bufferSize: 1400 }) - expect(unpacked[0]).toBe(list[0]) - expect(unpacked[1]).toBe(list[1]) - }) - - it.each( - validNodesAndConfig() - )('should unpack Nodes', (struct, expectedNode, config) => { - const node = packAndUnpack(struct, config) - - expect(node).toEqual(expectedNode) - }) - - it.each( - invalidNodesConfig() - )('should thrown error for unpacking invalid Nodes', (struct) => { - expect(() => packAndUnpack(struct)).toThrow() - }) - - it.each( - validRelationshipsAndConfig() - )('should unpack Relationships', (struct, expectedRelationship, config) => { - const releationship = packAndUnpack(struct, config) - - expect(releationship).toEqual(expectedRelationship) - }) - - it.each( - invalidRelationshipsConfig() - )('should thrown error for unpacking invalid Relationships', (struct) => { - expect(() => packAndUnpack(struct)).toThrow() - }) - - it.each( - validUnboundRelationshipsAndConfig() - )('should unpack UnboundRelationships', (struct, expectedRelationship, config) => { - const releationship = packAndUnpack(struct, config) - - expect(releationship).toEqual(expectedRelationship) - }) - - it.each( - invalidUnboundRelationshipsConfig() - )('should thrown error for unpacking invalid UnboundRelationships', (struct) => { - expect(() => packAndUnpack(struct)).toThrow() - }) - - function validNodesAndConfig () { - function validWithNumber () { - const identity = 1 - const labels = ['a', 'b'] - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedNode = new Node(identity, labels, properties, elementId) - const nodeStruct = new Structure(0x4e, [ - identity, labels, properties, elementId - ]) - return [nodeStruct, expectedNode, { disableLosslessIntegers: true, useBigInt: false }] - } - - function validWithoutOldIdentifiersLossy () { - const identity = null - const labels = ['a', 'b'] - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedNode = new Node(-1, labels, properties, elementId) - const nodeStruct = new Structure(0x4e, [ - identity, labels, properties, elementId - ]) - return [nodeStruct, expectedNode, { disableLosslessIntegers: true, useBigInt: false }] - } - - function validWithoutOldIdentifiersLosslessInteger () { - const identity = null - const labels = ['a', 'b'] - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedNode = new Node(int(-1), labels, properties, elementId) - const nodeStruct = new Structure(0x4e, [ - identity, labels, properties, elementId - ]) - return [nodeStruct, expectedNode, { disableLosslessIntegers: false, useBigInt: false }] - } - - function validWithoutOldIdentifiersBigInt () { - const identity = null - const labels = ['a', 'b'] - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedNode = new Node(BigInt(-1), labels, properties, elementId) - const nodeStruct = new Structure(0x4e, [ - identity, labels, properties, elementId - ]) - return [nodeStruct, expectedNode, { disableLosslessIntegers: false, useBigInt: true }] - } - - function validWithInt () { - const identity = int(1) - const labels = ['a', 'b'] - const properties = { a: int(1), b: int(2) } - const elementId = 'element_id_1' - const expectedNode = new Node(identity, labels, properties, elementId) - const nodeStruct = new Structure(0x4e, [ - identity, labels, properties, elementId - ]) - return [nodeStruct, expectedNode, { disableLosslessIntegers: false, useBigInt: false }] - } - - function validWithBigInt () { - const identity = BigInt(1) - const labels = ['a', 'b'] - const properties = { a: BigInt(1), b: BigInt(2) } - const elementId = 'element_id_1' - const expectedNode = new Node(identity, labels, properties, elementId) - const nodeStruct = new Structure(0x4e, [ - identity, labels, properties, elementId - ]) - return [nodeStruct, expectedNode, { disableLosslessIntegers: false, useBigInt: true }] - } - - return [ - validWithNumber(), - validWithInt(), - validWithBigInt(), - validWithoutOldIdentifiersLossy(), - validWithoutOldIdentifiersLosslessInteger(), - validWithoutOldIdentifiersBigInt() - ] - } - - function invalidNodesConfig () { - return [ - [new Structure(0x4e, [1, ['a', 'b'], { a: 1, b: 2 }])], - [new Structure(0x4e, [1, ['a', 'b'], { a: 1, b: 2 }, 'elementId', 'myId'])] - ] - } - - function validRelationshipsAndConfig () { - function validWithNumber () { - const identity = 1 - const start = 2 - const end = 3 - const type = 'KNOWS' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const startNodeElementId = 'element_id_2' - const endNodeElementId = 'element_id_3' - const expectedRel = new Relationship( - identity, start, end, type, properties, - elementId, startNodeElementId, endNodeElementId) - const relStruct = new Structure(0x52, [ - identity, start, end, type, properties, elementId, - startNodeElementId, endNodeElementId - ]) - return [relStruct, expectedRel, { disableLosslessIntegers: true, useBigInt: false }] - } - - function validWithoutOldIdentifiersLossy () { - const identity = null - const start = null - const end = null - const type = 'KNOWS' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const startNodeElementId = 'element_id_2' - const endNodeElementId = 'element_id_3' - const expectedRel = new Relationship( - -1, -1, -1, type, properties, - elementId, startNodeElementId, endNodeElementId) - const relStruct = new Structure(0x52, [ - identity, start, end, type, properties, elementId, - startNodeElementId, endNodeElementId - ]) - return [relStruct, expectedRel, { disableLosslessIntegers: true, useBigInt: false }] - } - - function validWithoutOldIdentifiersLossLess () { - const identity = null - const start = null - const end = null - const type = 'KNOWS' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const startNodeElementId = 'element_id_2' - const endNodeElementId = 'element_id_3' - const expectedRel = new Relationship( - int(-1), int(-1), int(-1), type, properties, - elementId, startNodeElementId, endNodeElementId) - const relStruct = new Structure(0x52, [ - identity, start, end, type, properties, elementId, - startNodeElementId, endNodeElementId - ]) - return [relStruct, expectedRel, { disableLosslessIntegers: false, useBigInt: false }] - } - - function validWithoutOldIdentifiersBigInt () { - const identity = null - const start = null - const end = null - const type = 'KNOWS' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const startNodeElementId = 'element_id_2' - const endNodeElementId = 'element_id_3' - const expectedRel = new Relationship( - BigInt(-1), BigInt(-1), BigInt(-1), type, properties, - elementId, startNodeElementId, endNodeElementId) - const relStruct = new Structure(0x52, [ - identity, start, end, type, properties, elementId, - startNodeElementId, endNodeElementId - ]) - return [relStruct, expectedRel, { disableLosslessIntegers: true, useBigInt: true }] - } - - function validWithInt () { - const identity = int(1) - const start = int(2) - const end = int(3) - const type = 'KNOWS' - const properties = { a: int(1), b: int(2) } - const elementId = 'element_id_1' - const startNodeElementId = 'element_id_2' - const endNodeElementId = 'element_id_3' - const expectedRel = new Relationship( - identity, start, end, type, properties, - elementId, startNodeElementId, endNodeElementId) - const relStruct = new Structure(0x52, [ - identity, start, end, type, properties, elementId, - startNodeElementId, endNodeElementId - ]) - return [relStruct, expectedRel, { disableLosslessIntegers: false, useBigInt: false }] - } - - function validWithBigInt () { - const identity = BigInt(1) - const start = BigInt(2) - const end = BigInt(3) - const type = 'KNOWS' - const properties = { a: BigInt(1), b: BigInt(2) } - const elementId = 'element_id_1' - const startNodeElementId = 'element_id_2' - const endNodeElementId = 'element_id_3' - const expectedRel = new Relationship( - identity, start, end, type, properties, - elementId, startNodeElementId, endNodeElementId) - const relStruct = new Structure(0x52, [ - identity, start, end, type, properties, elementId, - startNodeElementId, endNodeElementId - ]) - return [relStruct, expectedRel, { disableLosslessIntegers: false, useBigInt: true }] - } - - return [ - validWithNumber(), - validWithInt(), - validWithBigInt(), - validWithoutOldIdentifiersLossy(), - validWithoutOldIdentifiersLossLess(), - validWithoutOldIdentifiersBigInt() - ] - } - - function invalidRelationshipsConfig () { - return [ - [new Structure(0x52, [1, 2, 3, 'rel', { a: 1, b: 2 }, 'elementId', 'startNodeId'])], - [new Structure(0x52, [1, 2, 3, 'rel', { a: 1, b: 2 }, 'elementId', 'startNodeId', 'endNodeId', 'myId'])] - ] - } - - function validUnboundRelationshipsAndConfig () { - function validWithNumber () { - const identity = 1 - const type = 'DOESNT_KNOW' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedUnboundRel = new UnboundRelationship(identity, type, properties, elementId) - const struct = new Structure(0x72, [ - identity, type, properties, elementId - ]) - return [struct, expectedUnboundRel, { disableLosslessIntegers: true, useBigInt: false }] - } - - function validWithoutOldIdentifiersLossy () { - const identity = null - const type = 'DOESNT_KNOW' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedUnboundRel = new UnboundRelationship(-1, type, properties, elementId) - const struct = new Structure(0x72, [ - identity, type, properties, elementId - ]) - return [struct, expectedUnboundRel, { disableLosslessIntegers: true, useBigInt: false }] - } - - function validWithoutOldIdentifiersLossless () { - const identity = null - const type = 'DOESNT_KNOW' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedUnboundRel = new UnboundRelationship(int(-1), type, properties, elementId) - const struct = new Structure(0x72, [ - identity, type, properties, elementId - ]) - return [struct, expectedUnboundRel, { disableLosslessIntegers: false, useBigInt: false }] - } - - function validWithoutOldIdentifiersBigInt () { - const identity = null - const type = 'DOESNT_KNOW' - const properties = { a: 1, b: 2 } - const elementId = 'element_id_1' - const expectedUnboundRel = new UnboundRelationship(BigInt(-1), type, properties, elementId) - const struct = new Structure(0x72, [ - identity, type, properties, elementId - ]) - return [struct, expectedUnboundRel, { disableLosslessIntegers: false, useBigInt: true }] - } - - function validWithInt () { - const identity = int(1) - const type = 'DOESNT_KNOW' - const properties = { a: int(1), b: int(2) } - const elementId = 'element_id_1' - const expectedUnboundRel = new UnboundRelationship(identity, type, properties, elementId) - const struct = new Structure(0x72, [ - identity, type, properties, elementId - ]) - return [struct, expectedUnboundRel, { disableLosslessIntegers: false, useBigInt: false }] - } - - function validWithBigInt () { - const identity = BigInt(1) - const type = 'DOESNT_KNOW' - const properties = { a: BigInt(1), b: BigInt(2) } - const elementId = 'element_id_1' - const expectedUnboundRel = new UnboundRelationship(identity, type, properties, elementId) - const struct = new Structure(0x72, [ - identity, type, properties, elementId - ]) - return [struct, expectedUnboundRel, { disableLosslessIntegers: false, useBigInt: true }] - } - - return [ - validWithNumber(), - validWithInt(), - validWithBigInt(), - validWithoutOldIdentifiersLossy(), - validWithoutOldIdentifiersLossless(), - validWithoutOldIdentifiersBigInt() - ] - } - - function invalidUnboundRelationshipsConfig () { - return [ - [new Structure(0x72, [1, 'DOESNT_KNOW', { a: 1, b: 2 }])], - [new Structure(0x72, [1, 'DOESNT_KNOW', { a: 1, b: 2 }, 'elementId', 'myId'])] - ] - } -}) - -function packAndUnpack ( - val, - { bufferSize = 128, disableLosslessIntegers = false, useBigInt = false } = {} -) { - const buffer = alloc(bufferSize) - new Packer(buffer).packable(val)() - buffer.reset() - return new Unpacker(disableLosslessIntegers, useBigInt).unpack(buffer) -} diff --git a/packages/neo4j-driver/test/internal/temporal-factory.test.js b/packages/neo4j-driver/test/internal/temporal-factory.test.js index faf95b858..d4fc13645 100644 --- a/packages/neo4j-driver/test/internal/temporal-factory.test.js +++ b/packages/neo4j-driver/test/internal/temporal-factory.test.js @@ -18,7 +18,7 @@ */ import { int } from 'neo4j-driver-core' -import * as factory from '../../../bolt-connection/lib/packstream/temporal-factory' +import * as factory from '../../../bolt-connection/lib/bolt/temporal-factory' import { types } from '../../src' describe('#unit temporal-factory', () => { diff --git a/packages/testkit-backend/src/channel/testkit-protocol.js b/packages/testkit-backend/src/channel/testkit-protocol.js index e8b1f2d60..f6282c4c7 100644 --- a/packages/testkit-backend/src/channel/testkit-protocol.js +++ b/packages/testkit-backend/src/channel/testkit-protocol.js @@ -61,8 +61,8 @@ export default class Protocol extends EventEmitter { } serializeResponse (response) { - console.log('> writing response', response) const responseStr = stringify(response) + console.log('> writing response', responseStr) return ['#response begin', responseStr, '#response end'].join('\n') + '\n' } diff --git a/packages/testkit-backend/src/skipped-tests/common.js b/packages/testkit-backend/src/skipped-tests/common.js index a6f61fc00..f2fe10fb1 100644 --- a/packages/testkit-backend/src/skipped-tests/common.js +++ b/packages/testkit-backend/src/skipped-tests/common.js @@ -1,6 +1,12 @@ import skip, { ifEquals, ifEndsWith } from './skip' const skippedTests = [ + skip( + 'Testkit implemenation is deprecated', + ifEquals('stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_node_only_element_id'), + ifEquals('stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_path_element_ids_with_only_string'), + ifEquals('stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_rel_only_element_id') + ), skip( 'Skipped because server doesn\'t support protocol 5.0 yet', ifEndsWith('neo4j.test_summary.TestSummary.test_protocol_version_information')