From 2a6c6f18d3e9df863458b2efefa8d2a44766a41a Mon Sep 17 00:00:00 2001 From: Dmitriy Tverdiakov <11927660+injectives@users.noreply.github.com> Date: Mon, 1 Mar 2021 20:06:29 +0000 Subject: [PATCH] Migrating tests to testkit (#832) * Migrating tests to testkit Short summary of this update: - removed migrated tests - verifyConnectivity support - resolver support - consume support Test mapping (dest: stub/routing.py): - shouldHandleAcquireReadSession -> test_should_read_successfully_from_reader_using_session_run - shouldHandleAcquireReadTransaction -> test_should_read_successfully_from_reader_using_tx_function - shouldHandleAcquireReadSessionAndTransaction -> test_should_read_successfully_from_reader_using_tx_run - shouldRoundRobinReadServers -> test_should_round_robin_readers_when_reading_using_session_run - shouldRoundRobinReadServersWhenUsingTransaction -> test_should_round_robin_readers_when_reading_using_tx_run - shouldThrowSessionExpiredIfReadServerDisappears -> test_should_fail_when_reading_from_unexpectedly_interrupting_reader_using_session_run - shouldThrowSessionExpiredIfReadServerDisappearsWhenUsingTransaction -> test_should_fail_when_reading_from_unexpectedly_interrupting_reader_using_tx_run - shouldThrowSessionExpiredIfWriteServerDisappears -> test_should_fail_when_writing_on_unexpectedly_interrupting_writer_using_session_run - shouldThrowSessionExpiredIfWriteServerDisappearsWhenUsingTransaction -> test_should_fail_when_writing_on_unexpectedly_interrupting_writer_using_tx_run - shouldHandleAcquireWriteSession -> test_should_write_successfully_on_writer_using_session_run - shouldHandleAcquireWriteTransaction -> test_should_write_successfully_on_writer_using_tx_function - shouldHandleAcquireWriteSessionAndTransaction -> test_should_write_successfully_on_writer_using_tx_run - shouldRoundRobinWriteSessions -> test_should_round_robin_writers_when_writing_using_session_run - shouldRoundRobinWriteSessionsInTransaction -> test_should_round_robin_writers_when_writing_using_tx_run - shouldFailOnNonDiscoverableServer -> test_should_fail_discovery_when_router_fails_with_procedure_not_found_code - shouldFailRandomFailureInGetServers -> test_should_fail_discovery_when_router_fails_with_unknown_code - shouldHandleLeaderSwitchWhenWriting -> test_should_fail_when_writing_on_writer_that_returns_not_a_leader_code - shouldHandleLeaderSwitchWhenWritingWithoutConsuming -> test_should_fail_when_writing_without_explicit_consumption_on_writer_that_returns_not_a_leader_code - shouldHandleLeaderSwitchWhenWritingInTransaction -> test_should_fail_when_writing_on_writer_that_returns_not_a_leader_code_using_tx_run - shouldUseWriteSessionModeAndInitialBookmark -> test_should_use_write_session_mode_and_initial_bookmark_when_writing_using_tx_run - shouldUseReadSessionModeAndInitialBookmark -> test_should_use_read_session_mode_and_initial_bookmark_when_reading_using_tx_run - shouldPassBookmarkFromTransactionToTransaction -> test_should_pass_bookmark_from_tx_to_tx_using_tx_run - shouldRetryReadTransactionUntilSuccess -> test_should_retry_read_tx_until_success - shouldRetryWriteTransactionUntilSuccess -> test_should_retry_write_tx_until_success - shouldRetryReadTransactionAndPerformRediscoveryUntilSuccess -> test_should_retry_read_tx_and_rediscovery_until_success - shouldRetryWriteTransactionAndPerformRediscoveryUntilSuccess -> test_should_retry_write_tx_and_rediscovery_until_success - shouldUseInitialRouterForRediscoveryWhenAllOtherRoutersAreDead -> test_should_use_initial_router_for_discovery_when_others_unavailable - shouldInvokeProcedureGetRoutingTableWhenServerVersionPermits -> test_should_successfully_read_from_readable_router_using_tx_function - shouldSendEmptyRoutingContextInHelloMessage -> test_should_send_empty_hello - shouldServeReadsButFailWritesWhenNoWritersAvailable -> test_should_serve_reads_and_fail_writes_when_no_writers_available - shouldAcceptRoutingTableWithoutWritersAndThenRediscover -> test_should_accept_routing_table_without_writers_and_then_rediscover - shouldTreatRoutingTableWithSingleRouterAsValid -> test_should_accept_routing_table_with_single_router - shouldSendMultipleBookmarks -> test_should_successfully_send_multiple_bookmarks - shouldForgetAddressOnDatabaseUnavailableError -> test_should_forget_address_on_database_unavailable_error - shouldUseResolverDuringRediscoveryWhenExistingRoutersFail -> test_should_use_resolver_during_rediscovery_when_existing_routers_fail - shouldRevertToInitialRouterIfKnownRouterThrowsProtocolErrors -> test_should_revert_to_initial_router_if_known_router_throws_protocol_errors * Removing redundant stub server scripts --- .../integration/RoutingDriverBoltKitIT.java | 942 +----------------- .../acquire_endpoints_v3_9010.script | 10 - .../acquire_endpoints_v3_empty.script | 10 - ...s_v3_point_to_empty_router_and_exit.script | 10 - ...endpoints_v3_three_servers_and_exit.script | 12 - .../test/resources/dead_read_server.script | 8 - .../src/test/resources/discover_failed.script | 12 - .../test/resources/discover_no_writers.script | 10 - .../resources/discover_no_writers_9010.script | 10 - .../test/resources/discover_one_router.script | 10 - .../resources/discover_servers_9010.script | 10 - ...mpty_routing_context_in_hello_neo4j.script | 17 - .../test/resources/get_routing_table.script | 18 - .../resources/not_able_to_write_server.script | 13 - .../resources/read_server_v3_read_tx.script | 16 - .../resources/read_tx_with_bookmarks.script | 15 - .../rediscover_using_initial_router.script | 18 - .../write_read_tx_with_bookmarks.script | 22 - .../resources/write_server_v3_write.script | 9 - .../resources/write_with_bookmarks.script | 9 - .../test/resources/writer_unavailable.script | 10 - testkit-backend/pom.xml | 2 +- .../org/testkit/backend/CommandProcessor.java | 7 +- .../org/testkit/backend/TestkitState.java | 3 + .../backend/messages/requests/NewDriver.java | 27 + .../requests/ResolverResolutionCompleted.java | 56 ++ .../messages/requests/ResultConsume.java | 61 ++ .../messages/requests/TestkitRequest.java | 4 +- .../messages/requests/VerifyConnectivity.java | 50 + .../messages/responses/DriverError.java | 2 + .../responses/ResolverResolutionRequired.java | 47 + .../messages/responses/ResultSummary.java | 35 + 32 files changed, 302 insertions(+), 1183 deletions(-) delete mode 100644 driver/src/test/resources/acquire_endpoints_v3_9010.script delete mode 100644 driver/src/test/resources/acquire_endpoints_v3_empty.script delete mode 100644 driver/src/test/resources/acquire_endpoints_v3_point_to_empty_router_and_exit.script delete mode 100644 driver/src/test/resources/acquire_endpoints_v3_three_servers_and_exit.script delete mode 100644 driver/src/test/resources/dead_read_server.script delete mode 100644 driver/src/test/resources/discover_failed.script delete mode 100644 driver/src/test/resources/discover_no_writers.script delete mode 100644 driver/src/test/resources/discover_no_writers_9010.script delete mode 100644 driver/src/test/resources/discover_one_router.script delete mode 100644 driver/src/test/resources/discover_servers_9010.script delete mode 100644 driver/src/test/resources/empty_routing_context_in_hello_neo4j.script delete mode 100644 driver/src/test/resources/get_routing_table.script delete mode 100644 driver/src/test/resources/not_able_to_write_server.script delete mode 100644 driver/src/test/resources/read_server_v3_read_tx.script delete mode 100644 driver/src/test/resources/read_tx_with_bookmarks.script delete mode 100644 driver/src/test/resources/rediscover_using_initial_router.script delete mode 100644 driver/src/test/resources/write_read_tx_with_bookmarks.script delete mode 100644 driver/src/test/resources/write_server_v3_write.script delete mode 100644 driver/src/test/resources/write_with_bookmarks.script delete mode 100644 driver/src/test/resources/writer_unavailable.script create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResolverResolutionCompleted.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/VerifyConnectivity.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResolverResolutionRequired.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java diff --git a/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitIT.java b/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitIT.java index a47a249d19..70fbd9f826 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/RoutingDriverBoltKitIT.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -30,12 +29,8 @@ import java.net.URI; import java.util.ArrayList; import java.util.Comparator; -import java.util.HashSet; import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.neo4j.driver.AccessMode; @@ -46,16 +41,13 @@ import org.neo4j.driver.GraphDatabase; import org.neo4j.driver.Logger; import org.neo4j.driver.Record; -import org.neo4j.driver.Result; import org.neo4j.driver.Session; import org.neo4j.driver.Transaction; import org.neo4j.driver.TransactionWork; import org.neo4j.driver.async.AsyncSession; -import org.neo4j.driver.exceptions.ServiceUnavailableException; import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.exceptions.TransientException; import org.neo4j.driver.internal.DriverFactory; -import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.cluster.RoutingSettings; import org.neo4j.driver.internal.retry.RetrySettings; import org.neo4j.driver.internal.security.SecurityPlanImpl; @@ -71,13 +63,10 @@ import org.neo4j.driver.util.StubServerController; import static java.util.Arrays.asList; -import static java.util.Collections.singleton; -import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.junit.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -90,7 +79,6 @@ import static org.neo4j.driver.internal.InternalBookmark.parse; import static org.neo4j.driver.util.StubServer.INSECURE_CONFIG; import static org.neo4j.driver.util.StubServer.insecureBuilder; -import static org.neo4j.driver.util.TestUtil.asOrderedSet; class RoutingDriverBoltKitIT { @@ -108,441 +96,6 @@ public void killServers() stubController.reset(); } - @Test - void shouldHandleAcquireReadSession() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a read server - StubServer readServer = stubController.startStub( "read_server_v3_read.script", 9005 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - { - List result = session.run( "MATCH (n) RETURN n.name" ).list( record -> record.get( "n.name" ).asString() ); - - assertThat( result, equalTo( asList( "Bob", "Alice", "Tina" ) ) ); - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldHandleAcquireReadTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a read server - StubServer readServer = stubController.startStub( "read_server_v3_read_tx.script", 9005 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - - { - List result = session.readTransaction( tx -> tx.run( "MATCH (n) RETURN n.name" ).list( record -> record.get( "n.name" ).asString() ) ); - - assertThat( result, equalTo( asList( "Bob", "Alice", "Tina" ) ) ); - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldHandleAcquireReadSessionAndTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a read server - StubServer readServer = stubController.startStub( "read_server_v3_read_tx.script", 9005 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ); Transaction tx = session.beginTransaction() ) - { - List result = tx.run( "MATCH (n) RETURN n.name" ).list( record -> record.get( "n.name" ).asString() ); - - assertThat( result, equalTo( asList( "Bob", "Alice", "Tina" ) ) ); - tx.commit(); - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldRoundRobinReadServers() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START two read servers - StubServer readServer1 = stubController.startStub( "read_server_v3_read.script", 9005 ); - StubServer readServer2 = stubController.startStub( "read_server_v3_read.script", 9006 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ) ) - { - // Run twice, one on each read server - for ( int i = 0; i < 2; i++ ) - { - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - { - assertThat( session.run( "MATCH (n) RETURN n.name" ).list( record -> record.get( "n.name" ).asString() ), - equalTo( asList( "Bob", "Alice", "Tina" ) ) ); - } - } - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer1.exitStatus(), equalTo( 0 ) ); - assertThat( readServer2.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldRoundRobinReadServersWhenUsingTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START two read servers - StubServer readServer1 = stubController.startStub( "read_server_v3_read_tx.script", 9005 ); - StubServer readServer2 = stubController.startStub( "read_server_v3_read_tx.script", 9006 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ) ) - { - // Run twice, one on each read server - for ( int i = 0; i < 2; i++ ) - { - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ); - Transaction tx = session.beginTransaction() ) - { - assertThat( tx.run( "MATCH (n) RETURN n.name" ).list( record -> record.get( "n.name" ).asString() ), - equalTo( asList( "Bob", "Alice", "Tina" ) ) ); - tx.commit(); - } - } - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer1.exitStatus(), equalTo( 0 ) ); - assertThat( readServer2.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldThrowSessionExpiredIfReadServerDisappears() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a read server - final StubServer readServer = stubController.startStub( "dead_read_server.script", 9005 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - - //Expect - assertThrows( SessionExpiredException.class, () -> - { - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - { - session.run( "MATCH (n) RETURN n.name" ); - } - } ); - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldThrowSessionExpiredIfReadServerDisappearsWhenUsingTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a read server - final StubServer readServer = stubController.startStub( "dead_read_server_tx.script", 9005 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - - //Expect - SessionExpiredException e = assertThrows( SessionExpiredException.class, () -> - { - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ); - Transaction tx = session.beginTransaction() ) - { - tx.run( "MATCH (n) RETURN n.name" ); - tx.commit(); - } - } ); - assertEquals( "Server at 127.0.0.1:9005 is no longer available", e.getMessage() ); - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( readServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldThrowSessionExpiredIfWriteServerDisappears() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a dead write server - final StubServer writeServer = stubController.startStub( "dead_write_server.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - - //Expect - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ) ) - { - assertThrows( SessionExpiredException.class, () -> session.run( "CREATE (n {name:'Bob'})" ).consume() ); - } - finally - { - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer.exitStatus(), equalTo( 0 ) ); - } - } - - @Test - void shouldThrowSessionExpiredIfWriteServerDisappearsWhenUsingTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a dead write servers - final StubServer writeServer = stubController.startStub( "dead_read_server_tx.script", 9007 ); - - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - //Expect - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ); Transaction tx = session.beginTransaction() ) - { - assertThrows( SessionExpiredException.class, () -> tx.run( "MATCH (n) RETURN n.name" ).consume() ); - } - finally - { - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer.exitStatus(), equalTo( 0 ) ); - } - } - - @Test - void shouldHandleAcquireWriteSession() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server - StubServer writeServer = stubController.startStub( "write_server_v3_write.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ) ) - { - session.run( "CREATE (n {name:'Bob'})" ); - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldHandleAcquireWriteTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server - StubServer writeServer = stubController.startStub( "write_server_v3_write_tx.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); Session session = driver.session() ) - { - session.writeTransaction( t -> t.run( "CREATE (n {name:'Bob'})" ) ); - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldHandleAcquireWriteSessionAndTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server - StubServer writeServer = stubController.startStub( "write_server_v3_write_tx.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ); Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE (n {name:'Bob'})" ); - tx.commit(); - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldRoundRobinWriteSessions() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server - StubServer writeServer1 = stubController.startStub( "write_server_v3_write.script", 9007 ); - StubServer writeServer2 = stubController.startStub( "write_server_v3_write.script", 9008 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ) ) - { - for ( int i = 0; i < 2; i++ ) - { - try ( Session session = driver.session() ) - { - session.run( "CREATE (n {name:'Bob'})" ); - } - } - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer1.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer2.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldRoundRobinWriteSessionsInTransaction() throws Exception - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server - StubServer writeServer1 = stubController.startStub( "write_server_v3_write_tx.script", 9007 ); - StubServer writeServer2 = stubController.startStub( "write_server_v3_write_tx.script", 9008 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ) ) - { - for ( int i = 0; i < 2; i++ ) - { - try ( Session session = driver.session(); Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE (n {name:'Bob'})" ); - tx.commit(); - } - } - } - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer1.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer2.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldFailOnNonDiscoverableServer() throws IOException, InterruptedException - { - // Given - stubController.startStub( "discover_not_supported_9001.script", 9001 ); - - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - final Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - - //Expect - assertThrows( ServiceUnavailableException.class, driver::verifyConnectivity ); - } - - @Test - void shouldFailRandomFailureInGetServers() throws IOException, InterruptedException - { - // Given - stubController.startStub( "discover_failed.script", 9001 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - final Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - - //Expect - assertThrows( ServiceUnavailableException.class, driver::verifyConnectivity ); - } - - @Test - void shouldHandleLeaderSwitchWhenWriting() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server that doesn't accept writes - stubController.startStub( "not_able_to_write_server.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - boolean failed = false; - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ) ) - { - session.run( "CREATE ()" ).consume(); - } - catch ( SessionExpiredException e ) - { - failed = true; - assertThat( e.getMessage(), equalTo( "Server at 127.0.0.1:9007 no longer accepts writes" ) ); - } - assertTrue( failed ); - - driver.close(); - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldHandleLeaderSwitchWhenWritingWithoutConsuming() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server that doesn't accept writes - stubController.startStub( "not_able_to_write_server.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - boolean failed = false; - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ) ) - { - session.run( "CREATE ()" ); - } - catch ( SessionExpiredException e ) - { - failed = true; - assertThat( e.getMessage(), equalTo( "Server at 127.0.0.1:9007 no longer accepts writes" ) ); - } - assertTrue( failed ); - - driver.close(); - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldHandleLeaderSwitchWhenWritingInTransaction() throws IOException, InterruptedException, StubServer.ForceKilled - { - // Given - StubServer server = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - //START a write server that doesn't accept writes - stubController.startStub( "not_able_to_write_server.script", 9007 ); - URI uri = URI.create( "neo4j://127.0.0.1:9001" ); - Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); - boolean failed = false; - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).build() ); Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE ()" ).consume(); - } - catch ( SessionExpiredException e ) - { - failed = true; - assertThat( e.getMessage(), equalTo( "Server at 127.0.0.1:9007 no longer accepts writes" ) ); - } - assertTrue( failed ); - - driver.close(); - // Finally - assertThat( server.exitStatus(), equalTo( 0 ) ); - } - @Test void shouldHandleLeaderSwitchAndRetryWhenWritingInTxFunction() throws IOException, InterruptedException { @@ -636,97 +189,28 @@ void shouldHandleLeaderSwitchAndRetryWhenWritingInTxFunctionRX() throws IOExcept Flux fluxOfNames = Flux.usingWhen( Mono.fromSupplier( () -> driver.rxSession( builder().withDatabase( "mydatabase" ).build() ) ), session -> session.writeTransaction( tx -> - { - RxResult result = tx.run( "RETURN 1" ); - return Flux.from( result.records() ).limitRate( 100 ).thenMany( tx.run( "MATCH (n) RETURN n.name" ).records() ).limitRate( 100 ).map( - RoutingDriverBoltKitIT::extractNameField ); - } ), RxSession::close ); - - StepVerifier.create( fluxOfNames ).expectNext( "Foo", "Bar" ).verifyComplete(); - - // Finally - driver.close(); - assertThat( server.exitStatus(), equalTo( 0 ) ); - assertThat( writeServer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldSendInitialBookmark() throws Exception - { - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer writer = stubController.startStub( "write_tx_with_bookmarks.script", 9007 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ); - Session session = driver.session( builder().withBookmarks( parse( "OldBookmark" ) ).build() ) ) - { - try ( Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE (n {name:'Bob'})" ); - tx.commit(); - } - - assertEquals( parse( "NewBookmark" ), session.lastBookmark() ); - } - - assertThat( router.exitStatus(), equalTo( 0 ) ); - assertThat( writer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldUseWriteSessionModeAndInitialBookmark() throws Exception - { - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer writer = stubController.startStub( "write_tx_with_bookmarks.script", 9008 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.WRITE ).withBookmarks( parse( "OldBookmark" ) ).build() ) ) - { - try ( Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE (n {name:'Bob'})" ); - tx.commit(); - } - - assertEquals( parse( "NewBookmark" ), session.lastBookmark() ); - } - - assertThat( router.exitStatus(), equalTo( 0 ) ); - assertThat( writer.exitStatus(), equalTo( 0 ) ); - } - - @Test - void shouldUseReadSessionModeAndInitialBookmark() throws Exception - { - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer writer = stubController.startStub( "read_tx_with_bookmarks.script", 9005 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).withBookmarks( parse( "OldBookmark" ) ).build() ) ) - { - try ( Transaction tx = session.beginTransaction() ) - { - List records = tx.run( "MATCH (n) RETURN n.name AS name" ).list(); - assertEquals( 2, records.size() ); - assertEquals( "Bob", records.get( 0 ).get( "name" ).asString() ); - assertEquals( "Alice", records.get( 1 ).get( "name" ).asString() ); - tx.commit(); - } + { + RxResult result = tx.run( "RETURN 1" ); + return Flux.from( result.records() ).limitRate( 100 ).thenMany( tx.run( "MATCH (n) RETURN n.name" ).records() ).limitRate( 100 ).map( + RoutingDriverBoltKitIT::extractNameField ); + } ), RxSession::close ); - assertEquals( parse( "NewBookmark" ), session.lastBookmark() ); - } + StepVerifier.create( fluxOfNames ).expectNext( "Foo", "Bar" ).verifyComplete(); - assertThat( router.exitStatus(), equalTo( 0 ) ); - assertThat( writer.exitStatus(), equalTo( 0 ) ); + // Finally + driver.close(); + assertThat( server.exitStatus(), equalTo( 0 ) ); + assertThat( writeServer.exitStatus(), equalTo( 0 ) ); } @Test - void shouldPassBookmarkFromTransactionToTransaction() throws Exception + void shouldSendInitialBookmark() throws Exception { StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer writer = stubController.startStub( "write_read_tx_with_bookmarks.script", 9007 ); + StubServer writer = stubController.startStub( "write_tx_with_bookmarks.script", 9007 ); try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ); - Session session = driver.session( builder().withBookmarks( parse( "BookmarkA" ) ).build() ) ) + Session session = driver.session( builder().withBookmarks( parse( "OldBookmark" ) ).build() ) ) { try ( Transaction tx = session.beginTransaction() ) { @@ -734,69 +218,13 @@ void shouldPassBookmarkFromTransactionToTransaction() throws Exception tx.commit(); } - assertEquals( parse( "BookmarkB" ), session.lastBookmark() ); - - try ( Transaction tx = session.beginTransaction() ) - { - List records = tx.run( "MATCH (n) RETURN n.name AS name" ).list(); - assertEquals( 1, records.size() ); - assertEquals( "Bob", records.get( 0 ).get( "name" ).asString() ); - tx.commit(); - } - - assertEquals( parse( "BookmarkC" ), session.lastBookmark() ); + assertEquals( parse( "NewBookmark" ), session.lastBookmark() ); } assertThat( router.exitStatus(), equalTo( 0 ) ); assertThat( writer.exitStatus(), equalTo( 0 ) ); } - @Test - void shouldRetryReadTransactionUntilSuccess() throws Exception - { - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer brokenReader = stubController.startStub( "dead_read_server_tx.script", 9005 ); - StubServer reader = stubController.startStub( "read_server_v3_read_tx.script", 9006 ); - - try ( Driver driver = newDriverWithSleeplessClock( "neo4j://127.0.0.1:9001" ); Session session = driver.session() ) - { - AtomicInteger invocations = new AtomicInteger(); - List records = session.readTransaction( queryWork( "MATCH (n) RETURN n.name", invocations ) ); - - assertEquals( 3, records.size() ); - assertEquals( 2, invocations.get() ); - } - finally - { - assertEquals( 0, router.exitStatus() ); - assertEquals( 0, brokenReader.exitStatus() ); - assertEquals( 0, reader.exitStatus() ); - } - } - - @Test - void shouldRetryWriteTransactionUntilSuccess() throws Exception - { - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer brokenWriter = stubController.startStub( "dead_write_server.script", 9007 ); - StubServer writer = stubController.startStub( "write_server_v3_write_tx.script", 9008 ); - - try ( Driver driver = newDriverWithSleeplessClock( "neo4j://127.0.0.1:9001" ); Session session = driver.session() ) - { - AtomicInteger invocations = new AtomicInteger(); - List records = session.writeTransaction( queryWork( "CREATE (n {name:'Bob'})", invocations ) ); - - assertEquals( 0, records.size() ); - assertEquals( 2, invocations.get() ); - } - finally - { - assertEquals( 0, router.exitStatus() ); - assertEquals( 0, brokenWriter.exitStatus() ); - assertEquals( 0, writer.exitStatus() ); - } - } - @Test void shouldRetryWriteTransactionUntilSuccessWithWhenLeaderIsRemoved() throws Exception { @@ -906,104 +334,6 @@ void shouldRetryWriteTransactionUntilFailure() throws Exception } } - @Test - void shouldRetryReadTransactionAndPerformRediscoveryUntilSuccess() throws Exception - { - StubServer router1 = stubController.startStub( "acquire_endpoints_v3_9010.script", 9010 ); - StubServer brokenReader1 = stubController.startStub( "dead_read_server_tx.script", 9005 ); - StubServer brokenReader2 = stubController.startStub( "dead_read_server_tx.script", 9006 ); - StubServer router2 = stubController.startStub( "discover_servers_9010.script", 9003 ); - StubServer reader = stubController.startStub( "read_server_v3_read_tx.script", 9004 ); - - try ( Driver driver = newDriverWithSleeplessClock( "neo4j://127.0.0.1:9010" ); Session session = driver.session() ) - { - AtomicInteger invocations = new AtomicInteger(); - List records = session.readTransaction( queryWork( "MATCH (n) RETURN n.name", invocations ) ); - - assertEquals( 3, records.size() ); - assertEquals( 3, invocations.get() ); - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertEquals( 0, brokenReader1.exitStatus() ); - assertEquals( 0, brokenReader2.exitStatus() ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, reader.exitStatus() ); - } - } - - @Test - void shouldRetryWriteTransactionAndPerformRediscoveryUntilSuccess() throws Exception - { - StubServer router1 = stubController.startStub( "discover_servers_9010.script", 9010 ); - StubServer brokenWriter1 = stubController.startStub( "dead_write_server.script", 9001 ); - StubServer router2 = stubController.startStub( "acquire_endpoints_v3_9010.script", 9002 ); - StubServer brokenWriter2 = stubController.startStub( "dead_write_server.script", 9008 ); - StubServer writer = stubController.startStub( "write_server_v3_write_tx.script", 9007 ); - - try ( Driver driver = newDriverWithSleeplessClock( "neo4j://127.0.0.1:9010" ); Session session = driver.session() ) - { - AtomicInteger invocations = new AtomicInteger(); - List records = session.writeTransaction( queryWork( "CREATE (n {name:'Bob'})", invocations ) ); - - assertEquals( 0, records.size() ); - assertEquals( 3, invocations.get() ); - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertEquals( 0, brokenWriter1.exitStatus() ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, writer.exitStatus() ); - assertEquals( 0, brokenWriter2.exitStatus() ); - } - } - - @Test - void shouldUseInitialRouterForRediscoveryWhenAllOtherRoutersAreDead() throws Exception - { - // initial router does not have itself in the returned set of routers - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ) ) - { - driver.verifyConnectivity(); - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - { - // restart router on the same port with different script that contains itself as reader - assertEquals( 0, router.exitStatus() ); - - router = stubController.startStub( "rediscover_using_initial_router.script", 9001 ); - - List names = readStrings( "MATCH (n) RETURN n.name AS name", session ); - assertEquals( asList( "Bob", "Alice" ), names ); - } - } - - assertEquals( 0, router.exitStatus() ); - } - - @Test - void shouldInvokeProcedureGetRoutingTableWhenServerVersionPermits() throws Exception - { - // stub server is both a router and reader - StubServer server = stubController.startStub( "get_routing_table.script", 9001 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ); Session session = driver.session() ) - { - List records = session.run( "MATCH (n) RETURN n.name AS name" ).list(); - assertEquals( 3, records.size() ); - assertEquals( "Alice", records.get( 0 ).get( "name" ).asString() ); - assertEquals( "Bob", records.get( 1 ).get( "name" ).asString() ); - assertEquals( "Eve", records.get( 2 ).get( "name" ).asString() ); - } - finally - { - assertEquals( 0, server.exitStatus() ); - } - } - @Test void shouldSendRoutingContextToServer() throws Exception { @@ -1044,168 +374,6 @@ void shouldSendRoutingContextInHelloMessage() throws Exception } } - @Test - void shouldSendEmptyRoutingContextInHelloMessage() throws Exception - { - // stub server is both a router and reader - StubServer server = StubServer.start( "empty_routing_context_in_hello_neo4j.script", 9001 ); - - URI uri = URI.create( "neo4j://127.0.0.1:9001/" ); - try ( Driver driver = GraphDatabase.driver( uri, INSECURE_CONFIG ); Session session = driver.session() ) - { - List records = session.run( "MATCH (n) RETURN n.name AS name" ).list(); - assertEquals( 2, records.size() ); - assertEquals( "Alice", records.get( 0 ).get( "name" ).asString() ); - assertEquals( "Bob", records.get( 1 ).get( "name" ).asString() ); - } - finally - { - assertEquals( 0, server.exitStatus() ); - } - } - - @Test - void shouldServeReadsButFailWritesWhenNoWritersAvailable() throws Exception - { - StubServer router1 = stubController.startStub( "discover_no_writers_9010.script", 9010 ); - StubServer router2 = stubController.startStub( "discover_no_writers_9010.script", 9004 ); - StubServer reader = stubController.startStub( "read_server_v3_read_tx.script", 9003 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9010", INSECURE_CONFIG ); Session session = driver.session() ) - { - assertEquals( asList( "Bob", "Alice", "Tina" ), readStrings( "MATCH (n) RETURN n.name", session ) ); - - assertThrows( SessionExpiredException.class, () -> session.run( "CREATE (n {name:'Bob'})" ).consume() ); - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, reader.exitStatus() ); - } - } - - @Test - void shouldAcceptRoutingTableWithoutWritersAndThenRediscover() throws Exception - { - // first router does not have itself in the resulting routing table so connection - // towards it will be closed after rediscovery - StubServer router1 = stubController.startStub( "discover_no_writers_9010.script", 9010 ); - StubServer router2 = null; - StubServer reader = stubController.startStub( "read_server_v3_read_tx.script", 9003 ); - StubServer writer = stubController.startStub( "write_with_bookmarks.script", 9007 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9010", INSECURE_CONFIG ) ) - { - driver.verifyConnectivity(); - try ( Session session = driver.session() ) - { - // start another router which knows about writes, use same address as the initial router - router2 = stubController.startStub( "acquire_endpoints_v3_9010.script", 9010 ); - - assertEquals( asList( "Bob", "Alice", "Tina" ), readStrings( "MATCH (n) RETURN n.name", session ) ); - - Result createResult = session.run( "CREATE (n {name:'Bob'})" ); - assertFalse( createResult.hasNext() ); - } - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertNotNull( router2 ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, reader.exitStatus() ); - assertEquals( 0, writer.exitStatus() ); - } - } - - @Test - void shouldTreatRoutingTableWithSingleRouterAsValid() throws Exception - { - StubServer router = stubController.startStub( "discover_one_router.script", 9010 ); - StubServer reader1 = stubController.startStub( "read_server_v3_read.script", 9003 ); - StubServer reader2 = stubController.startStub( "read_server_v3_read.script", 9004 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9010", INSECURE_CONFIG ); - Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - { - // returned routing table contains only one router, this should be fine and we should be able to - // read multiple times without additional rediscovery - - Result readResult1 = session.run( "MATCH (n) RETURN n.name" ); - assertEquals( 3, readResult1.list().size() ); - assertEquals( "127.0.0.1:9003", readResult1.consume().server().address() ); - - Result readResult2 = session.run( "MATCH (n) RETURN n.name" ); - assertEquals( 3, readResult2.list().size() ); - assertEquals( "127.0.0.1:9004", readResult2.consume().server().address() ); - } - finally - { - assertEquals( 0, router.exitStatus() ); - assertEquals( 0, reader1.exitStatus() ); - assertEquals( 0, reader2.exitStatus() ); - } - } - - @Test - void shouldSendMultipleBookmarks() throws Exception - { - StubServer router = stubController.startStub( "acquire_endpoints_v3.script", 9001 ); - StubServer writer = stubController.startStub( "multiple_bookmarks.script", 9007 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", INSECURE_CONFIG ); Session session = driver.session( builder().withBookmarks( - InternalBookmark.parse( asOrderedSet( "neo4j:bookmark:v1:tx5", "neo4j:bookmark:v1:tx29", "neo4j:bookmark:v1:tx94", "neo4j:bookmark:v1:tx56", - "neo4j:bookmark:v1:tx16", "neo4j:bookmark:v1:tx68" ) ) ).build() ) ) - { - try ( Transaction tx = session.beginTransaction() ) - { - tx.run( "CREATE (n {name:'Bob'})" ); - tx.commit(); - } - - assertEquals( parse( "neo4j:bookmark:v1:tx95" ), session.lastBookmark() ); - } - finally - { - assertEquals( 0, router.exitStatus() ); - assertEquals( 0, writer.exitStatus() ); - } - } - - @Test - void shouldForgetAddressOnDatabaseUnavailableError() throws Exception - { - // perform initial discovery using router1 - StubServer router1 = stubController.startStub( "discover_servers_9010.script", 9010 ); - - // attempt to write using writer1 which fails with 'Neo.TransientError.General.DatabaseUnavailable' - // it should then be forgotten and trigger new rediscovery - StubServer writer1 = stubController.startStub( "writer_unavailable.script", 9001 ); - - // perform rediscovery using router2, it should return a valid writer2 - StubServer router2 = stubController.startStub( "acquire_endpoints_v3_9010.script", 9002 ); - - // write on writer2 should be successful - StubServer writer2 = stubController.startStub( "write_server_v3_write_tx.script", 9007 ); - - try ( Driver driver = newDriverWithSleeplessClock( "neo4j://127.0.0.1:9010" ); Session session = driver.session() ) - { - AtomicInteger invocations = new AtomicInteger(); - List records = session.writeTransaction( queryWork( "CREATE (n {name:'Bob'})", invocations ) ); - - assertThat( records, hasSize( 0 ) ); - assertEquals( 2, invocations.get() ); - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertEquals( 0, writer1.exitStatus() ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, writer2.exitStatus() ); - } - } - @Test void shouldFailInitialDiscoveryWhenConfiguredResolverThrows() { @@ -1220,53 +388,6 @@ void shouldFailInitialDiscoveryWhenConfiguredResolverThrows() verify( resolver ).resolve( ServerAddress.of( "my.server.com", 9001 ) ); } - @Test - void shouldUseResolverDuringRediscoveryWhenExistingRoutersFail() throws Exception - { - StubServer router1 = stubController.startStub( "get_routing_table.script", 9001 ); - StubServer router2 = stubController.startStub( "acquire_endpoints_v3.script", 9042 ); - StubServer reader = stubController.startStub( "read_server_v3_read_tx.script", 9005 ); - - AtomicBoolean resolverInvoked = new AtomicBoolean(); - ServerAddressResolver resolver = address -> - { - if ( resolverInvoked.compareAndSet( false, true ) ) - { - // return the address first time - return singleton( address ); - } - if ( "127.0.0.1".equals( address.host() ) && address.port() == 9001 ) - { - // return list of addresses where onl 9042 is functional - return new HashSet<>( - asList( ServerAddress.of( "127.0.0.1", 9010 ), ServerAddress.of( "127.0.0.1", 9011 ), ServerAddress.of( "127.0.0.1", 9042 ) ) ); - } - throw new AssertionError(); - }; - - Config config = insecureBuilder().withResolver( resolver ).build(); - - try ( Driver driver = GraphDatabase.driver( "neo4j://127.0.0.1:9001", config ) ) - { - try ( Session session = driver.session() ) - { - // run first query against 9001, which should return result and exit - List names1 = session.run( "MATCH (n) RETURN n.name AS name" ).list( record -> record.get( "name" ).asString() ); - assertEquals( asList( "Alice", "Bob", "Eve" ), names1 ); - - // run second query with retries, it should rediscover using 9042 returned by the resolver and read from 9005 - List names2 = session.readTransaction( tx -> tx.run( "MATCH (n) RETURN n.name" ).list( RoutingDriverBoltKitIT::extractNameField ) ); - assertEquals( asList( "Bob", "Alice", "Tina" ), names2 ); - } - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, reader.exitStatus() ); - } - } - @Test void useSessionAfterDriverIsClosed() throws Exception { @@ -1294,41 +415,6 @@ void useSessionAfterDriverIsClosed() throws Exception } } - @Test - void shouldRevertToInitialRouterIfKnownRouterThrowsProtocolErrors() throws Exception - { - ServerAddressResolver resolver = a -> - { - SortedSet addresses = new TreeSet<>( new PortBasedServerAddressComparator() ); - addresses.add( ServerAddress.of( "127.0.0.1", 9001 ) ); - addresses.add( ServerAddress.of( "127.0.0.1", 9003 ) ); - return addresses; - }; - - Config config = insecureBuilder().withResolver( resolver ).build(); - - StubServer router1 = stubController.startStub( "acquire_endpoints_v3_point_to_empty_router_and_exit.script", 9001 ); - StubServer router2 = stubController.startStub( "acquire_endpoints_v3_empty.script", 9004 ); - StubServer router3 = stubController.startStub( "acquire_endpoints_v3_three_servers_and_exit.script", 9003 ); - StubServer reader = stubController.startStub( "read_server_v3_read_tx.script", 9002 ); - - try ( Driver driver = GraphDatabase.driver( "neo4j://my.virtual.host:8080", config ) ) - { - try ( Session session = driver.session( builder().withDefaultAccessMode( AccessMode.READ ).build() ) ) - { - List records = session.readTransaction( tx -> tx.run( "MATCH (n) RETURN n.name" ).list() ); - assertEquals( 3, records.size() ); - } - } - finally - { - assertEquals( 0, router1.exitStatus() ); - assertEquals( 0, router2.exitStatus() ); - assertEquals( 0, router3.exitStatus() ); - assertEquals( 0, reader.exitStatus() ); - } - } - @Test void shouldServerWithBoltV4SupportMultiDb() throws Throwable { diff --git a/driver/src/test/resources/acquire_endpoints_v3_9010.script b/driver/src/test/resources/acquire_endpoints_v3_9010.script deleted file mode 100644 index 44a7fc6269..0000000000 --- a/driver/src/test/resources/acquire_endpoints_v3_9010.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9010"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9007","127.0.0.1:9008"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9005","127.0.0.1:9006"], "role": "READ"},{"addresses": ["127.0.0.1:9001","127.0.0.1:9002","127.0.0.1:9003"], "role": "ROUTE"}]] - SUCCESS {} diff --git a/driver/src/test/resources/acquire_endpoints_v3_empty.script b/driver/src/test/resources/acquire_endpoints_v3_empty.script deleted file mode 100644 index 9f63f22d6c..0000000000 --- a/driver/src/test/resources/acquire_endpoints_v3_empty.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "my.virtual.host:8080"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, []] - SUCCESS {} diff --git a/driver/src/test/resources/acquire_endpoints_v3_point_to_empty_router_and_exit.script b/driver/src/test/resources/acquire_endpoints_v3_point_to_empty_router_and_exit.script deleted file mode 100644 index f6598a6d52..0000000000 --- a/driver/src/test/resources/acquire_endpoints_v3_point_to_empty_router_and_exit.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "my.virtual.host:8080"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9010"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9011"], "role": "READ"},{"addresses": ["127.0.0.1:9004"], "role": "ROUTE"}]] - SUCCESS {} diff --git a/driver/src/test/resources/acquire_endpoints_v3_three_servers_and_exit.script b/driver/src/test/resources/acquire_endpoints_v3_three_servers_and_exit.script deleted file mode 100644 index c6ad010fd4..0000000000 --- a/driver/src/test/resources/acquire_endpoints_v3_three_servers_and_exit.script +++ /dev/null @@ -1,12 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE -!: AUTO BEGIN - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "my.virtual.host:8080"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9002","127.0.0.1:9003"], "role": "READ"},{"addresses": ["127.0.0.1:9001","127.0.0.1:9002","127.0.0.1:9003"], "role": "ROUTE"}]] - SUCCESS {} - diff --git a/driver/src/test/resources/dead_read_server.script b/driver/src/test/resources/dead_read_server.script deleted file mode 100644 index 186d3d81fb..0000000000 --- a/driver/src/test/resources/dead_read_server.script +++ /dev/null @@ -1,8 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "MATCH (n) RETURN n.name" {} {"mode": "r"} -C: PULL_ALL -S: diff --git a/driver/src/test/resources/discover_failed.script b/driver/src/test/resources/discover_failed.script deleted file mode 100644 index dc4d7dc34a..0000000000 --- a/driver/src/test/resources/discover_failed.script +++ /dev/null @@ -1,12 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {}} {} - PULL_ALL -S: FAILURE {"code": "Neo.ClientError.General.Unknown", "message": "wut!"} -S: IGNORED -C: RESET -S: SUCCESS {} -S: diff --git a/driver/src/test/resources/discover_no_writers.script b/driver/src/test/resources/discover_no_writers.script deleted file mode 100644 index 915e8fbada..0000000000 --- a/driver/src/test/resources/discover_no_writers.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9001"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": [],"role": "WRITE"}, {"addresses": ["127.0.0.1:9002","127.0.0.1:9003"], "role": "READ"},{"addresses": ["127.0.0.1:9004","127.0.0.1:9005"], "role": "ROUTE"}]] - SUCCESS {} diff --git a/driver/src/test/resources/discover_no_writers_9010.script b/driver/src/test/resources/discover_no_writers_9010.script deleted file mode 100644 index 860c371bf9..0000000000 --- a/driver/src/test/resources/discover_no_writers_9010.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9010"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": [],"role": "WRITE"}, {"addresses": ["127.0.0.1:9002","127.0.0.1:9003"], "role": "READ"},{"addresses": ["127.0.0.1:9004","127.0.0.1:9005"], "role": "ROUTE"}]] - SUCCESS {} diff --git a/driver/src/test/resources/discover_one_router.script b/driver/src/test/resources/discover_one_router.script deleted file mode 100644 index 74d8afc424..0000000000 --- a/driver/src/test/resources/discover_one_router.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9010"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001","127.0.0.1:9002"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9003","127.0.0.1:9004"], "role": "READ"},{"addresses": ["127.0.0.1:9005"], "role": "ROUTE"}]] - SUCCESS {} diff --git a/driver/src/test/resources/discover_servers_9010.script b/driver/src/test/resources/discover_servers_9010.script deleted file mode 100644 index fe2b0b6df8..0000000000 --- a/driver/src/test/resources/discover_servers_9010.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9010"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9002","127.0.0.1:9003","127.0.0.1:9004"], "role": "READ"},{"addresses": ["127.0.0.1:9001","127.0.0.1:9002","127.0.0.1:9003"], "role": "ROUTE"}]] - SUCCESS {} diff --git a/driver/src/test/resources/empty_routing_context_in_hello_neo4j.script b/driver/src/test/resources/empty_routing_context_in_hello_neo4j.script deleted file mode 100644 index f23e4a20b6..0000000000 --- a/driver/src/test/resources/empty_routing_context_in_hello_neo4j.script +++ /dev/null @@ -1,17 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO GOODBYE - -C: HELLO {"scheme": "none", "user_agent": "neo4j-java/dev", "routing": { "address": "127.0.0.1:9001"} } -S: SUCCESS {"server": "Neo4j/3.5.0", "connection_id": "bolt-123456789"} -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": { "address": "127.0.0.1:9001"} } {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "READ"},{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "ROUTE"}]] - SUCCESS {} -C: RUN "MATCH (n) RETURN n.name AS name" {} {} - PULL_ALL -S: SUCCESS {"fields": ["name"]} - RECORD ["Alice"] - RECORD ["Bob"] - SUCCESS {} diff --git a/driver/src/test/resources/get_routing_table.script b/driver/src/test/resources/get_routing_table.script deleted file mode 100644 index 8e8207a294..0000000000 --- a/driver/src/test/resources/get_routing_table.script +++ /dev/null @@ -1,18 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9001"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "READ"},{"addresses": ["127.0.0.1:9001", "127.0.0.1:9002"], "role": "ROUTE"}]] - SUCCESS {} -C: RUN "MATCH (n) RETURN n.name AS name" {} {} - PULL_ALL -S: SUCCESS {"fields": ["name"]} - RECORD ["Alice"] - RECORD ["Bob"] - RECORD ["Eve"] - SUCCESS {} -S: diff --git a/driver/src/test/resources/not_able_to_write_server.script b/driver/src/test/resources/not_able_to_write_server.script deleted file mode 100644 index 6cef3e3361..0000000000 --- a/driver/src/test/resources/not_able_to_write_server.script +++ /dev/null @@ -1,13 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE -!: AUTO BEGIN -!: AUTO ROLLBACK - -C: RUN "CREATE ()" {} {} -C: PULL_ALL -S: FAILURE {"code": "Neo.ClientError.Cluster.NotALeader", "message": "blabla"} -S: IGNORED -C: RESET -S: SUCCESS {} diff --git a/driver/src/test/resources/read_server_v3_read_tx.script b/driver/src/test/resources/read_server_v3_read_tx.script deleted file mode 100644 index eb231ceef1..0000000000 --- a/driver/src/test/resources/read_server_v3_read_tx.script +++ /dev/null @@ -1,16 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: BEGIN { "mode": "r" } -S: SUCCESS {} -C: RUN "MATCH (n) RETURN n.name" {} {} - PULL_ALL -S: SUCCESS {"fields": ["n.name"]} - RECORD ["Bob"] - RECORD ["Alice"] - RECORD ["Tina"] - SUCCESS {} -C: COMMIT -S: SUCCESS { "bookmark": "ABookmark" } diff --git a/driver/src/test/resources/read_tx_with_bookmarks.script b/driver/src/test/resources/read_tx_with_bookmarks.script deleted file mode 100644 index b2ff13ad4f..0000000000 --- a/driver/src/test/resources/read_tx_with_bookmarks.script +++ /dev/null @@ -1,15 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: BEGIN {"bookmarks": ["OldBookmark"], "mode": "r"} -S: SUCCESS {} -C: RUN "MATCH (n) RETURN n.name AS name" {} {} - PULL_ALL -S: SUCCESS {"fields": ["name"]} - RECORD ["Bob"] - RECORD ["Alice"] - SUCCESS {} -C: COMMIT -S: SUCCESS {"bookmark": "NewBookmark"} diff --git a/driver/src/test/resources/rediscover_using_initial_router.script b/driver/src/test/resources/rediscover_using_initial_router.script deleted file mode 100644 index 0424345453..0000000000 --- a/driver/src/test/resources/rediscover_using_initial_router.script +++ /dev/null @@ -1,18 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE -!: AUTO BEGIN -!: AUTO COMMIT - -C: RUN "CALL dbms.cluster.routing.getRoutingTable($context)" {"context": {"address": "127.0.0.1:9001"}} {} - PULL_ALL -S: SUCCESS {"fields": ["ttl", "servers"]} - RECORD [9223372036854775807, [{"addresses": ["127.0.0.1:9008"],"role": "WRITE"}, {"addresses": ["127.0.0.1:9001","127.0.0.1:9009","127.0.0.1:9010"], "role": "READ"},{"addresses": ["127.0.0.1:9001","127.0.0.1:9011"], "role": "ROUTE"}]] - SUCCESS {} -C: RUN "MATCH (n) RETURN n.name AS name" {} {} - PULL_ALL -S: SUCCESS {"fields": ["name"]} - RECORD ["Bob"] - RECORD ["Alice"] - SUCCESS {} diff --git a/driver/src/test/resources/write_read_tx_with_bookmarks.script b/driver/src/test/resources/write_read_tx_with_bookmarks.script deleted file mode 100644 index 73d106108d..0000000000 --- a/driver/src/test/resources/write_read_tx_with_bookmarks.script +++ /dev/null @@ -1,22 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO GOODBYE -!: AUTO HELLO - -C: BEGIN {"bookmarks": ["BookmarkA"]} -S: SUCCESS {} -C: RUN "CREATE (n {name:'Bob'})" {} {} - PULL_ALL -S: SUCCESS {} - SUCCESS {} -C: COMMIT -S: SUCCESS {"bookmark": "BookmarkB"} -C: BEGIN {"bookmarks": ["BookmarkB"]} -S: SUCCESS {} -C: RUN "MATCH (n) RETURN n.name AS name" {} {} - PULL_ALL -S: SUCCESS {"fields": ["name"]} - RECORD ["Bob"] - SUCCESS {} -C: COMMIT -S: SUCCESS {"bookmark": "BookmarkC"} diff --git a/driver/src/test/resources/write_server_v3_write.script b/driver/src/test/resources/write_server_v3_write.script deleted file mode 100644 index 4527fb8302..0000000000 --- a/driver/src/test/resources/write_server_v3_write.script +++ /dev/null @@ -1,9 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CREATE (n {name:'Bob'})" {} {} - PULL_ALL -S: SUCCESS {} - SUCCESS {} diff --git a/driver/src/test/resources/write_with_bookmarks.script b/driver/src/test/resources/write_with_bookmarks.script deleted file mode 100644 index 79821f9fdb..0000000000 --- a/driver/src/test/resources/write_with_bookmarks.script +++ /dev/null @@ -1,9 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE - -C: RUN "CREATE (n {name:'Bob'})" {} {"bookmarks": ["ABookmark"]} - PULL_ALL -S: SUCCESS {} - SUCCESS {} diff --git a/driver/src/test/resources/writer_unavailable.script b/driver/src/test/resources/writer_unavailable.script deleted file mode 100644 index b041ff3b8e..0000000000 --- a/driver/src/test/resources/writer_unavailable.script +++ /dev/null @@ -1,10 +0,0 @@ -!: BOLT 3 -!: AUTO RESET -!: AUTO HELLO -!: AUTO GOODBYE -!: AUTO BEGIN - -C: RUN "CREATE (n {name:'Bob'})" {} {} -C: PULL_ALL -S: FAILURE {"code": "Neo.TransientError.General.DatabaseUnavailable", "message": "Database is busy doing store copy"} -S: IGNORED diff --git a/testkit-backend/pom.xml b/testkit-backend/pom.xml index 6def17fdf1..8df01c8760 100644 --- a/testkit-backend/pom.xml +++ b/testkit-backend/pom.xml @@ -35,7 +35,7 @@ org.projectlombok lombok - 1.18.12 + 1.18.16 provided 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 index 3bb94ccb28..317fa8087b 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java @@ -20,7 +20,6 @@ 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; @@ -147,7 +146,7 @@ else if ( currentLine.equals( "#request end" ) ) // Error to track String id = testkitState.newId(); testkitState.getErrors().put( id, (Neo4jException) e ); - writeResponse( driverError( id ) ); + writeResponse( driverError( id, (Neo4jException) e ) ); System.out.println( "Neo4jException: " + e ); } else @@ -174,9 +173,9 @@ else if ( currentLine.equals( "#request end" ) ) } } - private DriverError driverError( String id ) + private DriverError driverError( String id, Neo4jException e ) { - return DriverError.builder().data( DriverError.DriverErrorBody.builder().id( id ).build() ).build(); + return DriverError.builder().data( DriverError.DriverErrorBody.builder().id( id ).errorType( e.getClass().getName() ).build() ).build(); } public void processRequest( String request ) 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 index dc413a7ec6..1d966da508 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; @@ -30,6 +31,7 @@ import org.neo4j.driver.Result; import org.neo4j.driver.Transaction; import org.neo4j.driver.exceptions.Neo4jException; +import org.neo4j.driver.net.ServerAddress; @Getter public class TestkitState @@ -42,6 +44,7 @@ public class TestkitState private int idGenerator = 0; private final Consumer responseWriter; private final Supplier processor; + private final Map> idToServerAddresses = new HashMap<>(); public TestkitState( Consumer responseWriter, Supplier processor ) { 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 index 2937c09363..c34bafc40d 100644 --- 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 @@ -23,6 +23,7 @@ import lombok.Setter; import neo4j.org.testkit.backend.TestkitState; import neo4j.org.testkit.backend.messages.responses.Driver; +import neo4j.org.testkit.backend.messages.responses.ResolverResolutionRequired; import neo4j.org.testkit.backend.messages.responses.TestkitErrorResponse; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; @@ -32,6 +33,7 @@ import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Config; import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.net.ServerAddressResolver; @Setter @Getter @@ -59,11 +61,35 @@ public TestkitResponse process( TestkitState testkitState ) } Config.ConfigBuilder configBuilder = Config.builder(); + if ( data.isResolverRegistered() ) + { + configBuilder.withResolver( callbackResolver( testkitState ) ); + } 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(); } + private ServerAddressResolver callbackResolver( TestkitState testkitState ) + { + return address -> + { + String callbackId = testkitState.newId(); + ResolverResolutionRequired.ResolverResolutionRequiredBody body = + ResolverResolutionRequired.ResolverResolutionRequiredBody.builder() + .id( callbackId ) + .address( address.toString() ) + .build(); + ResolverResolutionRequired response = + ResolverResolutionRequired.builder() + .data( body ) + .build(); + testkitState.getResponseWriter().accept( response ); + testkitState.getProcessor().get(); + return testkitState.getIdToServerAddresses().remove( callbackId ); + }; + } + @Setter @Getter @NoArgsConstructor @@ -72,5 +98,6 @@ public static class NewDriverBody private String uri; private AuthorizationToken authorizationToken; private String userAgent; + private boolean resolverRegistered; } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResolverResolutionCompleted.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResolverResolutionCompleted.java new file mode 100644 index 0000000000..9408cea5f4 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResolverResolutionCompleted.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) "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 java.util.LinkedHashSet; +import java.util.List; +import java.util.stream.Collectors; + +import org.neo4j.driver.internal.BoltServerAddress; + +@Setter +@Getter +@NoArgsConstructor +public class ResolverResolutionCompleted implements TestkitRequest +{ + private ResolverResolutionCompletedBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + testkitState.getIdToServerAddresses().put( data.getRequestId(), data.getAddresses().stream().map( BoltServerAddress::new ) + .collect( Collectors.toCollection( LinkedHashSet::new ) ) ); + return null; + } + + @Setter + @Getter + @NoArgsConstructor + public static class ResolverResolutionCompletedBody + { + private String requestId; + private List addresses; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java new file mode 100644 index 0000000000..3a4df65624 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) "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.ResultSummary; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import org.neo4j.driver.Result; +import org.neo4j.driver.exceptions.NoSuchRecordException; + +@Setter +@Getter +@NoArgsConstructor +public class ResultConsume implements TestkitRequest +{ + private ResultConsumeBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + try + { + Result result = testkitState.getResults().get( data.getResultId() ); + result.consume(); + return ResultSummary.builder().build(); + } + catch ( NoSuchRecordException ignored ) + { + return NullRecord.builder().build(); + } + } + + @Setter + @Getter + @NoArgsConstructor + public static class ResultConsumeBody + { + private String resultId; + } +} 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 index b1a9e0ceb2..8a50c459f7 100644 --- 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 @@ -27,11 +27,13 @@ @JsonSubTypes( { @JsonSubTypes.Type( NewDriver.class ), @JsonSubTypes.Type( NewSession.class ), @JsonSubTypes.Type( SessionRun.class ), @JsonSubTypes.Type( ResultNext.class ), + @JsonSubTypes.Type( ResultConsume.class ), @JsonSubTypes.Type( VerifyConnectivity.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 ), - @JsonSubTypes.Type( SessionLastBookmarks.class ), @JsonSubTypes.Type( SessionWriteTransaction.class ) + @JsonSubTypes.Type( SessionLastBookmarks.class ), @JsonSubTypes.Type( SessionWriteTransaction.class ), + @JsonSubTypes.Type( ResolverResolutionCompleted.class ) } ) public interface TestkitRequest { diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/VerifyConnectivity.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/VerifyConnectivity.java new file mode 100644 index 0000000000..e985452acc --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/VerifyConnectivity.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) "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 VerifyConnectivity implements TestkitRequest +{ + private VerifyConnectivityBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + String id = data.getDriverId(); + testkitState.getDrivers().get( id ).verifyConnectivity(); + return Driver.builder().data( Driver.DriverBody.builder().id( id ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class VerifyConnectivityBody + { + private String driverId; + } +} 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 index bd19554770..2e373e8e1d 100644 --- 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 @@ -41,5 +41,7 @@ public String testkitName() public static class DriverErrorBody { private String id; + + private String errorType; } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResolverResolutionRequired.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResolverResolutionRequired.java new file mode 100644 index 0000000000..685e3a3742 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResolverResolutionRequired.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) "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 ResolverResolutionRequired implements TestkitResponse +{ + private ResolverResolutionRequiredBody data; + + @Override + public String testkitName() + { + return "ResolverResolutionRequired"; + } + + @Setter + @Getter + @Builder + public static class ResolverResolutionRequiredBody + { + private String id; + + private String address; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java new file mode 100644 index 0000000000..0df7a42e1a --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) "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 ResultSummary implements TestkitResponse +{ + @Override + public String testkitName() + { + return "ResultSummary"; + } +}