@Test public void shouldStoreIndexFailureWhenFailingToCreateOnlineAccessorAfterRecoveringPopulatingIndex() throws Exception { // given long indexId = 1; StoreIndexDescriptor indexRule = index.withId( indexId ); IndexingService indexing = newIndexingServiceWithMockedDependencies( populator, accessor, withData(), indexRule ); IOException exception = new IOException( "Expected failure" ); when( nameLookup.labelGetName( labelId ) ).thenReturn( "TheLabel" ); when( nameLookup.propertyKeyGetName( propertyKeyId ) ).thenReturn( "propertyKey" ); when( indexProvider.getInitialState( indexRule ) ).thenReturn( POPULATING ); when( indexProvider.getOnlineAccessor( any( StoreIndexDescriptor.class ), any( IndexSamplingConfig.class ) ) ) .thenThrow( exception ); life.start(); ArgumentCaptor<Boolean> closeArgs = ArgumentCaptor.forClass( Boolean.class ); // when waitForIndexesToGetIntoState( indexing, InternalIndexState.FAILED, indexId ); verify( populator, timeout( 10000 ).times( 2 ) ).close( closeArgs.capture() ); // then assertEquals( FAILED, indexing.getIndexProxy( 1 ).getState() ); assertEquals( asList( true, false ), closeArgs.getAllValues() ); assertThat( storedFailure(), containsString( format( "java.io.IOException: Expected failure%n\tat " ) ) ); internalLogProvider.assertAtLeastOnce( inLog( IndexPopulationJob.class ).error( equalTo( "Failed to populate index: [:TheLabel(propertyKey) [provider: {key=quantum-dex, version=25.0}]]" ), causedBy( exception ) ) ); internalLogProvider.assertNone( inLog( IndexPopulationJob.class ).info( "Index population completed. Index is now online: [%s]", ":TheLabel(propertyKey) [provider: {key=quantum-dex, version=25.0}]" ) ); }
@Test public void shouldNotAuthorizeInvalidCredentials() throws Exception { // Given final AuthorizationEnabledFilter filter = new AuthorizationEnabledFilter( () -> authManager, logProvider ); String credentials = Base64.encodeBase64String( "foo:bar".getBytes( StandardCharsets.UTF_8 ) ); BasicLoginContext loginContext = mock( BasicLoginContext.class ); AuthSubject authSubject = mock( AuthSubject.class ); when( servletRequest.getMethod() ).thenReturn( "GET" ); when( servletRequest.getContextPath() ).thenReturn( "/db/data" ); when( servletRequest.getHeader( HttpHeaders.AUTHORIZATION ) ).thenReturn( "BASIC " + credentials ); when( servletRequest.getRemoteAddr() ).thenReturn( "remote_ip_address" ); when( authManager.login( authTokenArgumentMatcher( authToken( "foo", "bar" ) ) ) ).thenReturn( loginContext ); when( loginContext.subject() ).thenReturn( authSubject ); when( authSubject.getAuthenticationResult() ).thenReturn( AuthenticationResult.FAILURE ); // When filter.doFilter( servletRequest, servletResponse, filterChain ); // Then verifyNoMoreInteractions( filterChain ); logProvider.assertExactly( inLog( AuthorizationEnabledFilter.class ) .warn( "Failed authentication attempt for '%s' from %s", "foo", "remote_ip_address" ) ); verify( servletResponse ).setStatus( 401 ); verify( servletResponse ).addHeader( HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8" ); assertThat( outputStream.toString( StandardCharsets.UTF_8.name() ), containsString( "\"code\" : \"Neo.ClientError.Security.Unauthorized\"" ) ); assertThat( outputStream.toString( StandardCharsets.UTF_8.name() ), containsString( "\"message\" : \"Invalid username or password.\"" ) ); }
Config config = Config.defaults( default_schema_provider, PROVIDER_DESCRIPTOR.name() ); IndexProviderMap providerMap = life.add( new DefaultIndexProviderMap( buildIndexDependencies( provider ), config ) ); TokenNameLookup mockLookup = mock( TokenNameLookup.class ); StoreIndexDescriptor failedIndex = storeIndex( 3, 2, 2, PROVIDER_DESCRIPTOR ); life.add( IndexingServiceFactory.createIndexingService( config, mock( JobScheduler.class ), providerMap, mock( IndexStoreView.class ), mockLookup, asList( onlineIndex, populatingIndex, failedIndex ), internalLogProvider, userLogProvider, IndexingService.NO_MONITOR, schemaState ) ); when( provider.getInitialState( onlineIndex ) ) .thenReturn( ONLINE ); when( provider.getInitialState( populatingIndex ) ) .thenReturn( InternalIndexState.POPULATING ); when( provider.getInitialState( failedIndex ) ) .thenReturn( InternalIndexState.FAILED ); when(mockLookup.labelGetName( 1 )).thenReturn( "LabelOne" ); internalLogProvider.assertAtLeastOnce( logMatch.debug( "IndexingService.init: index 1 on :LabelOne(propertyOne) is ONLINE" ), logMatch.debug( "IndexingService.init: index 2 on :LabelOne(propertyTwo) is POPULATING" ), logMatch.debug( "IndexingService.init: index 3 on :LabelTwo(propertyTwo) is FAILED" ) );
@Test public void logConstraintJobProgress() throws Exception { // Given createNode( map( name, "irrelephant" ), FIRST ); AssertableLogProvider logProvider = new AssertableLogProvider(); FlippableIndexProxy index = mock( FlippableIndexProxy.class ); when( index.getState() ).thenReturn( InternalIndexState.POPULATING ); IndexPopulator populator = spy( indexPopulator( false ) ); try { IndexPopulationJob job = newIndexPopulationJob( populator, index, indexStoreView, logProvider, EntityType.NODE, indexDescriptor( FIRST, name, true ) ); // When job.run(); // Then LogMatcherBuilder match = inLog( IndexPopulationJob.class ); logProvider.assertExactly( match.info( "Index population started: [%s]", ":FIRST(name)" ), match.info( "Index created. Starting data checks. Index [%s] is %s.", ":FIRST(name)", "POPULATING" ) ); } finally { populator.close( true ); } }
DefaultIndexProviderMap providerMap = new DefaultIndexProviderMap( buildIndexDependencies( provider ), config ); providerMap.init(); TokenNameLookup mockLookup = mock( TokenNameLookup.class ); mock( JobScheduler.class ), providerMap, storeView, mockLookup, asList( onlineIndex, populatingIndex, failedIndex ), internalLogProvider, userLogProvider, IndexingService.NO_MONITOR, schemaState ); when( provider.getInitialState( onlineIndex ) ) .thenReturn( ONLINE ); when( provider.getInitialState( populatingIndex ) ) .thenReturn( InternalIndexState.POPULATING ); when( provider.getInitialState( failedIndex ) ) .thenReturn( InternalIndexState.FAILED ); when( storeView.indexSample( anyLong(), any( DoubleLongRegister.class ) ) ).thenReturn( newDoubleLongRegister( 32L, 32L ) ); internalLogProvider.clear(); verify( provider ).getPopulationFailure( failedIndex ); internalLogProvider.assertAtLeastOnce( logMatch.debug( "IndexingService.start: index 1 on :LabelOne(propertyOne) is ONLINE" ), logMatch.debug( "IndexingService.start: index 2 on :LabelOne(propertyTwo) is POPULATING" ), logMatch.debug( "IndexingService.start: index 3 on :LabelTwo(propertyTwo) is FAILED" ) );
@Test public void shouldNotLogConnectionResetErrors() throws Exception { // Given AssertableLogProvider logProvider = new AssertableLogProvider(); HouseKeeper keeper = new HouseKeeper( null, logProvider.getLog( HouseKeeper.class ) ); Channel channel = mock( Channel.class ); when( channel.toString() ).thenReturn( "[some channel info]" ); ChannelHandlerContext ctx = mock( ChannelHandlerContext.class ); when( ctx.channel() ).thenReturn( channel ); when( ctx.executor() ).thenReturn( mock( EventExecutor.class ) ); IOException connResetError = new IOException( "Connection reset by peer" ); // When keeper.exceptionCaught( ctx, connResetError ); // Then logProvider.assertExactly( AssertableLogProvider.inLog( HouseKeeper.class ).warn( "Fatal error occurred when handling a client connection, " + "remote peer unexpectedly closed connection: %s", channel ) ); }
@Test public void shouldLogExceptionOnExceptionCaught() { AssertableLogProvider logProvider = new AssertableLogProvider(); BoltConnection connection = mock( BoltConnection.class ); channel = new EmbeddedChannel( new HouseKeeper( connection, logProvider.getLog( HouseKeeper.class ) ) ); RuntimeException exception = new RuntimeException( "some exception" ); channel.pipeline().fireExceptionCaught( exception ); verify( connection ).stop(); logProvider.assertExactly( inLog( HouseKeeper.class ).error( startsWith( "Fatal error occurred when handling a client connection" ), equalTo( exception ) ) ); }
@Override public void log( @Nonnull String message, @Nonnull Throwable throwable ) { matcher = builder.error( messageMatcher( message ), sameInstance( throwable ) ); }
@Test public void processNextBatchShouldThrowAssertionErrorIfStatementOpen() throws Exception { BoltConnection connection = newConnection( 1 ); connection.enqueue( Jobs.noop() ); connection.enqueue( Jobs.noop() ); // force to a message waiting loop when( stateMachine.hasOpenStatement() ).thenReturn( true ); connection.processNextBatch(); logProvider.assertExactly( AssertableLogProvider.inLog( DefaultBoltConnection.class.getName() ).error( startsWith( "Unexpected error" ), isA( AssertionError.class ) ) ); }
@Test public void processNextBatchShouldNotThrowAssertionErrorIfStatementOpenButStopping() throws Exception { BoltConnection connection = newConnection( 1 ); connection.enqueue( Jobs.noop() ); connection.enqueue( Jobs.noop() ); // force to a message waiting loop when( stateMachine.hasOpenStatement() ).thenReturn( true ); connection.stop(); connection.processNextBatch(); logProvider.assertNone( AssertableLogProvider.inLog( DefaultBoltConnection.class.getName() ).error( startsWith( "Unexpected error" ), isA( AssertionError.class ) ) ); }
@Test public void shouldLogJobFailure() throws Exception { // Given createNode( map( name, "irrelephant" ), FIRST ); AssertableLogProvider logProvider = new AssertableLogProvider(); FlippableIndexProxy index = mock( FlippableIndexProxy.class ); IndexPopulator populator = spy( indexPopulator( false ) ); IndexPopulationJob job = newIndexPopulationJob( populator, index, indexStoreView, logProvider, EntityType.NODE, indexDescriptor( FIRST, name, false ) ); Throwable failure = new IllegalStateException( "not successful" ); doThrow( failure ).when( populator ).create(); // When job.run(); // Then LogMatcherBuilder match = inLog( IndexPopulationJob.class ); logProvider.assertAtLeastOnce( match.error( is( "Failed to populate index: [:FIRST(name)]" ), sameInstance( failure ) ) ); }
@Test public void processNextBatchShouldCloseConnectionAndLogOnUnexpectedException() { RuntimeException exception = new RuntimeException( "unexpected exception" ); BoltConnection connection = newConnection(); connection.enqueue( machine -> { throw exception; } ); connection.processNextBatch(); verify( stateMachine ).close(); logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).error( containsString( "Unexpected error detected in bolt session" ), is( exception ) ) ); }
@Test public void shouldLogIOErrors() { // given IOException failure = new IOException(); OutputStream output = mock( OutputStream.class, new ThrowsException( failure ) ); AssertableLogProvider logProvider = new AssertableLogProvider(); ExecutionResultSerializer serializer = getSerializerWith( output, null, logProvider ); // when serializer.finish(); // then logProvider.assertExactly( AssertableLogProvider.inLog( ExecutionResultSerializer.class ).error( is( "Failed to generate JSON output." ), sameInstance( failure ) ) ); }
@Test public void failingJobShouldLogAndStopConnection() throws Throwable { AtomicBoolean stopped = new AtomicBoolean(); String id = UUID.randomUUID().toString(); BoltConnection connection = newConnection( id ); doThrow( new RuntimeException( "some unexpected error" ) ).when( connection ).processNextBatch(); doAnswer( inv -> stopped.getAndSet( true ) ).when( connection ).stop(); boltScheduler.start(); boltScheduler.created( connection ); boltScheduler.enqueued( connection, Jobs.noop() ); Predicates.await( () -> stopped.get(), 1, MINUTES ); assertFalse( boltScheduler.isActive( connection ) ); verify( connection ).processNextBatch(); verify( connection ).stop(); logProvider.assertExactly( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).error( containsString( "Unexpected error during job scheduling for session" ), matchesExceptionMessage( containsString( "some unexpected error" ) ) ) ); }
@Test public void logModuleSetUpError() { Config config = Config.defaults(); IdGeneratorFactory idGeneratorFactory = mock( IdGeneratorFactory.class ); Throwable openStoresError = new RuntimeException( "Can't set up modules" ); doThrow( openStoresError ).when( idGeneratorFactory ).create( any( File.class ), anyLong(), anyBoolean() ); CommunityIdTypeConfigurationProvider idTypeConfigurationProvider = new CommunityIdTypeConfigurationProvider(); AssertableLogProvider logProvider = new AssertableLogProvider(); SimpleLogService logService = new SimpleLogService( logProvider, logProvider ); PageCache pageCache = pageCacheRule.getPageCache( fs.get() ); Dependencies dependencies = new Dependencies(); dependencies.satisfyDependencies( idGeneratorFactory, idTypeConfigurationProvider, config, logService ); NeoStoreDataSource dataSource = dsRule.getDataSource( dir.databaseLayout(), fs.get(), pageCache, dependencies ); try { dataSource.start(); fail( "Exception expected" ); } catch ( Exception e ) { assertEquals( openStoresError, e ); } logProvider.assertAtLeastOnce( inLog( NeoStoreDataSource.class ).warn( equalTo( "Exception occurred while setting up store modules. Attempting to close things down." ), equalTo( openStoresError ) ) ); }
@Test public void shouldLogOnUnexpectedExceptionsAndClosesContext() throws Throwable { // Given ChannelHandlerContext context = channelHandlerContextMock(); AssertableLogProvider logging = new AssertableLogProvider(); TransportSelectionHandler handler = new TransportSelectionHandler( null, null, false, false, logging, null ); // When Throwable cause = new Throwable( "Oh no!" ); handler.exceptionCaught( context, cause ); // Then verify( context ).close(); logging.assertExactly( inLog( TransportSelectionHandler.class ) .error( equalTo( "Fatal error occurred when initialising pipeline: " + context.channel() ), sameInstance( cause ) ) ); }
@Test public void shouldLogDatabasePanicEvent() { // GIVEN AssertableLogProvider logProvider = new AssertableLogProvider(); DatabaseHealth databaseHealth = new DatabaseHealth( mock( DatabasePanicEventGenerator.class ), logProvider.getLog( DatabaseHealth.class ) ); databaseHealth.healed(); // WHEN String message = "Listen everybody... panic!"; Exception exception = new Exception( message ); databaseHealth.panic( exception ); // THEN logProvider.assertAtLeastOnce( inLog( DatabaseHealth.class ).error( is( "Database panic: The database has encountered a critical error, " + "and needs to be restarted. Please see database logs for more details." ), sameInstance( exception ) ) ); }
@Test public void processNextBatchShouldCloseConnectionOnFatalAuthenticationError() { BoltConnection connection = newConnection(); connection.enqueue( machine -> { throw new BoltConnectionAuthFatality( "auth failure", new RuntimeException( "inner error" ) ); } ); connection.processNextBatch(); verify( stateMachine ).close(); logProvider.assertNone( AssertableLogProvider.inLog( containsString( BoltServer.class.getPackage().getName() ) ).warn( any( String.class ) ) ); }
@Test public void shouldRejectIfHttp() { // Given BoltProtocol protocol = newBoltProtocol( 1 ); BoltProtocolFactory handlerFactory = newProtocolFactory( 1, protocol ); EmbeddedChannel channel = new EmbeddedChannel( new ProtocolHandshaker( handlerFactory, boltChannel, logProvider, false, true ) ); // When FullHttpRequest request = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.POST, "http://hello_world:10000" ); request.headers().setInt( HttpHeaderNames.CONTENT_LENGTH, 0 ); channel.writeInbound( request ); // Then assertEquals( 0, channel.outboundMessages().size() ); assertFalse( channel.isActive() ); verify( protocol, never() ).install(); logProvider.assertExactly( AssertableLogProvider.inLog( ProtocolHandshaker.class ).warn( "Unsupported connection type: 'HTTP'. Bolt protocol only operates over a TCP connection or WebSocket." ) ); }