From acd1bd0f67ea3fd5da5994aa1033684eed9ec4ab Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 30 Sep 2020 17:39:43 +0200 Subject: [PATCH] Remove protocol 1 and 2 The reference this protocols are not needed since it's not used by the driver anymore. During the remotion of these protocol version, the tests were refactoried to be specific per version instead of assume the re-use done on the protocols implementation. --- .../org/neo4j/driver/TransactionConfig.java | 1 - .../async/connection/BoltProtocolUtil.java | 2 - .../internal/handlers/PullHandlers.java | 8 - .../internal/messaging/BoltProtocol.java | 17 +- .../CommonMessageReader.java} | 12 +- .../CommonValuePacker.java} | 148 ++++- .../CommonValueUnpacker.java} | 176 +++++- .../internal/messaging/v1/BoltProtocolV1.java | 287 ---------- .../messaging/v1/MessageFormatV1.java | 45 -- .../messaging/v1/MessageWriterV1.java | 61 --- .../internal/messaging/v1/ValuePackerV1.java | 129 ----- .../internal/messaging/v2/BoltProtocolV2.java | 43 -- .../messaging/v2/MessageFormatV2.java | 63 --- .../messaging/v2/MessageReaderV2.java | 30 - .../messaging/v2/MessageWriterV2.java | 30 - .../messaging/v2/ValueUnpackerV2.java | 179 ------ .../messaging/v3/MessageFormatV3.java | 4 +- .../messaging/v3/MessageWriterV3.java | 4 +- .../messaging/v4/MessageFormatV4.java | 4 +- .../messaging/v4/MessageWriterV4.java | 4 +- .../driver/internal/InternalResultTest.java | 11 +- .../async/AsyncResultCursorImplTest.java | 8 +- .../async/UnmanagedTransactionTest.java | 25 +- .../connection/BoltProtocolUtilTest.java | 2 - .../ChannelPipelineBuilderImplTest.java | 4 +- .../HandshakeCompletedListenerTest.java | 21 - .../connection/HandshakeHandlerTest.java | 16 - .../inbound/InboundMessageHandlerTest.java | 12 +- .../outbound/OutboundMessageHandlerTest.java | 15 +- .../handlers/HelloResponseHandlerTest.java | 4 +- .../handlers/InitResponseHandlerTest.java | 8 +- .../LegacyPullAllResponseHandlerTest.java | 9 +- .../handlers/RunResponseHandlerTest.java | 10 +- ...ionPullResponseCompletionListenerTest.java | 6 +- ...ionPullResponseCompletionListenerTest.java | 2 +- .../pulln/AutoPullResponseHandlerTest.java | 12 +- .../internal/messaging/BoltProtocolTest.java | 6 +- .../internal/messaging/MessageFormatTest.java | 42 +- .../messaging/v1/BoltProtocolV1Test.java | 389 ------------- .../messaging/v1/MessageReaderV1Test.java | 59 -- .../messaging/v1/MessageWriterV1Test.java | 79 --- .../messaging/v2/BoltProtocolV2Test.java | 38 -- .../messaging/v2/MessageFormatV2Test.java | 422 -------------- .../messaging/v2/MessageReaderV2Test.java | 65 --- .../messaging/v2/MessageWriterV2Test.java | 76 --- .../messaging/v3/BoltProtocolV3Test.java | 24 +- .../messaging/v3/MessageFormatV3Test.java | 19 +- .../messaging/v3/MessageReaderV3Test.java | 120 ++++ .../messaging/v3/MessageWriterV3Test.java | 41 +- .../messaging/v4/BoltProtocolV4Test.java | 305 ++++++++++- .../messaging/v4/MessageFormatV4Test.java | 15 +- .../messaging/v4/MessageReaderV4Test.java | 120 ++++ .../messaging/v4/MessageWriterV4Test.java | 41 +- .../messaging/v41/BoltProtocolV41Test.java | 514 +++++++++++++++++- .../messaging/v41/MessageFormatV41Test.java | 58 ++ .../messaging/v41/MessageReaderV41Test.java | 120 ++++ .../messaging/v41/MessageWriterV41Test.java | 137 +++++ .../AbstractMessageReaderTestBase.java | 81 +-- .../messaging/KnowledgeableMessageFormat.java | 21 +- .../java/org/neo4j/driver/util/TestUtil.java | 68 +-- 60 files changed, 1931 insertions(+), 2341 deletions(-) rename driver/src/main/java/org/neo4j/driver/internal/messaging/{v1/MessageReaderV1.java => common/CommonMessageReader.java} (91%) rename driver/src/main/java/org/neo4j/driver/internal/messaging/{v2/ValuePackerV2.java => common/CommonValuePacker.java} (61%) rename driver/src/main/java/org/neo4j/driver/internal/messaging/{v1/ValueUnpackerV1.java => common/CommonValueUnpacker.java} (58%) delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageFormatV1.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValuePackerV1.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2.java delete mode 100644 driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValueUnpackerV2.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1Test.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2Test.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2Test.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2Test.java delete mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageReaderV3Test.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageReaderV4Test.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageFormatV41Test.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageReaderV41Test.java create mode 100644 driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageWriterV41Test.java diff --git a/driver/src/main/java/org/neo4j/driver/TransactionConfig.java b/driver/src/main/java/org/neo4j/driver/TransactionConfig.java index d7dee3e0bd..3739699cfc 100644 --- a/driver/src/main/java/org/neo4j/driver/TransactionConfig.java +++ b/driver/src/main/java/org/neo4j/driver/TransactionConfig.java @@ -202,7 +202,6 @@ public Builder withTimeout( Duration timeout ) public Builder withMetadata( Map metadata ) { requireNonNull( metadata, "Transaction metadata should not be null" ); - this.metadata = Extract.mapOfValues( metadata ); return this; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java index 065837b762..a3174d99f9 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtil.java @@ -21,8 +21,6 @@ import io.netty.buffer.ByteBuf; import org.neo4j.driver.internal.messaging.BoltProtocolVersion; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41; diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java index 14e383a787..8f12430d7c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/PullHandlers.java @@ -24,19 +24,11 @@ import org.neo4j.driver.internal.handlers.pulln.AutoPullResponseHandler; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; import org.neo4j.driver.internal.handlers.pulln.PullResponseHandler; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.Connection; public class PullHandlers { - public static PullAllResponseHandler newBoltV1PullAllHandler(Query query, RunResponseHandler runHandler, - Connection connection, UnmanagedTransaction tx ) - { - PullResponseCompletionListener completionListener = createPullResponseCompletionListener( connection, BookmarkHolder.NO_OP, tx ); - - return new LegacyPullAllResponseHandler(query, runHandler, connection, BoltProtocolV1.METADATA_EXTRACTOR, completionListener ); - } public static PullAllResponseHandler newBoltV3PullAllHandler(Query query, RunResponseHandler runHandler, Connection connection, BookmarkHolder bookmarkHolder, UnmanagedTransaction tx ) diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java index 713123ffc9..c3cbcdebed 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/BoltProtocol.java @@ -21,7 +21,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelPromise; -import java.util.Map; import java.util.concurrent.CompletionStage; import org.neo4j.driver.AuthToken; @@ -30,15 +29,12 @@ import org.neo4j.driver.Session; import org.neo4j.driver.Transaction; import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.BookmarkHolder; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.async.UnmanagedTransaction; import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.ResultCursorFactory; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41; @@ -155,22 +151,15 @@ static BoltProtocol forChannel( Channel channel ) */ static BoltProtocol forVersion( BoltProtocolVersion version ) { - if ( BoltProtocolV1.VERSION.equals( version ) ) - { - return BoltProtocolV1.INSTANCE; - } - else if ( BoltProtocolV2.VERSION.equals( version ) ) - { - return BoltProtocolV2.INSTANCE; - } - else if ( BoltProtocolV3.VERSION.equals( version ) ) + if ( BoltProtocolV3.VERSION.equals( version ) ) { return BoltProtocolV3.INSTANCE; } else if ( BoltProtocolV4.VERSION.equals( version ) ) { return BoltProtocolV4.INSTANCE; - } else if ( BoltProtocolV41.VERSION.equals( version ) ) + } + else if ( BoltProtocolV41.VERSION.equals( version ) ) { return BoltProtocolV41.INSTANCE; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonMessageReader.java similarity index 91% rename from driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1.java rename to driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonMessageReader.java index f4920c8e99..e221c6a453 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonMessageReader.java @@ -16,11 +16,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.messaging.v1; +package org.neo4j.driver.internal.messaging.common; import java.io.IOException; import java.util.Map; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.messaging.ResponseMessageHandler; import org.neo4j.driver.internal.messaging.ValueUnpacker; @@ -29,18 +30,17 @@ import org.neo4j.driver.internal.messaging.response.RecordMessage; import org.neo4j.driver.internal.messaging.response.SuccessMessage; import org.neo4j.driver.internal.packstream.PackInput; -import org.neo4j.driver.Value; -public class MessageReaderV1 implements MessageFormat.Reader +public class CommonMessageReader implements MessageFormat.Reader { private final ValueUnpacker unpacker; - public MessageReaderV1( PackInput input ) + public CommonMessageReader( PackInput input ) { - this( new ValueUnpackerV1( input ) ); + this( new CommonValueUnpacker( input ) ); } - protected MessageReaderV1( ValueUnpacker unpacker ) + protected CommonMessageReader( ValueUnpacker unpacker ) { this.unpacker = unpacker; } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValuePackerV2.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonValuePacker.java similarity index 61% rename from driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValuePackerV2.java rename to driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonValuePacker.java index 89bb611ba0..74fb43da33 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValuePackerV2.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonValuePacker.java @@ -16,7 +16,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.messaging.v2; + +package org.neo4j.driver.internal.messaging.common; import java.io.IOException; import java.time.LocalDate; @@ -26,47 +27,99 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Map; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.InternalPoint2D; import org.neo4j.driver.internal.InternalPoint3D; -import org.neo4j.driver.internal.messaging.v1.ValuePackerV1; +import org.neo4j.driver.internal.messaging.ValuePacker; import org.neo4j.driver.internal.packstream.PackOutput; -import org.neo4j.driver.internal.types.TypeConstructor; +import org.neo4j.driver.internal.packstream.PackStream; import org.neo4j.driver.internal.value.InternalValue; import org.neo4j.driver.types.IsoDuration; import org.neo4j.driver.types.Point; import static java.time.ZoneOffset.UTC; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_TIME_WITH_ZONE_ID; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_TIME_WITH_ZONE_OFFSET; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DURATION; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DURATION_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_DATE_TIME; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_DATE_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_TIME; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_2D_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_2D_STRUCT_TYPE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_3D_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_3D_STRUCT_TYPE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.TIME; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.TIME_STRUCT_SIZE; - -public class ValuePackerV2 extends ValuePackerV1 + +public class CommonValuePacker implements ValuePacker { - public ValuePackerV2( PackOutput output ) + + public static final byte DATE = 'D'; + public static final int DATE_STRUCT_SIZE = 1; + + public static final byte TIME = 'T'; + public static final int TIME_STRUCT_SIZE = 2; + + public static final byte LOCAL_TIME = 't'; + public static final int LOCAL_TIME_STRUCT_SIZE = 1; + + public static final byte LOCAL_DATE_TIME = 'd'; + public static final int LOCAL_DATE_TIME_STRUCT_SIZE = 2; + + public static final byte DATE_TIME_WITH_ZONE_OFFSET = 'F'; + public static final byte DATE_TIME_WITH_ZONE_ID = 'f'; + public static final int DATE_TIME_STRUCT_SIZE = 3; + + public static final byte DURATION = 'E'; + public static final int DURATION_TIME_STRUCT_SIZE = 4; + + public static final byte POINT_2D_STRUCT_TYPE = 'X'; + public static final int POINT_2D_STRUCT_SIZE = 3; + + public static final byte POINT_3D_STRUCT_TYPE = 'Y'; + public static final int POINT_3D_STRUCT_SIZE = 4; + + protected final PackStream.Packer packer; + + public CommonValuePacker( PackOutput output ) + { + this.packer = new PackStream.Packer( output ); + } + + @Override + public final void packStructHeader( int size, byte signature ) throws IOException + { + packer.packStructHeader( size, signature ); + } + + @Override + public final void pack( String string ) throws IOException { - super( output ); + packer.pack( string ); + } + + @Override + public final void pack( Value value ) throws IOException + { + if ( value instanceof InternalValue ) + { + packInternalValue( ((InternalValue) value) ); + } + else + { + throw new IllegalArgumentException( "Unable to pack: " + value ); + } } @Override + public final void pack( Map map ) throws IOException + { + if ( map == null || map.size() == 0 ) + { + packer.packMapHeader( 0 ); + return; + } + packer.packMapHeader( map.size() ); + for ( Map.Entry entry : map.entrySet() ) + { + packer.pack( entry.getKey() ); + pack( entry.getValue() ); + } + } + protected void packInternalValue( InternalValue value ) throws IOException { - TypeConstructor typeConstructor = value.typeConstructor(); - switch ( typeConstructor ) + switch ( value.typeConstructor() ) { case DATE: packDate( value.asLocalDate() ); @@ -89,8 +142,49 @@ protected void packInternalValue( InternalValue value ) throws IOException case POINT: packPoint( value.asPoint() ); break; + case NULL: + packer.packNull(); + break; + + case BYTES: + packer.pack( value.asByteArray() ); + break; + + case STRING: + packer.pack( value.asString() ); + break; + + case BOOLEAN: + packer.pack( value.asBoolean() ); + break; + + case INTEGER: + packer.pack( value.asLong() ); + break; + + case FLOAT: + packer.pack( value.asDouble() ); + break; + + case MAP: + packer.packMapHeader( value.size() ); + for ( String s : value.keys() ) + { + packer.pack( s ); + pack( value.get( s ) ); + } + break; + + case LIST: + packer.packListHeader( value.size() ); + for ( Value item : value.values() ) + { + pack( item ); + } + break; + default: - super.packInternalValue( value ); + throw new IOException( "Unknown type: " + value.type().name() ); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValueUnpackerV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonValueUnpacker.java similarity index 58% rename from driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValueUnpackerV1.java rename to driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonValueUnpacker.java index f690656c38..ee3a574e6f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValueUnpackerV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/common/CommonValueUnpacker.java @@ -1,3 +1,4 @@ + /* * Copyright (c) 2002-2020 "Neo4j," * Neo4j Sweden AB [http://neo4j.com] @@ -16,15 +17,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver.internal.messaging.v1; +package org.neo4j.driver.internal.messaging.common; import java.io.IOException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.InternalNode; import org.neo4j.driver.internal.InternalPath; import org.neo4j.driver.internal.InternalRelationship; @@ -39,19 +50,53 @@ import org.neo4j.driver.internal.value.NodeValue; import org.neo4j.driver.internal.value.PathValue; import org.neo4j.driver.internal.value.RelationshipValue; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.types.Node; import org.neo4j.driver.types.Path; import org.neo4j.driver.types.Relationship; +import static java.time.ZoneOffset.UTC; +import static org.neo4j.driver.Values.isoDuration; +import static org.neo4j.driver.Values.point; import static org.neo4j.driver.Values.value; -public class ValueUnpackerV1 implements ValueUnpacker +public class CommonValueUnpacker implements ValueUnpacker { + + public static final byte DATE = 'D'; + public static final int DATE_STRUCT_SIZE = 1; + + public static final byte TIME = 'T'; + public static final int TIME_STRUCT_SIZE = 2; + + public static final byte LOCAL_TIME = 't'; + public static final int LOCAL_TIME_STRUCT_SIZE = 1; + + public static final byte LOCAL_DATE_TIME = 'd'; + public static final int LOCAL_DATE_TIME_STRUCT_SIZE = 2; + + public static final byte DATE_TIME_WITH_ZONE_OFFSET = 'F'; + public static final byte DATE_TIME_WITH_ZONE_ID = 'f'; + public static final int DATE_TIME_STRUCT_SIZE = 3; + + public static final byte DURATION = 'E'; + public static final int DURATION_TIME_STRUCT_SIZE = 4; + + public static final byte POINT_2D_STRUCT_TYPE = 'X'; + public static final int POINT_2D_STRUCT_SIZE = 3; + + public static final byte POINT_3D_STRUCT_TYPE = 'Y'; + public static final int POINT_3D_STRUCT_SIZE = 4; + + public static final byte NODE = 'N'; + public static final byte RELATIONSHIP = 'R'; + public static final byte UNBOUND_RELATIONSHIP = 'r'; + public static final byte PATH = 'P'; + + public static final int NODE_FIELDS = 3; + protected final PackStream.Unpacker unpacker; - public ValueUnpackerV1( PackInput input ) + public CommonValueUnpacker( PackInput input ) { this.unpacker = new PackStream.Unpacker( input ); } @@ -142,14 +187,41 @@ protected Value unpackStruct( long size, byte type ) throws IOException { switch ( type ) { - case MessageFormatV1.NODE: - ensureCorrectStructSize( TypeConstructor.NODE, MessageFormatV1.NODE_FIELDS, size ); + case DATE: + ensureCorrectStructSize( TypeConstructor.DATE, DATE_STRUCT_SIZE, size ); + return unpackDate(); + case TIME: + ensureCorrectStructSize( TypeConstructor.TIME, TIME_STRUCT_SIZE, size ); + return unpackTime(); + case LOCAL_TIME: + ensureCorrectStructSize( TypeConstructor.LOCAL_TIME, LOCAL_TIME_STRUCT_SIZE, size ); + return unpackLocalTime(); + case LOCAL_DATE_TIME: + ensureCorrectStructSize( TypeConstructor.LOCAL_DATE_TIME, LOCAL_DATE_TIME_STRUCT_SIZE, size ); + return unpackLocalDateTime(); + case DATE_TIME_WITH_ZONE_OFFSET: + ensureCorrectStructSize( TypeConstructor.DATE_TIME, DATE_TIME_STRUCT_SIZE, size ); + return unpackDateTimeWithZoneOffset(); + case DATE_TIME_WITH_ZONE_ID: + ensureCorrectStructSize( TypeConstructor.DATE_TIME, DATE_TIME_STRUCT_SIZE, size ); + return unpackDateTimeWithZoneId(); + case DURATION: + ensureCorrectStructSize( TypeConstructor.DURATION, DURATION_TIME_STRUCT_SIZE, size ); + return unpackDuration(); + case POINT_2D_STRUCT_TYPE: + ensureCorrectStructSize( TypeConstructor.POINT, POINT_2D_STRUCT_SIZE, size ); + return unpackPoint2D(); + case POINT_3D_STRUCT_TYPE: + ensureCorrectStructSize( TypeConstructor.POINT, POINT_3D_STRUCT_SIZE, size ); + return unpackPoint3D(); + case NODE: + ensureCorrectStructSize( TypeConstructor.NODE, NODE_FIELDS, size ); InternalNode adapted = unpackNode(); return new NodeValue( adapted ); - case MessageFormatV1.RELATIONSHIP: + case RELATIONSHIP: ensureCorrectStructSize( TypeConstructor.RELATIONSHIP, 5, size ); return unpackRelationship(); - case MessageFormatV1.PATH: + case PATH: ensureCorrectStructSize( TypeConstructor.PATH, 3, size ); return unpackPath(); default: @@ -196,8 +268,8 @@ private Value unpackPath() throws IOException Node[] uniqNodes = new Node[(int) unpacker.unpackListHeader()]; for ( int i = 0; i < uniqNodes.length; i++ ) { - ensureCorrectStructSize( TypeConstructor.NODE, MessageFormatV1.NODE_FIELDS, unpacker.unpackStructHeader() ); - ensureCorrectStructSignature( "NODE", MessageFormatV1.NODE, unpacker.unpackStructSignature() ); + ensureCorrectStructSize( TypeConstructor.NODE, NODE_FIELDS, unpacker.unpackStructHeader() ); + ensureCorrectStructSignature( "NODE", NODE, unpacker.unpackStructSignature() ); uniqNodes[i] = unpackNode(); } @@ -206,7 +278,7 @@ private Value unpackPath() throws IOException for ( int i = 0; i < uniqRels.length; i++ ) { ensureCorrectStructSize( TypeConstructor.RELATIONSHIP, 3, unpacker.unpackStructHeader() ); - ensureCorrectStructSignature( "UNBOUND_RELATIONSHIP", MessageFormatV1.UNBOUND_RELATIONSHIP, unpacker.unpackStructSignature() ); + ensureCorrectStructSignature( "UNBOUND_RELATIONSHIP", UNBOUND_RELATIONSHIP, unpacker.unpackStructSignature() ); long id = unpacker.unpackLong(); String relType = unpacker.unpackString(); Map props = unpackMap(); @@ -268,4 +340,84 @@ private void ensureCorrectStructSignature( String structName, byte expected, byt structName, Integer.toHexString( expected ), Integer.toHexString( actual ) ) ); } } + + private Value unpackDate() throws IOException + { + long epochDay = unpacker.unpackLong(); + return value( LocalDate.ofEpochDay( epochDay ) ); + } + + private Value unpackTime() throws IOException + { + long nanoOfDayLocal = unpacker.unpackLong(); + int offsetSeconds = Math.toIntExact( unpacker.unpackLong() ); + + LocalTime localTime = LocalTime.ofNanoOfDay( nanoOfDayLocal ); + ZoneOffset offset = ZoneOffset.ofTotalSeconds( offsetSeconds ); + return value( OffsetTime.of( localTime, offset ) ); + } + + private Value unpackLocalTime() throws IOException + { + long nanoOfDayLocal = unpacker.unpackLong(); + return value( LocalTime.ofNanoOfDay( nanoOfDayLocal ) ); + } + + private Value unpackLocalDateTime() throws IOException + { + long epochSecondUtc = unpacker.unpackLong(); + int nano = Math.toIntExact( unpacker.unpackLong() ); + return value( LocalDateTime.ofEpochSecond( epochSecondUtc, nano, UTC ) ); + } + + private Value unpackDateTimeWithZoneOffset() throws IOException + { + long epochSecondLocal = unpacker.unpackLong(); + int nano = Math.toIntExact( unpacker.unpackLong() ); + int offsetSeconds = Math.toIntExact( unpacker.unpackLong() ); + return value( newZonedDateTime( epochSecondLocal, nano, ZoneOffset.ofTotalSeconds( offsetSeconds ) ) ); + } + + private Value unpackDateTimeWithZoneId() throws IOException + { + long epochSecondLocal = unpacker.unpackLong(); + int nano = Math.toIntExact( unpacker.unpackLong() ); + String zoneIdString = unpacker.unpackString(); + return value( newZonedDateTime( epochSecondLocal, nano, ZoneId.of( zoneIdString ) ) ); + } + + private Value unpackDuration() throws IOException + { + long months = unpacker.unpackLong(); + long days = unpacker.unpackLong(); + long seconds = unpacker.unpackLong(); + int nanoseconds = Math.toIntExact( unpacker.unpackLong() ); + return isoDuration( months, days, seconds, nanoseconds ); + } + + private Value unpackPoint2D() throws IOException + { + int srid = Math.toIntExact( unpacker.unpackLong() ); + double x = unpacker.unpackDouble(); + double y = unpacker.unpackDouble(); + return point( srid, x, y ); + } + + private Value unpackPoint3D() throws IOException + { + int srid = Math.toIntExact( unpacker.unpackLong() ); + double x = unpacker.unpackDouble(); + double y = unpacker.unpackDouble(); + double z = unpacker.unpackDouble(); + return point( srid, x, y, z ); + } + + private static ZonedDateTime newZonedDateTime( long epochSecondLocal, long nano, ZoneId zoneId ) + { + Instant instant = Instant.ofEpochSecond( epochSecondLocal, nano ); + LocalDateTime localDateTime = LocalDateTime.ofInstant( instant, UTC ); + return ZonedDateTime.of( localDateTime, zoneId ); + } } + + diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java deleted file mode 100644 index 289a4bace7..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelPromise; - -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.Bookmark; -import org.neo4j.driver.Query; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.internal.BookmarkHolder; -import org.neo4j.driver.internal.DatabaseName; -import org.neo4j.driver.internal.InternalBookmark; -import org.neo4j.driver.internal.async.UnmanagedTransaction; -import org.neo4j.driver.internal.cluster.RoutingContext; -import org.neo4j.driver.internal.cursor.AsyncResultCursorOnlyFactory; -import org.neo4j.driver.internal.cursor.ResultCursorFactory; -import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; -import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; -import org.neo4j.driver.internal.handlers.InitResponseHandler; -import org.neo4j.driver.internal.handlers.NoOpResponseHandler; -import org.neo4j.driver.internal.handlers.PullAllResponseHandler; -import org.neo4j.driver.internal.handlers.PullHandlers; -import org.neo4j.driver.internal.handlers.RollbackTxResponseHandler; -import org.neo4j.driver.internal.handlers.RunResponseHandler; -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.messaging.BoltProtocolVersion; -import org.neo4j.driver.internal.messaging.Message; -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.request.InitMessage; -import org.neo4j.driver.internal.messaging.request.PullAllMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.security.InternalAuthToken; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.internal.util.Futures; -import org.neo4j.driver.internal.util.MetadataExtractor; - -import static java.util.Collections.emptyMap; -import static org.neo4j.driver.Values.ofValue; -import static org.neo4j.driver.Values.value; -import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; -import static org.neo4j.driver.internal.messaging.request.MultiDatabaseUtil.assertEmptyDatabaseName; -import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize; - -public class BoltProtocolV1 implements BoltProtocol -{ - public static final BoltProtocolVersion VERSION = new BoltProtocolVersion( 1, 0 ); - - public static final BoltProtocol INSTANCE = new BoltProtocolV1(); - - public static final MetadataExtractor METADATA_EXTRACTOR = new MetadataExtractor( "result_available_after", "result_consumed_after" ); - - private static final String BEGIN_QUERY = "BEGIN"; - private static final Message BEGIN_MESSAGE = new RunMessage( BEGIN_QUERY ); - private static final Message COMMIT_MESSAGE = new RunMessage( "COMMIT" ); - private static final Message ROLLBACK_MESSAGE = new RunMessage( "ROLLBACK" ); - - @Override - public MessageFormat createMessageFormat() - { - return new MessageFormatV1(); - } - - @Override - public void initializeChannel( String userAgent, AuthToken authToken, RoutingContext routingContext, - ChannelPromise channelInitializedPromise ) - { - Channel channel = channelInitializedPromise.channel(); - - InitMessage message = new InitMessage( userAgent, ((InternalAuthToken) authToken).toMap() ); - InitResponseHandler handler = new InitResponseHandler( channelInitializedPromise ); - - messageDispatcher( channel ).enqueue( handler ); - channel.writeAndFlush( message, channel.voidPromise() ); - } - - @Override - public void prepareToCloseChannel( Channel channel ) - { - // left empty on purpose. - } - - @Override - public CompletionStage beginTransaction( Connection connection, Bookmark bookmark, TransactionConfig config ) - { - try - { - verifyBeforeTransaction( config, connection.databaseName() ); - } - catch ( Exception error ) - { - return Futures.failedFuture( error ); - } - - if ( bookmark.isEmpty() ) - { - connection.write( - BEGIN_MESSAGE, NoOpResponseHandler.INSTANCE, - PullAllMessage.PULL_ALL, NoOpResponseHandler.INSTANCE ); - - return Futures.completedWithNull(); - } - else - { - CompletableFuture beginTxFuture = new CompletableFuture<>(); - connection.writeAndFlush( - new RunMessage( BEGIN_QUERY, SingleBookmarkHelper.asBeginTransactionParameters( bookmark ) ), NoOpResponseHandler.INSTANCE, - PullAllMessage.PULL_ALL, new BeginTxResponseHandler( beginTxFuture ) ); - - return beginTxFuture; - } - } - - - - @Override - public CompletionStage commitTransaction( Connection connection ) - { - CompletableFuture commitFuture = new CompletableFuture<>(); - - ResponseHandler pullAllHandler = new CommitTxResponseHandler( commitFuture ); - connection.writeAndFlush( - COMMIT_MESSAGE, NoOpResponseHandler.INSTANCE, - PullAllMessage.PULL_ALL, pullAllHandler ); - - return commitFuture; - } - - @Override - public CompletionStage rollbackTransaction( Connection connection ) - { - CompletableFuture rollbackFuture = new CompletableFuture<>(); - - ResponseHandler pullAllHandler = new RollbackTxResponseHandler( rollbackFuture ); - connection.writeAndFlush( - ROLLBACK_MESSAGE, NoOpResponseHandler.INSTANCE, - PullAllMessage.PULL_ALL, pullAllHandler ); - - return rollbackFuture; - } - - @Override - public ResultCursorFactory runInAutoCommitTransaction(Connection connection, Query query, BookmarkHolder bookmarkHolder, - TransactionConfig config, boolean waitForRunResponse, long ignored ) - { - // bookmarks are ignored for auto-commit transactions in this version of the protocol - verifyBeforeTransaction( config, connection.databaseName() ); - return buildResultCursorFactory( connection, query, null, waitForRunResponse ); - } - - @Override - public ResultCursorFactory runInUnmanagedTransaction(Connection connection, Query query, UnmanagedTransaction tx, - boolean waitForRunResponse, long ignored ) - { - return buildResultCursorFactory( connection, query, tx, waitForRunResponse ); - } - - @Override - public BoltProtocolVersion version() - { - return VERSION; - } - - private static ResultCursorFactory buildResultCursorFactory(Connection connection, Query query, - UnmanagedTransaction tx, boolean waitForRunResponse ) - { - String queryText = query.text(); - Map params = query.parameters().asMap( ofValue() ); - - RunMessage runMessage = new RunMessage( queryText, params ); - RunResponseHandler runHandler = new RunResponseHandler( METADATA_EXTRACTOR ); - PullAllResponseHandler pullAllHandler = PullHandlers.newBoltV1PullAllHandler( query, runHandler, connection, tx ); - - return new AsyncResultCursorOnlyFactory( connection, runMessage, runHandler, pullAllHandler, waitForRunResponse ); - } - - private void verifyBeforeTransaction( TransactionConfig config, DatabaseName databaseName ) - { - if ( config != null && !config.isEmpty() ) - { - throw txConfigNotSupported(); - } - assertEmptyDatabaseName( databaseName, version() ); - } - - private static ClientException txConfigNotSupported() - { - return new ClientException( "Driver is connected to the database that does not support transaction configuration. " + - "Please upgrade to neo4j 3.5.0 or later in order to use this functionality" ); - } - - static class SingleBookmarkHelper - { - private static final String BOOKMARK_PREFIX = "neo4j:bookmark:v1:tx"; - private static final long UNKNOWN_BOOKMARK_VALUE = -1; - - static Map asBeginTransactionParameters( Bookmark bookmark ) - { - if ( bookmark.isEmpty() ) - { - return emptyMap(); - } - - // Driver sends {bookmark: "max", bookmarks: ["one", "two", "max"]} instead of simple - // {bookmarks: ["one", "two", "max"]} for backwards compatibility reasons. Old servers can only accept single - // bookmark that is why driver has to parse and compare given list of bookmarks. This functionality will - // eventually be removed. - Map parameters = newHashMapWithSize( 1 ); - parameters.put( "bookmark", value( maxBookmark( bookmark.values() ) ) ); - parameters.put( "bookmarks", value( bookmark.values() ) ); - return parameters; - } - - private static String maxBookmark( Iterable bookmarks ) - { - if ( bookmarks == null ) - { - return null; - } - - Iterator iterator = bookmarks.iterator(); - - if ( !iterator.hasNext() ) - { - return null; - } - - String maxBookmark = iterator.next(); - long maxValue = bookmarkValue( maxBookmark ); - - while ( iterator.hasNext() ) - { - String bookmark = iterator.next(); - long value = bookmarkValue( bookmark ); - - if ( value > maxValue ) - { - maxBookmark = bookmark; - maxValue = value; - } - } - - return maxBookmark; - } - - private static long bookmarkValue( String value ) - { - if ( value != null && value.startsWith( BOOKMARK_PREFIX ) ) - { - try - { - return Long.parseLong( value.substring( BOOKMARK_PREFIX.length() ) ); - } - catch ( NumberFormatException e ) - { - return UNKNOWN_BOOKMARK_VALUE; - } - } - return UNKNOWN_BOOKMARK_VALUE; - } - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageFormatV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageFormatV1.java deleted file mode 100644 index 96fe643624..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageFormatV1.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.packstream.PackInput; -import org.neo4j.driver.internal.packstream.PackOutput; - -public class MessageFormatV1 implements MessageFormat -{ - public static final byte NODE = 'N'; - public static final byte RELATIONSHIP = 'R'; - public static final byte UNBOUND_RELATIONSHIP = 'r'; - public static final byte PATH = 'P'; - - public static final int NODE_FIELDS = 3; - - @Override - public MessageFormat.Writer newWriter( PackOutput output ) - { - return new MessageWriterV1( output ); - } - - @Override - public MessageFormat.Reader newReader( PackInput input ) - { - return new MessageReaderV1( input ); - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1.java deleted file mode 100644 index b67ae182a3..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import java.util.Map; - -import org.neo4j.driver.internal.messaging.AbstractMessageWriter; -import org.neo4j.driver.internal.messaging.MessageEncoder; -import org.neo4j.driver.internal.messaging.ValuePacker; -import org.neo4j.driver.internal.messaging.encode.DiscardAllMessageEncoder; -import org.neo4j.driver.internal.messaging.encode.InitMessageEncoder; -import org.neo4j.driver.internal.messaging.encode.PullAllMessageEncoder; -import org.neo4j.driver.internal.messaging.encode.ResetMessageEncoder; -import org.neo4j.driver.internal.messaging.encode.RunMessageEncoder; -import org.neo4j.driver.internal.messaging.request.DiscardAllMessage; -import org.neo4j.driver.internal.messaging.request.InitMessage; -import org.neo4j.driver.internal.messaging.request.PullAllMessage; -import org.neo4j.driver.internal.messaging.request.ResetMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.packstream.PackOutput; -import org.neo4j.driver.internal.util.Iterables; - -public class MessageWriterV1 extends AbstractMessageWriter -{ - public MessageWriterV1( PackOutput output ) - { - this( new ValuePackerV1( output ) ); - } - - protected MessageWriterV1( ValuePacker packer ) - { - super( packer, buildEncoders() ); - } - - private static Map buildEncoders() - { - Map result = Iterables.newHashMapWithSize( 6 ); - result.put( DiscardAllMessage.SIGNATURE, new DiscardAllMessageEncoder() ); - result.put( InitMessage.SIGNATURE, new InitMessageEncoder() ); - result.put( PullAllMessage.SIGNATURE, new PullAllMessageEncoder() ); - result.put( ResetMessage.SIGNATURE, new ResetMessageEncoder() ); - result.put( RunMessage.SIGNATURE, new RunMessageEncoder() ); - return result; - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValuePackerV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValuePackerV1.java deleted file mode 100644 index 62d163abc0..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v1/ValuePackerV1.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import java.io.IOException; -import java.util.Map; - -import org.neo4j.driver.internal.messaging.ValuePacker; -import org.neo4j.driver.internal.packstream.PackOutput; -import org.neo4j.driver.internal.packstream.PackStream; -import org.neo4j.driver.internal.value.InternalValue; -import org.neo4j.driver.Value; - -public class ValuePackerV1 implements ValuePacker -{ - protected final PackStream.Packer packer; - - public ValuePackerV1( PackOutput output ) - { - this.packer = new PackStream.Packer( output ); - } - - @Override - public final void packStructHeader( int size, byte signature ) throws IOException - { - packer.packStructHeader( size, signature ); - } - - @Override - public final void pack( String string ) throws IOException - { - packer.pack( string ); - } - - @Override - public final void pack( Value value ) throws IOException - { - if ( value instanceof InternalValue ) - { - packInternalValue( ((InternalValue) value) ); - } - else - { - throw new IllegalArgumentException( "Unable to pack: " + value ); - } - } - - @Override - public final void pack( Map map ) throws IOException - { - if ( map == null || map.size() == 0 ) - { - packer.packMapHeader( 0 ); - return; - } - packer.packMapHeader( map.size() ); - for ( Map.Entry entry : map.entrySet() ) - { - packer.pack( entry.getKey() ); - pack( entry.getValue() ); - } - } - - protected void packInternalValue( InternalValue value ) throws IOException - { - switch ( value.typeConstructor() ) - { - case NULL: - packer.packNull(); - break; - - case BYTES: - packer.pack( value.asByteArray() ); - break; - - case STRING: - packer.pack( value.asString() ); - break; - - case BOOLEAN: - packer.pack( value.asBoolean() ); - break; - - case INTEGER: - packer.pack( value.asLong() ); - break; - - case FLOAT: - packer.pack( value.asDouble() ); - break; - - case MAP: - packer.packMapHeader( value.size() ); - for ( String s : value.keys() ) - { - packer.pack( s ); - pack( value.get( s ) ); - } - break; - - case LIST: - packer.packListHeader( value.size() ); - for ( Value item : value.values() ) - { - pack( item ); - } - break; - - default: - throw new IOException( "Unknown type: " + value.type().name() ); - } - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2.java deleted file mode 100644 index 8ca7fc0aac..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.messaging.BoltProtocolVersion; -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; - -public class BoltProtocolV2 extends BoltProtocolV1 -{ - public static final BoltProtocolVersion VERSION = new BoltProtocolVersion( 2, 0 ); - - public static final BoltProtocol INSTANCE = new BoltProtocolV2(); - - @Override - public MessageFormat createMessageFormat() - { - return new MessageFormatV2(); - } - - @Override - public BoltProtocolVersion version() - { - return VERSION; - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2.java deleted file mode 100644 index addbc6ab23..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; -import org.neo4j.driver.internal.packstream.PackInput; -import org.neo4j.driver.internal.packstream.PackOutput; - -public class MessageFormatV2 extends MessageFormatV1 -{ - public static final byte DATE = 'D'; - public static final int DATE_STRUCT_SIZE = 1; - - public static final byte TIME = 'T'; - public static final int TIME_STRUCT_SIZE = 2; - - public static final byte LOCAL_TIME = 't'; - public static final int LOCAL_TIME_STRUCT_SIZE = 1; - - public static final byte LOCAL_DATE_TIME = 'd'; - public static final int LOCAL_DATE_TIME_STRUCT_SIZE = 2; - - public static final byte DATE_TIME_WITH_ZONE_OFFSET = 'F'; - public static final byte DATE_TIME_WITH_ZONE_ID = 'f'; - public static final int DATE_TIME_STRUCT_SIZE = 3; - - public static final byte DURATION = 'E'; - public static final int DURATION_TIME_STRUCT_SIZE = 4; - - public static final byte POINT_2D_STRUCT_TYPE = 'X'; - public static final int POINT_2D_STRUCT_SIZE = 3; - - public static final byte POINT_3D_STRUCT_TYPE = 'Y'; - public static final int POINT_3D_STRUCT_SIZE = 4; - - @Override - public Writer newWriter( PackOutput output ) - { - return new MessageWriterV2( output ); - } - - @Override - public Reader newReader( PackInput input ) - { - return new MessageReaderV2( input ); - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2.java deleted file mode 100644 index 914272eddf..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import org.neo4j.driver.internal.messaging.v1.MessageReaderV1; -import org.neo4j.driver.internal.packstream.PackInput; - -public class MessageReaderV2 extends MessageReaderV1 -{ - public MessageReaderV2( PackInput input ) - { - super( new ValueUnpackerV2( input ) ); - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2.java deleted file mode 100644 index 3612b6cd74..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import org.neo4j.driver.internal.messaging.v1.MessageWriterV1; -import org.neo4j.driver.internal.packstream.PackOutput; - -public class MessageWriterV2 extends MessageWriterV1 -{ - public MessageWriterV2( PackOutput output ) - { - super( new ValuePackerV2( output ) ); - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValueUnpackerV2.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValueUnpackerV2.java deleted file mode 100644 index 34911f7108..0000000000 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v2/ValueUnpackerV2.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import java.io.IOException; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; - -import org.neo4j.driver.internal.messaging.v1.ValueUnpackerV1; -import org.neo4j.driver.internal.packstream.PackInput; -import org.neo4j.driver.internal.types.TypeConstructor; -import org.neo4j.driver.Value; - -import static java.time.ZoneOffset.UTC; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_TIME_WITH_ZONE_ID; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DATE_TIME_WITH_ZONE_OFFSET; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DURATION; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.DURATION_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_DATE_TIME; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_DATE_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_TIME; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.LOCAL_TIME_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_2D_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_2D_STRUCT_TYPE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_3D_STRUCT_SIZE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.POINT_3D_STRUCT_TYPE; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.TIME; -import static org.neo4j.driver.internal.messaging.v2.MessageFormatV2.TIME_STRUCT_SIZE; -import static org.neo4j.driver.Values.isoDuration; -import static org.neo4j.driver.Values.point; -import static org.neo4j.driver.Values.value; - -public class ValueUnpackerV2 extends ValueUnpackerV1 -{ - public ValueUnpackerV2( PackInput input ) - { - super( input ); - } - - @Override - protected Value unpackStruct( long size, byte type ) throws IOException - { - switch ( type ) - { - case DATE: - ensureCorrectStructSize( TypeConstructor.DATE, DATE_STRUCT_SIZE, size ); - return unpackDate(); - case TIME: - ensureCorrectStructSize( TypeConstructor.TIME, TIME_STRUCT_SIZE, size ); - return unpackTime(); - case LOCAL_TIME: - ensureCorrectStructSize( TypeConstructor.LOCAL_TIME, LOCAL_TIME_STRUCT_SIZE, size ); - return unpackLocalTime(); - case LOCAL_DATE_TIME: - ensureCorrectStructSize( TypeConstructor.LOCAL_DATE_TIME, LOCAL_DATE_TIME_STRUCT_SIZE, size ); - return unpackLocalDateTime(); - case DATE_TIME_WITH_ZONE_OFFSET: - ensureCorrectStructSize( TypeConstructor.DATE_TIME, DATE_TIME_STRUCT_SIZE, size ); - return unpackDateTimeWithZoneOffset(); - case DATE_TIME_WITH_ZONE_ID: - ensureCorrectStructSize( TypeConstructor.DATE_TIME, DATE_TIME_STRUCT_SIZE, size ); - return unpackDateTimeWithZoneId(); - case DURATION: - ensureCorrectStructSize( TypeConstructor.DURATION, DURATION_TIME_STRUCT_SIZE, size ); - return unpackDuration(); - case POINT_2D_STRUCT_TYPE: - ensureCorrectStructSize( TypeConstructor.POINT, POINT_2D_STRUCT_SIZE, size ); - return unpackPoint2D(); - case POINT_3D_STRUCT_TYPE: - ensureCorrectStructSize( TypeConstructor.POINT, POINT_3D_STRUCT_SIZE, size ); - return unpackPoint3D(); - default: - return super.unpackStruct( size, type ); - } - } - - private Value unpackDate() throws IOException - { - long epochDay = unpacker.unpackLong(); - return value( LocalDate.ofEpochDay( epochDay ) ); - } - - private Value unpackTime() throws IOException - { - long nanoOfDayLocal = unpacker.unpackLong(); - int offsetSeconds = Math.toIntExact( unpacker.unpackLong() ); - - LocalTime localTime = LocalTime.ofNanoOfDay( nanoOfDayLocal ); - ZoneOffset offset = ZoneOffset.ofTotalSeconds( offsetSeconds ); - return value( OffsetTime.of( localTime, offset ) ); - } - - private Value unpackLocalTime() throws IOException - { - long nanoOfDayLocal = unpacker.unpackLong(); - return value( LocalTime.ofNanoOfDay( nanoOfDayLocal ) ); - } - - private Value unpackLocalDateTime() throws IOException - { - long epochSecondUtc = unpacker.unpackLong(); - int nano = Math.toIntExact( unpacker.unpackLong() ); - return value( LocalDateTime.ofEpochSecond( epochSecondUtc, nano, UTC ) ); - } - - private Value unpackDateTimeWithZoneOffset() throws IOException - { - long epochSecondLocal = unpacker.unpackLong(); - int nano = Math.toIntExact( unpacker.unpackLong() ); - int offsetSeconds = Math.toIntExact( unpacker.unpackLong() ); - return value( newZonedDateTime( epochSecondLocal, nano, ZoneOffset.ofTotalSeconds( offsetSeconds ) ) ); - } - - private Value unpackDateTimeWithZoneId() throws IOException - { - long epochSecondLocal = unpacker.unpackLong(); - int nano = Math.toIntExact( unpacker.unpackLong() ); - String zoneIdString = unpacker.unpackString(); - return value( newZonedDateTime( epochSecondLocal, nano, ZoneId.of( zoneIdString ) ) ); - } - - private Value unpackDuration() throws IOException - { - long months = unpacker.unpackLong(); - long days = unpacker.unpackLong(); - long seconds = unpacker.unpackLong(); - int nanoseconds = Math.toIntExact( unpacker.unpackLong() ); - return isoDuration( months, days, seconds, nanoseconds ); - } - - private Value unpackPoint2D() throws IOException - { - int srid = Math.toIntExact( unpacker.unpackLong() ); - double x = unpacker.unpackDouble(); - double y = unpacker.unpackDouble(); - return point( srid, x, y ); - } - - private Value unpackPoint3D() throws IOException - { - int srid = Math.toIntExact( unpacker.unpackLong() ); - double x = unpacker.unpackDouble(); - double y = unpacker.unpackDouble(); - double z = unpacker.unpackDouble(); - return point( srid, x, y, z ); - } - - private static ZonedDateTime newZonedDateTime( long epochSecondLocal, long nano, ZoneId zoneId ) - { - Instant instant = Instant.ofEpochSecond( epochSecondLocal, nano ); - LocalDateTime localDateTime = LocalDateTime.ofInstant( instant, UTC ); - return ZonedDateTime.of( localDateTime, zoneId ); - } -} diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3.java index 843af206f9..51bdaabd35 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3.java @@ -19,7 +19,7 @@ package org.neo4j.driver.internal.messaging.v3; import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v2.MessageReaderV2; +import org.neo4j.driver.internal.messaging.common.CommonMessageReader; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.packstream.PackOutput; @@ -34,6 +34,6 @@ public Writer newWriter( PackOutput output ) @Override public Reader newReader( PackInput input ) { - return new MessageReaderV2( input ); + return new CommonMessageReader( input ); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3.java index f6c2fd2c5a..55a2fd0dc5 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3.java @@ -22,6 +22,7 @@ import org.neo4j.driver.internal.messaging.AbstractMessageWriter; import org.neo4j.driver.internal.messaging.MessageEncoder; +import org.neo4j.driver.internal.messaging.common.CommonValuePacker; import org.neo4j.driver.internal.messaging.encode.BeginMessageEncoder; import org.neo4j.driver.internal.messaging.encode.CommitMessageEncoder; import org.neo4j.driver.internal.messaging.encode.DiscardAllMessageEncoder; @@ -40,7 +41,6 @@ import org.neo4j.driver.internal.messaging.request.ResetMessage; import org.neo4j.driver.internal.messaging.request.RollbackMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; -import org.neo4j.driver.internal.messaging.v2.ValuePackerV2; import org.neo4j.driver.internal.packstream.PackOutput; import org.neo4j.driver.internal.util.Iterables; @@ -48,7 +48,7 @@ public class MessageWriterV3 extends AbstractMessageWriter { public MessageWriterV3( PackOutput output ) { - super( new ValuePackerV2( output ), buildEncoders() ); + super( new CommonValuePacker( output ), buildEncoders() ); } private static Map buildEncoders() diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4.java index fe1c99cc87..8c413cd349 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4.java @@ -19,7 +19,7 @@ package org.neo4j.driver.internal.messaging.v4; import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v2.MessageReaderV2; +import org.neo4j.driver.internal.messaging.common.CommonMessageReader; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.packstream.PackOutput; @@ -34,6 +34,6 @@ public Writer newWriter( PackOutput output ) @Override public Reader newReader( PackInput input ) { - return new MessageReaderV2( input ); + return new CommonMessageReader( input ); } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4.java index 14c3c8ebfb..216ebf636f 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4.java @@ -22,6 +22,7 @@ import org.neo4j.driver.internal.messaging.AbstractMessageWriter; import org.neo4j.driver.internal.messaging.MessageEncoder; +import org.neo4j.driver.internal.messaging.common.CommonValuePacker; import org.neo4j.driver.internal.messaging.encode.BeginMessageEncoder; import org.neo4j.driver.internal.messaging.encode.CommitMessageEncoder; import org.neo4j.driver.internal.messaging.encode.DiscardMessageEncoder; @@ -40,7 +41,6 @@ import org.neo4j.driver.internal.messaging.request.ResetMessage; import org.neo4j.driver.internal.messaging.request.RollbackMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; -import org.neo4j.driver.internal.messaging.v2.ValuePackerV2; import org.neo4j.driver.internal.packstream.PackOutput; import org.neo4j.driver.internal.util.Iterables; @@ -48,7 +48,7 @@ public class MessageWriterV4 extends AbstractMessageWriter { public MessageWriterV4( PackOutput output ) { - super( new ValuePackerV2( output ), buildEncoders() ); + super( new CommonValuePacker( output ), buildEncoders() ); } private static Map buildEncoders() diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java index 607d38fe61..5b2d3ced15 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java @@ -25,19 +25,20 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import org.neo4j.driver.Record; import org.neo4j.driver.Query; +import org.neo4j.driver.Record; import org.neo4j.driver.Result; import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.NoSuchRecordException; +import org.neo4j.driver.exceptions.ResultConsumedException; import org.neo4j.driver.internal.cursor.AsyncResultCursor; import org.neo4j.driver.internal.cursor.AsyncResultCursorImpl; import org.neo4j.driver.internal.cursor.DisposableAsyncResultCursor; import org.neo4j.driver.internal.handlers.LegacyPullAllResponseHandler; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.PullResponseCompletionListener; -import org.neo4j.driver.exceptions.ResultConsumedException; import org.neo4j.driver.internal.handlers.RunResponseHandler; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.value.NullValue; import org.neo4j.driver.util.Pair; @@ -58,7 +59,6 @@ import static org.neo4j.driver.Values.ofString; import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.BoltServerAddress.LOCAL_DEFAULT; -import static org.neo4j.driver.internal.messaging.v1.BoltProtocolV1.METADATA_EXTRACTOR; import static org.neo4j.driver.util.TestUtil.anyServerVersion; class InternalResultTest @@ -353,7 +353,7 @@ void shouldNotPeekIntoTheFutureWhenResultIsEmpty() private Result createResult(int numberOfRecords ) { - RunResponseHandler runHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR ); + RunResponseHandler runHandler = new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV3.METADATA_EXTRACTOR ); runHandler.onSuccess( singletonMap( "fields", value( Arrays.asList( "k1", "k2" ) ) ) ); Query query = new Query( "" ); @@ -361,7 +361,8 @@ private Result createResult(int numberOfRecords ) when( connection.serverAddress() ).thenReturn( LOCAL_DEFAULT ); when( connection.serverVersion() ).thenReturn( anyServerVersion() ); PullAllResponseHandler pullAllHandler = - new LegacyPullAllResponseHandler(query, runHandler, connection, METADATA_EXTRACTOR, mock( PullResponseCompletionListener.class ) ); + new LegacyPullAllResponseHandler( query, runHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR, + mock( PullResponseCompletionListener.class ) ); for ( int i = 1; i <= numberOfRecords; i++ ) { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java index c14a7d5a36..8bc2548f24 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java @@ -27,8 +27,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; -import org.neo4j.driver.Record; import org.neo4j.driver.Query; +import org.neo4j.driver.Record; import org.neo4j.driver.exceptions.NoSuchRecordException; import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.internal.BoltServerAddress; @@ -36,12 +36,12 @@ import org.neo4j.driver.internal.cursor.AsyncResultCursorImpl; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.summary.InternalResultSummary; import org.neo4j.driver.internal.summary.InternalServerInfo; import org.neo4j.driver.internal.summary.InternalSummaryCounters; -import org.neo4j.driver.summary.ResultSummary; import org.neo4j.driver.summary.QueryType; +import org.neo4j.driver.summary.ResultSummary; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -409,6 +409,6 @@ private static AsyncResultCursorImpl newCursor(RunResponseHandler runHandler, Pu private static RunResponseHandler newRunResponseHandler() { - return new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV1.METADATA_EXTRACTOR ); + return new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV3.METADATA_EXTRACTOR ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java index 5ec591906c..418dd18b82 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/UnmanagedTransactionTest.java @@ -31,8 +31,6 @@ import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.DefaultBookmarkHolder; import org.neo4j.driver.internal.InternalBookmark; -import org.neo4j.driver.internal.messaging.request.PullAllMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; @@ -44,17 +42,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil.UNLIMITED_FETCH_SIZE; import static org.neo4j.driver.util.TestUtil.await; +import static org.neo4j.driver.util.TestUtil.beginMessage; import static org.neo4j.driver.util.TestUtil.connectionMock; -import static org.neo4j.driver.util.TestUtil.runMessageWithQueryMatcher; import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunAndPull; import static org.neo4j.driver.util.TestUtil.setupSuccessfulRunRx; +import static org.neo4j.driver.util.TestUtil.verifyBeginTx; +import static org.neo4j.driver.util.TestUtil.verifyRollbackTx; import static org.neo4j.driver.util.TestUtil.verifyRunAndPull; import static org.neo4j.driver.util.TestUtil.verifyRunRx; @@ -103,8 +102,8 @@ void shouldRollbackOnImplicitFailure() // Then InOrder order = inOrder( connection ); - order.verify( connection ).write( eq( new RunMessage( "BEGIN" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); - order.verify( connection ).writeAndFlush( eq( new RunMessage( "ROLLBACK" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); + verifyBeginTx( connection ); + verifyRollbackTx( connection ); order.verify( connection ).release(); } @@ -115,7 +114,7 @@ void shouldOnlyQueueMessagesWhenNoBookmarkGiven() beginTx( connection, InternalBookmark.empty() ); - verify( connection ).write( eq( new RunMessage( "BEGIN" ) ), any(), eq( PullAllMessage.PULL_ALL ), any() ); + verifyBeginTx( connection ); verify( connection, never() ).writeAndFlush( any(), any(), any(), any() ); } @@ -127,7 +126,7 @@ void shouldFlushWhenBookmarkGiven() beginTx( connection, bookmark ); - verify( connection ).writeAndFlush( any(), any(), eq( PullAllMessage.PULL_ALL ), any() ); + verifyBeginTx( connection, bookmark ); verify( connection, never() ).write( any(), any(), any(), any() ); } @@ -243,11 +242,11 @@ private static Connection connectionWithBegin( Consumer beginBe Connection connection = connectionMock(); doAnswer( invocation -> - { - ResponseHandler beginHandler = invocation.getArgument( 3 ); - beginBehaviour.accept( beginHandler ); - return null; - } ).when( connection ).writeAndFlush( argThat( runMessageWithQueryMatcher( "BEGIN" ) ), any(), any(), any() ); + { + ResponseHandler beginHandler = invocation.getArgument( 1 ); + beginBehaviour.accept( beginHandler ); + return null; + } ).when( connection ).writeAndFlush( argThat( beginMessage() ), any() ); return connection; } diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java index a37668bb69..6fc907704a 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/BoltProtocolUtilTest.java @@ -22,8 +22,6 @@ import io.netty.buffer.Unpooled; import org.junit.jupiter.api.Test; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41; diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java index 234998903e..ced7c805aa 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelPipelineBuilderImplTest.java @@ -31,7 +31,7 @@ import org.neo4j.driver.internal.async.inbound.InboundMessageHandler; import org.neo4j.driver.internal.async.inbound.MessageDecoder; import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.junit.MatcherAssert.assertThat; @@ -46,7 +46,7 @@ void shouldBuildPipeline() EmbeddedChannel channel = new EmbeddedChannel(); ChannelAttributes.setMessageDispatcher( channel, new InboundMessageDispatcher( channel, DEV_NULL_LOGGING ) ); - new ChannelPipelineBuilderImpl().build( new MessageFormatV1(), channel.pipeline(), DEV_NULL_LOGGING ); + new ChannelPipelineBuilderImpl().build( new MessageFormatV3(), channel.pipeline(), DEV_NULL_LOGGING ); Iterator> iterator = channel.pipeline().iterator(); assertThat( iterator.next().getValue(), instanceOf( ChunkDecoder.class ) ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java index 2f92487920..8a8dff21cd 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeCompletedListenerTest.java @@ -25,22 +25,14 @@ import java.io.IOException; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.Value; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.handlers.HelloResponseHandler; -import org.neo4j.driver.internal.handlers.InitResponseHandler; import org.neo4j.driver.internal.messaging.BoltProtocolVersion; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.request.HelloMessage; -import org.neo4j.driver.internal.messaging.request.InitMessage; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.spi.ResponseHandler; @@ -51,7 +43,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion; import static org.neo4j.driver.util.TestUtil.await; @@ -85,18 +76,6 @@ void shouldFailConnectionInitializedPromiseWhenHandshakeFails() assertEquals( cause, error ); } - @Test - void shouldWriteInitializationMessageInBoltV1WhenHandshakeCompleted() - { - testWritingOfInitializationMessage( BoltProtocolV1.VERSION, new InitMessage( USER_AGENT, authToken().toMap() ), InitResponseHandler.class ); - } - - @Test - void shouldWriteInitializationMessageInBoltV2WhenHandshakeCompleted() - { - testWritingOfInitializationMessage( BoltProtocolV2.VERSION, new InitMessage( USER_AGENT, authToken().toMap() ), InitResponseHandler.class ); - } - @Test void shouldWriteInitializationMessageInBoltV3WhenHandshakeCompleted() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java index b31513013d..f36b561101 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/HandshakeHandlerTest.java @@ -40,10 +40,6 @@ import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler; import org.neo4j.driver.internal.messaging.BoltProtocolVersion; import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; -import org.neo4j.driver.internal.messaging.v2.MessageFormatV2; import org.neo4j.driver.internal.util.ErrorUtil; import static io.netty.buffer.Unpooled.copyInt; @@ -187,18 +183,6 @@ void shouldTranslateSSLHandshakeException() assertNull( await( channel.closeFuture() ) ); } - @Test - void shouldSelectProtocolV1WhenServerSuggests() - { - testProtocolSelection( BoltProtocolV1.VERSION, MessageFormatV1.class ); - } - - @Test - void shouldSelectProtocolV2WhenServerSuggests() - { - testProtocolSelection( BoltProtocolV2.VERSION, MessageFormatV2.class ); - } - @Test void shouldFailGivenPromiseWhenServerSuggestsNoProtocol() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java index e7e574e2b6..8fcebf0dc9 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/inbound/InboundMessageHandlerTest.java @@ -29,19 +29,19 @@ import java.util.HashMap; import java.util.Map; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.Neo4jException; import org.neo4j.driver.internal.async.connection.ChannelAttributes; -import org.neo4j.driver.internal.util.messaging.KnowledgeableMessageFormat; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.messaging.MessageFormat.Reader; import org.neo4j.driver.internal.messaging.response.FailureMessage; import org.neo4j.driver.internal.messaging.response.IgnoredMessage; import org.neo4j.driver.internal.messaging.response.RecordMessage; import org.neo4j.driver.internal.messaging.response.SuccessMessage; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.io.MessageToByteBufWriter; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.internal.util.messaging.KnowledgeableMessageFormat; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.junit.MatcherAssert.assertThat; @@ -52,9 +52,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.internal.messaging.request.ResetMessage.RESET; -import static org.neo4j.driver.Values.value; class InboundMessageHandlerTest { @@ -70,7 +70,7 @@ void setUp() writer = new MessageToByteBufWriter( new KnowledgeableMessageFormat() ); ChannelAttributes.setMessageDispatcher( channel, messageDispatcher ); - InboundMessageHandler handler = new InboundMessageHandler( new MessageFormatV1(), DEV_NULL_LOGGING ); + InboundMessageHandler handler = new InboundMessageHandler( new MessageFormatV3(), DEV_NULL_LOGGING ); channel.pipeline().addFirst( handler ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java index 73e378832a..a39190267c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/outbound/OutboundMessageHandlerTest.java @@ -28,27 +28,26 @@ import java.util.HashMap; import java.util.Map; +import org.neo4j.driver.Query; +import org.neo4j.driver.Value; +import org.neo4j.driver.Values; import org.neo4j.driver.internal.async.connection.ChannelAttributes; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; import org.neo4j.driver.internal.packstream.PackOutput; -import org.neo4j.driver.Value; -import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; import static org.neo4j.driver.internal.messaging.MessageFormat.Writer; import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; -import static org.neo4j.driver.Values.value; import static org.neo4j.driver.util.TestUtil.assertByteBufContains; class OutboundMessageHandlerTest @@ -91,13 +90,13 @@ void shouldOutputByteBufAsWrittenByWriterAndMessageBoundary() @Test void shouldSupportByteArraysByDefault() { - OutboundMessageHandler handler = newHandler( new MessageFormatV1() ); + OutboundMessageHandler handler = newHandler( new MessageFormatV3() ); channel.pipeline().addLast( handler ); Map params = new HashMap<>(); params.put( "array", value( new byte[]{1, 2, 3} ) ); - assertTrue( channel.writeOutbound( new RunMessage( "RETURN 1", params ) ) ); + assertTrue( channel.writeOutbound( new Query( "RETURN 1", Values.value( params ) ) ) ); assertTrue( channel.finish() ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java index ef4a373943..b3e2a5eb23 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java @@ -36,8 +36,8 @@ import org.neo4j.driver.internal.async.inbound.ChannelErrorHandler; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41; import org.neo4j.driver.internal.util.ServerVersion; @@ -63,7 +63,7 @@ void setUp() { setMessageDispatcher( channel, new InboundMessageDispatcher( channel, DEV_NULL_LOGGING ) ); ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast( NAME, new OutboundMessageHandler( new MessageFormatV1(), DEV_NULL_LOGGING ) ); + pipeline.addLast( NAME, new OutboundMessageHandler( new MessageFormatV3(), DEV_NULL_LOGGING ) ); pipeline.addLast( new ChannelErrorHandler( DEV_NULL_LOGGING ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java index 00a350cea2..c3e54e3208 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/InitResponseHandlerTest.java @@ -29,14 +29,14 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import org.neo4j.driver.Query; import org.neo4j.driver.Value; import org.neo4j.driver.Values; import org.neo4j.driver.exceptions.UntrustedServerException; import org.neo4j.driver.internal.async.inbound.ChannelErrorHandler; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.async.outbound.OutboundMessageHandler; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; import static java.util.Collections.singletonMap; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -58,7 +58,7 @@ void setUp() { setMessageDispatcher( channel, new InboundMessageDispatcher( channel, DEV_NULL_LOGGING ) ); ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast( NAME, new OutboundMessageHandler( new MessageFormatV1(), DEV_NULL_LOGGING ) ); + pipeline.addLast( NAME, new OutboundMessageHandler( new MessageFormatV3(), DEV_NULL_LOGGING ) ); pipeline.addLast( new ChannelErrorHandler( DEV_NULL_LOGGING ) ); } @@ -100,7 +100,7 @@ void shouldAllowByteArrays() handler.onSuccess( metadata ); Map params = singletonMap( "array", value( new byte[]{1, 2, 3} ) ); - assertTrue( channel.writeOutbound( new RunMessage( "RETURN 1", params ) ) ); + assertTrue( channel.writeOutbound( new Query( "RETURN 1", Values.value( params ) ) ) ); assertTrue( channel.finish() ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/LegacyPullAllResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/LegacyPullAllResponseHandlerTest.java index 2c9bd0716f..9c5505b280 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/LegacyPullAllResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/LegacyPullAllResponseHandlerTest.java @@ -24,8 +24,9 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import org.neo4j.driver.Record; import org.neo4j.driver.Query; +import org.neo4j.driver.Record; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.summary.ResultSummary; @@ -43,7 +44,6 @@ import static org.mockito.Mockito.verify; import static org.neo4j.driver.Values.value; import static org.neo4j.driver.Values.values; -import static org.neo4j.driver.internal.messaging.v1.BoltProtocolV1.METADATA_EXTRACTOR; import static org.neo4j.driver.util.TestUtil.await; class LegacyPullAllResponseHandlerTest extends PullAllResponseHandlerTestBase @@ -237,8 +237,9 @@ void shouldEnableAutoReadOnConnectionWhenSummaryRequestedButNotAvailable() throw protected LegacyPullAllResponseHandler newHandler(Query query, List queryKeys, Connection connection ) { - RunResponseHandler runResponseHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR ); + RunResponseHandler runResponseHandler = new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV3.METADATA_EXTRACTOR ); runResponseHandler.onSuccess( singletonMap( "fields", value( queryKeys ) ) ); - return new LegacyPullAllResponseHandler(query, runResponseHandler, connection, METADATA_EXTRACTOR, mock( PullResponseCompletionListener.class ) ); + return new LegacyPullAllResponseHandler( query, runResponseHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR, + mock( PullResponseCompletionListener.class ) ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/RunResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/RunResponseHandlerTest.java index d76bfcd780..3c635bd865 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/RunResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/RunResponseHandlerTest.java @@ -25,7 +25,6 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.util.MetadataExtractor; @@ -115,11 +114,6 @@ void shouldReturnKeysWhenSucceeded() assertEquals( keyIndex, handler.queryKeys().keyIndex() ); } - @Test - void shouldReturnResultAvailableAfterWhenSucceededV1() - { - testResultAvailableAfterOnSuccess( "result_available_after", BoltProtocolV1.METADATA_EXTRACTOR ); - } @Test void shouldReturnResultAvailableAfterWhenSucceededV3() @@ -138,12 +132,12 @@ private static void testResultAvailableAfterOnSuccess( String key, MetadataExtra private static RunResponseHandler newHandler() { - return newHandler( BoltProtocolV1.METADATA_EXTRACTOR ); + return newHandler( BoltProtocolV3.METADATA_EXTRACTOR ); } private static RunResponseHandler newHandler( CompletableFuture runCompletedFuture ) { - return newHandler( runCompletedFuture, BoltProtocolV1.METADATA_EXTRACTOR ); + return newHandler( runCompletedFuture, BoltProtocolV3.METADATA_EXTRACTOR ); } private static RunResponseHandler newHandler( MetadataExtractor metadataExtractor ) diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java index d82bf6318d..40b869eef2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java @@ -27,6 +27,7 @@ import org.neo4j.driver.internal.BookmarkHolder; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; @@ -36,7 +37,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.neo4j.driver.Values.value; -import static org.neo4j.driver.internal.messaging.v1.BoltProtocolV1.METADATA_EXTRACTOR; import static org.neo4j.driver.util.TestUtil.anyServerVersion; class SessionPullResponseCompletionListenerTest @@ -81,9 +81,9 @@ void shouldUpdateBookmarksOnSuccess() private static ResponseHandler newHandler( Connection connection, PullResponseCompletionListener listener ) { - RunResponseHandler runHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR ); + RunResponseHandler runHandler = new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV3.METADATA_EXTRACTOR ); BasicPullResponseHandler handler = - new BasicPullResponseHandler( new Query( "RETURN 1" ), runHandler, connection, METADATA_EXTRACTOR, listener ); + new BasicPullResponseHandler( new Query( "RETURN 1" ), runHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR, listener ); handler.installRecordConsumer( ( record, throwable ) -> {} ); handler.installSummaryConsumer( ( resultSummary, throwable ) -> {} ); return handler; diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java index 910c146e89..018cd03e77 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java @@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.neo4j.driver.internal.messaging.v1.BoltProtocolV1.METADATA_EXTRACTOR; +import static org.neo4j.driver.internal.messaging.v3.BoltProtocolV3.METADATA_EXTRACTOR; import static org.neo4j.driver.util.TestUtil.anyServerVersion; class TransactionPullResponseCompletionListenerTest diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/AutoPullResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/AutoPullResponseHandlerTest.java index e44051280c..f81480ab43 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/AutoPullResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/AutoPullResponseHandlerTest.java @@ -33,6 +33,7 @@ import org.neo4j.driver.internal.handlers.PullResponseCompletionListener; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.value.BooleanValue; @@ -46,17 +47,17 @@ import static org.neo4j.driver.Values.value; import static org.neo4j.driver.Values.values; import static org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil.DEFAULT_FETCH_SIZE; -import static org.neo4j.driver.internal.messaging.v1.BoltProtocolV1.METADATA_EXTRACTOR; class AutoPullResponseHandlerTest extends PullAllResponseHandlerTestBase { @Override protected AutoPullResponseHandler newHandler( Query query, List queryKeys, Connection connection ) { - RunResponseHandler runResponseHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR ); + RunResponseHandler runResponseHandler = new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV3.METADATA_EXTRACTOR ); runResponseHandler.onSuccess( singletonMap( "fields", value( queryKeys ) ) ); AutoPullResponseHandler handler = - new AutoPullResponseHandler( query, runResponseHandler, connection, METADATA_EXTRACTOR, mock( PullResponseCompletionListener.class ), + new AutoPullResponseHandler( query, runResponseHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR, + mock( PullResponseCompletionListener.class ), DEFAULT_FETCH_SIZE ); handler.prePopulateRecords(); return handler; @@ -64,10 +65,11 @@ protected AutoPullResponseHandler newHandler( Query query, List queryKey protected AutoPullResponseHandler newHandler( Query query, Connection connection, long fetchSize ) { - RunResponseHandler runResponseHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR ); + RunResponseHandler runResponseHandler = new RunResponseHandler( new CompletableFuture<>(), BoltProtocolV3.METADATA_EXTRACTOR ); runResponseHandler.onSuccess( emptyMap() ); AutoPullResponseHandler handler = - new AutoPullResponseHandler( query, runResponseHandler, connection, METADATA_EXTRACTOR, mock( PullResponseCompletionListener.class ), + new AutoPullResponseHandler( query, runResponseHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR, + mock( PullResponseCompletionListener.class ), fetchSize ); handler.prePopulateRecords(); return handler; diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java index 45049b754c..1bf8666736 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/BoltProtocolTest.java @@ -21,10 +21,8 @@ import io.netty.channel.embedded.EmbeddedChannel; import org.junit.jupiter.api.Test; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; -import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.exceptions.ClientException; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; @@ -38,8 +36,6 @@ class BoltProtocolTest void shouldCreateProtocolForKnownVersions() { assertAll( - () -> assertThat( BoltProtocol.forVersion( BoltProtocolV1.VERSION ), instanceOf( BoltProtocolV1.class ) ), - () -> assertThat( BoltProtocol.forVersion( BoltProtocolV2.VERSION ), instanceOf( BoltProtocolV2.class ) ), () -> assertThat( BoltProtocol.forVersion( BoltProtocolV3.VERSION ), instanceOf( BoltProtocolV3.class ) ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java index d891291d16..15da3b75c6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java @@ -29,21 +29,22 @@ import java.util.List; import java.util.Map; +import org.neo4j.driver.Value; +import org.neo4j.driver.exceptions.ClientException; import org.neo4j.driver.internal.async.connection.BoltProtocolUtil; import org.neo4j.driver.internal.async.connection.ChannelPipelineBuilderImpl; import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; import org.neo4j.driver.internal.async.outbound.ChunkAwareByteBufOutput; +import org.neo4j.driver.internal.messaging.common.CommonValueUnpacker; import org.neo4j.driver.internal.messaging.request.InitMessage; import org.neo4j.driver.internal.messaging.response.FailureMessage; import org.neo4j.driver.internal.messaging.response.IgnoredMessage; import org.neo4j.driver.internal.messaging.response.RecordMessage; import org.neo4j.driver.internal.messaging.response.SuccessMessage; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; +import org.neo4j.driver.internal.packstream.PackStream; import org.neo4j.driver.internal.util.messaging.KnowledgeableMessageFormat; import org.neo4j.driver.internal.util.messaging.MemorizingInboundMessageDispatcher; -import org.neo4j.driver.internal.messaging.v1.MessageFormatV1; -import org.neo4j.driver.internal.packstream.PackStream; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; import static java.util.Arrays.asList; import static java.util.Collections.singletonMap; @@ -54,6 +55,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; @@ -63,12 +66,10 @@ import static org.neo4j.driver.internal.util.ValueFactory.filledNodeValue; import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue; import static org.neo4j.driver.internal.util.ValueFactory.filledRelationshipValue; -import static org.neo4j.driver.Values.parameters; -import static org.neo4j.driver.Values.value; class MessageFormatTest { - public MessageFormat format = new MessageFormatV1(); + public MessageFormat format = new MessageFormatV3(); @Test void shouldUnpackAllResponses() throws Throwable @@ -100,31 +101,6 @@ void shouldUnpackNodeRelationshipAndPath() throws Throwable } - @Test - void shouldErrorPackingNode() throws Throwable - { - // Given - Value value = filledNodeValue(); - expectIOExceptionWithMessage( value, "Unknown type: NODE" ); - } - - @Test - void shouldErrorPackingRelationship() throws Throwable - { - // Given - Value value = filledRelationshipValue(); - expectIOExceptionWithMessage( value, "Unknown type: RELATIONSHIP" ); - } - - @Test - void shouldErrorPackingPath() throws Throwable - { - // Given - Value value = filledPathValue(); - expectIOExceptionWithMessage( value, "Unknown type: PATH" ); - } - - @Test void shouldGiveHelpfulErrorOnMalformedNodeStruct() throws Throwable { @@ -136,7 +112,7 @@ void shouldGiveHelpfulErrorOnMalformedNodeStruct() throws Throwable packer.packStructHeader( 1, RecordMessage.SIGNATURE ); packer.packListHeader( 1 ); - packer.packStructHeader( 0, MessageFormatV1.NODE ); + packer.packStructHeader( 0, CommonValueUnpacker.NODE ); output.stop(); BoltProtocolUtil.writeMessageBoundary( buf ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java deleted file mode 100644 index 8685a07e66..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/BoltProtocolV1Test.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.Bookmark; -import org.neo4j.driver.Logging; -import org.neo4j.driver.Query; -import org.neo4j.driver.TransactionConfig; -import org.neo4j.driver.Value; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.internal.BookmarkHolder; -import org.neo4j.driver.internal.InternalBookmark; -import org.neo4j.driver.internal.async.UnmanagedTransaction; -import org.neo4j.driver.internal.async.connection.ChannelAttributes; -import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; -import org.neo4j.driver.internal.cluster.RoutingContext; -import org.neo4j.driver.internal.cursor.AsyncResultCursor; -import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; -import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; -import org.neo4j.driver.internal.handlers.NoOpResponseHandler; -import org.neo4j.driver.internal.handlers.PullAllResponseHandler; -import org.neo4j.driver.internal.handlers.RollbackTxResponseHandler; -import org.neo4j.driver.internal.handlers.RunResponseHandler; -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.request.InitMessage; -import org.neo4j.driver.internal.messaging.request.PullAllMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.security.InternalAuthToken; -import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.internal.util.Futures; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.startsWith; -import static org.hamcrest.junit.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.neo4j.driver.Values.value; -import static org.neo4j.driver.internal.DatabaseNameUtil.defaultDatabase; -import static org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil.UNLIMITED_FETCH_SIZE; -import static org.neo4j.driver.internal.messaging.v1.BoltProtocolV1.SingleBookmarkHelper.asBeginTransactionParameters; -import static org.neo4j.driver.internal.util.Futures.blockingGet; -import static org.neo4j.driver.util.TestUtil.await; -import static org.neo4j.driver.util.TestUtil.connectionMock; - -public class BoltProtocolV1Test -{ - private static final String QUERY_TEXT = "RETURN $x"; - private static final Map PARAMS = singletonMap( "x", value( 42 ) ); - private static final Query QUERY = new Query( QUERY_TEXT, value( PARAMS ) ); - - private final BoltProtocol protocol = createProtocol(); - private final EmbeddedChannel channel = new EmbeddedChannel(); - private final InboundMessageDispatcher messageDispatcher = new InboundMessageDispatcher( channel, Logging.none() ); - - @BeforeEach - void beforeEach() - { - ChannelAttributes.setMessageDispatcher( channel, messageDispatcher ); - } - - @AfterEach - void afterEach() - { - channel.finishAndReleaseAll(); - } - - @Test - void shouldCreateMessageFormat() - { - assertThat( protocol.createMessageFormat(), instanceOf( expectedMessageFormatType() ) ); - } - - @Test - void shouldInitializeChannel() - { - ChannelPromise promise = channel.newPromise(); - - protocol.initializeChannel( "MyDriver/5.3", dummyAuthToken(), RoutingContext.EMPTY, promise ); - - assertThat( channel.outboundMessages(), hasSize( 1 ) ); - assertThat( channel.outboundMessages().poll(), instanceOf( InitMessage.class ) ); - assertEquals( 1, messageDispatcher.queuedHandlersCount() ); - assertFalse( promise.isDone() ); - - messageDispatcher.handleSuccessMessage( singletonMap( "server", value( "Neo4j/3.1.0" ) ) ); - - assertTrue( promise.isDone() ); - assertTrue( promise.isSuccess() ); - } - - @Test - void shouldFailToInitializeChannelWhenErrorIsReceived() - { - ChannelPromise promise = channel.newPromise(); - - protocol.initializeChannel( "MyDriver/3.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); - - assertThat( channel.outboundMessages(), hasSize( 1 ) ); - assertThat( channel.outboundMessages().poll(), instanceOf( InitMessage.class ) ); - assertEquals( 1, messageDispatcher.queuedHandlersCount() ); - assertFalse( promise.isDone() ); - - messageDispatcher.handleFailureMessage( "Neo.TransientError.General.DatabaseUnavailable", "Oh no!" ); - - assertTrue( promise.isDone() ); - assertFalse( promise.isSuccess() ); - } - - @Test - void shouldBeginTransactionWithoutBookmark() - { - Connection connection = connectionMock( protocol ); - - CompletionStage stage = protocol.beginTransaction( connection, InternalBookmark.empty(), TransactionConfig.empty() ); - - verify( connection ).write( - new RunMessage( "BEGIN" ), NoOpResponseHandler.INSTANCE, - PullAllMessage.PULL_ALL, NoOpResponseHandler.INSTANCE ); - - assertNull( blockingGet( stage ) ); - } - - @Test - void shouldBeginTransactionWithBookmarks() - { - Connection connection = connectionMock( protocol ); - Bookmark bookmark = InternalBookmark.parse( "neo4j:bookmark:v1:tx100" ); - - CompletionStage stage = protocol.beginTransaction( connection, bookmark, TransactionConfig.empty() ); - - verify( connection ).writeAndFlush( - eq( new RunMessage( "BEGIN", asBeginTransactionParameters( bookmark ) ) ), eq( NoOpResponseHandler.INSTANCE ), - eq( PullAllMessage.PULL_ALL ), any( BeginTxResponseHandler.class ) ); - - assertNull( Futures.blockingGet( stage ) ); - } - - @Test - void shouldCommitTransaction() - { - String bookmarkString = "neo4j:bookmark:v1:tx1909"; - - Connection connection = mock( Connection.class ); - when( connection.protocol() ).thenReturn( protocol ); - doAnswer( invocation -> - { - ResponseHandler commitHandler = invocation.getArgument( 3 ); - commitHandler.onSuccess( singletonMap( "bookmark", value( bookmarkString ) ) ); - return null; - } ).when( connection ).writeAndFlush( eq( new RunMessage( "COMMIT" ) ), any(), any(), any() ); - - CompletionStage stage = protocol.commitTransaction( connection ); - - verify( connection ).writeAndFlush( - eq( new RunMessage( "COMMIT" ) ), eq( NoOpResponseHandler.INSTANCE ), - eq( PullAllMessage.PULL_ALL ), any( CommitTxResponseHandler.class ) ); - - assertEquals( InternalBookmark.parse( bookmarkString ), await( stage ) ); - } - - @Test - void shouldRollbackTransaction() - { - Connection connection = connectionMock( protocol ); - - CompletionStage stage = protocol.rollbackTransaction( connection ); - - verify( connection ).writeAndFlush( - eq( new RunMessage( "ROLLBACK" ) ), eq( NoOpResponseHandler.INSTANCE ), - eq( PullAllMessage.PULL_ALL ), any( RollbackTxResponseHandler.class ) ); - - assertNull( Futures.blockingGet( stage ) ); - } - - @Test - void shouldRunInAutoCommitTransactionWithoutWaitingForRunResponse() throws Exception - { - testRunWithoutWaitingForRunResponse( true ); - } - - @Test - void shouldRunInAutoCommitTransactionAndWaitForSuccessRunResponse() throws Exception - { - testRunWithWaitingForResponse( true, true ); - } - - @Test - void shouldRunInAutoCommitTransactionAndWaitForFailureRunResponse() throws Exception - { - testRunWithWaitingForResponse( false, true ); - } - - @Test - void shouldRunInTransactionWithoutWaitingForRunResponse() throws Exception - { - testRunWithoutWaitingForRunResponse( false ); - } - - @Test - void shouldRunInTransactionAndWaitForSuccessRunResponse() throws Exception - { - testRunWithWaitingForResponse( true, false ); - } - - @Test - void shouldRunInTransactionAndWaitForFailureRunResponse() throws Exception - { - testRunWithWaitingForResponse( false, false ); - } - - @Test - void shouldNotSupportTransactionConfigInBeginTransaction() - { - TransactionConfig config = TransactionConfig.builder() - .withTimeout( Duration.ofSeconds( 5 ) ) - .withMetadata( singletonMap( "key", "value" ) ) - .build(); - - CompletionStage txStage = protocol.beginTransaction( connectionMock( protocol ), InternalBookmark.empty(), config ); - - ClientException e = assertThrows( ClientException.class, () -> await( txStage ) ); - assertThat( e.getMessage(), startsWith( "Driver is connected to the database that does not support transaction configuration" ) ); - } - - @Test - void shouldNotSupportTransactionConfigForAutoCommitTransactions() - { - TransactionConfig config = TransactionConfig.builder() - .withTimeout( Duration.ofSeconds( 42 ) ) - .withMetadata( singletonMap( "hello", "world" ) ) - .build(); - - ClientException e = assertThrows( ClientException.class, - () -> protocol.runInAutoCommitTransaction( connectionMock( protocol ), new Query( "RETURN 1" ), BookmarkHolder.NO_OP, config, true, - UNLIMITED_FETCH_SIZE ) ); - assertThat( e.getMessage(), startsWith( "Driver is connected to the database that does not support transaction configuration" ) ); - } - - @Test - void shouldNotSupportDatabaseNameInBeginTransaction() - { - CompletionStage txStage = protocol.beginTransaction( connectionMock( "foo", protocol ), InternalBookmark.empty(), TransactionConfig.empty() ); - - ClientException e = assertThrows( ClientException.class, () -> await( txStage ) ); - assertThat( e.getMessage(), startsWith( "Database name parameter for selecting database is not supported" ) ); - } - - @Test - void shouldNotSupportDatabaseNameForAutoCommitTransactions() - { - ClientException e = assertThrows( ClientException.class, - () -> protocol.runInAutoCommitTransaction( connectionMock( "foo", protocol ), - new Query( "RETURN 1" ), BookmarkHolder.NO_OP, TransactionConfig.empty(), true, UNLIMITED_FETCH_SIZE ) ); - assertThat( e.getMessage(), startsWith( "Database name parameter for selecting database is not supported" ) ); - } - - protected BoltProtocol createProtocol() - { - return BoltProtocolV1.INSTANCE; - } - - protected Class expectedMessageFormatType() - { - return MessageFormatV1.class; - } - - private void testRunWithoutWaitingForRunResponse( boolean autoCommitTx ) throws Exception - { - Connection connection = mock( Connection.class ); - when( connection.databaseName() ).thenReturn( defaultDatabase() ); - - CompletionStage cursorStage; - if ( autoCommitTx ) - { - cursorStage = protocol - .runInAutoCommitTransaction( connection, QUERY, BookmarkHolder.NO_OP, TransactionConfig.empty(), false, UNLIMITED_FETCH_SIZE ) - .asyncResult(); - } - else - { - cursorStage = protocol - .runInUnmanagedTransaction( connection, QUERY, mock( UnmanagedTransaction.class ), false, UNLIMITED_FETCH_SIZE ) - .asyncResult(); - } - CompletableFuture cursorFuture = cursorStage.toCompletableFuture(); - - assertTrue( cursorFuture.isDone() ); - assertNotNull( cursorFuture.get() ); - verifyRunInvoked( connection ); - } - - private void testRunWithWaitingForResponse( boolean success, boolean session ) throws Exception - { - Connection connection = mock( Connection.class ); - when( connection.databaseName() ).thenReturn( defaultDatabase() ); - - CompletionStage cursorStage; - if ( session ) - { - cursorStage = protocol.runInAutoCommitTransaction( connection, QUERY, BookmarkHolder.NO_OP, TransactionConfig.empty(), true, UNLIMITED_FETCH_SIZE ) - .asyncResult(); - } - else - { - cursorStage = protocol.runInUnmanagedTransaction( connection, QUERY, mock( UnmanagedTransaction.class ), true, UNLIMITED_FETCH_SIZE ) - .asyncResult(); - } - CompletableFuture cursorFuture = cursorStage.toCompletableFuture(); - - assertFalse( cursorFuture.isDone() ); - ResponseHandler runResponseHandler = verifyRunInvoked( connection ); - - if ( success ) - { - runResponseHandler.onSuccess( emptyMap() ); - } - else - { - runResponseHandler.onFailure( new RuntimeException() ); - } - - assertTrue( cursorFuture.isDone() ); - assertNotNull( cursorFuture.get() ); - } - - private static ResponseHandler verifyRunInvoked( Connection connection ) - { - ArgumentCaptor runHandlerCaptor = ArgumentCaptor.forClass( ResponseHandler.class ); - ArgumentCaptor pullAllHandlerCaptor = ArgumentCaptor.forClass( ResponseHandler.class ); - - verify( connection ).write( eq( new RunMessage( QUERY_TEXT, PARAMS ) ), runHandlerCaptor.capture() ); - verify( connection ).writeAndFlush( eq( PullAllMessage.PULL_ALL ), pullAllHandlerCaptor.capture() ); - - assertThat( runHandlerCaptor.getValue(), instanceOf( RunResponseHandler.class ) ); - assertThat( pullAllHandlerCaptor.getValue(), instanceOf( PullAllResponseHandler.class ) ); - - return runHandlerCaptor.getValue(); - } - - private static InternalAuthToken dummyAuthToken() - { - return (InternalAuthToken) AuthTokens.basic( "hello", "world" ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1Test.java deleted file mode 100644 index 7e7c3632d7..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageReaderV1Test.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.time.LocalDateTime; - -import org.neo4j.driver.internal.util.messaging.AbstractMessageReaderTestBase; -import org.neo4j.driver.internal.messaging.response.RecordMessage; -import org.neo4j.driver.internal.packstream.PackInput; -import org.neo4j.driver.Value; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.neo4j.driver.internal.messaging.MessageFormat.Reader; -import static org.neo4j.driver.Values.point; -import static org.neo4j.driver.Values.value; - -class MessageReaderV1Test extends AbstractMessageReaderTestBase -{ - @Test - void shouldFailToReadMessageWithTemporalValue() - { - Value[] fields = {value( LocalDateTime.now() )}; - - assertThrows( IOException.class, () -> testMessageReading( new RecordMessage( fields ) ) ); - } - - @Test - void shouldFailToReadMessageWithSpatialValue() - { - Value[] fields = {point( 42, 1, 2 )}; - - assertThrows( IOException.class, () -> testMessageReading( new RecordMessage( fields ) ) ); - } - - @Override - protected Reader newReader( PackInput input ) - { - return new MessageReaderV1( input ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java deleted file mode 100644 index 17a9a3c13c..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v1/MessageWriterV1Test.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v1; - -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.stream.Stream; - -import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; -import org.neo4j.driver.internal.messaging.Message; -import org.neo4j.driver.internal.messaging.MessageFormat.Writer; -import org.neo4j.driver.internal.messaging.request.HelloMessage; -import org.neo4j.driver.internal.messaging.request.InitMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.packstream.PackOutput; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; -import static org.neo4j.driver.internal.messaging.request.CommitMessage.COMMIT; -import static org.neo4j.driver.internal.messaging.request.DiscardAllMessage.DISCARD_ALL; -import static org.neo4j.driver.internal.messaging.request.GoodbyeMessage.GOODBYE; -import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; -import static org.neo4j.driver.internal.messaging.request.ResetMessage.RESET; -import static org.neo4j.driver.internal.messaging.request.RollbackMessage.ROLLBACK; -import static org.neo4j.driver.Values.point; -import static org.neo4j.driver.Values.value; - -class MessageWriterV1Test extends AbstractMessageWriterTestBase -{ - @Override - protected Writer newWriter( PackOutput output ) - { - return new MessageWriterV1( output ); - } - - @Override - protected Stream supportedMessages() - { - return Stream.of( - new InitMessage( "MyDriver/1.2.3", singletonMap( "password", value( "hello" ) ) ), - new RunMessage( "RETURN 1", singletonMap( "key", value( 42 ) ) ), - PULL_ALL, - DISCARD_ALL, - RESET - ); - } - - @Override - protected Stream unsupportedMessages() - { - return Stream.of( - // Bolt V1 messages with Bolt V2 structs - new RunMessage( "RETURN $now", singletonMap( "now", value( LocalDateTime.now() ) ) ), - new RunMessage( "RETURN $here", singletonMap( "now", point( 42, 1, 1 ) ) ), - - // Bolt V3 messages - new HelloMessage( "Driver/2.3.4", emptyMap(), emptyMap() ), - GOODBYE, - COMMIT, - ROLLBACK - ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2Test.java deleted file mode 100644 index 7cb68fcadf..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/BoltProtocolV2Test.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1Test; - -class BoltProtocolV2Test extends BoltProtocolV1Test -{ - @Override - protected BoltProtocol createProtocol() - { - return BoltProtocolV2.INSTANCE; - } - - @Override - protected Class expectedMessageFormatType() - { - return MessageFormatV2.class; - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2Test.java deleted file mode 100644 index 56049fe4f3..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageFormatV2Test.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.neo4j.driver.Value; -import org.neo4j.driver.Values; -import org.neo4j.driver.internal.InternalPoint2D; -import org.neo4j.driver.internal.InternalPoint3D; -import org.neo4j.driver.internal.async.inbound.ByteBufInput; -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.ResponseMessageHandler; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.messaging.response.RecordMessage; -import org.neo4j.driver.internal.util.ThrowingConsumer; -import org.neo4j.driver.internal.util.io.ByteBufOutput; -import org.neo4j.driver.types.IsoDuration; -import org.neo4j.driver.types.Point; - -import static java.time.Month.APRIL; -import static java.time.Month.AUGUST; -import static java.time.Month.DECEMBER; -import static java.time.ZoneOffset.UTC; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonMap; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.neo4j.driver.Values.point; -import static org.neo4j.driver.Values.value; -import static org.neo4j.driver.internal.packstream.PackStream.FLOAT_64; -import static org.neo4j.driver.internal.packstream.PackStream.INT_16; -import static org.neo4j.driver.internal.packstream.PackStream.INT_32; -import static org.neo4j.driver.internal.packstream.PackStream.INT_64; -import static org.neo4j.driver.internal.packstream.PackStream.Packer; -import static org.neo4j.driver.internal.packstream.PackStream.STRING_8; -import static org.neo4j.driver.util.TestUtil.assertByteBufContains; - -class MessageFormatV2Test -{ - private final MessageFormatV2 messageFormat = new MessageFormatV2(); - - @Test - void shouldWritePoint2D() throws Exception - { - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $point", singletonMap( "point", point( 42, 12.99, -180.0 ) ) ) ); - - // point should be encoded as (byte SRID, FLOAT_64 header byte + double X, FLOAT_64 header byte + double Y) - int index = buf.readableBytes() - Double.BYTES - Byte.BYTES - Double.BYTES - Byte.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, (byte) 42, FLOAT_64, 12.99, FLOAT_64, -180.0 ); - } - - @Test - void shouldWritePoint3D() throws Exception - { - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $point", singletonMap( "point", point( 42, 0.51, 2.99, 100.123 ) ) ) ); - - // point should be encoded as (byte SRID, FLOAT_64 header byte + double X, FLOAT_64 header byte + double Y, FLOAT_64 header byte + double Z) - int index = buf.readableBytes() - Double.BYTES - Byte.BYTES - Double.BYTES - Byte.BYTES - Double.BYTES - Byte.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, (byte) 42, FLOAT_64, 0.51, FLOAT_64, 2.99, FLOAT_64, 100.123 ); - } - - @Test - void shouldReadPoint2D() throws Exception - { - Point point = new InternalPoint2D( 42, 120.65, -99.2 ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 3, (byte) 'X' ); - packer.pack( point.srid() ); - packer.pack( point.x() ); - packer.pack( point.y() ); - } ); - - assertEquals( point, unpacked ); - } - - @Test - void shouldReadPoint3D() throws Exception - { - Point point = new InternalPoint3D( 42, 85.391, 98.8, 11.1 ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 4, (byte) 'Y' ); - packer.pack( point.srid() ); - packer.pack( point.x() ); - packer.pack( point.y() ); - packer.pack( point.z() ); - } ); - - assertEquals( point, unpacked ); - } - - @Test - void shouldWriteDate() throws Exception - { - LocalDate date = LocalDate.ofEpochDay( 2147483650L ); - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $date", singletonMap( "date", value( date ) ) ) ); - - int index = buf.readableBytes() - Long.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, INT_64, date.toEpochDay() ); - } - - @Test - void shouldReadDate() throws Exception - { - LocalDate date = LocalDate.of( 2012, AUGUST, 3 ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 1, (byte) 'D' ); - packer.pack( date.toEpochDay() ); - } ); - - assertEquals( date, unpacked ); - } - - @Test - void shouldWriteTime() throws Exception - { - OffsetTime time = OffsetTime.of( 4, 16, 20, 999, ZoneOffset.MIN ); - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $time", singletonMap( "time", value( time ) ) ) ); - - int index = buf.readableBytes() - Long.BYTES - Byte.BYTES - Integer.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, INT_64, time.toLocalTime().toNanoOfDay(), INT_32, time.getOffset().getTotalSeconds() ); - } - - @Test - void shouldReadTime() throws Exception - { - OffsetTime time = OffsetTime.of( 23, 59, 59, 999, ZoneOffset.MAX ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 2, (byte) 'T' ); - packer.pack( time.toLocalTime().toNanoOfDay() ); - packer.pack( time.getOffset().getTotalSeconds() ); - } ); - - assertEquals( time, unpacked ); - } - - @Test - void shouldWriteLocalTime() throws Exception - { - LocalTime time = LocalTime.of( 12, 9, 18, 999_888 ); - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $time", singletonMap( "time", value( time ) ) ) ); - - int index = buf.readableBytes() - Long.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, INT_64, time.toNanoOfDay() ); - } - - @Test - void shouldReadLocalTime() throws Exception - { - LocalTime time = LocalTime.of( 12, 25 ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 1, (byte) 't' ); - packer.pack( time.toNanoOfDay() ); - } ); - - assertEquals( time, unpacked ); - } - - @Test - void shouldWriteLocalDateTime() throws Exception - { - LocalDateTime dateTime = LocalDateTime.of( 2049, DECEMBER, 12, 17, 25, 49, 199 ); - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $dateTime", singletonMap( "dateTime", value( dateTime ) ) ) ); - - int index = buf.readableBytes() - Long.BYTES - Byte.BYTES - Short.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, INT_64, dateTime.toEpochSecond( UTC ), INT_16, (short) dateTime.getNano() ); - } - - @Test - void shouldReadLocalDateTime() throws Exception - { - LocalDateTime dateTime = LocalDateTime.of( 1999, APRIL, 3, 19, 5, 5, 100_200_300 ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 2, (byte) 'd' ); - packer.pack( dateTime.toEpochSecond( UTC ) ); - packer.pack( dateTime.getNano() ); - } ); - - assertEquals( dateTime, unpacked ); - } - - @Test - void shouldWriteZonedDateTimeWithOffset() throws Exception - { - ZoneOffset zoneOffset = ZoneOffset.ofHoursMinutes( 9, 30 ); - ZonedDateTime dateTime = ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, zoneOffset ); - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $dateTime", singletonMap( "dateTime", value( dateTime ) ) ) ); - - int index = buf.readableBytes() - Integer.BYTES - Byte.BYTES - Short.BYTES - Byte.BYTES - Integer.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, - INT_32, (int) localEpochSecondOf( dateTime ), - INT_16, (short) dateTime.getNano(), - INT_32, zoneOffset.getTotalSeconds() ); - } - - @Test - void shouldReadZonedDateTimeWithOffset() throws Exception - { - ZoneOffset zoneOffset = ZoneOffset.ofHoursMinutes( -7, -15 ); - ZonedDateTime dateTime = ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, zoneOffset ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 3, (byte) 'F' ); - packer.pack( localEpochSecondOf( dateTime ) ); - packer.pack( dateTime.toInstant().getNano() ); - packer.pack( zoneOffset.getTotalSeconds() ); - } ); - - assertEquals( dateTime, unpacked ); - } - - @Test - void shouldWriteZonedDateTimeWithZoneId() throws Exception - { - String zoneName = "Europe/Stockholm"; - byte[] zoneNameBytes = zoneName.getBytes(); - ZonedDateTime dateTime = ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneId.of( zoneName ) ); - - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $dateTime", singletonMap( "dateTime", value( dateTime ) ) ) ); - - int index = buf.readableBytes() - zoneNameBytes.length - Byte.BYTES - Byte.BYTES - Short.BYTES - Byte.BYTES - Integer.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - List expectedBuf = new ArrayList<>( asList( - INT_32, (int) localEpochSecondOf( dateTime ), - INT_16, (short) dateTime.toInstant().getNano(), - STRING_8, (byte) zoneNameBytes.length ) ); - - for ( byte b : zoneNameBytes ) - { - expectedBuf.add( b ); - } - - assertByteBufContains( tailSlice, expectedBuf.toArray( new Number[0] ) ); - } - - @Test - void shouldReadZonedDateTimeWithZoneId() throws Exception - { - String zoneName = "Europe/Stockholm"; - ZonedDateTime dateTime = ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneId.of( zoneName ) ); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 3, (byte) 'f' ); - packer.pack( localEpochSecondOf( dateTime ) ); - packer.pack( dateTime.toInstant().getNano() ); - packer.pack( zoneName ); - } ); - - assertEquals( dateTime, unpacked ); - } - - @Test - void shouldWriteDuration() throws Exception - { - Value durationValue = Values.isoDuration( Long.MAX_VALUE - 1, Integer.MAX_VALUE - 1, Short.MAX_VALUE - 1, Byte.MAX_VALUE - 1 ); - IsoDuration duration = durationValue.asIsoDuration(); - - ByteBuf buf = Unpooled.buffer(); - MessageFormat.Writer writer = newWriter( buf ); - - writer.write( new RunMessage( "RETURN $duration", singletonMap( "duration", durationValue ) ) ); - - int index = buf.readableBytes() - Long.BYTES - Byte.BYTES - Integer.BYTES - Byte.BYTES - Short.BYTES - Byte.BYTES - Byte.BYTES; - ByteBuf tailSlice = buf.slice( index, buf.readableBytes() - index ); - - assertByteBufContains( tailSlice, - INT_64, duration.months(), INT_32, (int) duration.days(), INT_16, (short) duration.seconds(), (byte) duration.nanoseconds() ); - } - - @Test - void shouldReadDuration() throws Exception - { - Value durationValue = Values.isoDuration( 17, 22, 99, 15 ); - IsoDuration duration = durationValue.asIsoDuration(); - - Object unpacked = packAndUnpackValue( packer -> - { - packer.packStructHeader( 4, (byte) 'E' ); - packer.pack( duration.months() ); - packer.pack( duration.days() ); - packer.pack( duration.seconds() ); - packer.pack( duration.nanoseconds() ); - } ); - - assertEquals( duration, unpacked ); - } - - private Object packAndUnpackValue( ThrowingConsumer packAction ) throws Exception - { - ByteBuf buf = Unpooled.buffer(); - try - { - Packer packer = new Packer( new ByteBufOutput( buf ) ); - packer.packStructHeader( 1, RecordMessage.SIGNATURE ); - packer.packListHeader( 1 ); - packAction.accept( packer ); - - ByteBufInput input = new ByteBufInput(); - input.start( buf ); - MessageFormat.Reader reader = messageFormat.newReader( input ); - - List values = new ArrayList<>(); - ResponseMessageHandler responseHandler = recordMemorizingHandler( values ); - reader.read( responseHandler ); - input.stop(); - - assertEquals( 1, values.size() ); - return values.get( 0 ).asObject(); - } - finally - { - buf.release(); - } - } - - private static ResponseMessageHandler recordMemorizingHandler( List values ) throws IOException - { - ResponseMessageHandler responseHandler = mock( ResponseMessageHandler.class ); - doAnswer( invocation -> - { - Value[] arg = invocation.getArgument( 0 ); - Collections.addAll( values, arg ); - return null; - } ).when( responseHandler ).handleRecordMessage( any() ); - return responseHandler; - } - - private MessageFormat.Writer newWriter( ByteBuf buf ) - { - return messageFormat.newWriter( new ByteBufOutput( buf ) ); - } - - private static long localEpochSecondOf( ZonedDateTime dateTime ) - { - return dateTime.toLocalDateTime().toEpochSecond( UTC ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2Test.java deleted file mode 100644 index 94ebc57720..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageReaderV2Test.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import org.junit.jupiter.api.Test; - -import java.time.LocalDateTime; -import java.time.OffsetTime; -import java.time.ZonedDateTime; - -import org.neo4j.driver.internal.util.messaging.AbstractMessageReaderTestBase; -import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.ResponseMessageHandler; -import org.neo4j.driver.internal.messaging.response.RecordMessage; -import org.neo4j.driver.internal.packstream.PackInput; -import org.neo4j.driver.Value; - -import static org.mockito.Mockito.verify; -import static org.neo4j.driver.Values.point; -import static org.neo4j.driver.Values.value; - -class MessageReaderV2Test extends AbstractMessageReaderTestBase -{ - @Test - void shouldReadMessageWithTemporalValues() throws Exception - { - Value[] fields = {value( LocalDateTime.now() ), value( OffsetTime.now() ), value( ZonedDateTime.now() )}; - - ResponseMessageHandler handler = testMessageReading( new RecordMessage( fields ) ); - - verify( handler ).handleRecordMessage( fields ); - } - - @Test - void shouldReadMessageWithSpatialValues() throws Exception - { - Value[] fields = {point( 42, 1.1, 2.2 ), point( 4242, 3.3, 4.4 ), point( 24, 5.5, 6.6, 7.7 ), point( 2424, 8.8, 9.9, 10.1 )}; - - ResponseMessageHandler handler = testMessageReading( new RecordMessage( fields ) ); - - verify( handler ).handleRecordMessage( fields ); - } - - @Override - protected MessageFormat.Reader newReader( PackInput input ) - { - return new MessageReaderV2( input ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java deleted file mode 100644 index 77d1995e56..0000000000 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v2/MessageWriterV2Test.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2002-2020 "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. - */ -package org.neo4j.driver.internal.messaging.v2; - -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.stream.Stream; - -import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; -import org.neo4j.driver.internal.messaging.Message; -import org.neo4j.driver.internal.messaging.MessageFormat.Writer; -import org.neo4j.driver.internal.messaging.request.HelloMessage; -import org.neo4j.driver.internal.messaging.request.InitMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; -import org.neo4j.driver.internal.packstream.PackOutput; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; -import static org.neo4j.driver.internal.messaging.request.CommitMessage.COMMIT; -import static org.neo4j.driver.internal.messaging.request.DiscardAllMessage.DISCARD_ALL; -import static org.neo4j.driver.internal.messaging.request.GoodbyeMessage.GOODBYE; -import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; -import static org.neo4j.driver.internal.messaging.request.ResetMessage.RESET; -import static org.neo4j.driver.internal.messaging.request.RollbackMessage.ROLLBACK; -import static org.neo4j.driver.Values.point; -import static org.neo4j.driver.Values.value; - -class MessageWriterV2Test extends AbstractMessageWriterTestBase -{ - @Override - protected Writer newWriter( PackOutput output ) - { - return new MessageWriterV2( output ); - } - - @Override - protected Stream supportedMessages() - { - return Stream.of( - new InitMessage( "MyDriver/1.2.3", singletonMap( "password", value( "hello" ) ) ), - new RunMessage( "RETURN 1", singletonMap( "key", value( 42 ) ) ), - new RunMessage( "RETURN $now", singletonMap( "now", value( LocalDateTime.now() ) ) ), // RUN with temporal value - new RunMessage( "RETURN $here", singletonMap( "now", point( 42, 1, 1 ) ) ), // RUN with spatial value - PULL_ALL, - DISCARD_ALL, - RESET - ); - } - - @Override - protected Stream unsupportedMessages() - { - return Stream.of( - new HelloMessage( "JavaDriver/1.1.0", emptyMap(), emptyMap() ), - GOODBYE, - COMMIT, - ROLLBACK - ); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java index 96dda7eecc..7ed7341139 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/BoltProtocolV3Test.java @@ -33,7 +33,6 @@ import java.util.concurrent.CompletionStage; import org.neo4j.driver.AccessMode; -import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Bookmark; import org.neo4j.driver.Logging; @@ -340,14 +339,33 @@ void databaseNameForAutoCommitTransactions() testDatabaseNameSupport( true ); } + @Test + void shouldNotSupportDatabaseNameInBeginTransaction() + { + CompletionStage txStage = protocol.beginTransaction( connectionMock( "foo", protocol ), InternalBookmark.empty(), TransactionConfig.empty() ); + + ClientException e = assertThrows( ClientException.class, () -> await( txStage ) ); + assertThat( e.getMessage(), startsWith( "Database name parameter for selecting database is not supported" ) ); + } + + @Test + void shouldNotSupportDatabaseNameForAutoCommitTransactions() + { + ClientException e = assertThrows( ClientException.class, + () -> protocol.runInAutoCommitTransaction( connectionMock( "foo", protocol ), + new Query( "RETURN 1" ), BookmarkHolder.NO_OP, TransactionConfig.empty(), + true, UNLIMITED_FETCH_SIZE ) ); + assertThat( e.getMessage(), startsWith( "Database name parameter for selecting database is not supported" ) ); + } + protected void testDatabaseNameSupport( boolean autoCommitTx ) { ClientException e; if ( autoCommitTx ) { e = assertThrows( ClientException.class, - () -> protocol.runInAutoCommitTransaction( connectionMock( "foo", protocol ), new Query( "RETURN 1" ), BookmarkHolder.NO_OP, - TransactionConfig.empty(), true, UNLIMITED_FETCH_SIZE ) ); + () -> protocol.runInAutoCommitTransaction( connectionMock( "foo", protocol ), new Query( "RETURN 1" ), BookmarkHolder.NO_OP, + TransactionConfig.empty(), true, UNLIMITED_FETCH_SIZE ) ); } else { diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3Test.java index f46b68a8e2..b0e8f13221 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageFormatV3Test.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v2.MessageReaderV2; +import org.neo4j.driver.internal.messaging.common.CommonMessageReader; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.packstream.PackOutput; @@ -29,14 +29,19 @@ import static org.hamcrest.Matchers.instanceOf; import static org.mockito.Mockito.mock; +/** + * The MessageFormat under tests is the one provided by the {@link BoltProtocolV3} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ class MessageFormatV3Test { + private static MessageFormat messageFormat = BoltProtocolV3.INSTANCE.createMessageFormat(); + @Test void shouldCreateCorrectWriter() { - MessageFormatV3 format = new MessageFormatV3(); - - MessageFormat.Writer writer = format.newWriter( mock( PackOutput.class ) ); + MessageFormat.Writer writer = messageFormat.newWriter( mock( PackOutput.class ) ); assertThat( writer, instanceOf( MessageWriterV3.class ) ); } @@ -44,10 +49,8 @@ void shouldCreateCorrectWriter() @Test void shouldCreateCorrectReader() { - MessageFormatV3 format = new MessageFormatV3(); - - MessageFormat.Reader reader = format.newReader( mock( PackInput.class ) ); + MessageFormat.Reader reader = messageFormat.newReader( mock( PackInput.class ) ); - assertThat( reader, instanceOf( MessageReaderV2.class ) ); + assertThat( reader, instanceOf( CommonMessageReader.class ) ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageReaderV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageReaderV3Test.java new file mode 100644 index 0000000000..fc0a0bd1f8 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageReaderV3Test.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2002-2020 "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. + */ +package org.neo4j.driver.internal.messaging.v3; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.stream.Stream; + +import org.neo4j.driver.Value; +import org.neo4j.driver.Values; +import org.neo4j.driver.internal.InternalPoint2D; +import org.neo4j.driver.internal.InternalPoint3D; +import org.neo4j.driver.internal.messaging.Message; +import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.messaging.request.DiscardAllMessage; +import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.messaging.response.FailureMessage; +import org.neo4j.driver.internal.messaging.response.IgnoredMessage; +import org.neo4j.driver.internal.messaging.response.RecordMessage; +import org.neo4j.driver.internal.messaging.response.SuccessMessage; +import org.neo4j.driver.internal.packstream.PackInput; +import org.neo4j.driver.internal.util.messaging.AbstractMessageReaderTestBase; + +import static java.util.Arrays.asList; +import static java.util.Calendar.APRIL; +import static java.util.Calendar.AUGUST; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue; +import static org.neo4j.driver.internal.util.ValueFactory.emptyPathValue; +import static org.neo4j.driver.internal.util.ValueFactory.emptyRelationshipValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledNodeValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledRelationshipValue; + +/** + * The MessageReader under tests is the one provided by the {@link BoltProtocolV3} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ +public class MessageReaderV3Test extends AbstractMessageReaderTestBase +{ + + @Override + protected Stream supportedMessages() + { + return Stream.of( + // V2 Record types + record( value( new InternalPoint2D( 42, 120.65, -99.2 ) ) ), + record( value( new InternalPoint3D( 42, 85.391, 98.8, 11.1 ) ) ), + record( value( LocalDate.of( 2012, AUGUST, 3 ) ) ), + record( value( OffsetTime.of( 23, 59, 59, 999, ZoneOffset.MAX ) ) ), + record( value( LocalTime.of( 12, 25 ) ) ), + record( value( LocalDateTime.of( 1999, APRIL, 3, 19, 5, 5, 100_200_300 ) ) ), + record( value( ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneOffset.ofHoursMinutes( -7, -15 ) ) ) ), + record( value( ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneId.of( "Europe/Stockholm" ) ) ) ), + record( value( Values.isoDuration( Long.MAX_VALUE - 1, Integer.MAX_VALUE - 1, Short.MAX_VALUE - 1, Byte.MAX_VALUE - 1 ).asIsoDuration() ) ), + record( value( Values.isoDuration( 17, 22, 99, 15 ).asIsoDuration() ) ), + + // Bolt previous versions valid messages + new FailureMessage( "Hello", "World!" ), + IgnoredMessage.IGNORED, + new SuccessMessage( new HashMap<>() ), + record( value( 1337L ) ), + record( value( parameters( "cat", null, "dog", null ) ) ), + record( value( parameters( "k", 12, "a", "banana" ) ) ), + record( value( asList( "k", 12, "a", "banana" ) ) ), + + // V3 Record Types + record( emptyNodeValue() ), + record( filledNodeValue() ), + record( emptyRelationshipValue() ), + record( filledRelationshipValue() ), + record( filledPathValue() ), + record( emptyPathValue() ) + ); + } + + @Override + protected Stream unsupportedMessages() + { + return Stream.of( + DiscardAllMessage.DISCARD_ALL, + new RunMessage( "RETURN 42" ) + ); + } + + @Override + protected MessageFormat.Reader newReader( PackInput input ) + { + return BoltProtocolV3.INSTANCE.createMessageFormat().newReader( input ); + } + + private Message record( Value value ) + { + return new RecordMessage( new Value[]{value} ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java index 678aff290a..87d23c7d24 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v3/MessageWriterV3Test.java @@ -18,6 +18,12 @@ */ package org.neo4j.driver.internal.messaging.v3; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Collections; import java.util.stream.Stream; @@ -35,6 +41,7 @@ import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; import static java.time.Duration.ofSeconds; +import static java.util.Calendar.DECEMBER; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.neo4j.driver.AccessMode.READ; @@ -52,29 +59,49 @@ import static org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage.autoCommitTxRunMessage; import static org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage.unmanagedTxRunMessage; +/** + * The MessageWriter under tests is the one provided by the {@link BoltProtocolV3} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ class MessageWriterV3Test extends AbstractMessageWriterTestBase { @Override protected MessageFormat.Writer newWriter( PackOutput output ) { - return new MessageWriterV3( output ); + return BoltProtocolV3.INSTANCE.createMessageFormat().newWriter( output ); } @Override protected Stream supportedMessages() { return Stream.of( + // Bolt V2 Data Types + unmanagedTxRunMessage( new Query( "RETURN $point", singletonMap( "point", point( 42, 12.99, -180.0 ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $point", singletonMap( "point", point( 42, 0.51, 2.99, 100.123 ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $date", singletonMap( "date", value( LocalDate.ofEpochDay( 2147483650L ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $time", singletonMap( "time", value( OffsetTime.of( 4, 16, 20, 999, ZoneOffset.MIN ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $time", singletonMap( "time", value( LocalTime.of( 12, 9, 18, 999_888 ) ) ) ) ), + unmanagedTxRunMessage( + new Query( "RETURN $dateTime", singletonMap( "dateTime", value( LocalDateTime.of( 2049, DECEMBER, 12, 17, 25, 49, 199 ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $dateTime", singletonMap( "dateTime", value( ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneOffset + .ofHoursMinutes( 9, 30 ) ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $dateTime", singletonMap( "dateTime", value( ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneId.of( + "Europe/Stockholm" ) ) ) ) ) ), + // Bolt V3 messages new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap(), Collections.emptyMap() ), GOODBYE, - new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, defaultDatabase() ), - new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, defaultDatabase() ), + new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, + defaultDatabase() ), + new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, + defaultDatabase() ), COMMIT, ROLLBACK, autoCommitTxRunMessage( new Query( "RETURN 1" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), defaultDatabase(), READ, - InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), + InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), autoCommitTxRunMessage( new Query( "RETURN 1" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), defaultDatabase(), WRITE, - InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), + InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), unmanagedTxRunMessage( new Query( "RETURN 1" ) ), PULL_ALL, DISCARD_ALL, @@ -82,9 +109,9 @@ protected Stream supportedMessages() // Bolt V3 messages with struct values autoCommitTxRunMessage( new Query( "RETURN $x", singletonMap( "x", value( ZonedDateTime.now() ) ) ), ofSeconds( 1 ), emptyMap(), - defaultDatabase(), READ, InternalBookmark.empty() ), + defaultDatabase(), READ, InternalBookmark.empty() ), autoCommitTxRunMessage( new Query( "RETURN $x", singletonMap( "x", value( ZonedDateTime.now() ) ) ), ofSeconds( 1 ), emptyMap(), - defaultDatabase(), WRITE, InternalBookmark.empty() ), + defaultDatabase(), WRITE, InternalBookmark.empty() ), unmanagedTxRunMessage( new Query( "RETURN $x", singletonMap( "x", point( 42, 1, 2, 3 ) ) ) ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java index 6248b8433b..e64704cfa9 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/BoltProtocolV4Test.java @@ -18,66 +18,353 @@ */ package org.neo4j.driver.internal.messaging.v4; +import io.netty.channel.ChannelPromise; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.mockito.ArgumentCaptor; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import org.neo4j.driver.AccessMode; +import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Bookmark; +import org.neo4j.driver.Logging; +import org.neo4j.driver.Query; import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.BookmarkHolder; import org.neo4j.driver.internal.DatabaseName; import org.neo4j.driver.internal.DefaultBookmarkHolder; import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.async.UnmanagedTransaction; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; +import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cluster.RoutingContext; import org.neo4j.driver.internal.cursor.AsyncResultCursor; import org.neo4j.driver.internal.cursor.ResultCursorFactory; import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; +import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; import org.neo4j.driver.internal.handlers.NoOpResponseHandler; import org.neo4j.driver.internal.handlers.PullAllResponseHandler; +import org.neo4j.driver.internal.handlers.RollbackTxResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.BoltProtocol; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.messaging.request.BeginMessage; +import org.neo4j.driver.internal.messaging.request.CommitMessage; +import org.neo4j.driver.internal.messaging.request.GoodbyeMessage; +import org.neo4j.driver.internal.messaging.request.HelloMessage; import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.request.RollbackMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; -import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3Test; +import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; +import static java.time.Duration.ofSeconds; import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.junit.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.driver.AccessMode.WRITE; +import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.DatabaseNameUtil.database; import static org.neo4j.driver.internal.DatabaseNameUtil.defaultDatabase; import static org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil.UNLIMITED_FETCH_SIZE; +import static org.neo4j.driver.util.TestUtil.anyServerVersion; import static org.neo4j.driver.util.TestUtil.await; import static org.neo4j.driver.util.TestUtil.connectionMock; -public class BoltProtocolV4Test extends BoltProtocolV3Test +public final class BoltProtocolV4Test { - @Override - protected BoltProtocol createProtocol() + + protected static final String QUERY_TEXT = "RETURN $x"; + protected static final Map PARAMS = singletonMap( "x", value( 42 ) ); + protected static final Query QUERY = new Query( QUERY_TEXT, value( PARAMS ) ); + + protected final BoltProtocol protocol = createProtocol(); + private final EmbeddedChannel channel = new EmbeddedChannel(); + private final InboundMessageDispatcher messageDispatcher = new InboundMessageDispatcher( channel, Logging.none() ); + + private final TransactionConfig txConfig = TransactionConfig.builder() + .withTimeout( ofSeconds( 12 ) ) + .withMetadata( singletonMap( "key", value( 42 ) ) ) + .build(); + + @BeforeEach + void beforeEach() + { + ChannelAttributes.setMessageDispatcher( channel, messageDispatcher ); + } + + @AfterEach + void afterEach() + { + channel.finishAndReleaseAll(); + } + + @Test + void shouldCreateMessageFormat() + { + assertThat( protocol.createMessageFormat(), instanceOf( expectedMessageFormatType() ) ); + } + + @Test + void shouldInitializeChannel() + { + ChannelPromise promise = channel.newPromise(); + + protocol.initializeChannel( "MyDriver/0.0.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); + + assertThat( channel.outboundMessages(), hasSize( 1 ) ); + assertThat( channel.outboundMessages().poll(), instanceOf( HelloMessage.class ) ); + assertEquals( 1, messageDispatcher.queuedHandlersCount() ); + assertFalse( promise.isDone() ); + + Map metadata = new HashMap<>(); + metadata.put( "server", value( anyServerVersion().toString() ) ); + metadata.put( "connection_id", value( "bolt-42" ) ); + + messageDispatcher.handleSuccessMessage( metadata ); + + assertTrue( promise.isDone() ); + assertTrue( promise.isSuccess() ); + } + + @Test + void shouldPrepareToCloseChannel() + { + protocol.prepareToCloseChannel( channel ); + + assertThat( channel.outboundMessages(), hasSize( 1 ) ); + assertThat( channel.outboundMessages().poll(), instanceOf( GoodbyeMessage.class ) ); + assertEquals( 1, messageDispatcher.queuedHandlersCount() ); + } + + @Test + void shouldFailToInitializeChannelWhenErrorIsReceived() + { + ChannelPromise promise = channel.newPromise(); + + protocol.initializeChannel( "MyDriver/2.2.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); + + assertThat( channel.outboundMessages(), hasSize( 1 ) ); + assertThat( channel.outboundMessages().poll(), instanceOf( HelloMessage.class ) ); + assertEquals( 1, messageDispatcher.queuedHandlersCount() ); + assertFalse( promise.isDone() ); + + messageDispatcher.handleFailureMessage( "Neo.TransientError.General.DatabaseUnavailable", "Error!" ); + + assertTrue( promise.isDone() ); + assertFalse( promise.isSuccess() ); + } + + @Test + void shouldBeginTransactionWithoutBookmark() + { + Connection connection = connectionMock( protocol ); + + CompletionStage stage = protocol.beginTransaction( connection, InternalBookmark.empty(), TransactionConfig.empty() ); + + verify( connection ) + .write( new BeginMessage( InternalBookmark.empty(), TransactionConfig.empty(), defaultDatabase(), WRITE ), NoOpResponseHandler.INSTANCE ); + assertNull( await( stage ) ); + } + + @Test + void shouldBeginTransactionWithBookmarks() + { + Connection connection = connectionMock( protocol ); + Bookmark bookmark = InternalBookmark.parse( "neo4j:bookmark:v1:tx100" ); + + CompletionStage stage = protocol.beginTransaction( connection, bookmark, TransactionConfig.empty() ); + + verify( connection ) + .writeAndFlush( eq( new BeginMessage( bookmark, TransactionConfig.empty(), defaultDatabase(), WRITE ) ), any( BeginTxResponseHandler.class ) ); + assertNull( await( stage ) ); + } + + @Test + void shouldBeginTransactionWithConfig() + { + Connection connection = connectionMock( protocol ); + + CompletionStage stage = protocol.beginTransaction( connection, InternalBookmark.empty(), txConfig ); + + verify( connection ).write( new BeginMessage( InternalBookmark.empty(), txConfig, defaultDatabase(), WRITE ), NoOpResponseHandler.INSTANCE ); + assertNull( await( stage ) ); + } + + @Test + void shouldBeginTransactionWithBookmarksAndConfig() + { + Connection connection = connectionMock( protocol ); + Bookmark bookmark = InternalBookmark.parse( "neo4j:bookmark:v1:tx4242" ); + + CompletionStage stage = protocol.beginTransaction( connection, bookmark, txConfig ); + + verify( connection ).writeAndFlush( eq( new BeginMessage( bookmark, txConfig, defaultDatabase(), WRITE ) ), any( BeginTxResponseHandler.class ) ); + assertNull( await( stage ) ); + } + + @Test + void shouldCommitTransaction() + { + String bookmarkString = "neo4j:bookmark:v1:tx4242"; + + Connection connection = connectionMock( protocol ); + when( connection.protocol() ).thenReturn( protocol ); + doAnswer( invocation -> + { + ResponseHandler commitHandler = invocation.getArgument( 1 ); + commitHandler.onSuccess( singletonMap( "bookmark", value( bookmarkString ) ) ); + return null; + } ).when( connection ).writeAndFlush( eq( CommitMessage.COMMIT ), any() ); + + CompletionStage stage = protocol.commitTransaction( connection ); + + verify( connection ).writeAndFlush( eq( CommitMessage.COMMIT ), any( CommitTxResponseHandler.class ) ); + assertEquals( InternalBookmark.parse( bookmarkString ), await( stage ) ); + } + + @Test + void shouldRollbackTransaction() + { + Connection connection = connectionMock( protocol ); + + CompletionStage stage = protocol.rollbackTransaction( connection ); + + verify( connection ).writeAndFlush( eq( RollbackMessage.ROLLBACK ), any( RollbackTxResponseHandler.class ) ); + assertNull( await( stage ) ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionWithoutWaitingForRunResponse( AccessMode mode ) throws Exception + { + testRunWithoutWaitingForRunResponse( true, TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitWithConfigTransactionWithoutWaitingForRunResponse( AccessMode mode ) throws Exception + { + testRunWithoutWaitingForRunResponse( true, txConfig, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionAndWaitForSuccessRunResponse( AccessMode mode ) throws Exception + { + testSuccessfulRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.empty(), TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionWithBookmarkAndConfigAndWaitForSuccessRunResponse( AccessMode mode ) throws Exception + { + testSuccessfulRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.parse( "neo4j:bookmark:v1:tx65" ), txConfig, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionAndWaitForFailureRunResponse( AccessMode mode ) throws Exception + { + testFailedRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.empty(), TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionWithBookmarkAndConfigAndWaitForFailureRunResponse( AccessMode mode ) throws Exception + { + testFailedRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.parse( "neo4j:bookmark:v1:tx163" ), txConfig, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInUnmanagedTransactionWithoutWaitingForRunResponse( AccessMode mode ) throws Exception + { + testRunWithoutWaitingForRunResponse( false, TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInUnmanagedTransactionAndWaitForSuccessRunResponse( AccessMode mode ) throws Exception + { + testRunInUnmanagedTransactionAndWaitForRunResponse( true, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInUnmanagedTransactionAndWaitForFailureRunResponse( AccessMode mode ) throws Exception + { + testRunInUnmanagedTransactionAndWaitForRunResponse( false, mode ); + } + + @Test + void databaseNameInBeginTransaction() + { + testDatabaseNameSupport( false ); + } + + @Test + void databaseNameForAutoCommitTransactions() + { + testDatabaseNameSupport( true ); + } + + @Test + void shouldSupportDatabaseNameInBeginTransaction() + { + CompletionStage txStage = protocol.beginTransaction( connectionMock( "foo", protocol ), InternalBookmark.empty(), TransactionConfig.empty() ); + + assertDoesNotThrow( () -> await( txStage ) ); + } + + @Test + void shouldNotSupportDatabaseNameForAutoCommitTransactions() + { + assertDoesNotThrow( + () -> protocol.runInAutoCommitTransaction( connectionMock( "foo", protocol ), + new Query( "RETURN 1" ), BookmarkHolder.NO_OP, TransactionConfig.empty(), true, + UNLIMITED_FETCH_SIZE ) ); + } + + private BoltProtocol createProtocol() { return BoltProtocolV4.INSTANCE; } - @Override - protected Class expectedMessageFormatType() + private Class expectedMessageFormatType() { return MessageFormatV4.class; } - @Override + private static InternalAuthToken dummyAuthToken() + { + return (InternalAuthToken) AuthTokens.basic( "hello", "world" ); + } + protected void testFailedRunInAutoCommitTxWithWaitingForResponse( Bookmark bookmark, TransactionConfig config, AccessMode mode ) throws Exception { // Given @@ -101,7 +388,6 @@ protected void testFailedRunInAutoCommitTxWithWaitingForResponse( Bookmark bookm assertNotNull( cursorFuture.get() ); } - @Override protected void testSuccessfulRunInAutoCommitTxWithWaitingForResponse( Bookmark bookmark, TransactionConfig config, AccessMode mode ) throws Exception { // Given @@ -125,7 +411,6 @@ protected void testSuccessfulRunInAutoCommitTxWithWaitingForResponse( Bookmark b assertNotNull( cursorFuture.get() ); } - @Override protected void testRunInUnmanagedTransactionAndWaitForRunResponse(boolean success, AccessMode mode ) throws Exception { // Given @@ -154,7 +439,6 @@ protected void testRunInUnmanagedTransactionAndWaitForRunResponse(boolean succes assertNotNull( cursorFuture.get() ); } - @Override protected void testRunWithoutWaitingForRunResponse( boolean autoCommitTx, TransactionConfig config, AccessMode mode ) throws Exception { // Given @@ -190,7 +474,6 @@ protected void testRunWithoutWaitingForRunResponse( boolean autoCommitTx, Transa } } - @Override protected void testDatabaseNameSupport( boolean autoCommitTx ) { Connection connection = connectionMock( "foo", protocol ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4Test.java index b77d70b5de..42ca0246a6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageFormatV4Test.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.neo4j.driver.internal.messaging.MessageFormat; -import org.neo4j.driver.internal.messaging.v2.MessageReaderV2; +import org.neo4j.driver.internal.messaging.common.CommonMessageReader; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.packstream.PackOutput; @@ -29,13 +29,18 @@ import static org.hamcrest.Matchers.instanceOf; import static org.mockito.Mockito.mock; +/** + * The MessageFormat under tests is the one provided by the {@link BoltProtocolV4} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ class MessageFormatV4Test { + private static final MessageFormat format = BoltProtocolV4.INSTANCE.createMessageFormat(); + @Test void shouldCreateCorrectWriter() { - MessageFormatV4 format = new MessageFormatV4(); - MessageFormat.Writer writer = format.newWriter( mock( PackOutput.class ) ); assertThat( writer, instanceOf( MessageWriterV4.class ) ); @@ -44,10 +49,8 @@ void shouldCreateCorrectWriter() @Test void shouldCreateCorrectReader() { - MessageFormatV4 format = new MessageFormatV4(); - MessageFormat.Reader reader = format.newReader( mock( PackInput.class ) ); - assertThat( reader, instanceOf( MessageReaderV2.class ) ); + assertThat( reader, instanceOf( CommonMessageReader.class ) ); } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageReaderV4Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageReaderV4Test.java new file mode 100644 index 0000000000..eadacd3f92 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageReaderV4Test.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2002-2020 "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. + */ +package org.neo4j.driver.internal.messaging.v4; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.stream.Stream; + +import org.neo4j.driver.Value; +import org.neo4j.driver.Values; +import org.neo4j.driver.internal.InternalPoint2D; +import org.neo4j.driver.internal.InternalPoint3D; +import org.neo4j.driver.internal.messaging.Message; +import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.messaging.request.DiscardAllMessage; +import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.messaging.response.FailureMessage; +import org.neo4j.driver.internal.messaging.response.IgnoredMessage; +import org.neo4j.driver.internal.messaging.response.RecordMessage; +import org.neo4j.driver.internal.messaging.response.SuccessMessage; +import org.neo4j.driver.internal.packstream.PackInput; +import org.neo4j.driver.internal.util.messaging.AbstractMessageReaderTestBase; + +import static java.util.Arrays.asList; +import static java.util.Calendar.APRIL; +import static java.util.Calendar.AUGUST; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue; +import static org.neo4j.driver.internal.util.ValueFactory.emptyPathValue; +import static org.neo4j.driver.internal.util.ValueFactory.emptyRelationshipValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledNodeValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledRelationshipValue; + +/** + * The MessageReader under tests is the one provided by the {@link BoltProtocolV4} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ +public class MessageReaderV4Test extends AbstractMessageReaderTestBase +{ + + @Override + protected Stream supportedMessages() + { + return Stream.of( + // V2 Record types + record( value( new InternalPoint2D( 42, 120.65, -99.2 ) ) ), + record( value( new InternalPoint3D( 42, 85.391, 98.8, 11.1 ) ) ), + record( value( LocalDate.of( 2012, AUGUST, 3 ) ) ), + record( value( OffsetTime.of( 23, 59, 59, 999, ZoneOffset.MAX ) ) ), + record( value( LocalTime.of( 12, 25 ) ) ), + record( value( LocalDateTime.of( 1999, APRIL, 3, 19, 5, 5, 100_200_300 ) ) ), + record( value( ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneOffset.ofHoursMinutes( -7, -15 ) ) ) ), + record( value( ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneId.of( "Europe/Stockholm" ) ) ) ), + record( value( Values.isoDuration( Long.MAX_VALUE - 1, Integer.MAX_VALUE - 1, Short.MAX_VALUE - 1, Byte.MAX_VALUE - 1 ).asIsoDuration() ) ), + record( value( Values.isoDuration( 17, 22, 99, 15 ).asIsoDuration() ) ), + + // Bolt previous versions valid messages + new FailureMessage( "Hello", "World!" ), + IgnoredMessage.IGNORED, + new SuccessMessage( new HashMap<>() ), + record( value( 1337L ) ), + record( value( parameters( "cat", null, "dog", null ) ) ), + record( value( parameters( "k", 12, "a", "banana" ) ) ), + record( value( asList( "k", 12, "a", "banana" ) ) ), + + // V3 Record Types + record( emptyNodeValue() ), + record( filledNodeValue() ), + record( emptyRelationshipValue() ), + record( filledRelationshipValue() ), + record( filledPathValue() ), + record( emptyPathValue() ) + ); + } + + @Override + protected Stream unsupportedMessages() + { + return Stream.of( + DiscardAllMessage.DISCARD_ALL, + new RunMessage( "RETURN 42" ) + ); + } + + @Override + protected MessageFormat.Reader newReader( PackInput input ) + { + return BoltProtocolV4.INSTANCE.createMessageFormat().newReader( input ); + } + + private Message record( Value value ) + { + return new RecordMessage( new Value[]{value} ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java index 0df66da9c4..6a7803bc3b 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v4/MessageWriterV4Test.java @@ -18,6 +18,12 @@ */ package org.neo4j.driver.internal.messaging.v4; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Collections; import java.util.stream.Stream; @@ -32,11 +38,13 @@ import org.neo4j.driver.internal.messaging.request.InitMessage; import org.neo4j.driver.internal.messaging.request.PullMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.packstream.PackOutput; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; import static java.time.Duration.ofSeconds; +import static java.util.Calendar.DECEMBER; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.neo4j.driver.AccessMode.READ; @@ -55,18 +63,35 @@ import static org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage.autoCommitTxRunMessage; import static org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage.unmanagedTxRunMessage; +/** + * The MessageWriter under tests is the one provided by the {@link BoltProtocolV3} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ class MessageWriterV4Test extends AbstractMessageWriterTestBase { @Override protected MessageFormat.Writer newWriter( PackOutput output ) { - return new MessageWriterV4( output ); + return BoltProtocolV4.INSTANCE.createMessageFormat().newWriter( output ); } @Override protected Stream supportedMessages() { return Stream.of( + // Bolt V2 Data Types + unmanagedTxRunMessage( new Query( "RETURN $point", singletonMap( "point", point( 42, 12.99, -180.0 ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $point", singletonMap( "point", point( 42, 0.51, 2.99, 100.123 ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $date", singletonMap( "date", value( LocalDate.ofEpochDay( 2147483650L ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $time", singletonMap( "time", value( OffsetTime.of( 4, 16, 20, 999, ZoneOffset.MIN ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $time", singletonMap( "time", value( LocalTime.of( 12, 9, 18, 999_888 ) ) ) ) ), + unmanagedTxRunMessage( + new Query( "RETURN $dateTime", singletonMap( "dateTime", value( LocalDateTime.of( 2049, DECEMBER, 12, 17, 25, 49, 199 ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $dateTime", singletonMap( "dateTime", value( ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneOffset + .ofHoursMinutes( 9, 30 ) ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $dateTime", singletonMap( "dateTime", value( ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneId.of( + "Europe/Stockholm" ) ) ) ) ) ), // New Bolt V4 messages new PullMessage( 100, 200 ), @@ -75,23 +100,25 @@ protected Stream supportedMessages() // Bolt V3 messages new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap(), Collections.emptyMap() ), GOODBYE, - new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, defaultDatabase() ), - new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, database( "foo" ) ), + new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, + defaultDatabase() ), + new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, + database( "foo" ) ), COMMIT, ROLLBACK, RESET, autoCommitTxRunMessage( new Query( "RETURN 1" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), defaultDatabase(), READ, - InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), + InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), autoCommitTxRunMessage( new Query( "RETURN 1" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), database( "foo" ), WRITE, - InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), + InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), unmanagedTxRunMessage( new Query( "RETURN 1" ) ), // Bolt V3 messages with struct values autoCommitTxRunMessage( new Query( "RETURN $x", singletonMap( "x", value( ZonedDateTime.now() ) ) ), ofSeconds( 1 ), emptyMap(), - defaultDatabase(), READ, InternalBookmark.empty() ), + defaultDatabase(), READ, InternalBookmark.empty() ), autoCommitTxRunMessage( new Query( "RETURN $x", singletonMap( "x", value( ZonedDateTime.now() ) ) ), ofSeconds( 1 ), emptyMap(), database( "foo" ), - WRITE, InternalBookmark.empty() ), + WRITE, InternalBookmark.empty() ), unmanagedTxRunMessage( new Query( "RETURN $x", singletonMap( "x", point( 42, 1, 2, 3 ) ) ) ) ); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/BoltProtocolV41Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/BoltProtocolV41Test.java index c4992bf35c..0e31633629 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/BoltProtocolV41Test.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/BoltProtocolV41Test.java @@ -18,14 +18,520 @@ */ package org.neo4j.driver.internal.messaging.v41; +import io.netty.channel.ChannelPromise; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.ArgumentCaptor; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Bookmark; +import org.neo4j.driver.Logging; +import org.neo4j.driver.Query; +import org.neo4j.driver.TransactionConfig; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.BookmarkHolder; +import org.neo4j.driver.internal.DatabaseName; +import org.neo4j.driver.internal.DefaultBookmarkHolder; +import org.neo4j.driver.internal.InternalBookmark; +import org.neo4j.driver.internal.async.UnmanagedTransaction; +import org.neo4j.driver.internal.async.connection.ChannelAttributes; +import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher; +import org.neo4j.driver.internal.cluster.RoutingContext; +import org.neo4j.driver.internal.cursor.AsyncResultCursor; +import org.neo4j.driver.internal.cursor.ResultCursorFactory; +import org.neo4j.driver.internal.handlers.BeginTxResponseHandler; +import org.neo4j.driver.internal.handlers.CommitTxResponseHandler; +import org.neo4j.driver.internal.handlers.NoOpResponseHandler; +import org.neo4j.driver.internal.handlers.PullAllResponseHandler; +import org.neo4j.driver.internal.handlers.RollbackTxResponseHandler; +import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.BoltProtocol; -import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4Test; +import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.messaging.request.BeginMessage; +import org.neo4j.driver.internal.messaging.request.CommitMessage; +import org.neo4j.driver.internal.messaging.request.GoodbyeMessage; +import org.neo4j.driver.internal.messaging.request.HelloMessage; +import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.request.RollbackMessage; +import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; +import org.neo4j.driver.internal.messaging.v4.MessageFormatV4; +import org.neo4j.driver.internal.security.InternalAuthToken; +import org.neo4j.driver.internal.spi.Connection; +import org.neo4j.driver.internal.spi.ResponseHandler; + +import static java.time.Duration.ofSeconds; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.junit.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.neo4j.driver.AccessMode.WRITE; +import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.DatabaseNameUtil.database; +import static org.neo4j.driver.internal.DatabaseNameUtil.defaultDatabase; +import static org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil.UNLIMITED_FETCH_SIZE; +import static org.neo4j.driver.util.TestUtil.anyServerVersion; +import static org.neo4j.driver.util.TestUtil.await; +import static org.neo4j.driver.util.TestUtil.connectionMock; -public class BoltProtocolV41Test extends BoltProtocolV4Test +public final class BoltProtocolV41Test { - @Override - protected BoltProtocol createProtocol() + protected static final String QUERY_TEXT = "RETURN $x"; + protected static final Map PARAMS = singletonMap( "x", value( 42 ) ); + protected static final Query QUERY = new Query( QUERY_TEXT, value( PARAMS ) ); + + protected final BoltProtocol protocol = createProtocol(); + private final EmbeddedChannel channel = new EmbeddedChannel(); + private final InboundMessageDispatcher messageDispatcher = new InboundMessageDispatcher( channel, Logging.none() ); + + private final TransactionConfig txConfig = TransactionConfig.builder() + .withTimeout( ofSeconds( 12 ) ) + .withMetadata( singletonMap( "key", value( 42 ) ) ) + .build(); + + private BoltProtocol createProtocol() { return BoltProtocolV41.INSTANCE; } + + @BeforeEach + void beforeEach() + { + ChannelAttributes.setMessageDispatcher( channel, messageDispatcher ); + } + + @AfterEach + void afterEach() + { + channel.finishAndReleaseAll(); + } + + @Test + void shouldCreateMessageFormat() + { + assertThat( protocol.createMessageFormat(), instanceOf( expectedMessageFormatType() ) ); + } + + @Test + void shouldInitializeChannel() + { + ChannelPromise promise = channel.newPromise(); + + protocol.initializeChannel( "MyDriver/0.0.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); + + assertThat( channel.outboundMessages(), hasSize( 1 ) ); + assertThat( channel.outboundMessages().poll(), instanceOf( HelloMessage.class ) ); + assertEquals( 1, messageDispatcher.queuedHandlersCount() ); + assertFalse( promise.isDone() ); + + Map metadata = new HashMap<>(); + metadata.put( "server", value( anyServerVersion().toString() ) ); + metadata.put( "connection_id", value( "bolt-42" ) ); + + messageDispatcher.handleSuccessMessage( metadata ); + + assertTrue( promise.isDone() ); + assertTrue( promise.isSuccess() ); + } + + @Test + void shouldPrepareToCloseChannel() + { + protocol.prepareToCloseChannel( channel ); + + assertThat( channel.outboundMessages(), hasSize( 1 ) ); + assertThat( channel.outboundMessages().poll(), instanceOf( GoodbyeMessage.class ) ); + assertEquals( 1, messageDispatcher.queuedHandlersCount() ); + } + + @Test + void shouldFailToInitializeChannelWhenErrorIsReceived() + { + ChannelPromise promise = channel.newPromise(); + + protocol.initializeChannel( "MyDriver/2.2.1", dummyAuthToken(), RoutingContext.EMPTY, promise ); + + assertThat( channel.outboundMessages(), hasSize( 1 ) ); + assertThat( channel.outboundMessages().poll(), instanceOf( HelloMessage.class ) ); + assertEquals( 1, messageDispatcher.queuedHandlersCount() ); + assertFalse( promise.isDone() ); + + messageDispatcher.handleFailureMessage( "Neo.TransientError.General.DatabaseUnavailable", "Error!" ); + + assertTrue( promise.isDone() ); + assertFalse( promise.isSuccess() ); + } + + @Test + void shouldBeginTransactionWithoutBookmark() + { + Connection connection = connectionMock( protocol ); + + CompletionStage stage = protocol.beginTransaction( connection, InternalBookmark.empty(), TransactionConfig.empty() ); + + verify( connection ) + .write( new BeginMessage( InternalBookmark.empty(), TransactionConfig.empty(), defaultDatabase(), WRITE ), NoOpResponseHandler.INSTANCE ); + assertNull( await( stage ) ); + } + + @Test + void shouldBeginTransactionWithBookmarks() + { + Connection connection = connectionMock( protocol ); + Bookmark bookmark = InternalBookmark.parse( "neo4j:bookmark:v1:tx100" ); + + CompletionStage stage = protocol.beginTransaction( connection, bookmark, TransactionConfig.empty() ); + + verify( connection ) + .writeAndFlush( eq( new BeginMessage( bookmark, TransactionConfig.empty(), defaultDatabase(), WRITE ) ), any( BeginTxResponseHandler.class ) ); + assertNull( await( stage ) ); + } + + @Test + void shouldBeginTransactionWithConfig() + { + Connection connection = connectionMock( protocol ); + + CompletionStage stage = protocol.beginTransaction( connection, InternalBookmark.empty(), txConfig ); + + verify( connection ).write( new BeginMessage( InternalBookmark.empty(), txConfig, defaultDatabase(), WRITE ), NoOpResponseHandler.INSTANCE ); + assertNull( await( stage ) ); + } + + @Test + void shouldBeginTransactionWithBookmarksAndConfig() + { + Connection connection = connectionMock( protocol ); + Bookmark bookmark = InternalBookmark.parse( "neo4j:bookmark:v1:tx4242" ); + + CompletionStage stage = protocol.beginTransaction( connection, bookmark, txConfig ); + + verify( connection ).writeAndFlush( eq( new BeginMessage( bookmark, txConfig, defaultDatabase(), WRITE ) ), any( BeginTxResponseHandler.class ) ); + assertNull( await( stage ) ); + } + + @Test + void shouldCommitTransaction() + { + String bookmarkString = "neo4j:bookmark:v1:tx4242"; + + Connection connection = connectionMock( protocol ); + when( connection.protocol() ).thenReturn( protocol ); + doAnswer( invocation -> + { + ResponseHandler commitHandler = invocation.getArgument( 1 ); + commitHandler.onSuccess( singletonMap( "bookmark", value( bookmarkString ) ) ); + return null; + } ).when( connection ).writeAndFlush( eq( CommitMessage.COMMIT ), any() ); + + CompletionStage stage = protocol.commitTransaction( connection ); + + verify( connection ).writeAndFlush( eq( CommitMessage.COMMIT ), any( CommitTxResponseHandler.class ) ); + assertEquals( InternalBookmark.parse( bookmarkString ), await( stage ) ); + } + + @Test + void shouldRollbackTransaction() + { + Connection connection = connectionMock( protocol ); + + CompletionStage stage = protocol.rollbackTransaction( connection ); + + verify( connection ).writeAndFlush( eq( RollbackMessage.ROLLBACK ), any( RollbackTxResponseHandler.class ) ); + assertNull( await( stage ) ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionWithoutWaitingForRunResponse( AccessMode mode ) throws Exception + { + testRunWithoutWaitingForRunResponse( true, TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitWithConfigTransactionWithoutWaitingForRunResponse( AccessMode mode ) throws Exception + { + testRunWithoutWaitingForRunResponse( true, txConfig, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionAndWaitForSuccessRunResponse( AccessMode mode ) throws Exception + { + testSuccessfulRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.empty(), TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionWithBookmarkAndConfigAndWaitForSuccessRunResponse( AccessMode mode ) throws Exception + { + testSuccessfulRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.parse( "neo4j:bookmark:v1:tx65" ), txConfig, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionAndWaitForFailureRunResponse( AccessMode mode ) throws Exception + { + testFailedRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.empty(), TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInAutoCommitTransactionWithBookmarkAndConfigAndWaitForFailureRunResponse( AccessMode mode ) throws Exception + { + testFailedRunInAutoCommitTxWithWaitingForResponse( InternalBookmark.parse( "neo4j:bookmark:v1:tx163" ), txConfig, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInUnmanagedTransactionWithoutWaitingForRunResponse( AccessMode mode ) throws Exception + { + testRunWithoutWaitingForRunResponse( false, TransactionConfig.empty(), mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInUnmanagedTransactionAndWaitForSuccessRunResponse( AccessMode mode ) throws Exception + { + testRunInUnmanagedTransactionAndWaitForRunResponse( true, mode ); + } + + @ParameterizedTest + @EnumSource( AccessMode.class ) + void shouldRunInUnmanagedTransactionAndWaitForFailureRunResponse( AccessMode mode ) throws Exception + { + testRunInUnmanagedTransactionAndWaitForRunResponse( false, mode ); + } + + @Test + void databaseNameInBeginTransaction() + { + testDatabaseNameSupport( false ); + } + + @Test + void databaseNameForAutoCommitTransactions() + { + testDatabaseNameSupport( true ); + } + + @Test + void shouldSupportDatabaseNameInBeginTransaction() + { + CompletionStage txStage = protocol.beginTransaction( connectionMock( "foo", protocol ), InternalBookmark.empty(), TransactionConfig.empty() ); + + assertDoesNotThrow( () -> await( txStage ) ); + } + + @Test + void shouldNotSupportDatabaseNameForAutoCommitTransactions() + { + assertDoesNotThrow( + () -> protocol.runInAutoCommitTransaction( connectionMock( "foo", protocol ), + new Query( "RETURN 1" ), BookmarkHolder.NO_OP, TransactionConfig.empty(), true, + UNLIMITED_FETCH_SIZE ) ); + } + + private Class expectedMessageFormatType() + { + return MessageFormatV4.class; + } + + private void testFailedRunInAutoCommitTxWithWaitingForResponse( Bookmark bookmark, TransactionConfig config, AccessMode mode ) throws Exception + { + // Given + Connection connection = connectionMock( mode, protocol ); + BookmarkHolder bookmarkHolder = new DefaultBookmarkHolder( bookmark ); + + CompletableFuture cursorFuture = + protocol.runInAutoCommitTransaction( connection, QUERY, bookmarkHolder, config, true, UNLIMITED_FETCH_SIZE ) + .asyncResult() + .toCompletableFuture(); + + ResponseHandler runHandler = verifySessionRunInvoked( connection, bookmark, config, mode, defaultDatabase() ); + assertFalse( cursorFuture.isDone() ); + + // When I response to Run message with a failure + runHandler.onFailure( new RuntimeException() ); + + // Then + assertEquals( bookmark, bookmarkHolder.getBookmark() ); + assertTrue( cursorFuture.isDone() ); + assertNotNull( cursorFuture.get() ); + } + + private void testSuccessfulRunInAutoCommitTxWithWaitingForResponse( Bookmark bookmark, TransactionConfig config, AccessMode mode ) throws Exception + { + // Given + Connection connection = connectionMock( mode, protocol ); + BookmarkHolder bookmarkHolder = new DefaultBookmarkHolder( bookmark ); + + CompletableFuture cursorFuture = + protocol.runInAutoCommitTransaction( connection, QUERY, bookmarkHolder, config, true, UNLIMITED_FETCH_SIZE ) + .asyncResult() + .toCompletableFuture(); + + ResponseHandler runHandler = verifySessionRunInvoked( connection, bookmark, config, mode, defaultDatabase() ); + assertFalse( cursorFuture.isDone() ); + + // When I response to the run message + runHandler.onSuccess( emptyMap() ); + + // Then + assertEquals( bookmark, bookmarkHolder.getBookmark() ); + assertTrue( cursorFuture.isDone() ); + assertNotNull( cursorFuture.get() ); + } + + private void testRunInUnmanagedTransactionAndWaitForRunResponse( boolean success, AccessMode mode ) throws Exception + { + // Given + Connection connection = connectionMock( mode, protocol ); + + CompletableFuture cursorFuture = + protocol.runInUnmanagedTransaction( connection, QUERY, mock( UnmanagedTransaction.class ), true, UNLIMITED_FETCH_SIZE ) + .asyncResult() + .toCompletableFuture(); + + ResponseHandler runHandler = verifyTxRunInvoked( connection ); + assertFalse( cursorFuture.isDone() ); + + if ( success ) + { + runHandler.onSuccess( emptyMap() ); + } + else + { + // When responded with a failure + runHandler.onFailure( new RuntimeException() ); + } + + // Then + assertTrue( cursorFuture.isDone() ); + assertNotNull( cursorFuture.get() ); + } + + private void testRunWithoutWaitingForRunResponse( boolean autoCommitTx, TransactionConfig config, AccessMode mode ) throws Exception + { + // Given + Connection connection = connectionMock( mode, protocol ); + Bookmark initialBookmark = InternalBookmark.parse( "neo4j:bookmark:v1:tx987" ); + + CompletionStage cursorStage; + if ( autoCommitTx ) + { + BookmarkHolder bookmarkHolder = new DefaultBookmarkHolder( initialBookmark ); + cursorStage = protocol.runInAutoCommitTransaction( connection, QUERY, bookmarkHolder, config, false, UNLIMITED_FETCH_SIZE ) + .asyncResult(); + } + else + { + cursorStage = protocol.runInUnmanagedTransaction( connection, QUERY, mock( UnmanagedTransaction.class ), false, UNLIMITED_FETCH_SIZE ) + .asyncResult(); + } + + // When I complete it immediately without waiting for any responses to run message + CompletableFuture cursorFuture = cursorStage.toCompletableFuture(); + assertTrue( cursorFuture.isDone() ); + assertNotNull( cursorFuture.get() ); + + // Then + if ( autoCommitTx ) + { + verifySessionRunInvoked( connection, initialBookmark, config, mode, defaultDatabase() ); + } + else + { + verifyTxRunInvoked( connection ); + } + } + + private void testDatabaseNameSupport( boolean autoCommitTx ) + { + Connection connection = connectionMock( "foo", protocol ); + if ( autoCommitTx ) + { + ResultCursorFactory factory = + protocol.runInAutoCommitTransaction( connection, QUERY, BookmarkHolder.NO_OP, TransactionConfig.empty(), false, UNLIMITED_FETCH_SIZE ); + await( factory.asyncResult() ); + verifySessionRunInvoked( connection, InternalBookmark.empty(), TransactionConfig.empty(), AccessMode.WRITE, database( "foo" ) ); + } + else + { + CompletionStage txStage = protocol.beginTransaction( connection, InternalBookmark.empty(), TransactionConfig.empty() ); + await( txStage ); + verifyBeginInvoked( connection, InternalBookmark.empty(), TransactionConfig.empty(), AccessMode.WRITE, database( "foo" ) ); + } + } + + private ResponseHandler verifyTxRunInvoked( Connection connection ) + { + return verifyRunInvoked( connection, RunWithMetadataMessage.unmanagedTxRunMessage( QUERY ) ); + } + + private ResponseHandler verifySessionRunInvoked( Connection connection, Bookmark bookmark, TransactionConfig config, AccessMode mode, + DatabaseName databaseName ) + { + RunWithMetadataMessage runMessage = RunWithMetadataMessage.autoCommitTxRunMessage( QUERY, config, databaseName, mode, bookmark ); + return verifyRunInvoked( connection, runMessage ); + } + + private ResponseHandler verifyRunInvoked( Connection connection, RunWithMetadataMessage runMessage ) + { + ArgumentCaptor runHandlerCaptor = ArgumentCaptor.forClass( ResponseHandler.class ); + ArgumentCaptor pullHandlerCaptor = ArgumentCaptor.forClass( ResponseHandler.class ); + + verify( connection ).write( eq( runMessage ), runHandlerCaptor.capture() ); + verify( connection ).writeAndFlush( any( PullMessage.class ), pullHandlerCaptor.capture() ); + + assertThat( runHandlerCaptor.getValue(), instanceOf( RunResponseHandler.class ) ); + assertThat( pullHandlerCaptor.getValue(), instanceOf( PullAllResponseHandler.class ) ); + + return runHandlerCaptor.getValue(); + } + + private void verifyBeginInvoked( Connection connection, Bookmark bookmark, TransactionConfig config, AccessMode mode, DatabaseName databaseName ) + { + ArgumentCaptor beginHandlerCaptor = ArgumentCaptor.forClass( ResponseHandler.class ); + BeginMessage beginMessage = new BeginMessage( bookmark, config, databaseName, mode ); + + if ( bookmark.isEmpty() ) + { + verify( connection ).write( eq( beginMessage ), eq( NoOpResponseHandler.INSTANCE ) ); + } + else + { + verify( connection ).write( eq( beginMessage ), beginHandlerCaptor.capture() ); + assertThat( beginHandlerCaptor.getValue(), instanceOf( BeginTxResponseHandler.class ) ); + } + } + + private static InternalAuthToken dummyAuthToken() + { + return (InternalAuthToken) AuthTokens.basic( "hello", "world" ); + } } + diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageFormatV41Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageFormatV41Test.java new file mode 100644 index 0000000000..0fcd85bb3e --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageFormatV41Test.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2002-2020 "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. + */ +package org.neo4j.driver.internal.messaging.v41; + +import org.junit.jupiter.api.Test; + +import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.messaging.common.CommonMessageReader; +import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; +import org.neo4j.driver.internal.messaging.v4.MessageWriterV4; +import org.neo4j.driver.internal.packstream.PackInput; +import org.neo4j.driver.internal.packstream.PackOutput; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.Mockito.mock; + +/** + * The MessageFormat under tests is the one provided by the {@link BoltProtocolV3} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ +class MessageFormatV41Test +{ + private static final MessageFormat format = BoltProtocolV41.INSTANCE.createMessageFormat(); + + @Test + void shouldCreateCorrectWriter() + { + MessageFormat.Writer writer = format.newWriter( mock( PackOutput.class ) ); + + assertThat( writer, instanceOf( MessageWriterV4.class ) ); + } + + @Test + void shouldCreateCorrectReader() + { + MessageFormat.Reader reader = format.newReader( mock( PackInput.class ) ); + + assertThat( reader, instanceOf( CommonMessageReader.class ) ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageReaderV41Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageReaderV41Test.java new file mode 100644 index 0000000000..914e70a5e0 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageReaderV41Test.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2002-2020 "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. + */ +package org.neo4j.driver.internal.messaging.v41; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.stream.Stream; + +import org.neo4j.driver.Value; +import org.neo4j.driver.Values; +import org.neo4j.driver.internal.InternalPoint2D; +import org.neo4j.driver.internal.InternalPoint3D; +import org.neo4j.driver.internal.messaging.Message; +import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.messaging.request.DiscardAllMessage; +import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.messaging.response.FailureMessage; +import org.neo4j.driver.internal.messaging.response.IgnoredMessage; +import org.neo4j.driver.internal.messaging.response.RecordMessage; +import org.neo4j.driver.internal.messaging.response.SuccessMessage; +import org.neo4j.driver.internal.packstream.PackInput; +import org.neo4j.driver.internal.util.messaging.AbstractMessageReaderTestBase; + +import static java.util.Arrays.asList; +import static java.util.Calendar.APRIL; +import static java.util.Calendar.AUGUST; +import static org.neo4j.driver.Values.parameters; +import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue; +import static org.neo4j.driver.internal.util.ValueFactory.emptyPathValue; +import static org.neo4j.driver.internal.util.ValueFactory.emptyRelationshipValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledNodeValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue; +import static org.neo4j.driver.internal.util.ValueFactory.filledRelationshipValue; + +/** + * The MessageReader under tests is the one provided by the {@link BoltProtocolV41} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ +public class MessageReaderV41Test extends AbstractMessageReaderTestBase +{ + + @Override + protected Stream supportedMessages() + { + return Stream.of( + // V2 Record types + record( value( new InternalPoint2D( 42, 120.65, -99.2 ) ) ), + record( value( new InternalPoint3D( 42, 85.391, 98.8, 11.1 ) ) ), + record( value( LocalDate.of( 2012, AUGUST, 3 ) ) ), + record( value( OffsetTime.of( 23, 59, 59, 999, ZoneOffset.MAX ) ) ), + record( value( LocalTime.of( 12, 25 ) ) ), + record( value( LocalDateTime.of( 1999, APRIL, 3, 19, 5, 5, 100_200_300 ) ) ), + record( value( ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneOffset.ofHoursMinutes( -7, -15 ) ) ) ), + record( value( ZonedDateTime.of( 1823, 1, 12, 23, 59, 59, 999_999_999, ZoneId.of( "Europe/Stockholm" ) ) ) ), + record( value( Values.isoDuration( Long.MAX_VALUE - 1, Integer.MAX_VALUE - 1, Short.MAX_VALUE - 1, Byte.MAX_VALUE - 1 ).asIsoDuration() ) ), + record( value( Values.isoDuration( 17, 22, 99, 15 ).asIsoDuration() ) ), + + // Bolt previous versions valid messages + new FailureMessage( "Hello", "World!" ), + IgnoredMessage.IGNORED, + new SuccessMessage( new HashMap<>() ), + record( value( 1337L ) ), + record( value( parameters( "cat", null, "dog", null ) ) ), + record( value( parameters( "k", 12, "a", "banana" ) ) ), + record( value( asList( "k", 12, "a", "banana" ) ) ), + + // V3 Record Types + record( emptyNodeValue() ), + record( filledNodeValue() ), + record( emptyRelationshipValue() ), + record( filledRelationshipValue() ), + record( filledPathValue() ), + record( emptyPathValue() ) + ); + } + + @Override + protected Stream unsupportedMessages() + { + return Stream.of( + DiscardAllMessage.DISCARD_ALL, + new RunMessage( "RETURN 42" ) + ); + } + + @Override + protected MessageFormat.Reader newReader( PackInput input ) + { + return BoltProtocolV41.INSTANCE.createMessageFormat().newReader( input ); + } + + private Message record( Value value ) + { + return new RecordMessage( new Value[]{value} ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageWriterV41Test.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageWriterV41Test.java new file mode 100644 index 0000000000..dbe6e58a74 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/v41/MessageWriterV41Test.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2002-2020 "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. + */ +package org.neo4j.driver.internal.messaging.v41; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.stream.Stream; + +import org.neo4j.driver.Query; +import org.neo4j.driver.internal.InternalBookmark; +import org.neo4j.driver.internal.messaging.Message; +import org.neo4j.driver.internal.messaging.MessageFormat; +import org.neo4j.driver.internal.messaging.request.BeginMessage; +import org.neo4j.driver.internal.messaging.request.DiscardMessage; +import org.neo4j.driver.internal.messaging.request.HelloMessage; +import org.neo4j.driver.internal.messaging.request.InitMessage; +import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.request.RunMessage; +import org.neo4j.driver.internal.packstream.PackOutput; +import org.neo4j.driver.internal.security.InternalAuthToken; +import org.neo4j.driver.internal.util.messaging.AbstractMessageWriterTestBase; + +import static java.time.Duration.ofSeconds; +import static java.util.Calendar.DECEMBER; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.neo4j.driver.AccessMode.READ; +import static org.neo4j.driver.AccessMode.WRITE; +import static org.neo4j.driver.AuthTokens.basic; +import static org.neo4j.driver.Values.point; +import static org.neo4j.driver.Values.value; +import static org.neo4j.driver.internal.DatabaseNameUtil.database; +import static org.neo4j.driver.internal.DatabaseNameUtil.defaultDatabase; +import static org.neo4j.driver.internal.messaging.request.CommitMessage.COMMIT; +import static org.neo4j.driver.internal.messaging.request.DiscardAllMessage.DISCARD_ALL; +import static org.neo4j.driver.internal.messaging.request.GoodbyeMessage.GOODBYE; +import static org.neo4j.driver.internal.messaging.request.PullAllMessage.PULL_ALL; +import static org.neo4j.driver.internal.messaging.request.ResetMessage.RESET; +import static org.neo4j.driver.internal.messaging.request.RollbackMessage.ROLLBACK; +import static org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage.autoCommitTxRunMessage; +import static org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage.unmanagedTxRunMessage; + +/** + * The MessageWriter under tests is the one provided by the {@link BoltProtocolV41} and not an specific class implementation. + *

