/* * 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.Mockito; import java.io.IOException; import java.util.HashMap; import org.neo4j.driver.internal.net.BoltServerAddress; 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.util.Clock; import org.neo4j.driver.internal.util.Consumers; import org.neo4j.driver.v1.Value; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.exceptions.Neo4jException; import org.neo4j.driver.v1.exceptions.TransientException; import static junit.framework.TestCase.assertFalse; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; 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.net.BoltServerAddress.LOCAL_DEFAULT; public class ConnectionInvalidationTest { private final Connection delegate = mock( Connection.class ); private final Clock clock = mock( Clock.class ); private final PooledConnection conn = new PooledSocketConnection( delegate, Consumers.<PooledConnection>noOp(), Clock.SYSTEM ); @SuppressWarnings( "unchecked" ) @Test public void shouldNotInvalidateConnectionThatIsUnableToRun() throws Throwable { // Given a connection that's broken Mockito.doThrow( new ClientException( "That didn't work" ) ) .when( delegate ).run( anyString(), anyMap(), any( Collector.class ) ); PooledConnection conn = new PooledSocketConnection( delegate, Consumers.<PooledConnection>noOp(), clock ); PooledConnectionValidator validator = new PooledConnectionValidator( pool( true ) ); // When/Then BlockingPooledConnectionQueue queue = mock( BlockingPooledConnectionQueue.class ); PooledConnectionReleaseConsumer consumer = new PooledConnectionReleaseConsumer( queue,validator ); consumer.accept( conn ); verify( queue ).offer( conn ); } @Test public void shouldInvalidateConnectionWithUnknownAddress() { when( delegate.boltServerAddress() ).thenReturn( LOCAL_DEFAULT ); BlockingPooledConnectionQueue queue = mock( BlockingPooledConnectionQueue.class ); PooledConnectionValidator validator = new PooledConnectionValidator( pool( false ) ); PooledConnectionReleaseConsumer consumer = new PooledConnectionReleaseConsumer( queue, validator ); consumer.accept( conn ); verify( queue, never() ).offer( conn ); } @Test public void shouldInvalidConnectionIfFailedToReset() throws Throwable { // Given a connection that's broken Mockito.doThrow( new ClientException( "That didn't work" ) ).when( delegate ).reset(); PooledConnection conn = new PooledSocketConnection( delegate, Consumers.<PooledConnection>noOp(), clock ); PooledConnectionValidator validator = new PooledConnectionValidator( pool( true ) ); // When/Then BlockingPooledConnectionQueue queue = mock( BlockingPooledConnectionQueue.class ); PooledConnectionReleaseConsumer consumer = new PooledConnectionReleaseConsumer( queue, validator ); consumer.accept( conn ); verify( queue, never() ).offer( conn ); } @Test public void shouldInvalidateOnUnrecoverableProblems() throws Throwable { // When/Then assertUnrecoverable( new ClientException( "Hello, world!", new IOException() ) ); assertUnrecoverable( new ClientException( "Hello, world!" ) ); } @Test public void shouldNotInvalidateOnKnownRecoverableExceptions() throws Throwable { assertRecoverable( new ClientException( "Neo.ClientError.General.ReadOnly", "Hello, world!" ) ); assertRecoverable( new TransientException( "Neo.TransientError.General.ReadOnly", "Hello, world!" ) ); } @Test public void shouldInvalidateOnProtocolViolationExceptions() throws Throwable { assertUnrecoverable( new ClientException( "Neo.ClientError.Request.InvalidFormat", "Hello, world!" ) ); assertUnrecoverable( new ClientException( "Neo.ClientError.Request.Invalid", "Hello, world!" ) ); } @SuppressWarnings( "unchecked" ) private void assertUnrecoverable( Neo4jException exception ) { doThrow( exception ).when( delegate ) .run( eq( "assert unrecoverable" ), anyMap(), any( Collector.class ) ); // When try { conn.run( "assert unrecoverable", new HashMap<String,Value>(), Collector.NO_OP ); fail( "Should've rethrown exception" ); } catch ( Neo4jException e ) { assertThat( e, equalTo( exception ) ); } PooledConnectionValidator validator = new PooledConnectionValidator( pool( true ) ); // Then assertTrue( conn.hasUnrecoverableErrors() ); BlockingPooledConnectionQueue queue = mock( BlockingPooledConnectionQueue.class ); PooledConnectionReleaseConsumer consumer = new PooledConnectionReleaseConsumer( queue, validator ); consumer.accept( conn ); verify( queue, never() ).offer( conn ); } @SuppressWarnings( "unchecked" ) private void assertRecoverable( Neo4jException exception ) { doThrow( exception ).when( delegate ).run( eq( "assert recoverable" ), anyMap(), any( Collector.class ) ); // When try { conn.run( "assert recoverable", new HashMap<String,Value>(), Collector.NO_OP ); fail( "Should've rethrown exception" ); } catch ( Neo4jException e ) { assertThat( e, equalTo( exception ) ); } // Then assertFalse( conn.hasUnrecoverableErrors() ); PooledConnectionValidator validator = new PooledConnectionValidator( pool( true ) ); BlockingPooledConnectionQueue queue = mock( BlockingPooledConnectionQueue.class ); PooledConnectionReleaseConsumer consumer = new PooledConnectionReleaseConsumer( queue, validator ); consumer.accept( conn ); verify( queue ).offer( conn ); } private ConnectionPool pool( boolean hasAddress ) { ConnectionPool pool = mock( ConnectionPool.class ); when( pool.hasAddress( any( BoltServerAddress.class ) ) ).thenReturn( hasAddress ); return pool; } }