diff --git a/testkit-backend/pom.xml b/testkit-backend/pom.xml index 28d40371fe..df538d1e08 100644 --- a/testkit-backend/pom.xml +++ b/testkit-backend/pom.xml @@ -32,6 +32,24 @@ com.fasterxml.jackson.core jackson-databind + + org.projectlombok + lombok + 1.18.12 + provided + + + + + org.hamcrest + hamcrest-junit + test + + + org.junit.jupiter + junit-jupiter + test + @@ -49,7 +67,7 @@ - Runner + neo4j.org.testkit.backend.Runner testkit-backend diff --git a/testkit-backend/src/main/java/CommandProcessor.java b/testkit-backend/src/main/java/CommandProcessor.java deleted file mode 100644 index a594d1e764..0000000000 --- a/testkit-backend/src/main/java/CommandProcessor.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (c) 2002-2020 "Neo4j," - * Neo4j Sweden AB [http://neo4j.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. - */ -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.Bookmark; -import org.neo4j.driver.Driver; -import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Query; -import org.neo4j.driver.Record; -import org.neo4j.driver.Result; -import org.neo4j.driver.Session; -import org.neo4j.driver.SessionConfig; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.TransactionWork; -import org.neo4j.driver.exceptions.Neo4jException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.internal.InternalBookmark; - -public class CommandProcessor -{ - private final Map drivers = new HashMap<>(); - private final Map sessionStates = new HashMap<>(); - private final Map results = new HashMap<>(); - private final Map transactions = new HashMap<>(); - private final Map errors = new HashMap<>(); - - private int idGenerator = 0; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - private final BufferedReader in; - private final BufferedWriter out; - - public CommandProcessor(BufferedReader in, BufferedWriter out) { - this.in = in; - this.out = out; - } - - private String readLine() { - try { - return this.in.readLine(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private void write(String s) { - try { - this.out.write(s); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - // Logs to frontend - private void log(String s) { - try { - this.out.write(s + "\n"); - this.out.flush(); - } catch (IOException e) { } - System.out.println(s); - } - - private void flush() { - try { - this.out.flush(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - // Reads one request and writes the response. Returns false when not able to read anymore. - public boolean process() { - boolean inRequest = false; - StringBuilder request = new StringBuilder(); - - log("Waiting for request"); - - while (true) { - String currentLine = readLine(); - // End of stream - if ( currentLine == null) { - return false; - } - - if ( currentLine.equals( "#request begin" )) - { - inRequest = true; - } else if (currentLine.equals( "#request end" )) - { - if ( !inRequest ) - { - throw new RuntimeException( "Request end not expected" ); - } - try - { - processRequest( request.toString()); - } catch ( Exception e ){ - if (e instanceof Neo4jException) { - // Error to track - String id = newId(); - errors.put(id, (Neo4jException)e); - writeResponse(Testkit.wrap("DriverError", Testkit.id(id))); - System.out.println("Neo4jException: " + e); - } else { - // Unknown error, interpret this as a backend error. - // Report to frontend and rethrow, note that if socket been - // closed the writing will throw itself... - writeResponse(Testkit.wrap("BackendError", Testkit.msg(e.toString()))); - // This won't print if there was an IO exception since line above will rethrow - e.printStackTrace(); - throw e; - } - } - return true; - } else - { - if ( !inRequest ) - { - throw new RuntimeException( "Command Received whilst not in request"); - } - request.append( currentLine ); - } - } - } - - public void processRequest( String request) - { - System.out.println( "request = " + request + ", in = " + in + ", out = " + out ); - - JsonNode jsonRequest; - try { - jsonRequest = objectMapper.readTree( request ); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - String requestType = jsonRequest.get( "name" ).asText(); - JsonNode requestData = jsonRequest.get("data"); - log("Received request: " + requestType); - - if (requestType.equals( "NewDriver" )) - { - String id = newId(); - String response = Testkit.wrap("Driver", Testkit.id(id)); - - String uri = requestData.get("uri").asText(); - JsonNode requestAuth = requestData.get("authorizationToken").get("data"); - AuthToken authToken; - switch (requestAuth.get("scheme").asText()) { - case "basic": - authToken = AuthTokens.basic(requestAuth.get("principal").asText(), requestAuth.get("credentials").asText(), requestAuth.get("realm").asText()); - break; - default: - writeResponse(Testkit.wrap("BackendError", Testkit.msg("Unsupported auth scheme"))); - return; - } - - drivers.putIfAbsent( id, GraphDatabase.driver(uri, authToken)); - writeResponse( response); - } else if ( requestType.equals( "NewSession" )) - { - String id = requestData.get( "driverId" ).asText(); - Driver driver = drivers.get( id ); - AccessMode accessMode = requestData.get( "accessMode" ).asText().equals( "r" ) ? AccessMode.READ : AccessMode.WRITE; - //Bookmark bookmark = InternalBookmark.parse( requestData.get("bookmarks").asText() ); - Session session = driver.session( SessionConfig.builder() - .withDefaultAccessMode( accessMode ).build()); - String newId = newId(); - sessionStates.put( newId, new SessionState(session) ); - String response = Testkit.wrap("Session", Testkit.id(newId)); - - writeResponse( response); - } else if ( requestType.equals( "SessionRun" )) - { - String id = requestData.get( "sessionId" ).asText(); - Session session = sessionStates.get( id ).session; - JsonNode jsonParams = (JsonNode)requestData.get("params"); - Map params = new HashMap(); - Iterator> iter = jsonParams.fields(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - params.put(entry.getKey(), TestkitTypes.toDriver(entry.getValue())); - } - Query query = new Query(requestData.get("cypher").asText(), params); - Result result = session.run(query); - String newId = newId(); - - results.put( newId, result ); - String response = Testkit.wrap("Result", Testkit.id(newId)); - writeResponse( response); - } else if ( requestType.equals( "TransactionRun" )) - { - String txId = requestData.get("txId").asText(); - String cypher = requestData.get("cypher").asText(); - Transaction tx = transactions.get(txId); - Result result = tx.run(cypher); - String newId = newId(); - results.put(newId, result); - writeResponse(Testkit.wrap("Result", Testkit.id(newId))); - } else if ( requestType.equals( "RetryablePositive" )) - { - String id = requestData.get( "sessionId" ).asText(); - SessionState sessState = sessionStates.get( id ); - sessState.retryableState = 1; - } else if ( requestType.equals( "RetryableNegative" )) - { - String id = requestData.get( "sessionId" ).asText(); - SessionState sessState = sessionStates.get( id ); - sessState.retryableState = -1; - sessState.retryableErrorId = requestData.get("errorId").asText(); - } else if ( requestType.equals( "SessionReadTransaction" )) - { - String id = requestData.get( "sessionId" ).asText(); - SessionState sessState = sessionStates.get( id ); - sessState.session.readTransaction((Transaction tx) -> { - // Reset session state - sessState.retryableState = 0; - // Stash this transaction as there will be commands using it - String txId = newId(); - transactions.put(txId, tx); - // Instruct testkit client to send it's commands within the transaction - try { - writeResponse(Testkit.wrap("RetryableTry", Testkit.id(txId))); - } catch ( Exception e) { - e.printStackTrace(); - } - while ( true ) { - // Process commands as usual but blocking in here - process(); - // Check if state changed on session - switch (sessState.retryableState) { - case 0: - // Nothing happened to session state while processing command - break; - case 1: - // Client is happy to commit - return 0; - case -1: - // Client wants to rollback - if (sessState.retryableErrorId != "") { - Neo4jException err = errors.get(sessState.retryableErrorId); - throw err; - } else { - throw new RuntimeException("Error from client in retryable tx"); - } - } - } - }); - // Assume that exception is thrown by readTransaction when retry fails so this is - // never reached. - writeResponse(Testkit.wrap("RetryableDone", "{}")); - } else if ( requestType.equals( "ResultNext" )) - { - String id = requestData.get( "resultId" ).asText(); - Result result = results.get( id ); - - // TODO: Should we call next here and catch the exception instead? - if (!result.hasNext()) { - writeResponse(Testkit.wrap("NullRecord", "{}")); - return; - } - - Record record = result.next(); - String newId = newId(); - - results.put( newId, result ); - String response = Testkit.wrap("Record", Testkit.values(TestkitTypes.fromRecord(record))); - - writeResponse( response); - } else if ( requestType.equals( "SessionClose" )) - { - String id = requestData.get( "sessionId" ).asText(); - Session session = sessionStates.get( id ).session; - session.close(); - sessionStates.remove( id ); - - String response = Testkit.wrap("Session", Testkit.id(id)); - - writeResponse( response); - } - else if ( requestType.equals( "DriverClose" )) - { - String id = requestData.get( "driverId" ).asText(); - Driver driver = drivers.get( id ); - driver.close(); - drivers.remove( id ); - - String response = Testkit.wrap("Driver", Testkit.id(id)); - - writeResponse( response); - } - else - { - throw new RuntimeException("Request " + requestType + " not handled"); - } - } - - private void writeResponse(String response) - { - System.out.println( "response = " + response ); - - write( "#response begin\n" ); - write( response + "\n" ); - write( "#response end\n" ); - flush(); - } - - private String newId() - { - int nextNumber = idGenerator++; - return String.valueOf( nextNumber ); - } - - private SessionState getSessionState(JsonNode req) { - String id = req.get( "data" ).get( "sessionId" ).asText(); - return sessionStates.get( id ); - } -} diff --git a/testkit-backend/src/main/java/TestkitTypes.java b/testkit-backend/src/main/java/TestkitTypes.java deleted file mode 100644 index 479f3e4069..0000000000 --- a/testkit-backend/src/main/java/TestkitTypes.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2002-2020 "Neo4j," - * Neo4j Sweden AB [http://neo4j.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. - */ -import com.fasterxml.jackson.databind.JsonNode; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.lang.reflect.Array; - -import org.neo4j.driver.Record; -import org.neo4j.driver.Value; -import org.neo4j.driver.types.Node; -import org.neo4j.driver.internal.value.IntegerValue; -import org.neo4j.driver.internal.value.ListValue; -import org.neo4j.driver.internal.value.MapValue; -import org.neo4j.driver.internal.value.NodeValue; -import org.neo4j.driver.internal.value.NullValue; -import org.neo4j.driver.internal.value.StringValue; - - -public class TestkitTypes -{ - - public static String fromRecord( Record record ) - { - String v = ""; - - for ( Value value : record.values() ) - { - if (v != "") { - v += ","; - } - v += toTestkitType(value); - } - - return v; - } - - - private static String toTestkitType( Object obj ) - { - if ( obj instanceof Value) { - Value value = (Value) obj; - if ( value instanceof NullValue ) - { - return "{\"data\" : null, \"name\" : \"CypherNull\"}"; - } else if ( value instanceof IntegerValue ) - { - return toTestkitType(value.asInt()); - } else if ( value instanceof StringValue ) - { - return toTestkitType(value.asString()); - } else if ( value instanceof ListValue ) - { - return toTestkitType(((ListValue)value).asList()); - } else if ( value instanceof NodeValue ) - { - Node node = ((NodeValue)value).asNode(); - String v = String.format("{\"id\":%s,\"labels\":%s,\"props\":%s}", - toTestkitType(node.id()), toTestkitType(node.labels()), toTestkitType(node.asMap())); - return Testkit.wrap("Node", v); - } else if ( value instanceof MapValue ) - { - return "{\"data\" : {\"value\": {}, \"name\" : \"CypherMap\"}"; - } - } else { - if (obj instanceof Integer || obj instanceof Long) { - return Testkit.wrap("CypherInt", Testkit.value(obj.toString())); - } else if (obj instanceof String) { - return Testkit.wrap("CypherString", Testkit.value("\""+obj.toString()+"\"")); - } else if (obj instanceof List) { - List list = (List)obj; - String v = ""; - for (int i = 0; i < list.size(); i++) { - if (i > 0) { - v += ","; - } - v += toTestkitType(list.get(i)); - } - return Testkit.wrap("CypherList", Testkit.value("["+v+"]")); - } else if (obj instanceof Map) { - Map map = (Map)obj; - String v = ""; - - for (Map.Entry entry : map.entrySet()) { - if (v != "") { - v += ","; - } - v += String.format("\"%s\":%s", entry.getKey(), toTestkitType(entry.getValue())); - } - return Testkit.wrap("CypherMap", Testkit.value("{"+v+"}")); - } - } - - throw new RuntimeException("Can not convert to testkit type:"+obj.getClass()); - } - - public static Object toDriver(JsonNode node) - { - String name = node.get("name").asText(); - JsonNode data = node.get("data"); - if (name.equals("CypherInt")) { - return data.get("value").asInt(); - } - throw new RuntimeException("Can not convert from " + node + " to driver type"); - } -} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java new file mode 100644 index 0000000000..18371081d4 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import neo4j.org.testkit.backend.messages.TestkitModule; +import neo4j.org.testkit.backend.messages.requests.TestkitRequest; +import neo4j.org.testkit.backend.messages.responses.DriverError; +import neo4j.org.testkit.backend.messages.responses.TestkitErrorResponse; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.UncheckedIOException; + +import org.neo4j.driver.exceptions.Neo4jException; + +public class CommandProcessor +{ + private final TestkitState testkitState; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private final BufferedReader in; + private final BufferedWriter out; + + public CommandProcessor( BufferedReader in, BufferedWriter out ) + { + this.in = in; + this.out = out; + configureObjectMapper(); + this.testkitState = new TestkitState( this::writeResponse, this::process ); + } + + private void configureObjectMapper() + { + TestkitModule testkitModule = new TestkitModule(); + this.objectMapper.registerModule( testkitModule ); + this.objectMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + } + + private String readLine() + { + try + { + return this.in.readLine(); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private void write( String s ) + { + try + { + this.out.write( s ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + // Logs to frontend + private void log( String s ) + { + try + { + this.out.write( s + "\n" ); + this.out.flush(); + } + catch ( IOException e ) + { + } + System.out.println( s ); + } + + private void flush() + { + try + { + this.out.flush(); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + // Reads one request and writes the response. Returns false when not able to read anymore. + public boolean process() + { + boolean inRequest = false; + StringBuilder request = new StringBuilder(); + + log( "Waiting for request" ); + + while ( true ) + { + String currentLine = readLine(); + // End of stream + if ( currentLine == null ) + { + return false; + } + + if ( currentLine.equals( "#request begin" ) ) + { + inRequest = true; + } + else if ( currentLine.equals( "#request end" ) ) + { + if ( !inRequest ) + { + throw new RuntimeException( "Request end not expected" ); + } + try + { + processRequest( request.toString() ); + } + catch ( Exception e ) + { + if ( e instanceof Neo4jException ) + { + // Error to track + String id = testkitState.newId(); + testkitState.getErrors().put( id, (Neo4jException) e ); + writeResponse( driverError( id ) ); + System.out.println( "Neo4jException: " + e ); + } + else + { + // Unknown error, interpret this as a backend error. + // Report to frontend and rethrow, note that if socket been + // closed the writing will throw itself... + writeResponse( TestkitErrorResponse.builder().errorMessage( e.toString() ).build() ); + // This won't print if there was an IO exception since line above will rethrow + e.printStackTrace(); + throw e; + } + } + return true; + } + else + { + if ( !inRequest ) + { + throw new RuntimeException( "Command Received whilst not in request" ); + } + request.append( currentLine ); + } + } + } + + private DriverError driverError( String id ) + { + return DriverError.builder().data( DriverError.DriverErrorBody.builder().id( id ).build() ).build(); + } + + public void processRequest( String request ) + { + System.out.println( "request = " + request + ", in = " + in + ", out = " + out ); + try + { + TestkitRequest testkitMessage = objectMapper.readValue( request, TestkitRequest.class ); + TestkitResponse response = testkitMessage.process( testkitState ); + if ( response != null ) + { + writeResponse( response ); + } + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private void writeResponse( TestkitResponse response ) + { + try + { + String responseStr = objectMapper.writeValueAsString( response ); + System.out.println("response = " + responseStr + ", in = " + in + ", out = " + out); + write( "#response begin\n" ); + write( responseStr + "\n" ); + write( "#response end\n" ); + flush(); + } + catch ( JsonProcessingException ex ) + { + throw new RuntimeException( "Error writing response", ex ); + } + } +} diff --git a/testkit-backend/src/main/java/Runner.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/Runner.java similarity index 67% rename from testkit-backend/src/main/java/Runner.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/Runner.java index f0580e68a4..27b3a3963f 100644 --- a/testkit-backend/src/main/java/Runner.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/Runner.java @@ -16,41 +16,60 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package neo4j.org.testkit.backend; + import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.UncheckedIOException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.CompletableFuture; public class Runner { public static void main( String[] args ) throws IOException { - ServerSocket serverSocket = new ServerSocket(9876); - System.out.println("Starting Java Testkit Backend Started"); + ServerSocket serverSocket = new ServerSocket( 9876 ); - while (true) { - System.out.println("Listening on socket"); - Socket clientSocket = serverSocket.accept(); + System.out.println( "Java TestKit Backend Started on port: " + serverSocket.getLocalPort() ); + while ( true ) + { + final Socket clientSocket = serverSocket.accept(); + CompletableFuture.runAsync( () -> handleClient( clientSocket ) ); + } + } + + private static void handleClient( Socket clientSocket ) + { + try + { System.out.println( "Handling connection from: " + clientSocket.getRemoteSocketAddress() ); BufferedReader in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream() ) ); BufferedWriter out = new BufferedWriter( new OutputStreamWriter( clientSocket.getOutputStream() ) ); - CommandProcessor commandProcessor = new CommandProcessor(in, out); + CommandProcessor commandProcessor = new CommandProcessor( in, out ); boolean cont = true; - while (cont) { - try { + while ( cont ) + { + try + { cont = commandProcessor.process(); - } catch (Exception e) { + } + catch ( Exception e ) + { e.printStackTrace(); clientSocket.close(); cont = false; } } } + catch ( IOException ex ) + { + throw new UncheckedIOException( ex ); + } } } diff --git a/testkit-backend/src/main/java/SessionState.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/SessionState.java similarity index 91% rename from testkit-backend/src/main/java/SessionState.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/SessionState.java index e3487a25d2..8afd8a83a6 100644 --- a/testkit-backend/src/main/java/SessionState.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/SessionState.java @@ -16,9 +16,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package neo4j.org.testkit.backend; + +import lombok.Getter; +import lombok.Setter; import org.neo4j.driver.Session; +@Getter +@Setter public class SessionState { public Session session; diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java new file mode 100644 index 0000000000..2bb6e98808 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend; + +import lombok.Getter; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.neo4j.driver.Driver; +import org.neo4j.driver.Result; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.exceptions.Neo4jException; + +@Getter +public class TestkitState +{ + private final Map drivers = new HashMap<>(); + private final Map sessionStates = new HashMap<>(); + private final Map results = new HashMap<>(); + private final Map transactions = new HashMap<>(); + private final Map errors = new HashMap<>(); + private int idGenerator = 0; + private final Consumer responseWriter; + private final Supplier processor; + + public TestkitState( Consumer responseWriter, Supplier processor ) + { + this.responseWriter = responseWriter; + this.processor = processor; + } + + public String newId() + { + return String.valueOf( idGenerator++ ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java new file mode 100644 index 0000000000..a4b725c6cf --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestkitCypherTypeDeserializer extends StdDeserializer> +{ + public TestkitCypherTypeDeserializer() + { + super( Map.class ); + } + + public TestkitCypherTypeDeserializer( Class typeClass ) + { + super( typeClass ); + } + + @Override + public Map deserialize( JsonParser p, DeserializationContext ctxt ) throws IOException, JsonProcessingException + { + Map result = new HashMap<>(); + + String key; + if ( p.isExpectedStartObjectToken() ) + { + key = p.nextFieldName(); + } + else + { + JsonToken t = p.getCurrentToken(); + if ( t == JsonToken.END_OBJECT ) + { + return Collections.emptyMap(); + } + if ( t != JsonToken.FIELD_NAME ) + { + ctxt.reportWrongTokenException( this, JsonToken.FIELD_NAME, null ); + } + key = p.getCurrentName(); + } + + for ( ; key != null; key = p.nextFieldName() ) + { + String paramType = null; + + if ( p.nextToken() == JsonToken.START_OBJECT ) + { + String fieldName = p.nextFieldName(); + if ( fieldName.equals( "name" ) ) + { + paramType = p.nextTextValue(); + Class mapValueType = cypherTypeToJavaType( paramType ); + p.nextFieldName(); // next is data which we can drop + p.nextToken(); + p.nextToken(); + p.nextToken(); + if ( mapValueType == null ) + { + result.put( key, null ); + } else + { + result.put( key, p.readValueAs( mapValueType ) ); + } + + } + } + + } + return result; + } + + private Class cypherTypeToJavaType( String typeString ) + { + switch ( typeString ) + { + case "CypherBool": + return Boolean.class; + case "CypherInt": + return Integer.class; + case "CypherFloat": + return Double.class; + case "CypherString": + return String.class; + case "CypherList": + return List.class; + case "CypherMap": + return Map.class; + case "CypherNull": + return null; + default: + return null; + } + } +} + diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java new file mode 100644 index 0000000000..588d7949c9 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitListValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitMapValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitNodeValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitRecordSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitValueSerializer; + +import org.neo4j.driver.Record; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.internal.value.MapValue; +import org.neo4j.driver.internal.value.NodeValue; + +public class TestkitModule extends SimpleModule +{ + public TestkitModule() + { + this.addSerializer( Value.class, new TestkitValueSerializer() ); + this.addSerializer( NodeValue.class, new TestkitNodeValueSerializer() ); + this.addSerializer( ListValue.class, new TestkitListValueSerializer() ); + this.addSerializer( Record.class, new TestkitRecordSerializer() ); + this.addSerializer( MapValue.class, new TestkitMapValueSerializer() ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java new file mode 100644 index 0000000000..3b870dc3be --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Map; + +@Setter +@Getter +@NoArgsConstructor +@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="name") +public class AuthorizationToken +{ + @JsonProperty("data") + private Map tokens; + +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java new file mode 100644 index 0000000000..b081f0176f --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Driver; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class DriverClose implements TestkitRequest +{ + private DriverCloseBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + testkitState.getDrivers().get( data.getDriverId() ).close(); + return Driver.builder().data( Driver.DriverBody.builder().id( data.getDriverId() ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + private static class DriverCloseBody + { + private String driverId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java new file mode 100644 index 0000000000..c9a77274d5 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Driver; +import neo4j.org.testkit.backend.messages.responses.TestkitErrorResponse; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Optional; + +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Config; +import org.neo4j.driver.GraphDatabase; + +@Setter +@Getter +@NoArgsConstructor +public class NewDriver implements TestkitRequest +{ + private NewDriverBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + String id = testkitState.newId(); + + AuthToken authToken; + switch ( data.getAuthorizationToken().getTokens().get( "scheme" ) ) + { + case "basic": + authToken = AuthTokens.basic( data.authorizationToken.getTokens().get( "principal" ), + data.authorizationToken.getTokens().get( "credentials" ), + data.authorizationToken.getTokens().get( "realm" ) ); + break; + default: + return TestkitErrorResponse.builder().errorMessage( "Auth scheme " + data.authorizationToken.getTokens().get( "scheme" ) + "not implemented" ) + .build(); + } + + Config.ConfigBuilder configBuilder = Config.builder(); + Optional.ofNullable( data.userAgent ).ifPresent( configBuilder::withUserAgent ); + testkitState.getDrivers().putIfAbsent( id, GraphDatabase.driver( data.uri, authToken, configBuilder.build() ) ); + return Driver.builder().data( Driver.DriverBody.builder().id( id ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class NewDriverBody + { + private String uri; + private AuthorizationToken authorizationToken; + private String userAgent; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java new file mode 100644 index 0000000000..98e9688cb5 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Session; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Driver; +import org.neo4j.driver.SessionConfig; +import org.neo4j.driver.internal.InternalBookmark; + +@Setter +@Getter +@NoArgsConstructor +public class NewSession implements TestkitRequest +{ + private NewSessionBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + Driver driver = testkitState.getDrivers().get( data.getDriverId() ); + AccessMode formattedAccessMode = data.getAccessMode().equals( "r" ) ? AccessMode.READ : AccessMode.WRITE; + SessionConfig.Builder builder = SessionConfig.builder() + .withDefaultAccessMode( formattedAccessMode ); + + Optional.ofNullable( data.bookmarks ) + .map( bookmarks -> bookmarks.stream().map( InternalBookmark::parse ).collect( Collectors.toList() ) ) + .ifPresent( builder::withBookmarks ); + + Optional.ofNullable( data.database ).ifPresent( builder::withDatabase ); + + org.neo4j.driver.Session session = driver.session( builder.build() ); + String newId = testkitState.newId(); + testkitState.getSessionStates().put( newId, new SessionState( session ) ); + + return Session.builder().data( Session.SessionBody.builder().id( newId ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class NewSessionBody + { + private String driverId; + private String accessMode; + private List bookmarks; + private String database; + private int fetchSize; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java new file mode 100644 index 0000000000..4105802170 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.NullRecord; +import neo4j.org.testkit.backend.messages.responses.Record; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import org.neo4j.driver.Result; +import org.neo4j.driver.exceptions.NoSuchRecordException; + +@Setter +@Getter +@NoArgsConstructor +public class ResultNext implements TestkitRequest +{ + private ResultNextBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + try + { + Result result = testkitState.getResults().get( data.getResultId() ); + org.neo4j.driver.Record record = result.next(); + return Record.builder().data( Record.RecordBody.builder().values( record ).build() ).build(); + } + catch ( NoSuchRecordException ignored ) + { + return NullRecord.builder().build(); + } + } + + @Setter + @Getter + @NoArgsConstructor + public static class ResultNextBody + { + private String resultId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java new file mode 100644 index 0000000000..3c438b0f91 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class RetryableNegative implements TestkitRequest +{ + private RetryableNegativeBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + SessionState sessionState = testkitState.getSessionStates().getOrDefault( data.sessionId, null ); + if ( sessionState == null ) + { + throw new RuntimeException( "Could not find session" ); + } + sessionState.setRetryableState( -1 ); + sessionState.setRetryableErrorId( data.errorId ); + return null; + } + + @Setter + @Getter + @NoArgsConstructor + public static class RetryableNegativeBody + { + private String sessionId; + private String errorId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java new file mode 100644 index 0000000000..a8e8179c80 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class RetryablePositive implements TestkitRequest +{ + private RetryablePositiveBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + SessionState sessionState = testkitState.getSessionStates().get( data.sessionId ); + if ( sessionState == null ) + { + throw new RuntimeException( "Could not find session" ); + } + sessionState.setRetryableState( 1 ); + return null; + } + + @Setter + @Getter + @NoArgsConstructor + public static class RetryablePositiveBody + { + private String sessionId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java new file mode 100644 index 0000000000..8f91cf45e3 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; +import neo4j.org.testkit.backend.messages.responses.Transaction; + +import java.util.Map; +import java.util.Optional; + +import org.neo4j.driver.TransactionConfig; + +@Setter +@Getter +@NoArgsConstructor +public class SessionBeginTransaction implements TestkitRequest +{ + private SessionBeginTransactionBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getSessionStates().getOrDefault( data.sessionId, null ) ) + .map( SessionState::getSession ) + .map( session -> + { + TransactionConfig.Builder builder = TransactionConfig.builder(); + Optional.ofNullable( data.txMeta ).ifPresent( builder::withMetadata ); + // Optional.ofNullable( data.timeout ).ifPresent( builder::withTimeout ); + String txId = testkitState.newId(); + org.neo4j.driver.Transaction tx = session.beginTransaction( builder.build() ); + testkitState.getTransactions().put( txId, tx ); + return transaction( txId ); + } ) + .orElseThrow( () -> new RuntimeException( "Could not find session" ) ); + } + + private Transaction transaction( String txId ) + { + return Transaction.builder().data( Transaction.TransactionBody.builder().id( txId ).build() ).build(); + } + + @Getter + @NoArgsConstructor + @Setter + public static class SessionBeginTransactionBody + { + private String sessionId; + private Map txMeta; + private Integer timeout; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java new file mode 100644 index 0000000000..b4b5f2feca --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Session; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class SessionClose implements TestkitRequest +{ + private SessionCloseBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + testkitState.getSessionStates().get( data.getSessionId() ).getSession().close(); + return Session.builder().data( Session.SessionBody.builder().id( data.getSessionId() ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + private static class SessionCloseBody + { + private String sessionId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java new file mode 100644 index 0000000000..956b9a6629 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.RetryableDone; +import neo4j.org.testkit.backend.messages.responses.RetryableTry; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Optional; + +import org.neo4j.driver.Session; +import org.neo4j.driver.TransactionWork; + +@Setter +@Getter +@NoArgsConstructor +public class SessionReadTransaction implements TestkitRequest +{ + private SessionReadTransactionBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getSessionStates().getOrDefault( data.sessionId, null ) ) + .map( sessionState -> + { + Session session = sessionState.getSession(); + session.readTransaction( handle( testkitState, sessionState ) ); + return retryableDone(); + } ).orElseThrow( () -> new RuntimeException( "Could not find session" ) ); + } + + private TransactionWork handle( TestkitState testkitState, SessionState sessionState ) + { + return tx -> + { + System.out.println( "Start" ); + sessionState.setRetryableState( 0 ); + String txId = testkitState.newId(); + testkitState.getTransactions().put( txId, tx ); + testkitState.getResponseWriter().accept( retryableTry( txId ) ); + + while ( true ) + { + // Process commands as usual but blocking in here + testkitState.getProcessor().get(); + + // Check if state changed on session + switch ( sessionState.retryableState ) + { + case 0: + // Nothing happened to session state while processing command + break; + case 1: + // Client is happy to commit + return 0; + case -1: + // Client wants to rollback + if ( !"".equals( sessionState.retryableErrorId ) ) + { + throw testkitState.getErrors().get( sessionState.retryableErrorId ); + } + else + { + throw new RuntimeException( "Error from client in retryable tx" ); + } + } + } + }; + } + + private RetryableTry retryableTry( String txId ) + { + return RetryableTry.builder().data( RetryableTry.RetryableTryBody.builder().id( txId ).build() ).build(); + } + + private RetryableDone retryableDone() + { + return RetryableDone.builder().build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class SessionReadTransactionBody + { + private String sessionId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java new file mode 100644 index 0000000000..0432c16748 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.TestkitCypherTypeDeserializer; +import neo4j.org.testkit.backend.messages.responses.Result; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Map; +import java.util.Optional; + +import org.neo4j.driver.Query; +import org.neo4j.driver.Session; + +@Setter +@Getter +@NoArgsConstructor +public class SessionRun implements TestkitRequest +{ + private SessionRunBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + Session session = testkitState.getSessionStates().get( data.getSessionId() ).getSession(); + Query query = Optional.ofNullable( data.params ) + .map( params -> new Query( data.cypher, data.params ) ) + .orElseGet( () -> new Query( data.cypher ) ); + org.neo4j.driver.Result result = session.run( query ); + String newId = testkitState.newId(); + testkitState.getResults().put( newId, result ); + + return Result.builder().data( Result.ResultBody.builder().id( newId ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class SessionRunBody + { + private String sessionId; + private String cypher; + + @JsonDeserialize( using = TestkitCypherTypeDeserializer.class ) + private Map params; + private String txMeta; + private int timeout; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java new file mode 100644 index 0000000000..6157c38086 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name" ) +@JsonSubTypes( { + @JsonSubTypes.Type( NewDriver.class ), @JsonSubTypes.Type( NewSession.class ), + @JsonSubTypes.Type( SessionRun.class ), @JsonSubTypes.Type( ResultNext.class ), + @JsonSubTypes.Type( SessionClose.class ), @JsonSubTypes.Type( DriverClose.class ), + @JsonSubTypes.Type( RetryableNegative.class ), @JsonSubTypes.Type( SessionReadTransaction.class ), + @JsonSubTypes.Type( TransactionRun.class ), @JsonSubTypes.Type( RetryablePositive.class ), + @JsonSubTypes.Type( SessionBeginTransaction.class ), @JsonSubTypes.Type( TransactionCommit.class ) +} ) +public interface TestkitRequest +{ + TestkitResponse process( TestkitState testkitState ); +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java new file mode 100644 index 0000000000..be08777a59 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; +import neo4j.org.testkit.backend.messages.responses.Transaction; + +import java.util.Optional; + +@Getter +@NoArgsConstructor +@Setter +public class TransactionCommit implements TestkitRequest +{ + private TransactionCommitBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getTransactions().get( data.txId ) ) + .map( tx -> + { + tx.commit(); + return transaction( data.txId ); + } ) + .orElseThrow( () -> new RuntimeException( "Could not find transaction" ) ); + } + + private Transaction transaction( String txId ) + { + return Transaction.builder().data( Transaction.TransactionBody.builder().id( txId ).build() ).build(); + } + + @Getter + @NoArgsConstructor + @Setter + public static class TransactionCommitBody + { + private String txId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java new file mode 100644 index 0000000000..eb18d403ee --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Result; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Optional; + +@Setter +@Getter +@NoArgsConstructor +public class TransactionRun implements TestkitRequest +{ + private TransactionRunBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getTransactions().get( data.txId ) ) + .map( tx -> tx.run( data.cypher ) ) + .map( result -> + { + String resultId = testkitState.newId(); + testkitState.getResults().put( resultId, result ); + return result( resultId ); + } ) + .orElseThrow( () -> new RuntimeException( "Could not find transaction" ) ); + } + + private Result result( String resultId ) + { + return Result.builder().data( Result.ResultBody.builder().id( resultId ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class TransactionRunBody + { + private String txId; + private String cypher; + } +} diff --git a/testkit-backend/src/main/java/Testkit.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Driver.java similarity index 58% rename from testkit-backend/src/main/java/Testkit.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Driver.java index ea2d4fda55..9a6c0e2d2b 100644 --- a/testkit-backend/src/main/java/Testkit.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Driver.java @@ -16,29 +16,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package neo4j.org.testkit.backend.messages.responses; -public class Testkit -{ - public static String wrap(String name, String data) - { - return String.format("{\"name\": \"%s\", \"data\":%s}", name, data); - } +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; - public static String value(String v) { - return "{\"value\":"+v+"}"; - } - - public static String values(String v) { - return "{\"values\":["+v+"]}"; - } +@Setter +@Getter +@Builder +public class Driver implements TestkitResponse +{ + private final DriverBody data; - public static String id(String v) { - return "{\"id\":"+v+"}"; + @Override + public String testkitName() + { + return "Driver"; } - public static String msg(String msg) { - return "{\"msg\":\""+msg+"\"}"; + @Setter + @Getter + @Builder + public static class DriverBody + { + private final String id; } } - - diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java new file mode 100644 index 0000000000..e5fcf72c4b --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class DriverError implements TestkitResponse +{ + private DriverErrorBody data; + + @Override + public String testkitName() + { + return "DriverError"; + } + + @Setter + @Getter + @Builder + public static class DriverErrorBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java new file mode 100644 index 0000000000..716600ca28 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class NullRecord implements TestkitResponse +{ + @Override + public String testkitName() + { + return "NullRecord"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java new file mode 100644 index 0000000000..e132b7d9f9 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Record implements TestkitResponse +{ + private RecordBody data; + + @Override + public String testkitName() + { + return "Record"; + } + + @Setter + @Getter + @Builder + public static class RecordBody + { + private org.neo4j.driver.Record values; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java new file mode 100644 index 0000000000..9ed51048f8 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Result implements TestkitResponse +{ + private ResultBody data; + + @Override + public String testkitName() + { + return "Result"; + } + + @Setter + @Getter + @Builder + public static class ResultBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java new file mode 100644 index 0000000000..a13543f035 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class RetryableDone implements TestkitResponse +{ + @Override + public String testkitName() + { + return "RetryableDone"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java new file mode 100644 index 0000000000..0fc93a4da8 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class RetryableTry implements TestkitResponse +{ + private RetryableTryBody data; + + @Override + public String testkitName() + { + return "RetryableTry"; + } + + @Setter + @Getter + @Builder + public static class RetryableTryBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java new file mode 100644 index 0000000000..772d71045d --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Session implements TestkitResponse +{ + private SessionBody data; + + @Override + public String testkitName() + { + return "Session"; + } + + @Setter + @Getter + @Builder + public static class SessionBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java new file mode 100644 index 0000000000..13a7e5a1ae --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class TeskitBackendErrorResponse implements TestkitResponse +{ + @Override + public String testkitName() + { + return "BackendError"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java new file mode 100644 index 0000000000..eadf0c4ef3 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class TestkitErrorResponse implements TestkitResponse +{ + @JsonProperty("msg") + private String errorMessage; + + @Override + public String testkitName() + { + return "Error"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java new file mode 100644 index 0000000000..b945e4307e --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="name") +public interface TestkitResponse +{ + String testkitName(); +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java new file mode 100644 index 0000000000..9f491a78ed --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Transaction implements TestkitResponse +{ + private final TransactionBody data; + + @Override + public String testkitName() + { + return "Transaction"; + } + + @Setter + @Getter + @Builder + public static class TransactionBody + { + private final String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java new file mode 100644 index 0000000000..fa29189226 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + +import java.io.IOException; +import java.util.List; + +@AllArgsConstructor( access = AccessLevel.PRIVATE ) +public final class GenUtils +{ + public static void object( JsonGenerator gen, RunnableWithIOException runnable ) throws IOException + { + gen.writeStartObject(); + runnable.run(); + gen.writeEndObject(); + } + + public static void list( JsonGenerator gen, List list ) throws IOException + { + gen.writeStartArray(); + for ( T element : list ) + { + gen.writeObject( element ); + } + gen.writeEndArray(); + } + + public static void cypherObject( JsonGenerator gen, String name, T value ) throws IOException + { + cypherObject( gen, name, () -> gen.writeObjectField("value", value ) ); + } + + public static void cypherObject( JsonGenerator gen, String name, RunnableWithIOException runnable ) throws IOException + { + object( gen, () -> + { + gen.writeStringField( "name", name ); + gen.writeFieldName( "data" ); + object( gen, runnable ); + } ); + } + + interface RunnableWithIOException + { + void run() throws IOException; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java new file mode 100644 index 0000000000..54d661a640 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.function.Function; + +import org.neo4j.driver.internal.value.ListValue; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.list; + +public class TestkitListValueSerializer extends StdSerializer +{ + public TestkitListValueSerializer() + { + super( ListValue.class ); + } + + @Override + public void serialize( ListValue listValue, JsonGenerator gen, SerializerProvider serializerProvider ) throws IOException + { + cypherObject( gen, "CypherList", () -> + { + gen.writeFieldName( "value" ); + list( gen, listValue.asList( Function.identity() ) ); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java new file mode 100644 index 0000000000..fa626d1281 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.Map; +import java.util.function.Function; + +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.MapValue; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.object; + +public class TestkitMapValueSerializer extends StdSerializer +{ + public TestkitMapValueSerializer() + { + super( MapValue.class ); + } + + @Override + public void serialize( MapValue mapValue, JsonGenerator gen, SerializerProvider serializerProvider ) throws IOException + { + cypherObject( gen, "CypherMap", () -> + { + gen.writeFieldName( "value" ); + object( gen, () -> + { + for ( Map.Entry entry : mapValue.asMap( Function.identity() ).entrySet() ) + { + gen.writeObjectField( entry.getKey(), entry.getValue() ); + } + } ); + } ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java new file mode 100644 index 0000000000..c452fd1be8 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.function.Function; +import java.util.stream.StreamSupport; + +import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.internal.value.MapValue; +import org.neo4j.driver.internal.value.NodeValue; +import org.neo4j.driver.internal.value.StringValue; +import org.neo4j.driver.types.Node; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +public class TestkitNodeValueSerializer extends StdSerializer +{ + public TestkitNodeValueSerializer() + { + super( NodeValue.class ); + } + + @Override + public void serialize( NodeValue nodeValue, JsonGenerator gen, SerializerProvider serializerProvider ) throws IOException + { + + cypherObject( gen, "Node", () -> + { + Node node = nodeValue.asNode(); + gen.writeObjectField( "id", node.id() ); + + StringValue[] labels = StreamSupport.stream( node.labels().spliterator(), false ) + .map( StringValue::new ) + .toArray( StringValue[]::new ); + + gen.writeObjectField( "labels", new ListValue( labels ) ); + gen.writeObjectField( "props", new MapValue( node.asMap( Function.identity() ) ) ); + + } ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java new file mode 100644 index 0000000000..997c19a990 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +import org.neo4j.driver.Record; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.list; + +public class TestkitRecordSerializer extends StdSerializer +{ + + public TestkitRecordSerializer() + { + super( org.neo4j.driver.Record.class ); + } + + @Override + public void serialize( Record record, JsonGenerator gen, SerializerProvider provider ) throws IOException + { + list( gen, record.values() ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java new file mode 100644 index 0000000000..6ffbecebf2 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.types.InternalTypeSystem; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +public class TestkitValueSerializer extends StdSerializer +{ + public TestkitValueSerializer( ) + { + super( Value.class ); + } + + @Override + public void serialize( Value value, JsonGenerator gen, SerializerProvider provider ) throws IOException + { + if ( InternalTypeSystem.TYPE_SYSTEM.BOOLEAN().isTypeOf( value ) ) + { + cypherObject( gen, "CypherBool", value.asBoolean() ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.NULL().isTypeOf( value ) ) { + cypherObject( gen, "CypherNull", () -> gen.writeNullField( "value" ) ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.INTEGER().isTypeOf( value ) ) { + cypherObject( gen, "CypherInt", value.asInt() ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.FLOAT().isTypeOf( value ) ) { + cypherObject( gen, "CypherFloat", value.asDouble() ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.STRING().isTypeOf( value ) ) { + cypherObject( gen, "CypherString", value.asString() ); + } + + } +} diff --git a/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java new file mode 100644 index 0000000000..2bf55b1231 --- /dev/null +++ b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import neo4j.org.testkit.backend.messages.TestkitModule; +import neo4j.org.testkit.backend.messages.requests.NewDriver; +import neo4j.org.testkit.backend.messages.requests.NewSession; +import neo4j.org.testkit.backend.messages.requests.TestkitRequest; +import neo4j.org.testkit.backend.messages.requests.SessionRun; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +class MessageDeserializerTest +{ + private static final ObjectMapper mapper = new ObjectMapper(); + + @BeforeAll + static void setUp() + { + TestkitModule tkm = new TestkitModule(); + + //mapper.registerModule( tkm ); + mapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + } + + @Test + void testDeserializeNewDriver() throws JsonProcessingException + { + Object message = mapper.readValue( + "{\"name\": \"NewDriver\", \"data\": {\"uri\": \"bolt://localhost:7687\", " + + "\"authorizationToken\": {\"name\": \"AuthorizationToken\", \"data\": {\"scheme\": \"basic\", \"principal\": \"neo4j\", " + + "\"credentials\": \"pass\", \"realm\": \"\", \"ticket\": \"\"}}, \"userAgent\": null}}", + TestkitRequest.class ); + + assertThat( message, instanceOf( NewDriver.class ) ); + + NewDriver newDriver = (NewDriver) message; + assertThat( newDriver.getData().getUri(), equalTo( "bolt://localhost:7687" ) ); + assertThat( newDriver.getData().getAuthorizationToken().getTokens().get( "scheme" ), equalTo( "basic" ) ); + assertThat( newDriver.getData().getAuthorizationToken().getTokens().get( "principal" ), equalTo( "neo4j" ) ); + } + + @Test + void testDeserializerNewSession() throws JsonProcessingException + { + Object message = mapper.readValue( + "{\"name\": \"NewSession\", " + + "\"data\": {\"driverId\": \"0\", \"accessMode\": \"w\", \"bookmarks\": null, \"database\": null, \"fetchSize\": null}}", + TestkitRequest.class ); + + assertThat( message, instanceOf( NewSession.class ) ); + + NewSession sessionRequest = (NewSession) message; + assertThat( sessionRequest.getData().getDriverId(), equalTo( "0" ) ); + assertThat( sessionRequest.getData().getAccessMode(), equalTo( "w" ) ); + } + + @Test + void testDeserializerNewSessionRun() throws JsonProcessingException + { + Object message = mapper.readValue( + "{\"name\": \"SessionRun\", \"data\": {\"sessionId\": \"1\", \"cypher\": \"RETURN $x as y\", " + + "\"params\": {\"x\": {\"name\": \"CypherBool\", \"data\": {\"value\": true}}}, \"txMeta\": null, \"timeout\": null}}", + TestkitRequest.class ); + + assertThat( message, instanceOf( SessionRun.class ) ); + + SessionRun sessionRun = (SessionRun) message; + assertThat( sessionRun.getData().getSessionId(), equalTo( "1" ) ); + assertThat( sessionRun.getData().getCypher(), equalTo( "RETURN $x as y" ) ); + } + +} \ No newline at end of file diff --git a/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java new file mode 100644 index 0000000000..494ff07d39 --- /dev/null +++ b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 neo4j.org.testkit.backend; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import neo4j.org.testkit.backend.messages.TestkitModule; +import neo4j.org.testkit.backend.messages.responses.Driver; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class MessageSerializerTest +{ + private static final ObjectMapper mapper = new ObjectMapper(); + + @BeforeAll + static void setUp() + { + TestkitModule tkm = new TestkitModule(); + + mapper.registerModule( tkm ); + mapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + } + + @Test + void shouldSerializerNewDriverResponse() throws JsonProcessingException + { + Driver response = Driver.builder().data( Driver.DriverBody.builder().id( "1" ).build() ).build(); + String expectedOutput = "{\"name\":\"Driver\",\"data\":{\"id\":\"1\"}}"; + + String serializedResponse = mapper.writeValueAsString( response ); + + assertThat( serializedResponse, equalTo( expectedOutput ) ); + } +}