+ * It's done on this way to make easy to replace the implementation and still getting the same behaviour. + */ +class MessageWriterV41Test extends AbstractMessageWriterTestBase +{ + @Override + protected MessageFormat.Writer newWriter( PackOutput output ) + { + return BoltProtocolV41.INSTANCE.createMessageFormat().newWriter( output ); + } + + @Override + protected Stream supportedMessages() + { + return Stream.of( + // Bolt V2 Data Types + unmanagedTxRunMessage( new Query( "RETURN $point", singletonMap( "point", point( 42, 12.99, -180.0 ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $point", singletonMap( "point", point( 42, 0.51, 2.99, 100.123 ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $date", singletonMap( "date", value( LocalDate.ofEpochDay( 2147483650L ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $time", singletonMap( "time", value( OffsetTime.of( 4, 16, 20, 999, ZoneOffset.MIN ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $time", singletonMap( "time", value( LocalTime.of( 12, 9, 18, 999_888 ) ) ) ) ), + unmanagedTxRunMessage( + new Query( "RETURN $dateTime", singletonMap( "dateTime", value( LocalDateTime.of( 2049, DECEMBER, 12, 17, 25, 49, 199 ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $dateTime", singletonMap( "dateTime", value( ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneOffset + .ofHoursMinutes( 9, 30 ) ) ) ) ) ), + unmanagedTxRunMessage( new Query( "RETURN $dateTime", singletonMap( "dateTime", value( ZonedDateTime.of( 2000, 1, 10, 12, 2, 49, 300, ZoneId.of( + "Europe/Stockholm" ) ) ) ) ) ), + + // New Bolt V4 messages + new PullMessage( 100, 200 ), + new DiscardMessage( 300, 400 ), + + // Bolt V3 messages + new HelloMessage( "MyDriver/1.2.3", ((InternalAuthToken) basic( "neo4j", "neo4j" )).toMap(), Collections.emptyMap() ), + GOODBYE, + new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), READ, + defaultDatabase() ), + new BeginMessage( InternalBookmark.parse( "neo4j:bookmark:v1:tx123" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), WRITE, + database( "foo" ) ), + COMMIT, + ROLLBACK, + + RESET, + autoCommitTxRunMessage( new Query( "RETURN 1" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), defaultDatabase(), READ, + InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), + autoCommitTxRunMessage( new Query( "RETURN 1" ), ofSeconds( 5 ), singletonMap( "key", value( 42 ) ), database( "foo" ), WRITE, + InternalBookmark.parse( "neo4j:bookmark:v1:tx1" ) ), + unmanagedTxRunMessage( new Query( "RETURN 1" ) ), + + // Bolt V3 messages with struct values + autoCommitTxRunMessage( new Query( "RETURN $x", singletonMap( "x", value( ZonedDateTime.now() ) ) ), ofSeconds( 1 ), emptyMap(), + defaultDatabase(), READ, InternalBookmark.empty() ), + autoCommitTxRunMessage( new Query( "RETURN $x", singletonMap( "x", value( ZonedDateTime.now() ) ) ), ofSeconds( 1 ), emptyMap(), + database( "foo" ), + WRITE, InternalBookmark.empty() ), + unmanagedTxRunMessage( new Query( "RETURN $x", singletonMap( "x", point( 42, 1, 2, 3 ) ) ) ) + ); + } + + @Override + protected Stream unsupportedMessages() + { + return Stream.of( + // Bolt V1, V2 and V3 messages + new InitMessage( "Apa", emptyMap() ), + new RunMessage( "RETURN 1" ), + PULL_ALL, + DISCARD_ALL + ); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/messaging/AbstractMessageReaderTestBase.java b/driver/src/test/java/org/neo4j/driver/internal/util/messaging/AbstractMessageReaderTestBase.java index 9e134d8a96..5325bd2957 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/messaging/AbstractMessageReaderTestBase.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/messaging/AbstractMessageReaderTestBase.java @@ -20,75 +20,82 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.TestFactory; import java.io.IOException; -import java.util.Map; +import java.util.stream.Stream; import org.neo4j.driver.internal.async.inbound.ByteBufInput; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.messaging.MessageFormat; import org.neo4j.driver.internal.messaging.ResponseMessageHandler; -import org.neo4j.driver.internal.messaging.request.DiscardAllMessage; -import org.neo4j.driver.internal.messaging.request.RunMessage; import org.neo4j.driver.internal.messaging.response.FailureMessage; import org.neo4j.driver.internal.messaging.response.IgnoredMessage; import org.neo4j.driver.internal.messaging.response.RecordMessage; import org.neo4j.driver.internal.messaging.response.SuccessMessage; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.util.io.ByteBufOutput; -import org.neo4j.driver.Value; -import static java.util.Collections.singletonMap; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.neo4j.driver.Values.value; public abstract class AbstractMessageReaderTestBase { - @Test - void shouldReadSuccessMessage() throws Exception + @TestFactory + Stream shouldReadSupportedMessages() { - Map metadata = singletonMap( "hello", value( "world" ) ); - - ResponseMessageHandler handler = testMessageReading( new SuccessMessage( metadata ) ); - - verify( handler ).handleSuccessMessage( metadata ); + return supportedMessages().map( message -> + dynamicTest( message.toString(), () -> testSupportedMessageReading( message ) ) ); } - @Test - void shouldReadFailureMessage() throws Exception + private void testSupportedMessageReading( Message message ) throws IOException { - ResponseMessageHandler handler = testMessageReading( new FailureMessage( "Hello", "World" ) ); - - verify( handler ).handleFailureMessage( "Hello", "World" ); + ResponseMessageHandler handler = testMessageReading( message ); + + if ( message instanceof SuccessMessage ) + { + SuccessMessage successMessage = (SuccessMessage) message; + verify( handler ).handleSuccessMessage( successMessage.metadata() ); + } + else if ( message instanceof FailureMessage ) + { + FailureMessage failureMessage = (FailureMessage) message; + verify( handler ).handleFailureMessage( failureMessage.code(), failureMessage.message() ); + } + else if ( message instanceof IgnoredMessage ) + { + verify( handler ).handleIgnoredMessage(); + } + else if ( message instanceof RecordMessage ) + { + RecordMessage recordMessage = (RecordMessage) message; + verify( handler ).handleRecordMessage( recordMessage.fields() ); + } + else + { + fail( "Unsupported message type " + message.getClass().getSimpleName() ); + } } - @Test - void shouldReadIgnoredMessage() throws Exception + @TestFactory + Stream shouldFailToReadUnsupportedMessages() { - ResponseMessageHandler handler = testMessageReading( IgnoredMessage.IGNORED ); - - verify( handler ).handleIgnoredMessage(); + return unsupportedMessages().map( message -> + dynamicTest( message.toString(), () -> testUnsupportedMessageReading( message ) ) ); } - @Test - void shouldReadRecordMessage() throws Exception + private void testUnsupportedMessageReading( Message message ) throws IOException { - Value[] fields = {value( 1 ), value( 2 ), value( "42" )}; - - ResponseMessageHandler handler = testMessageReading( new RecordMessage( fields ) ); - - verify( handler ).handleRecordMessage( fields ); + assertThrows( IOException.class, () -> testMessageReading( message ) ); } - @Test - void shouldFailToReadUnknownMessage() - { - assertThrows( IOException.class, () -> testMessageReading( DiscardAllMessage.DISCARD_ALL ) ); - assertThrows( IOException.class, () -> testMessageReading( new RunMessage( "RETURN 42" ) ) ); - } + protected abstract Stream supportedMessages(); + + protected abstract Stream unsupportedMessages(); protected abstract MessageFormat.Reader newReader( PackInput input ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/messaging/KnowledgeableMessageFormat.java b/driver/src/test/java/org/neo4j/driver/internal/util/messaging/KnowledgeableMessageFormat.java index 57ff8d1c57..55cf576679 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/messaging/KnowledgeableMessageFormat.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/messaging/KnowledgeableMessageFormat.java @@ -23,6 +23,8 @@ import org.neo4j.driver.internal.messaging.AbstractMessageWriter; import org.neo4j.driver.internal.messaging.MessageEncoder; +import org.neo4j.driver.internal.messaging.common.CommonValuePacker; +import org.neo4j.driver.internal.messaging.common.CommonValueUnpacker; import org.neo4j.driver.internal.messaging.encode.DiscardAllMessageEncoder; import org.neo4j.driver.internal.messaging.encode.InitMessageEncoder; import org.neo4j.driver.internal.messaging.encode.PullAllMessageEncoder; @@ -37,8 +39,7 @@ import org.neo4j.driver.internal.messaging.response.IgnoredMessage; import org.neo4j.driver.internal.messaging.response.RecordMessage; import org.neo4j.driver.internal.messaging.response.SuccessMessage; -import org.neo4j.driver.internal.messaging.v2.MessageFormatV2; -import org.neo4j.driver.internal.messaging.v2.ValuePackerV2; +import org.neo4j.driver.internal.messaging.v3.MessageFormatV3; import org.neo4j.driver.internal.packstream.PackOutput; import org.neo4j.driver.internal.types.TypeConstructor; import org.neo4j.driver.internal.util.Iterables; @@ -49,10 +50,10 @@ import org.neo4j.driver.types.Relationship; /** - * This class provides the missing server side packing methods to serialize Node, Relationship and Path. - * It also allows writing of server side messages like SUCCESS, FAILURE, IGNORED and RECORD. + * This class provides the missing server side packing methods to serialize Node, Relationship and Path. It also allows writing of server side messages like + * SUCCESS, FAILURE, IGNORED and RECORD. */ -public class KnowledgeableMessageFormat extends MessageFormatV2 +public class KnowledgeableMessageFormat extends MessageFormatV3 { @Override public Writer newWriter( PackOutput output ) @@ -85,7 +86,7 @@ static Map buildEncoders() } } - private static class KnowledgeableValuePacker extends ValuePackerV2 + private static class KnowledgeableValuePacker extends CommonValuePacker { KnowledgeableValuePacker( PackOutput output ) { @@ -119,7 +120,7 @@ protected void packInternalValue( InternalValue value ) throws IOException private void packPath( Path path ) throws IOException { - packer.packStructHeader( 3, PATH ); + packer.packStructHeader( 3, CommonValueUnpacker.PATH ); // Unique nodes Map nodeIdx = Iterables.newLinkedHashMapWithSize( path.length() + 1 ); @@ -148,7 +149,7 @@ private void packPath( Path path ) throws IOException packer.packListHeader( relIdx.size() ); for ( Relationship rel : relIdx.keySet() ) { - packer.packStructHeader( 3, UNBOUND_RELATIONSHIP ); + packer.packStructHeader( 3, CommonValueUnpacker.UNBOUND_RELATIONSHIP ); packer.pack( rel.id() ); packer.pack( rel.type() ); packProperties( rel ); @@ -169,7 +170,7 @@ private void packPath( Path path ) throws IOException private void packRelationship( Relationship rel ) throws IOException { - packer.packStructHeader( 5, RELATIONSHIP ); + packer.packStructHeader( 5, CommonValueUnpacker.RELATIONSHIP ); packer.pack( rel.id() ); packer.pack( rel.startNodeId() ); packer.pack( rel.endNodeId() ); @@ -181,7 +182,7 @@ private void packRelationship( Relationship rel ) throws IOException private void packNode( Node node ) throws IOException { - packer.packStructHeader( NODE_FIELDS, NODE ); + packer.packStructHeader( CommonValueUnpacker.NODE_FIELDS, CommonValueUnpacker.NODE ); packer.pack( node.id() ); Iterable labels = node.labels(); diff --git a/driver/src/test/java/org/neo4j/driver/util/TestUtil.java b/driver/src/test/java/org/neo4j/driver/util/TestUtil.java index 8db61f8682..0c8daa05c6 100644 --- a/driver/src/test/java/org/neo4j/driver/util/TestUtil.java +++ b/driver/src/test/java/org/neo4j/driver/util/TestUtil.java @@ -43,6 +43,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BooleanSupplier; +import java.util.function.Predicate; import org.neo4j.driver.AccessMode; import org.neo4j.driver.Bookmark; @@ -66,8 +67,6 @@ import org.neo4j.driver.internal.messaging.request.RollbackMessage; import org.neo4j.driver.internal.messaging.request.RunMessage; import org.neo4j.driver.internal.messaging.request.RunWithMetadataMessage; -import org.neo4j.driver.internal.messaging.v1.BoltProtocolV1; -import org.neo4j.driver.internal.messaging.v2.BoltProtocolV2; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; import org.neo4j.driver.internal.messaging.v41.BoltProtocolV41; @@ -313,10 +312,10 @@ public static NetworkSession newSession( ConnectionProvider connectionProvider ) } public static NetworkSession newSession( ConnectionProvider connectionProvider, AccessMode mode, - RetryLogic retryLogic, Bookmark bookmark ) + RetryLogic retryLogic, Bookmark bookmark ) { return new NetworkSession( connectionProvider, retryLogic, defaultDatabase(), mode, new DefaultBookmarkHolder( bookmark ), UNLIMITED_FETCH_SIZE, - DEV_NULL_LOGGING ); + DEV_NULL_LOGGING ); } public static void verifyRunRx( Connection connection, String query ) @@ -370,30 +369,29 @@ public static void verifyBeginTx( Connection connectionMock, Bookmark bookmark ) public static void setupFailingRun( Connection connection, Throwable error ) { doAnswer( invocation -> - { - ResponseHandler runHandler = invocation.getArgument( 1 ); - runHandler.onFailure( error ); - return null; - } ).when( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any() ); + { + ResponseHandler runHandler = invocation.getArgument( 1 ); + runHandler.onFailure( error ); + return null; + } ).when( connection ).writeAndFlush( any( RunWithMetadataMessage.class ), any() ); doAnswer( invocation -> - { - ResponseHandler pullHandler = invocation.getArgument( 1 ); - pullHandler.onFailure( error ); - return null; - } ).when( connection ).writeAndFlush( any( PullMessage.class ), any() ); - + { + ResponseHandler pullHandler = invocation.getArgument( 1 ); + pullHandler.onFailure( error ); + return null; + } ).when( connection ).writeAndFlush( any( PullMessage.class ), any() ); } public static void setupFailingBegin( Connection connection, Throwable error ) { // with bookmarks doAnswer( invocation -> - { - ResponseHandler handler = invocation.getArgument( 1 ); - handler.onFailure( error ); - return null; - } ).when( connection ).writeAndFlush( any( BeginMessage.class ), any( BeginTxResponseHandler.class ) ); + { + ResponseHandler handler = invocation.getArgument( 1 ); + handler.onFailure( error ); + return null; + } ).when( connection ).writeAndFlush( any( BeginMessage.class ), any( BeginTxResponseHandler.class ) ); } public static void setupFailingCommit( Connection connection ) @@ -496,9 +494,8 @@ public static void setupSuccessfulRunAndPull( Connection connection, String quer } ).when( connection ).writeAndFlush( any( PullMessage.class ), any() ); } - public static Connection connectionMock() - { - return connectionMock( BoltProtocolV2.INSTANCE ); + public static Connection connectionMock( ) { + return connectionMock( BoltProtocol.forVersion( new BoltProtocolVersion( 4, 1 ) ) ); } public static Connection connectionMock( BoltProtocol protocol ) @@ -525,14 +522,9 @@ public static Connection connectionMock( String databaseName, AccessMode mode, B when( connection.mode() ).thenReturn( mode ); when( connection.databaseName() ).thenReturn( database( databaseName ) ); BoltProtocolVersion version = protocol.version(); - if ( version.equals( BoltProtocolV1.VERSION ) || version.equals( BoltProtocolV2.VERSION ) ) - { - setupSuccessfulPullAll( connection, "COMMIT" ); - setupSuccessfulPullAll( connection, "ROLLBACK" ); - setupSuccessfulPullAll( connection, "BEGIN" ); - } - else if ( version.equals( BoltProtocolV3.VERSION ) || version.equals( BoltProtocolV4.VERSION ) || - version.equals( BoltProtocolV41.VERSION )) + + if ( version.equals( BoltProtocolV3.VERSION ) || version.equals( BoltProtocolV4.VERSION ) || + version.equals( BoltProtocolV41.VERSION ) ) { setupSuccessResponse( connection, CommitMessage.class ); setupSuccessResponse( connection, RollbackMessage.class ); @@ -630,16 +622,26 @@ public static String randomString( int size ) return sb.toString(); } - public static ArgumentMatcher runMessageWithQueryMatcher(String query ) + public static ArgumentMatcher runMessageWithQueryMatcher( String query ) { return message -> message instanceof RunMessage && Objects.equals( query, ((RunMessage) message).query() ); } - public static ArgumentMatcher runWithMetaMessageWithQueryMatcher(String query ) + public static ArgumentMatcher runWithMetaMessageWithQueryMatcher( String query ) { return message -> message instanceof RunWithMetadataMessage && Objects.equals( query, ((RunWithMetadataMessage) message).query() ); } + public static ArgumentMatcher beginMessage() + { + return beginMessageWithPredicate( ignored -> true ); + } + + public static ArgumentMatcher beginMessageWithPredicate( Predicate predicate ) + { + return message -> message instanceof BeginMessage && predicate.test( (BeginMessage) message ); + } + /** * Used in tests that expect a server version but the tests do not depend on server version to behave differently. */