From 8ff22f38f9e7d1c1194a08c07a61531a63074765 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Fri, 1 Jul 2022 17:29:09 +0200 Subject: [PATCH 1/7] Validate the ZoneId of the DateTime with ZoneId The validation of the DateTime was only being done in the new patched protocol while unpacking the struct. This changes force any new DateTime with ZoneID to have a valid ZoneId. This changes also treats struct unpacking errors and defers the occurred to the moment the object is manipulate. For instance, a DateTime with invalid ZoneId returned in a Record will not break the records consumption until any code try to interacts with the broken DateTime. --- .../bolt-connection/src/bolt/transformer.js | 15 ++++++--- .../bolt-protocol-v5x0.test.js.snap | 4 +-- .../test/bolt/bolt-protocol-v1.test.js | 5 +-- .../test/bolt/bolt-protocol-v2.test.js | 11 ++++--- .../test/bolt/bolt-protocol-v3.test.js | 11 ++++--- .../test/bolt/bolt-protocol-v4x0.test.js | 11 ++++--- .../test/bolt/bolt-protocol-v4x1.test.js | 11 ++++--- .../test/bolt/bolt-protocol-v4x2.test.js | 11 ++++--- .../test/bolt/bolt-protocol-v4x3.test.js | 26 ++++++++------- .../test/bolt/bolt-protocol-v4x4.test.js | 26 ++++++++------- .../test/bolt/bolt-protocol-v5x0.test.js | 5 +-- packages/core/src/internal/temporal-util.ts | 10 ++++++ packages/core/src/internal/util.ts | 32 ++++++++++++++++++- packages/core/src/temporal-types.ts | 1 + .../neo4j-driver/test/temporal-types.test.js | 6 ++++ .../src/skipped-tests/common.js | 5 ++- .../testkit-backend/src/skipped-tests/skip.js | 29 ++++++++++++++--- 17 files changed, 154 insertions(+), 65 deletions(-) diff --git a/packages/bolt-connection/src/bolt/transformer.js b/packages/bolt-connection/src/bolt/transformer.js index 485c157dd..726f05fe7 100644 --- a/packages/bolt-connection/src/bolt/transformer.js +++ b/packages/bolt-connection/src/bolt/transformer.js @@ -18,6 +18,9 @@ */ import { structure } from '../packstream' +import { internal } from 'neo4j-driver-core' + +const { util } = internal /** * Class responsible for applying the expected {@link TypeTransformer} to @@ -43,11 +46,15 @@ export default class Transformer { * @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) + try { + if (struct instanceof structure.Structure && this._transformersPerSignature.has(struct.signature)) { + const { fromStructure } = this._transformersPerSignature.get(struct.signature) + return fromStructure(struct) + } + return struct + } catch (error) { + return util.createBrokenObject(error) } - return struct } /** 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 index 4d3877736..b379fa05c 100644 --- 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 @@ -36,9 +36,9 @@ exports[`#unit BoltProtocolV5x0 .unpack() should not unpack with wrong size (Nod 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 less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; -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 (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; 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"`; 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 533f0023b..9d69a2909 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js @@ -568,7 +568,8 @@ describe('#unit BoltProtocolV1', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -580,7 +581,7 @@ describe('#unit BoltProtocolV1', () => { ['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'])] + ['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( 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 94575ea0a..a9d1f1a50 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js @@ -154,7 +154,7 @@ describe('#unit BoltProtocolV2', () => { ['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')], + ['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)] @@ -380,7 +380,7 @@ describe('#unit BoltProtocolV2', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -396,7 +396,8 @@ describe('#unit BoltProtocolV2', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -442,8 +443,8 @@ describe('#unit BoltProtocolV2', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) 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 f8a6e914a..b3139c3cd 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js @@ -359,7 +359,7 @@ describe('#unit BoltProtocolV3', () => { ['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')], + ['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)] @@ -585,7 +585,7 @@ describe('#unit BoltProtocolV3', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -601,7 +601,8 @@ describe('#unit BoltProtocolV3', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -647,8 +648,8 @@ describe('#unit BoltProtocolV3', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) 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 d92f36330..0bd626c9d 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js @@ -277,7 +277,7 @@ describe('#unit BoltProtocolV4x0', () => { ['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')], + ['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)] @@ -503,7 +503,7 @@ describe('#unit BoltProtocolV4x0', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -519,7 +519,8 @@ describe('#unit BoltProtocolV4x0', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -565,8 +566,8 @@ describe('#unit BoltProtocolV4x0', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) 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 48f8ca724..c80b94f86 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js @@ -151,7 +151,7 @@ describe('#unit BoltProtocolV4x1', () => { ['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')], + ['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)] @@ -377,7 +377,7 @@ describe('#unit BoltProtocolV4x1', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -393,7 +393,8 @@ describe('#unit BoltProtocolV4x1', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -439,8 +440,8 @@ describe('#unit BoltProtocolV4x1', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) 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 2000283a3..998ac3206 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js @@ -150,7 +150,7 @@ describe('#unit BoltProtocolV4x2', () => { ['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')], + ['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)] @@ -376,7 +376,7 @@ describe('#unit BoltProtocolV4x2', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -392,7 +392,8 @@ describe('#unit BoltProtocolV4x2', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -438,8 +439,8 @@ describe('#unit BoltProtocolV4x2', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) 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 34bc28f27..6243e1b01 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js @@ -363,7 +363,7 @@ describe('#unit BoltProtocolV4x3', () => { ['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')], + ['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)] @@ -589,7 +589,7 @@ describe('#unit BoltProtocolV4x3', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -605,7 +605,8 @@ describe('#unit BoltProtocolV4x3', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -651,8 +652,8 @@ describe('#unit BoltProtocolV4x3', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) @@ -848,7 +849,7 @@ describe('#unit BoltProtocolV4x3', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x69, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x69, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const packable = protocol.packable(struct) @@ -857,7 +858,8 @@ describe('#unit BoltProtocolV4x3', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -900,7 +902,7 @@ describe('#unit BoltProtocolV4x3', () => { ], [ 'DateTimeWithZoneId/0x66', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo']) ] ])('should unpack deprecated temporal types as unknown structs (%s)', (_, struct) => { const packable = protocol.packable(struct) @@ -948,7 +950,7 @@ describe('#unit BoltProtocolV4x3', () => { ], [ 'DateTimeWithZoneId/0x69', - new structure.Structure(0x69, [1, 2, 'America/Sao Paulo']) + new structure.Structure(0x69, [1, 2, 'America/Sao_Paulo']) ] ])('should unpack utc temporal types as unknown structs (%s)', (_, struct) => { const packable = protocol.packable(struct) @@ -969,8 +971,8 @@ describe('#unit BoltProtocolV4x3', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao_Paulo') ] ])('should unpack temporal types without utc fix (%s)', (_, struct, object) => { const packable = protocol.packable(struct) @@ -984,7 +986,7 @@ describe('#unit BoltProtocolV4x3', () => { }) it.each([ - ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['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)] ])('should pack temporal types (no utc) (%s)', (_, object) => { const packable = protocol.packable(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 a0a046ace..945ded5da 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js @@ -396,7 +396,7 @@ describe('#unit BoltProtocolV4x4', () => { ['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')], + ['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)] @@ -622,7 +622,7 @@ describe('#unit BoltProtocolV4x4', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const buffer = alloc(256) @@ -638,7 +638,8 @@ describe('#unit BoltProtocolV4x4', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -684,8 +685,8 @@ describe('#unit BoltProtocolV4x4', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + 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) @@ -881,7 +882,7 @@ describe('#unit BoltProtocolV4x4', () => { ], [ 'DateTimeWithZoneId with more fields', - new structure.Structure(0x69, [1, 2, 'America/Sao Paulo', 'Brasil']) + new structure.Structure(0x69, [1, 2, 'America/Sao_Paulo', 'Brasil']) ] ])('should not unpack with wrong size (%s)', (_, struct) => { const packable = protocol.packable(struct) @@ -890,7 +891,8 @@ describe('#unit BoltProtocolV4x4', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -933,7 +935,7 @@ describe('#unit BoltProtocolV4x4', () => { ], [ 'DateTimeWithZoneId/0x66', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo']) ] ])('should unpack deprecated temporal types as unknown structs (%s)', (_, struct) => { const packable = protocol.packable(struct) @@ -981,7 +983,7 @@ describe('#unit BoltProtocolV4x4', () => { ], [ 'DateTimeWithZoneId/0x69', - new structure.Structure(0x69, [1, 2, 'America/Sao Paulo']) + new structure.Structure(0x69, [1, 2, 'America/Sao_Paulo']) ] ])('should unpack utc temporal types as unknown structs (%s)', (_, struct) => { const packable = protocol.packable(struct) @@ -1002,8 +1004,8 @@ describe('#unit BoltProtocolV4x4', () => { ], [ 'DateTimeWithZoneId', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']), - new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao Paulo') + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo']), + new DateTime(1970, 1, 1, 0, 0, 1, 2, undefined, 'America/Sao_Paulo') ] ])('should unpack temporal types without utc fix (%s)', (_, struct, object) => { const packable = protocol.packable(struct) @@ -1017,7 +1019,7 @@ describe('#unit BoltProtocolV4x4', () => { }) it.each([ - ['DateTimeWithZoneId', new DateTime(1, 1, 1, 1, 1, 1, 1, undefined, 'America/Sao Paulo')], + ['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)] ])('should pack temporal types (no utc) (%s)', (_, object) => { const packable = protocol.packable(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 223cac107..a2bcd6c99 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js @@ -790,7 +790,8 @@ describe('#unit BoltProtocolV5x0', () => { buffer.reset() - expect(() => protocol.unpack(buffer)).toThrowErrorMatchingSnapshot() + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() }) it.each([ @@ -891,7 +892,7 @@ describe('#unit BoltProtocolV5x0', () => { ], [ 'DateTimeWithZoneId/0x66', - new structure.Structure(0x66, [1, 2, 'America/Sao Paulo']) + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo']) ] ])('should unpack deprecated temporal types as unknown structs (%s)', (_, struct) => { const buffer = alloc(256) diff --git a/packages/core/src/internal/temporal-util.ts b/packages/core/src/internal/temporal-util.ts index 7598d237e..f23e43884 100644 --- a/packages/core/src/internal/temporal-util.ts +++ b/packages/core/src/internal/temporal-util.ts @@ -417,6 +417,16 @@ export function assertValidNanosecond ( ) } +export function assertValidZoneId (fieldName: string, zoneId: string): void { + try { + Intl.DateTimeFormat(undefined, { timeZone: zoneId }) + } catch (e) { + throw newError( + `${fieldName} is expected to be a valid ZoneId but was: "${zoneId}"` + ) + } +} + /** * Check if the given value is of expected type and is in the expected range. * @param {Integer|number} value the value to check. diff --git a/packages/core/src/internal/util.ts b/packages/core/src/internal/util.ts index f5d6bbbbf..a75fa6353 100644 --- a/packages/core/src/internal/util.ts +++ b/packages/core/src/internal/util.ts @@ -224,6 +224,35 @@ function isString (str: any): str is string { return Object.prototype.toString.call(str) === '[object String]' } +/** + * Creates a object which all method call will thrown the given error + * + * @param {Error} error The error + * @param {any} object The object. Default: {} + * @returns {any} A broken object + */ +function createBrokenObject (error: Error, object: any = {}): T { + const thrown: () => void = () => { + throw error + } + + return new Proxy(object, { + get: thrown, + set: thrown, + apply: thrown, + construct: thrown, + defineProperty: thrown, + deleteProperty: thrown, + getOwnPropertyDescriptor: thrown, + getPrototypeOf: thrown, + has: thrown, + isExtensible: thrown, + ownKeys: thrown, + preventExtensions: thrown, + setPrototypeOf: thrown + }) +} + export { isEmptyObjectOrNull, isObject, @@ -235,5 +264,6 @@ export { assertValidDate, validateQueryAndParameters, ENCRYPTION_ON, - ENCRYPTION_OFF + ENCRYPTION_OFF, + createBrokenObject } diff --git a/packages/core/src/temporal-types.ts b/packages/core/src/temporal-types.ts index 26cc0179e..36281048e 100644 --- a/packages/core/src/temporal-types.ts +++ b/packages/core/src/temporal-types.ts @@ -768,6 +768,7 @@ function verifyTimeZoneArguments ( if (idDefined) { assertString(timeZoneId, 'Time zone ID') + util.assertValidZoneId('Time zone ID', timeZoneId) result[1] = timeZoneId } diff --git a/packages/neo4j-driver/test/temporal-types.test.js b/packages/neo4j-driver/test/temporal-types.test.js index 395ce09a6..b972cbcaf 100644 --- a/packages/neo4j-driver/test/temporal-types.test.js +++ b/packages/neo4j-driver/test/temporal-types.test.js @@ -1400,6 +1400,12 @@ describe('#integration temporal-types', () => { verifyTimeZoneOffset(neo4jDateTime5, -1 * 150 * 60, '-02:30') }, 60000) + it('should not create DateTime with invalid ZoneId', () => { + expect(() => dateTimeWithZoneId(1999, 10, 1, 10, 15, 0, 0, 'Europe/Neo4j')).toThrowError( + 'Time zone ID is expected to be a valid ZoneId but was "Europe/Neo4j"' + ) + }) + function testSendAndReceiveRandomTemporalValues (valueGenerator) { const asyncFunction = (index, callback) => { testSendReceiveTemporalValue(valueGenerator()) diff --git a/packages/testkit-backend/src/skipped-tests/common.js b/packages/testkit-backend/src/skipped-tests/common.js index e0602f137..bbe75f9be 100644 --- a/packages/testkit-backend/src/skipped-tests/common.js +++ b/packages/testkit-backend/src/skipped-tests/common.js @@ -1,8 +1,11 @@ -import skip, { ifEquals, ifEndsWith } from './skip' +import skip, { ifEquals, ifEndsWith, endsWith, ifStartsWith, startsWith, not } from './skip' const skippedTests = [ skip( 'Driver does not return offset for old DateTime implementations', + ifStartsWith('stub.types.test_temporal_types.TestTemporalTypes') + .and(not(startsWith('stub.types.test_temporal_types.TestTemporalTypesV5'))) + .and(endsWith('test_zoned_date_time')), ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_nested_datetime'), ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_should_echo_all_timezone_ids'), ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_cypher_created_datetime') diff --git a/packages/testkit-backend/src/skipped-tests/skip.js b/packages/testkit-backend/src/skipped-tests/skip.js index adb997a97..30d5d14b8 100644 --- a/packages/testkit-backend/src/skipped-tests/skip.js +++ b/packages/testkit-backend/src/skipped-tests/skip.js @@ -1,21 +1,42 @@ + +function asComposablePredicate (predicate) { + return new Proxy(predicate, { + get: (target, p) => { + if (p === 'and') { + return otherPredicate => asComposablePredicate(testName => target(testName) && otherPredicate(testName)) + } else if (p === 'or') { + return otherPredicate => asComposablePredicate(testName => target(testName) || otherPredicate(testName)) + } + return target[p] + } + }) +} + export function ifEndsWith (suffix) { - return testName => testName.endsWith(suffix) + return asComposablePredicate(testName => testName.endsWith(suffix)) } export function ifStartsWith (prefix) { - return testName => testName.startsWith(prefix) + return asComposablePredicate(testName => testName.startsWith(prefix)) } export function ifEquals (expectedName) { - return testName => testName === expectedName + return asComposablePredicate(testName => testName === expectedName) } export function or () { - return testName => [...arguments].find(predicate => predicate(testName)) + return asComposablePredicate(testName => [...arguments].find(predicate => predicate(testName))) +} + +export function not (predicate) { + return asComposablePredicate(testName => !predicate(testName)) } export function skip (reason, ...predicate) { return { reason, predicate: or(...predicate) } } +export const endsWith = ifEndsWith +export const startsWith = ifStartsWith + export default skip From 76151302e7b7bb98fd9ac1c9603d21b6caa18f8c Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Mon, 4 Jul 2022 12:59:16 +0200 Subject: [PATCH 2/7] Fix test setup --- packages/neo4j-driver/test/temporal-types.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/neo4j-driver/test/temporal-types.test.js b/packages/neo4j-driver/test/temporal-types.test.js index b972cbcaf..6eab9bd10 100644 --- a/packages/neo4j-driver/test/temporal-types.test.js +++ b/packages/neo4j-driver/test/temporal-types.test.js @@ -43,8 +43,8 @@ const MIN_YEAR = -MAX_YEAR const MAX_TIME_ZONE_OFFSET = 64800 const MIN_TIME_ZONE_OFFSET = -MAX_TIME_ZONE_OFFSET const SECONDS_PER_MINUTE = 60 -const MIN_ZONE_ID = 'Etc/GMT+12' -const MAX_ZONE_ID = 'Etc/GMT-14' +const MIN_ZONE_ID = 'Pacific/Samoa' +const MAX_ZONE_ID = 'Pacific/Kiritimati' const ZONE_IDS = ['Europe/Zaporozhye', 'Europe/London', 'UTC', 'Africa/Cairo'] describe('#integration temporal-types', () => { From f60a03596587cb631e75fa6d57c8d269c1d6bbeb Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 5 Jul 2022 13:06:56 +0200 Subject: [PATCH 3/7] Adjusting tests --- packages/neo4j-driver/test/temporal-types.test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/neo4j-driver/test/temporal-types.test.js b/packages/neo4j-driver/test/temporal-types.test.js index 6eab9bd10..b23a45066 100644 --- a/packages/neo4j-driver/test/temporal-types.test.js +++ b/packages/neo4j-driver/test/temporal-types.test.js @@ -643,9 +643,6 @@ describe('#integration temporal-types', () => { 'Asia/Yangon' ).toString() ).toEqual('-30455-05-05T12:24:10.000000123[Asia/Yangon]') - expect( - dateTimeWithZoneId(248, 12, 30, 23, 59, 59, 3, 'CET').toString() - ).toEqual('0248-12-30T23:59:59.000000003[CET]') }, 60000) it('should expose local time components in time', () => { @@ -1402,7 +1399,7 @@ describe('#integration temporal-types', () => { it('should not create DateTime with invalid ZoneId', () => { expect(() => dateTimeWithZoneId(1999, 10, 1, 10, 15, 0, 0, 'Europe/Neo4j')).toThrowError( - 'Time zone ID is expected to be a valid ZoneId but was "Europe/Neo4j"' + 'Time zone ID is expected to be a valid ZoneId but was: "Europe/Neo4j"' ) }) From d6b5e07235cc1eb549e20b0631c1128d8cd59b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Barc=C3=A9los?= Date: Tue, 5 Jul 2022 16:02:37 +0200 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Robsdedude --- packages/core/src/internal/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/internal/util.ts b/packages/core/src/internal/util.ts index a75fa6353..118dee090 100644 --- a/packages/core/src/internal/util.ts +++ b/packages/core/src/internal/util.ts @@ -225,7 +225,7 @@ function isString (str: any): str is string { } /** - * Creates a object which all method call will thrown the given error + * Creates a object which all method call will throw the given error * * @param {Error} error The error * @param {any} object The object. Default: {} From 8dc36a2a154ee017e2a4ca4fdb2d96b1df6b8314 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 5 Jul 2022 16:05:30 +0200 Subject: [PATCH 5/7] Rename `thrown` to `fail` --- packages/core/src/internal/util.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/src/internal/util.ts b/packages/core/src/internal/util.ts index 118dee090..a33839372 100644 --- a/packages/core/src/internal/util.ts +++ b/packages/core/src/internal/util.ts @@ -232,24 +232,24 @@ function isString (str: any): str is string { * @returns {any} A broken object */ function createBrokenObject (error: Error, object: any = {}): T { - const thrown: () => void = () => { + const fail: () => void = () => { throw error } return new Proxy(object, { - get: thrown, - set: thrown, - apply: thrown, - construct: thrown, - defineProperty: thrown, - deleteProperty: thrown, - getOwnPropertyDescriptor: thrown, - getPrototypeOf: thrown, - has: thrown, - isExtensible: thrown, - ownKeys: thrown, - preventExtensions: thrown, - setPrototypeOf: thrown + get: fail, + set: fail, + apply: fail, + construct: fail, + defineProperty: fail, + deleteProperty: fail, + getOwnPropertyDescriptor: fail, + getPrototypeOf: fail, + has: fail, + isExtensible: fail, + ownKeys: fail, + preventExtensions: fail, + setPrototypeOf: fail }) } From 40966436ec363d1888e42413342f86e14244f53e Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 5 Jul 2022 16:53:15 +0200 Subject: [PATCH 6/7] Fix typescript --- packages/core/src/internal/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/internal/util.ts b/packages/core/src/internal/util.ts index a33839372..2b6b3e722 100644 --- a/packages/core/src/internal/util.ts +++ b/packages/core/src/internal/util.ts @@ -232,7 +232,7 @@ function isString (str: any): str is string { * @returns {any} A broken object */ function createBrokenObject (error: Error, object: any = {}): T { - const fail: () => void = () => { + const fail: () => T = () => { throw error } From 6e3ca9c1b639f93f03966072a8afda23cc42b19c Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 7 Jul 2022 17:26:56 +0200 Subject: [PATCH 7/7] Skipping invalid tz-id --- .../testkit-backend/src/request-handlers.js | 20 ++++++++++++++++++- packages/testkit-backend/src/responses.js | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/testkit-backend/src/request-handlers.js b/packages/testkit-backend/src/request-handlers.js index aecdfdace..9abc93dd8 100644 --- a/packages/testkit-backend/src/request-handlers.js +++ b/packages/testkit-backend/src/request-handlers.js @@ -342,11 +342,29 @@ export function StartTest (context, { testName }, wire) { } const shouldRunTest = context.getShouldRunTestFunction() shouldRunTest(testName, { - onRun: () => wire.writeResponse(responses.RunTest()), + onRun: () => { + if (testName === 'neo4j.datatypes.test_temporal_types.TestDataTypes.test_date_time_cypher_created_tz_id') { + return wire.writeResponse(responses.RunSubTests()) + } + return wire.writeResponse(responses.RunTest()) + }, onSkip: reason => wire.writeResponse(responses.SkipTest({ reason })) }) } +export function StartSubTest (context, { testName, subtestArguments }, wire) { + if (testName === 'neo4j.datatypes.test_temporal_types.TestDataTypes.test_date_time_cypher_created_tz_id') { + try { + Intl.DateTimeFormat(undefined, { timeZone: subtestArguments.tz_id }) + return wire.writeResponse(responses.RunTest()) + } catch (e) { + wire.writeResponse(responses.SkipTest({ reason: `Unsupported tzid: ${subtestArguments.tz_id}` })) + } + } else { + wire.writeBackendError(`No entry for ${testName} in StartSubTest`) + } +} + export function GetFeatures (context, _params, wire) { wire.writeResponse(responses.FeatureList({ features: context.getFeatures() diff --git a/packages/testkit-backend/src/responses.js b/packages/testkit-backend/src/responses.js index c0331bc70..03a9f0487 100644 --- a/packages/testkit-backend/src/responses.js +++ b/packages/testkit-backend/src/responses.js @@ -85,6 +85,10 @@ export function RunTest () { return response('RunTest', null) } +export function RunSubTests () { + return response('RunSubTests', null) +} + export function SkipTest ({ reason }) { return response('SkipTest', { reason }) }