Skip to content

Commit 6f12e4c

Browse files
authored
Add Temporary:FullSummary support in Testkit backend (#1109) (#1114)
1 parent ad1a312 commit 6f12e4c

File tree

4 files changed

+292
-17
lines changed

4 files changed

+292
-17
lines changed

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/GetFeatures.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ public class GetFeatures implements TestkitRequest
5757
"Temporary:DriverMaxConnectionPoolSize",
5858
"Temporary:ConnectionAcquisitionTimeout",
5959
"Temporary:GetConnectionPoolMetrics",
60-
"Temporary:CypherPathAndRelationship"
60+
"Temporary:CypherPathAndRelationship",
61+
"Temporary:FullSummary"
6162
) );
6263

6364
private static final Set<String> SYNC_FEATURES = new HashSet<>( Arrays.asList(

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,22 @@
2626
import neo4j.org.testkit.backend.messages.responses.TestkitResponse;
2727
import reactor.core.publisher.Mono;
2828

29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
2932
import java.util.concurrent.CompletionStage;
33+
import java.util.concurrent.TimeUnit;
34+
import java.util.function.Function;
35+
import java.util.stream.Collectors;
3036

37+
import org.neo4j.driver.Query;
3138
import org.neo4j.driver.Result;
3239
import org.neo4j.driver.exceptions.NoSuchRecordException;
40+
import org.neo4j.driver.summary.InputPosition;
41+
import org.neo4j.driver.summary.Plan;
42+
import org.neo4j.driver.summary.ProfiledPlan;
43+
import org.neo4j.driver.summary.QueryType;
44+
import org.neo4j.driver.summary.SummaryCounters;
3345

3446
@Setter
3547
@Getter
@@ -74,8 +86,50 @@ private Summary createResponse( org.neo4j.driver.summary.ResultSummary summary )
7486
.protocolVersion( summary.server().protocolVersion() )
7587
.agent( summary.server().agent() )
7688
.build();
89+
SummaryCounters summaryCounters = summary.counters();
90+
Summary.Counters counters = Summary.Counters.builder()
91+
.constraintsAdded( summaryCounters.constraintsAdded() )
92+
.constraintsRemoved( summaryCounters.constraintsRemoved() )
93+
.containsSystemUpdates( summaryCounters.containsSystemUpdates() )
94+
.containsUpdates( summaryCounters.containsUpdates() )
95+
.indexesAdded( summaryCounters.indexesAdded() )
96+
.indexesRemoved( summaryCounters.indexesRemoved() )
97+
.labelsAdded( summaryCounters.labelsAdded() )
98+
.labelsRemoved( summaryCounters.labelsRemoved() )
99+
.nodesCreated( summaryCounters.nodesCreated() )
100+
.nodesDeleted( summaryCounters.nodesDeleted() )
101+
.propertiesSet( summaryCounters.propertiesSet() )
102+
.relationshipsCreated( summaryCounters.relationshipsCreated() )
103+
.relationshipsDeleted( summaryCounters.relationshipsDeleted() )
104+
.systemUpdates( summaryCounters.systemUpdates() )
105+
.build();
106+
Query summaryQuery = summary.query();
107+
Summary.Query query = Summary.Query.builder()
108+
.text( summaryQuery.text() )
109+
.parameters( summaryQuery.parameters().asMap( Function.identity(), null ) )
110+
.build();
111+
List<Summary.Notification> notifications = summary.notifications().stream()
112+
.map( s -> Summary.Notification.builder()
113+
.code( s.code() )
114+
.title( s.title() )
115+
.description( s.description() )
116+
.position( toInputPosition( s.position() ) )
117+
.severity( s.severity() )
118+
.build() )
119+
.collect( Collectors.toList() );
77120
Summary.SummaryBody data = Summary.SummaryBody.builder()
78121
.serverInfo( serverInfo )
122+
.counters( counters )
123+
.query( query )
124+
.database( summary.database().name() )
125+
.notifications( notifications )
126+
.plan( toPlan( summary.plan() ) )
127+
.profile( toProfile( summary.profile() ) )
128+
.queryType( toQueryType( summary.queryType() ) )
129+
.resultAvailableAfter( summary.resultAvailableAfter( TimeUnit.MILLISECONDS ) == -1
130+
? null : summary.resultAvailableAfter( TimeUnit.MILLISECONDS ) )
131+
.resultConsumedAfter( summary.resultConsumedAfter( TimeUnit.MILLISECONDS ) == -1
132+
? null : summary.resultConsumedAfter( TimeUnit.MILLISECONDS ) )
79133
.build();
80134
return Summary.builder()
81135
.data( data )
@@ -88,4 +142,91 @@ public static class ResultConsumeBody
88142
{
89143
private String resultId;
90144
}
145+
146+
private static Summary.InputPosition toInputPosition( InputPosition position )
147+
{
148+
if ( position == null )
149+
{
150+
return null;
151+
}
152+
return Summary.InputPosition.builder()
153+
.offset( position.offset() )
154+
.line( position.line() )
155+
.column( position.column() )
156+
.build();
157+
}
158+
159+
private static Summary.Plan toPlan( Plan plan )
160+
{
161+
if ( plan == null )
162+
{
163+
return null;
164+
}
165+
Map<String,Object> args = new HashMap<>();
166+
plan.arguments().forEach( ( key, value ) -> args.put( key, value.asObject() ) );
167+
return Summary.Plan.builder()
168+
.operatorType( plan.operatorType() )
169+
.args( args )
170+
.identifiers( plan.identifiers() )
171+
.children( plan.children().stream()
172+
.map( ResultConsume::toPlan )
173+
.collect( Collectors.toList() ) )
174+
.build();
175+
}
176+
177+
private static Summary.Profile toProfile( ProfiledPlan plan )
178+
{
179+
if ( plan == null )
180+
{
181+
return null;
182+
}
183+
Map<String,Object> args = new HashMap<>();
184+
plan.arguments().forEach( ( key, value ) -> args.put( key, value.asObject() ) );
185+
return Summary.Profile.builder()
186+
.operatorType( plan.operatorType() )
187+
.args( args )
188+
.identifiers( plan.identifiers() )
189+
.dbHits( plan.dbHits() )
190+
.rows( plan.records() )
191+
.hasPageCacheStats( plan.hasPageCacheStats() )
192+
.pageCacheHits( plan.pageCacheHits() )
193+
.pageCacheMisses( plan.pageCacheMisses() )
194+
.pageCacheHitRatio( plan.pageCacheHitRatio() )
195+
.time( plan.time() )
196+
.children( plan.children().stream()
197+
.map( ResultConsume::toProfile )
198+
.collect( Collectors.toList() ) )
199+
.build();
200+
}
201+
202+
private static String toQueryType( QueryType type )
203+
{
204+
if ( type == null )
205+
{
206+
return null;
207+
}
208+
209+
String typeStr;
210+
if ( type == QueryType.READ_ONLY )
211+
{
212+
typeStr = "r";
213+
}
214+
else if ( type == QueryType.READ_WRITE )
215+
{
216+
typeStr = "rw";
217+
}
218+
else if ( type == QueryType.WRITE_ONLY )
219+
{
220+
typeStr = "w";
221+
}
222+
else if ( type == QueryType.SCHEMA_WRITE )
223+
{
224+
typeStr = "s";
225+
}
226+
else
227+
{
228+
throw new IllegalStateException( "Unexpected query type" );
229+
}
230+
return typeStr;
231+
}
91232
}

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/StartTest.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,29 @@
3535
@Getter
3636
public class StartTest implements TestkitRequest
3737
{
38+
private static final Map<String,String> COMMON_SKIP_PATTERN_TO_REASON = new HashMap<>();
3839
private static final Map<String,String> ASYNC_SKIP_PATTERN_TO_REASON = new HashMap<>();
3940
private static final Map<String,String> REACTIVE_SKIP_PATTERN_TO_REASON = new HashMap<>();
4041

4142
static
4243
{
44+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_invalid_query_type$", "Does not report type exception" );
45+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_no_notifications$", "An empty list is returned when there are no notifications" );
46+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_no_notification_info$", "An empty list is returned when there are no notifications" );
47+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_notifications_without_position$", "Null value is provided when position is absent" );
48+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_multiple_notifications$", "Null value is provided when position is absent" );
49+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_not_contains_system_updates$", "Contains updates because value is over zero" );
50+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_not_contains_updates$", "Contains updates because value is over zero" );
51+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_profile$", "Missing stats are reported with 0 value" );
52+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_server_info$", "Address includes domain name" );
53+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_contains_system_updates$", "Does not contain updates because value is zero" );
54+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_partial_summary_contains_updates$", "Does not contain updates because value is zero" );
55+
COMMON_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_supports_multi_db$", "Database is None" );
56+
57+
ASYNC_SKIP_PATTERN_TO_REASON.putAll( COMMON_SKIP_PATTERN_TO_REASON );
4358
ASYNC_SKIP_PATTERN_TO_REASON.put( "^.*\\.test_should_reject_server_using_verify_connectivity_bolt_3x0$", "Does not error as expected" );
4459

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

8197
@Override
8298
public CompletionStage<TestkitResponse> processAsync( TestkitState testkitState )
8399
{
84-
TestkitResponse testkitResponse = ASYNC_SKIP_PATTERN_TO_REASON
85-
.entrySet()
86-
.stream()
87-
.filter( entry -> data.getTestName().matches( entry.getKey() ) )
88-
.findFirst()
89-
.map( entry -> (TestkitResponse) SkipTest.builder()
90-
.data( SkipTest.SkipTestBody.builder()
91-
.reason( entry.getValue() )
92-
.build() )
93-
.build() )
94-
.orElseGet( () -> RunTest.builder().build() );
95-
100+
TestkitResponse testkitResponse = createResponse( ASYNC_SKIP_PATTERN_TO_REASON );
96101
return CompletableFuture.completedFuture( testkitResponse );
97102
}
98103

99104
@Override
100105
public Mono<TestkitResponse> processRx( TestkitState testkitState )
101106
{
102-
TestkitResponse testkitResponse = REACTIVE_SKIP_PATTERN_TO_REASON
107+
TestkitResponse testkitResponse = createResponse( REACTIVE_SKIP_PATTERN_TO_REASON );
108+
return Mono.fromCompletionStage( CompletableFuture.completedFuture( testkitResponse ) );
109+
}
110+
111+
private TestkitResponse createResponse( Map<String,String> skipPatternToReason )
112+
{
113+
return skipPatternToReason
103114
.entrySet()
104115
.stream()
105116
.filter( entry -> data.getTestName().matches( entry.getKey() ) )
@@ -110,8 +121,6 @@ public Mono<TestkitResponse> processRx( TestkitState testkitState )
110121
.build() )
111122
.build() )
112123
.orElseGet( () -> RunTest.builder().build() );
113-
114-
return Mono.fromCompletionStage( CompletableFuture.completedFuture( testkitResponse ) );
115124
}
116125

117126
@Setter

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020

2121
import lombok.Builder;
2222
import lombok.Getter;
23+
import lombok.experimental.SuperBuilder;
24+
25+
import java.util.List;
26+
import java.util.Map;
27+
28+
import org.neo4j.driver.Value;
2329

2430
@Getter
2531
@Builder
@@ -38,6 +44,24 @@ public String testkitName()
3844
public static class SummaryBody
3945
{
4046
private ServerInfo serverInfo;
47+
48+
private Counters counters;
49+
50+
private Query query;
51+
52+
private String database;
53+
54+
private List<Notification> notifications;
55+
56+
private Plan plan;
57+
58+
private Profile profile;
59+
60+
private String queryType;
61+
62+
private Long resultAvailableAfter;
63+
64+
private Long resultConsumedAfter;
4165
}
4266

4367
@Getter
@@ -50,4 +74,104 @@ public static class ServerInfo
5074

5175
private String agent;
5276
}
77+
78+
@Getter
79+
@Builder
80+
public static class Counters
81+
{
82+
private int constraintsAdded;
83+
84+
private int constraintsRemoved;
85+
86+
private boolean containsSystemUpdates;
87+
88+
private boolean containsUpdates;
89+
90+
private int indexesAdded;
91+
92+
private int indexesRemoved;
93+
94+
private int labelsAdded;
95+
96+
private int labelsRemoved;
97+
98+
private int nodesCreated;
99+
100+
private int nodesDeleted;
101+
102+
private int propertiesSet;
103+
104+
private int relationshipsCreated;
105+
106+
private int relationshipsDeleted;
107+
108+
private int systemUpdates;
109+
}
110+
111+
@Getter
112+
@Builder
113+
public static class Query
114+
{
115+
private String text;
116+
117+
private Map<String,Value> parameters;
118+
}
119+
120+
@Getter
121+
@Builder
122+
public static class Notification
123+
{
124+
private String code;
125+
126+
private String title;
127+
128+
private String description;
129+
130+
private InputPosition position;
131+
132+
private String severity;
133+
}
134+
135+
@Getter
136+
@Builder
137+
public static class InputPosition
138+
{
139+
private int offset;
140+
141+
private int line;
142+
143+
private int column;
144+
}
145+
146+
@Getter
147+
@SuperBuilder
148+
public static class Plan
149+
{
150+
private String operatorType;
151+
152+
private Map<String,Object> args;
153+
154+
private List<String> identifiers;
155+
156+
private List<Plan> children;
157+
}
158+
159+
@Getter
160+
@SuperBuilder
161+
public static class Profile extends Plan
162+
{
163+
private long dbHits;
164+
165+
private long rows;
166+
167+
private boolean hasPageCacheStats;
168+
169+
private long pageCacheHits;
170+
171+
private long pageCacheMisses;
172+
173+
private double pageCacheHitRatio;
174+
175+
private long time;
176+
}
53177
}

0 commit comments

Comments
 (0)