Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Temporary:FullSummary support in Testkit backend #1114

Merged
merged 1 commit into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public class GetFeatures implements TestkitRequest
"Temporary:DriverMaxConnectionPoolSize",
"Temporary:ConnectionAcquisitionTimeout",
"Temporary:GetConnectionPoolMetrics",
"Temporary:CypherPathAndRelationship"
"Temporary:CypherPathAndRelationship",
"Temporary:FullSummary"
) );

private static final Set<String> SYNC_FEATURES = new HashSet<>( Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,22 @@
import neo4j.org.testkit.backend.messages.responses.TestkitResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.neo4j.driver.Query;
import org.neo4j.driver.Result;
import org.neo4j.driver.exceptions.NoSuchRecordException;
import org.neo4j.driver.summary.InputPosition;
import org.neo4j.driver.summary.Plan;
import org.neo4j.driver.summary.ProfiledPlan;
import org.neo4j.driver.summary.QueryType;
import org.neo4j.driver.summary.SummaryCounters;

@Setter
@Getter
Expand Down Expand Up @@ -74,8 +86,50 @@ private Summary createResponse( org.neo4j.driver.summary.ResultSummary summary )
.protocolVersion( summary.server().protocolVersion() )
.agent( summary.server().agent() )
.build();
SummaryCounters summaryCounters = summary.counters();
Summary.Counters counters = Summary.Counters.builder()
.constraintsAdded( summaryCounters.constraintsAdded() )
.constraintsRemoved( summaryCounters.constraintsRemoved() )
.containsSystemUpdates( summaryCounters.containsSystemUpdates() )
.containsUpdates( summaryCounters.containsUpdates() )
.indexesAdded( summaryCounters.indexesAdded() )
.indexesRemoved( summaryCounters.indexesRemoved() )
.labelsAdded( summaryCounters.labelsAdded() )
.labelsRemoved( summaryCounters.labelsRemoved() )
.nodesCreated( summaryCounters.nodesCreated() )
.nodesDeleted( summaryCounters.nodesDeleted() )
.propertiesSet( summaryCounters.propertiesSet() )
.relationshipsCreated( summaryCounters.relationshipsCreated() )
.relationshipsDeleted( summaryCounters.relationshipsDeleted() )
.systemUpdates( summaryCounters.systemUpdates() )
.build();
Query summaryQuery = summary.query();
Summary.Query query = Summary.Query.builder()
.text( summaryQuery.text() )
.parameters( summaryQuery.parameters().asMap( Function.identity(), null ) )
.build();
List<Summary.Notification> notifications = summary.notifications().stream()
.map( s -> Summary.Notification.builder()
.code( s.code() )
.title( s.title() )
.description( s.description() )
.position( toInputPosition( s.position() ) )
.severity( s.severity() )
.build() )
.collect( Collectors.toList() );
Summary.SummaryBody data = Summary.SummaryBody.builder()
.serverInfo( serverInfo )
.counters( counters )
.query( query )
.database( summary.database().name() )
.notifications( notifications )
.plan( toPlan( summary.plan() ) )
.profile( toProfile( summary.profile() ) )
.queryType( toQueryType( summary.queryType() ) )
.resultAvailableAfter( summary.resultAvailableAfter( TimeUnit.MILLISECONDS ) == -1
? null : summary.resultAvailableAfter( TimeUnit.MILLISECONDS ) )
.resultConsumedAfter( summary.resultConsumedAfter( TimeUnit.MILLISECONDS ) == -1
? null : summary.resultConsumedAfter( TimeUnit.MILLISECONDS ) )
.build();
return Summary.builder()
.data( data )
Expand All @@ -88,4 +142,91 @@ public static class ResultConsumeBody
{
private String resultId;
}

private static Summary.InputPosition toInputPosition( InputPosition position )
{
if ( position == null )
{
return null;
}
return Summary.InputPosition.builder()
.offset( position.offset() )
.line( position.line() )
.column( position.column() )
.build();
}

private static Summary.Plan toPlan( Plan plan )
{
if ( plan == null )
{
return null;
}
Map<String,Object> args = new HashMap<>();
plan.arguments().forEach( ( key, value ) -> args.put( key, value.asObject() ) );
return Summary.Plan.builder()
.operatorType( plan.operatorType() )
.args( args )
.identifiers( plan.identifiers() )
.children( plan.children().stream()
.map( ResultConsume::toPlan )
.collect( Collectors.toList() ) )
.build();
}

private static Summary.Profile toProfile( ProfiledPlan plan )
{
if ( plan == null )
{
return null;
}
Map<String,Object> args = new HashMap<>();
plan.arguments().forEach( ( key, value ) -> args.put( key, value.asObject() ) );
return Summary.Profile.builder()
.operatorType( plan.operatorType() )
.args( args )
.identifiers( plan.identifiers() )
.dbHits( plan.dbHits() )
.rows( plan.records() )
.hasPageCacheStats( plan.hasPageCacheStats() )
.pageCacheHits( plan.pageCacheHits() )
.pageCacheMisses( plan.pageCacheMisses() )
.pageCacheHitRatio( plan.pageCacheHitRatio() )
.time( plan.time() )
.children( plan.children().stream()
.map( ResultConsume::toProfile )
.collect( Collectors.toList() ) )
.build();
}

private static String toQueryType( QueryType type )
{
if ( type == null )
{
return null;
}

String typeStr;
if ( type == QueryType.READ_ONLY )
{
typeStr = "r";
}
else if ( type == QueryType.READ_WRITE )
{
typeStr = "rw";
}
else if ( type == QueryType.WRITE_ONLY )
{
typeStr = "w";
}
else if ( type == QueryType.SCHEMA_WRITE )
{
typeStr = "s";
}
else
{
throw new IllegalStateException( "Unexpected query type" );
}
return typeStr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,29 @@
@Getter
public class StartTest implements TestkitRequest
{
private static final Map<String,String> COMMON_SKIP_PATTERN_TO_REASON = new HashMap<>();
private static final Map<String,String> ASYNC_SKIP_PATTERN_TO_REASON = new HashMap<>();
private static final Map<String,String> REACTIVE_SKIP_PATTERN_TO_REASON = new HashMap<>();

static
{
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_invalid_query_type$", "Does not report type exception" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_no_notifications$", "An empty list is returned when there are no notifications" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_no_notification_info$", "An empty list is returned when there are no notifications" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_notifications_without_position$", "Null value is provided when position is absent" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_multiple_notifications$", "Null value is provided when position is absent" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_not_contains_system_updates$", "Contains updates because value is over zero" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_not_contains_updates$", "Contains updates because value is over zero" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_profile$", "Missing stats are reported with 0 value" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_server_info$", "Address includes domain name" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_contains_system_updates$", "Does not contain updates because value is zero" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_contains_updates$", "Does not contain updates because value is zero" );
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_supports_multi_db$", "Database is None" );

ASYNC_SKIP_PATTERN_TO_REASON.putAll( COMMON_SKIP_PATTERN_TO_REASON );
ASYNC_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_should_reject_server_using_verify_connectivity_bolt_3x0$", "Does not error as expected" );

REACTIVE_SKIP_PATTERN_TO_REASON.putAll( COMMON_SKIP_PATTERN_TO_REASON );
// Current limitations (require further investigation or bug fixing)
String skipMessage = "Does not report RUN FAILURE";
REACTIVE_SKIP_PATTERN_TO_REASON.put( "^.*\\.Routing[^.]+\\.test_should_write_successfully_on_leader_switch_using_tx_function$", skipMessage );
Expand Down Expand Up @@ -75,31 +91,26 @@ public class StartTest implements TestkitRequest
@Override
public TestkitResponse process( TestkitState testkitState )
{
return RunTest.builder().build();
return createResponse( COMMON_SKIP_PATTERN_TO_REASON );
}

@Override
public CompletionStage<TestkitResponse> processAsync( TestkitState testkitState )
{
TestkitResponse testkitResponse = ASYNC_SKIP_PATTERN_TO_REASON
.entrySet()
.stream()
.filter( entry -> data.getTestName().matches( entry.getKey() ) )
.findFirst()
.map( entry -> (TestkitResponse) SkipTest.builder()
.data( SkipTest.SkipTestBody.builder()
.reason( entry.getValue() )
.build() )
.build() )
.orElseGet( () -> RunTest.builder().build() );

TestkitResponse testkitResponse = createResponse( ASYNC_SKIP_PATTERN_TO_REASON );
return CompletableFuture.completedFuture( testkitResponse );
}

@Override
public Mono<TestkitResponse> processRx( TestkitState testkitState )
{
TestkitResponse testkitResponse = REACTIVE_SKIP_PATTERN_TO_REASON
TestkitResponse testkitResponse = createResponse( REACTIVE_SKIP_PATTERN_TO_REASON );
return Mono.fromCompletionStage( CompletableFuture.completedFuture( testkitResponse ) );
}

private TestkitResponse createResponse( Map<String,String> skipPatternToReason )
{
return skipPatternToReason
.entrySet()
.stream()
.filter( entry -> data.getTestName().matches( entry.getKey() ) )
Expand All @@ -110,8 +121,6 @@ public Mono<TestkitResponse> processRx( TestkitState testkitState )
.build() )
.build() )
.orElseGet( () -> RunTest.builder().build() );

return Mono.fromCompletionStage( CompletableFuture.completedFuture( testkitResponse ) );
}

@Setter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@

import lombok.Builder;
import lombok.Getter;
import lombok.experimental.SuperBuilder;

import java.util.List;
import java.util.Map;

import org.neo4j.driver.Value;

@Getter
@Builder
Expand All @@ -38,6 +44,24 @@ public String testkitName()
public static class SummaryBody
{
private ServerInfo serverInfo;

private Counters counters;

private Query query;

private String database;

private List<Notification> notifications;

private Plan plan;

private Profile profile;

private String queryType;

private Long resultAvailableAfter;

private Long resultConsumedAfter;
}

@Getter
Expand All @@ -50,4 +74,104 @@ public static class ServerInfo

private String agent;
}

@Getter
@Builder
public static class Counters
{
private int constraintsAdded;

private int constraintsRemoved;

private boolean containsSystemUpdates;

private boolean containsUpdates;

private int indexesAdded;

private int indexesRemoved;

private int labelsAdded;

private int labelsRemoved;

private int nodesCreated;

private int nodesDeleted;

private int propertiesSet;

private int relationshipsCreated;

private int relationshipsDeleted;

private int systemUpdates;
}

@Getter
@Builder
public static class Query
{
private String text;

private Map<String,Value> parameters;
}

@Getter
@Builder
public static class Notification
{
private String code;

private String title;

private String description;

private InputPosition position;

private String severity;
}

@Getter
@Builder
public static class InputPosition
{
private int offset;

private int line;

private int column;
}

@Getter
@SuperBuilder
public static class Plan
{
private String operatorType;

private Map<String,Object> args;

private List<String> identifiers;

private List<Plan> children;
}

@Getter
@SuperBuilder
public static class Profile extends Plan
{
private long dbHits;

private long rows;

private boolean hasPageCacheStats;

private long pageCacheHits;

private long pageCacheMisses;

private double pageCacheHitRatio;

private long time;
}
}