diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationResult.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java similarity index 90% rename from community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationResult.java rename to community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java index 9cbc6a99aca9e..df7653ae1a0ac 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationResult.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/BoltAuthenticationHelper.java @@ -25,15 +25,16 @@ import org.neo4j.bolt.runtime.BoltQuerySource; import org.neo4j.bolt.runtime.StateMachineContext; import org.neo4j.bolt.runtime.StatementProcessor; +import org.neo4j.bolt.security.auth.AuthenticationResult; import org.neo4j.values.storable.Values; -public class BoltAuthenticationResult +public class BoltAuthenticationHelper { public static boolean processAuthentication( String userAgent, Map authToken, StateMachineContext context ) throws BoltConnectionFatality { try { - org.neo4j.bolt.security.auth.AuthenticationResult authResult = context.boltSpi().authenticate( authToken ); + AuthenticationResult authResult = context.boltSpi().authenticate( authToken ); String username = authResult.getLoginContext().subject().username(); context.authenticatedAsUser( username ); @@ -56,7 +57,7 @@ public static boolean processAuthentication( String userAgent, Map serverImmediatelyDisconnects() + { + return new TypeSafeMatcher() + { + @Override + protected boolean matchesSafely( TransportConnection connection ) + { + try + { + connection.recv( 1 ); + } + catch ( Exception e ) + { + // take an IOException on send/receive as evidence of disconnection + return e instanceof IOException; + } + return false; + } + + @Override + public void describeTo( Description description ) + { + description.appendText( "Eventually Disconnects" ); + } + }; + } + public interface MessageEncoder { byte[] encode( Neo4jPack neo4jPack, RequestMessage... messages ) throws IOException; diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java new file mode 100644 index 0000000000000..1aa18170e2a10 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v3/messaging/decoder/GoodbyeMessageDecoderTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.decoder; + +import org.junit.jupiter.api.Test; + +import org.neo4j.bolt.messaging.RequestMessageDecoder; +import org.neo4j.bolt.runtime.BoltConnection; +import org.neo4j.bolt.runtime.BoltResponseHandler; +import org.neo4j.bolt.v3.messaging.request.GoodbyeMessage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.neo4j.bolt.v3.messaging.decoder.HelloMessageDecoderTest.assertOriginalMessageEqualsToDecoded; +import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; + +class GoodbyeMessageDecoderTest +{ + private final BoltResponseHandler responseHandler = mock( BoltResponseHandler.class ); + private final BoltConnection connection = mock( BoltConnection.class ); + private final RequestMessageDecoder decoder = new GoodbyeMessageDecoder( connection, responseHandler ); + + @Test + void shouldReturnCorrectSignature() + { + assertEquals( GoodbyeMessage.SIGNATURE, decoder.signature() ); + } + + @Test + void shouldReturnConnectResponseHandler() + { + assertEquals( responseHandler, decoder.responseHandler() ); + } + + @Test + void shouldDecodeGoodbyeMessage() throws Exception + { + assertOriginalMessageEqualsToDecoded( GOODBYE_MESSAGE, decoder ); + verify( connection ).stop(); + } +} diff --git a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java index 91fc96c28b8e1..c4ed4e045ff85 100644 --- a/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java +++ b/community/community-it/bolt-it/src/test/java/org/neo4j/bolt/v3/runtime/integration/GoodbyeMessageIT.java @@ -19,19 +19,24 @@ */ package org.neo4j.bolt.v3.runtime.integration; -import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import org.junit.Test; -import java.io.IOException; import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; import org.neo4j.bolt.v1.messaging.request.ResetMessage; -import org.neo4j.bolt.v1.transport.socket.client.TransportConnection; +import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; import org.neo4j.bolt.v3.messaging.request.BeginMessage; import org.neo4j.bolt.v3.messaging.request.RunMessage; +import org.neo4j.function.Predicates; +import org.neo4j.kernel.api.KernelTransactionHandle; import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.api.KernelTransactions; +import org.neo4j.kernel.internal.GraphDatabaseAPI; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.allOf; @@ -43,6 +48,7 @@ import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgFailure; import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.msgSuccess; import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.eventuallyReceives; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.serverImmediatelyDisconnects; import static org.neo4j.bolt.v3.messaging.request.GoodbyeMessage.GOODBYE_MESSAGE; public class GoodbyeMessageIT extends BoltV3TransportBase @@ -90,6 +96,7 @@ public void shouldCloseConnectionInStreaming() throws Throwable // Then assertThat( connection, serverImmediatelyDisconnects() ); + assertThat( server, eventuallyClosesTransaction() ); } @Test @@ -126,6 +133,7 @@ public void shouldCloseConnectionInTxReady() throws Throwable // Then assertThat( connection, serverImmediatelyDisconnects() ); + assertThat( server, eventuallyClosesTransaction() ); } @Test @@ -138,11 +146,12 @@ public void shouldCloseConnectionInTxStreaming() throws Throwable connection.send( util.chunk( new BeginMessage(), new RunMessage( "UNWIND [1,2,3] AS a RETURN a, a * a AS a_squared" ) ) ); Matcher> entryFieldMatcher = hasEntry( is( "fields" ), equalTo( asList( "a", "a_squared" ) ) ); assertThat( connection, util.eventuallyReceives( msgSuccess(), msgSuccess( allOf( entryFieldMatcher, hasKey( "t_first" ) ) ) ) ); + // you shall be in the tx_streaming state now connection.send( util.chunk( GOODBYE_MESSAGE ) ); - // Then assertThat( connection, serverImmediatelyDisconnects() ); + assertThat( server, eventuallyClosesTransaction() ); } @Test @@ -158,31 +167,36 @@ public void shouldDropConnectionImmediatelyAfterGoodbye() throws Throwable assertThat( connection, serverImmediatelyDisconnects() ); } - private static Matcher serverImmediatelyDisconnects() + private static Matcher eventuallyClosesTransaction() { - return new TypeSafeMatcher() + return new TypeSafeMatcher() { @Override - protected boolean matchesSafely( TransportConnection connection ) + public void describeTo( org.hamcrest.Description description ) + { + description.appendText( "Eventually close all transactions" ); + } + + @Override + protected boolean matchesSafely( Neo4jWithSocket server ) { + BooleanSupplier condition = () -> getActiveTransactions( server ).size() == 0; try { - connection.recv( 1 ); + Predicates.await( condition, 2, TimeUnit.SECONDS ); + return true; } catch ( Exception e ) { - // take an IOException on send/receive as evidence of disconnection - return e instanceof IOException; + return false; } - return false; } - @Override - public void describeTo( Description description ) + private Set getActiveTransactions( Neo4jWithSocket server ) { - description.appendText( "Eventually Disconnects" ); + GraphDatabaseAPI gdb = (GraphDatabaseAPI) server.graphDatabaseService(); + return gdb.getDependencyResolver().resolveDependency( KernelTransactions.class ).activeTransactions(); } }; } - }