From a03e1a598b4d898ef5151faade45c011e52d6607 Mon Sep 17 00:00:00 2001 From: Heinrich Klobuczek Date: Sun, 8 Oct 2023 13:36:47 +0200 Subject: [PATCH] partial implementation of testkit tests for temporal types --- .github/workflows/specs.yml | 2 +- Rakefile | 4 ++-- jruby/neo4j/driver/ext/ruby_converter.rb | 2 +- .../driver/internal/duration_normalizer.rb | 5 +++++ .../messaging/common/common_value_unpacker.rb | 5 +---- testkit-backend/bin/setup | 2 +- testkit-backend/lib/testkit/backend.rb | 1 + .../lib/testkit/backend/messages/conversion.rb | 18 ++++++++++++++++++ .../lib/testkit/backend/messages/request.rb | 2 +- .../backend/messages/requests/cypher_date.rb | 9 +++++++++ .../messages/requests/cypher_date_time.rb | 15 +++++++++++++++ .../messages/requests/cypher_duration.rb | 9 +++++++++ .../backend/messages/requests/cypher_time.rb | 13 +++++++++++++ .../backend/messages/requests/get_features.rb | 2 +- testkit-backend/testkit-backend.gemspec | 2 +- 15 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 testkit-backend/lib/testkit/backend/messages/requests/cypher_date.rb create mode 100644 testkit-backend/lib/testkit/backend/messages/requests/cypher_date_time.rb create mode 100644 testkit-backend/lib/testkit/backend/messages/requests/cypher_duration.rb create mode 100644 testkit-backend/lib/testkit/backend/messages/requests/cypher_time.rb diff --git a/.github/workflows/specs.yml b/.github/workflows/specs.yml index d47434ea..40e75e04 100644 --- a/.github/workflows/specs.yml +++ b/.github/workflows/specs.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: ruby: [ jruby, ruby ] - neo4j: [ 4.4.20, 5.7.0 ] + neo4j: [ 4.4.26, 5.12.0 ] include: - ruby: jruby java-version: 17 diff --git a/Rakefile b/Rakefile index 486a8876..26509154 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ HOE = Class.new(Hoe) do end.spec 'neo4j-ruby-driver' do developer 'Heinrich Klobuczek', 'heinrich@mail.com' - dependency 'activesupport', '>= 0' + dependency 'activesupport', '< 7.1' # dependency 'async-rspec', '>= 0', :dev dependency 'ffaker', '>= 0', :dev dependency 'hoe', '>= 0', :dev @@ -41,7 +41,7 @@ end.spec 'neo4j-ruby-driver' do dependency 'jar-dependencies', '>= 0.4.1' dependency 'ruby-maven', '>= 0', :dev - spec_extras[:requirements] = ->(requirements) { requirements << 'jar org.neo4j.driver, neo4j-java-driver-all, 5.8.0' } + spec_extras[:requirements] = ->(requirements) { requirements << 'jar org.neo4j.driver, neo4j-java-driver-all, 5.13.0' } spec_extras[:platform] = 'java' else require_ruby_version '>= 3.1' diff --git a/jruby/neo4j/driver/ext/ruby_converter.rb b/jruby/neo4j/driver/ext/ruby_converter.rb index 3778b351..317790b4 100644 --- a/jruby/neo4j/driver/ext/ruby_converter.rb +++ b/jruby/neo4j/driver/ext/ruby_converter.rb @@ -16,7 +16,7 @@ def as_ruby_object date = as_local_date Date.new(date.year, date.month_value, date.day_of_month) when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::DURATION - ActiveSupport::Duration.parse(as_iso_duration.to_s) + Driver::Internal::DurationNormalizer.create(*%i[months days seconds nanoseconds].map(&as_object.method(:send))) when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::POINT point = as_point Types::Point.new(srid: point.srid, x: point.x, y: point.y, z: nullable(point.z)) diff --git a/lib/neo4j/driver/internal/duration_normalizer.rb b/lib/neo4j/driver/internal/duration_normalizer.rb index b2f1169a..e44e5f83 100644 --- a/lib/neo4j/driver/internal/duration_normalizer.rb +++ b/lib/neo4j/driver/internal/duration_normalizer.rb @@ -21,6 +21,11 @@ def milliseconds(duration) duration&.in_milliseconds&.round end + def create(months, days, seconds, nanoseconds) + { months:, days:, seconds: seconds + (nanoseconds.zero? ? 0 : nanoseconds * BigDecimal('1e-9')) } + .sum { |key, value| ActiveSupport::Duration.send(key, value) } + end + private def divmod(number, factor) diff --git a/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb b/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb index e0d38b2f..4c4fd787 100644 --- a/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb +++ b/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb @@ -214,10 +214,7 @@ def unpack_date_time_with_zone_id end def unpack_duration - ActiveSupport::Duration.months(unpack) + - ActiveSupport::Duration.days(unpack) + - ActiveSupport::Duration.seconds(unpack) + - ActiveSupport::Duration.seconds(unpack * BigDecimal('1e-9')) + DurationNormalizer.create(*4.times.map { unpack }) end def unpack_point2_d diff --git a/testkit-backend/bin/setup b/testkit-backend/bin/setup index dce67d86..6cdca353 100755 --- a/testkit-backend/bin/setup +++ b/testkit-backend/bin/setup @@ -3,6 +3,6 @@ set -euo pipefail IFS=$'\n\t' set -vx -bundle install +bundle update # Do any other automated setup that you need to do here diff --git a/testkit-backend/lib/testkit/backend.rb b/testkit-backend/lib/testkit/backend.rb index 15986862..efaa6d91 100644 --- a/testkit-backend/lib/testkit/backend.rb +++ b/testkit-backend/lib/testkit/backend.rb @@ -1,6 +1,7 @@ require "active_support/core_ext/module/attribute_accessors" require 'active_support/inflector' require 'async/io' +require 'bigdecimal' require 'neo4j/driver' require 'nio' require 'testkit/backend/loader' diff --git a/testkit-backend/lib/testkit/backend/messages/conversion.rb b/testkit-backend/lib/testkit/backend/messages/conversion.rb index 7dde80a8..186a5ccc 100644 --- a/testkit-backend/lib/testkit/backend/messages/conversion.rb +++ b/testkit-backend/lib/testkit/backend/messages/conversion.rb @@ -18,6 +18,18 @@ def to_testkit(object) else value_entity('CypherString', object) end + when Date + named_entity('CypherDate', **map_of(object, :year, :month, :day)) + when Neo4j::Driver::Types::OffsetTime + named_entity('CypherTime', hour: object.hour, minute: object.min, second: object.sec, nanosecond: object.nsec, 'utc_offset_s' => object.utc_offset) + when Neo4j::Driver::Types::LocalTime + named_entity('CypherTime', hour: object.hour, minute: object.min, second: object.sec, nanosecond: object.nsec) + when Neo4j::Driver::Types::LocalDateTime + named_entity('CypherDateTime', **map_of(object, :year, :month, :day, :hour), minute: object.min, second: object.sec, nanosecond: object.nsec) + when Time + named_entity('CypherDateTime', **map_of(object, :year, :month, :day, :hour), minute: object.min, second: object.sec, nanosecond: object.nsec, 'utc_offset_s' => object.utc_offset, 'timezone_id' => object.try(:time_zone)&.name) + when ActiveSupport::Duration + named_entity('CypherDuration', **%i[months days seconds nanoseconds].zip(Neo4j::Driver::Internal::DurationNormalizer.normalize(object)).to_h) when Symbol to_testkit(object.to_s) when Neo4j::Driver::Types::Path @@ -50,6 +62,12 @@ def float_encode(f) f end end + + private + + def map_of(object, *keys) + keys.to_h { |key| [key, object.send(key)] } + end end end end diff --git a/testkit-backend/lib/testkit/backend/messages/request.rb b/testkit-backend/lib/testkit/backend/messages/request.rb index 24e57e1d..685bb81d 100644 --- a/testkit-backend/lib/testkit/backend/messages/request.rb +++ b/testkit-backend/lib/testkit/backend/messages/request.rb @@ -56,7 +56,7 @@ def reference(name) def named_entity(name, **hash) { name: name }.tap do |entity| - entity[:data] = hash.transform_keys { |key| key.to_s.camelize(:lower) } unless hash.empty? + entity[:data] = hash.transform_keys { |key| key.is_a?(String) ? key : key.to_s.camelize(:lower) } unless hash.empty? end end diff --git a/testkit-backend/lib/testkit/backend/messages/requests/cypher_date.rb b/testkit-backend/lib/testkit/backend/messages/requests/cypher_date.rb new file mode 100644 index 00000000..35fe42d0 --- /dev/null +++ b/testkit-backend/lib/testkit/backend/messages/requests/cypher_date.rb @@ -0,0 +1,9 @@ +module Testkit::Backend::Messages + module Requests + class CypherDate < Request + def to_object + Date.new(year, month, day) + end + end + end +end diff --git a/testkit-backend/lib/testkit/backend/messages/requests/cypher_date_time.rb b/testkit-backend/lib/testkit/backend/messages/requests/cypher_date_time.rb new file mode 100644 index 00000000..4caf2673 --- /dev/null +++ b/testkit-backend/lib/testkit/backend/messages/requests/cypher_date_time.rb @@ -0,0 +1,15 @@ +module Testkit::Backend::Messages + module Requests + class CypherDateTime < Request + def to_object + if timezone_id + Time.new(year, month, day, hour, minute, second + nanosecond * 1e-9, utc_offset_s).in_time_zone(TZInfo::Timezone.get(timezone_id)) + elsif utc_offset_s + Time.new(year, month, day, hour, minute, second + nanosecond * 1e-9, utc_offset_s) + else + Neo4j::Driver::Types::LocalDateTime.new(Time.new(year, month, day, hour, minute, second + nanosecond * 1e-9)) + end + end + end + end +end diff --git a/testkit-backend/lib/testkit/backend/messages/requests/cypher_duration.rb b/testkit-backend/lib/testkit/backend/messages/requests/cypher_duration.rb new file mode 100644 index 00000000..4e173d15 --- /dev/null +++ b/testkit-backend/lib/testkit/backend/messages/requests/cypher_duration.rb @@ -0,0 +1,9 @@ +module Testkit::Backend::Messages + module Requests + class CypherDuration < Request + def to_object + Neo4j::Driver::Internal::DurationNormalizer.create(months, days, seconds, nanoseconds) + end + end + end +end diff --git a/testkit-backend/lib/testkit/backend/messages/requests/cypher_time.rb b/testkit-backend/lib/testkit/backend/messages/requests/cypher_time.rb new file mode 100644 index 00000000..316d402b --- /dev/null +++ b/testkit-backend/lib/testkit/backend/messages/requests/cypher_time.rb @@ -0,0 +1,13 @@ +module Testkit::Backend::Messages + module Requests + class CypherTime < Request + def to_object + if utc_offset_s + Neo4j::Driver::Types::OffsetTime.new(Time.new(1, 1, 1, hour, minute, second + nanosecond * 1e-9, utc_offset_s)) + else + Neo4j::Driver::Types::LocalTime.new(Time.new(1, 1, 1, hour, minute, second + nanosecond * 1e-9)) + end + end + end + end +end diff --git a/testkit-backend/lib/testkit/backend/messages/requests/get_features.rb b/testkit-backend/lib/testkit/backend/messages/requests/get_features.rb index 73020868..a2d93cd5 100644 --- a/testkit-backend/lib/testkit/backend/messages/requests/get_features.rb +++ b/testkit-backend/lib/testkit/backend/messages/requests/get_features.rb @@ -25,7 +25,7 @@ class GetFeatures < Request 'Feature:API:SSLConfig' => 'ja', 'Feature:API:SSLSchemes' => 'ja', 'Feature:API:Type.Spatial' => '', - 'Feature:API:Type.Temporal' => 'a', + 'Feature:API:Type.Temporal' => 'a', # Most tests pass. Implement SubTests to skip some e.g. old System timezone 'Feature:Auth:Bearer' => 'jar', 'Feature:Auth:Custom' => 'jar', 'Feature:Auth:Kerberos' => 'jar', diff --git a/testkit-backend/testkit-backend.gemspec b/testkit-backend/testkit-backend.gemspec index c289bdc9..2a3953cc 100644 --- a/testkit-backend/testkit-backend.gemspec +++ b/testkit-backend/testkit-backend.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'async-io' spec.add_dependency 'nio4r' spec.add_dependency 'zeitwerk' - spec.add_dependency 'activesupport' + spec.add_dependency 'activesupport', '< 7.1' spec.add_development_dependency 'rspec' # spec.add_development_dependency "async-rspec", "~> 1.10" end