/* * Copyright (c) 2002-2017 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.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.net.pooling; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import java.io.IOException; import java.util.Map; import java.util.Queue; import org.neo4j.driver.internal.messaging.Message; import org.neo4j.driver.internal.net.BoltServerAddress; import org.neo4j.driver.internal.net.SocketClient; import org.neo4j.driver.internal.net.SocketConnection; import org.neo4j.driver.internal.spi.Collector; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.spi.PooledConnection; import org.neo4j.driver.internal.summary.InternalServerInfo; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.internal.util.Consumers; import org.neo4j.driver.v1.exceptions.DatabaseException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.neo4j.driver.internal.logging.DevNullLogger.DEV_NULL_LOGGER; import static org.neo4j.driver.internal.messaging.ResetMessage.RESET; import static org.neo4j.driver.internal.net.BoltServerAddress.LOCAL_DEFAULT; public class PooledConnectionValidatorTest { @Test public void isNotReusableWhenPoolHasNoAddress() { Connection connection = mock( Connection.class ); PooledConnection pooledConnection = newPooledConnection( connection ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( false ) ); assertFalse( validator.isReusable( pooledConnection ) ); verify( connection, never() ).reset(); verify( connection, never() ).sync(); } @Test @SuppressWarnings( "unchecked" ) public void isNotReusableWhenHasUnrecoverableErrors() { Connection connection = mock( Connection.class ); DatabaseException runError = new DatabaseException( "", "" ); doThrow( runError ).when( connection ).run( anyString(), any( Map.class ), any( Collector.class ) ); PooledConnection pooledConnection = newPooledConnection( connection ); try { pooledConnection.run( "BEGIN", null, null ); fail( "Exception expected" ); } catch ( Exception e ) { assertSame( runError, e ); } assertTrue( pooledConnection.hasUnrecoverableErrors() ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( true ) ); assertFalse( validator.isReusable( pooledConnection ) ); verify( connection, never() ).reset(); verify( connection, never() ).sync(); } @Test public void resetAndSyncValidConnectionWhenCheckingIfReusable() { Connection connection = mock( Connection.class ); PooledConnection pooledConnection = newPooledConnection( connection ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( true ) ); boolean connectionIsValid = validator.isReusable( pooledConnection ); assertTrue( connectionIsValid ); InOrder inOrder = inOrder( connection ); inOrder.verify( connection ).reset(); inOrder.verify( connection ).sync(); } @Test public void sendsSingleResetMessageForValidConnectionWhenCheckingIfReusable() throws IOException { SocketClient socket = mock( SocketClient.class ); InternalServerInfo serverInfo = new InternalServerInfo( LOCAL_DEFAULT, "v1" ); Connection connection = new SocketConnection( socket, serverInfo, DEV_NULL_LOGGER ); PooledConnection pooledConnection = newPooledConnection( connection ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( true ) ); boolean connectionIsValid = validator.isReusable( pooledConnection ); assertTrue( connectionIsValid ); ArgumentCaptor<Queue<Message>> captor = messagesCaptor(); verify( socket ).send( captor.capture() ); assertEquals( 1, captor.getAllValues().size() ); Queue<Message> messages = captor.getValue(); assertEquals( 1, messages.size() ); assertEquals( RESET, messages.peek() ); } @Test public void isConnectedReturnsFalseWhenResetFails() { Connection connection = mock( Connection.class ); doThrow( new RuntimeException() ).when( connection ).reset(); PooledConnection pooledConnection = newPooledConnection( connection ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( true ) ); assertFalse( validator.isConnected( pooledConnection ) ); verify( connection ).reset(); verify( connection, never() ).sync(); } @Test public void isConnectedReturnsFalseWhenSyncFails() { Connection connection = mock( Connection.class ); doThrow( new RuntimeException() ).when( connection ).sync(); PooledConnection pooledConnection = newPooledConnection( connection ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( true ) ); assertFalse( validator.isConnected( pooledConnection ) ); verify( connection ).reset(); verify( connection ).sync(); } @Test public void isConnectedReturnsTrueWhenUnderlyingConnectionWorks() { Connection connection = mock( Connection.class ); PooledConnection pooledConnection = newPooledConnection( connection ); PooledConnectionValidator validator = new PooledConnectionValidator( connectionPoolMock( true ) ); assertTrue( validator.isConnected( pooledConnection ) ); verify( connection ).reset(); verify( connection ).sync(); } private static PooledConnection newPooledConnection( Connection connection ) { return new PooledSocketConnection( connection, Consumers.<PooledConnection>noOp(), Clock.SYSTEM ); } private static ConnectionPool connectionPoolMock( boolean knowsAddressed ) { ConnectionPool pool = mock( ConnectionPool.class ); when( pool.hasAddress( any( BoltServerAddress.class ) ) ).thenReturn( knowsAddressed ); return pool; } @SuppressWarnings( "unchecked" ) private static ArgumentCaptor<Queue<Message>> messagesCaptor() { return (ArgumentCaptor) ArgumentCaptor.forClass( Queue.class ); } }