From d25bcbadd3b7d1d0bcda68dc5fb810186cf0952e Mon Sep 17 00:00:00 2001 From: Dmitriy Tverdiakov Date: Tue, 28 Jun 2022 17:28:38 +0100 Subject: [PATCH] Migrate tests depending on Boltkit to use Docker Boltkit has been deprecated and requires additional setup on the host to run tests. This update migrates tests depending on Boltkit to use Docker that is needed for Testkit testing as well. --- README.md | 17 +- driver/pom.xml | 4 + .../driver/integration/ConnectionPoolIT.java | 3 +- .../driver/integration/CredentialsIT.java | 62 +--- .../driver/integration/DirectDriverIT.java | 16 - .../driver/integration/EncryptionIT.java | 10 +- .../org/neo4j/driver/integration/ErrorIT.java | 9 +- .../neo4j/driver/integration/LoadCSVIT.java | 6 +- .../driver/integration/ServerKilledIT.java | 13 +- .../neo4j/driver/integration/SessionIT.java | 4 +- .../integration/TrustCustomCertificateIT.java | 10 +- .../async/AsyncSessionServerRestartIT.java | 12 +- .../integration/async/AsyncTransactionIT.java | 2 +- .../integration/reactive/RxTransactionIT.java | 2 +- .../async/pool/NettyChannelPoolIT.java | 3 +- .../util/Neo4jWithFeatureCondition.java | 20 +- .../driver/util/CertificateExtension.java | 63 ---- .../neo4j/driver/util/CertificateUtil.java | 33 +- .../neo4j/driver/util/DatabaseExtension.java | 305 ++++++++++++++--- .../org/neo4j/driver/util/Neo4jRunner.java | 317 ------------------ .../org/neo4j/driver/util/Neo4jSettings.java | 21 +- .../driver/util/ProcessEnvConfigurator.java | 64 ---- .../driver/util/cc/CommandLineException.java | 29 -- .../neo4j/driver/util/cc/CommandLineUtil.java | 109 ------ driver/src/test/resources/logback-test.xml | 14 + driver/src/test/resources/nginx.conf | 30 ++ examples/pom.xml | 4 + .../org/neo4j/docs/driver/ExamplesIT.java | 94 +++--- pom.xml | 2 +- testkit-tests/pom.xml | 2 +- 30 files changed, 439 insertions(+), 841 deletions(-) delete mode 100644 driver/src/test/java/org/neo4j/driver/util/CertificateExtension.java delete mode 100644 driver/src/test/java/org/neo4j/driver/util/Neo4jRunner.java delete mode 100644 driver/src/test/java/org/neo4j/driver/util/ProcessEnvConfigurator.java delete mode 100644 driver/src/test/java/org/neo4j/driver/util/cc/CommandLineException.java delete mode 100644 driver/src/test/java/org/neo4j/driver/util/cc/CommandLineUtil.java create mode 100644 driver/src/test/resources/logback-test.xml create mode 100644 driver/src/test/resources/nginx.conf diff --git a/README.md b/README.md index 8e4b4bd413..7642301a08 100644 --- a/README.md +++ b/README.md @@ -97,19 +97,18 @@ To use the driver in a project, please use the released driver via Maven Central #### Running Tests and Creating a Package -Our test setup requires a bit of infrastructure to run. -We rely mostly on [`testkit`](https://github.com/neo4j-drivers/testkit). -Testkit is a tooling that is used to run integration tests for all of the drivers we provide. - -Some older tests still rely on [`boltkit`](https://github.com/neo4j-drivers/boltkit), the predecessor to Testkit. -If `boltkit` is not installed, then all tests that requires `boltkit` will be ignored and will not be executed. - -In case you want or can verify contributions with unit tests alone, use the following command to skip all integration and Testkit tests: - +The following command may be used to unit test and install artifacts without running integration tests: ``` mvn clean install -DskipITs -P skip-testkit ``` +Integration tests have the following prerequisites: +- Docker +- [`Testkit`](https://github.com/neo4j-drivers/testkit) + +Testkit that is a tooling that is used to run integration tests for all official Neo4j drivers. +It can be executed using Docker during Maven build and in such case does not require additional setup. See the instructions below for more details. + There are 2 ways of running Testkit tests: 1. Using the `testkit-tests` module of this project. 2. Manually cloning Testkit and running it directly. diff --git a/driver/pom.xml b/driver/pom.xml index c16eccd294..0b71dff909 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -100,6 +100,10 @@ org.reactivestreams reactive-streams-tck + + ch.qos.logback + logback-classic + diff --git a/driver/src/test/java/org/neo4j/driver/integration/ConnectionPoolIT.java b/driver/src/test/java/org/neo4j/driver/integration/ConnectionPoolIT.java index 02b849ae19..c9c7cf0d1f 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ConnectionPoolIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ConnectionPoolIT.java @@ -80,7 +80,8 @@ void shouldRecoverFromDownedServer() throws Throwable { sessionGrabber.start(); // When - neo4j.forceRestartDb(); + neo4j.stopProxy(); + neo4j.startProxy(); // Then we accept a hump with failing sessions, but demand that failures stop as soon as the server is back up. sessionGrabber.assertSessionsAvailableWithin(120); diff --git a/driver/src/test/java/org/neo4j/driver/integration/CredentialsIT.java b/driver/src/test/java/org/neo4j/driver/integration/CredentialsIT.java index c5d2777878..720f40c75f 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/CredentialsIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/CredentialsIT.java @@ -25,13 +25,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.neo4j.driver.AuthTokens.basic; import static org.neo4j.driver.AuthTokens.custom; -import static org.neo4j.driver.Values.ofValue; -import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING; -import static org.neo4j.driver.util.Neo4jRunner.PASSWORD; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -44,11 +39,7 @@ import org.neo4j.driver.Value; import org.neo4j.driver.exceptions.AuthenticationException; import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.internal.security.InternalAuthToken; -import org.neo4j.driver.internal.util.DisabledOnNeo4jWith; -import org.neo4j.driver.internal.util.Neo4jFeature; import org.neo4j.driver.util.DatabaseExtension; -import org.neo4j.driver.util.Neo4jSettings; import org.neo4j.driver.util.ParallelizableIT; @ParallelizableIT @@ -56,46 +47,10 @@ class CredentialsIT { @RegisterExtension static final DatabaseExtension neo4j = new DatabaseExtension(); - @Test - @DisabledOnNeo4jWith(Neo4jFeature.BOLT_V4) - // This feature is removed in 4.0 - void shouldBePossibleToChangePassword() throws Exception { - String newPassword = "secret"; - String tmpDataDir = Files.createTempDirectory(Paths.get("target"), "tmp") - .toAbsolutePath() - .toString() - .replace("\\", "/"); - - neo4j.restartDb(Neo4jSettings.TEST_SETTINGS.updateWith(Neo4jSettings.DATA_DIR, tmpDataDir)); - - AuthToken authToken = new InternalAuthToken(parameters( - "scheme", "basic", - "principal", "neo4j", - "credentials", "neo4j", - "new_credentials", newPassword) - .asMap(ofValue())); - - // change the password - try (Driver driver = GraphDatabase.driver(neo4j.uri(), authToken); - Session session = driver.session()) { - session.run("RETURN 1").consume(); - } - - // verify old password does not work - final Driver badDriver = GraphDatabase.driver(CredentialsIT.neo4j.uri(), basic("neo4j", PASSWORD)); - assertThrows(AuthenticationException.class, badDriver::verifyConnectivity); - - // verify new password works - try (Driver driver = GraphDatabase.driver(CredentialsIT.neo4j.uri(), AuthTokens.basic("neo4j", newPassword)); - Session session = driver.session()) { - session.run("RETURN 2").consume(); - } - } - @Test void basicCredentialsShouldWork() { // When & Then - try (Driver driver = GraphDatabase.driver(neo4j.uri(), basic("neo4j", PASSWORD)); + try (Driver driver = GraphDatabase.driver(neo4j.uri(), basic("neo4j", neo4j.adminPassword())); Session session = driver.session()) { Value single = session.run("RETURN 1").single().get(0); assertThat(single.asLong(), equalTo(1L)); @@ -105,7 +60,8 @@ void basicCredentialsShouldWork() { @Test void shouldGetHelpfulErrorOnInvalidCredentials() { SecurityException e = assertThrows(SecurityException.class, () -> { - try (Driver driver = GraphDatabase.driver(neo4j.uri(), basic("thisisnotthepassword", PASSWORD)); + try (Driver driver = + GraphDatabase.driver(neo4j.uri(), basic("thisisnotthepassword", neo4j.adminPassword())); Session session = driver.session()) { session.run("RETURN 1"); } @@ -116,7 +72,7 @@ void shouldGetHelpfulErrorOnInvalidCredentials() { @Test void shouldBeAbleToProvideRealmWithBasicAuth() { // When & Then - try (Driver driver = GraphDatabase.driver(neo4j.uri(), basic("neo4j", PASSWORD, "native")); + try (Driver driver = GraphDatabase.driver(neo4j.uri(), basic("neo4j", neo4j.adminPassword(), "native")); Session session = driver.session()) { Value single = session.run("CREATE () RETURN 1").single().get(0); assertThat(single.asLong(), equalTo(1L)); @@ -126,7 +82,8 @@ void shouldBeAbleToProvideRealmWithBasicAuth() { @Test void shouldBeAbleToConnectWithCustomToken() { // When & Then - try (Driver driver = GraphDatabase.driver(neo4j.uri(), custom("neo4j", PASSWORD, "native", "basic")); + try (Driver driver = + GraphDatabase.driver(neo4j.uri(), custom("neo4j", neo4j.adminPassword(), "native", "basic")); Session session = driver.session()) { Value single = session.run("CREATE () RETURN 1").single().get(0); assertThat(single.asLong(), equalTo(1L)); @@ -138,7 +95,8 @@ void shouldBeAbleToConnectWithCustomTokenWithAdditionalParameters() { Map params = singletonMap("secret", 16); // When & Then - try (Driver driver = GraphDatabase.driver(neo4j.uri(), custom("neo4j", PASSWORD, "native", "basic", params)); + try (Driver driver = GraphDatabase.driver( + neo4j.uri(), custom("neo4j", neo4j.adminPassword(), "native", "basic", params)); Session session = driver.session()) { Value single = session.run("CREATE () RETURN 1").single().get(0); assertThat(single.asLong(), equalTo(1L)); @@ -147,12 +105,12 @@ void shouldBeAbleToConnectWithCustomTokenWithAdditionalParameters() { @Test void directDriverShouldFailEarlyOnWrongCredentials() { - testDriverFailureOnWrongCredentials("bolt://localhost:" + neo4j.boltPort()); + testDriverFailureOnWrongCredentials(neo4j.uri().toString()); } @Test void routingDriverShouldFailEarlyOnWrongCredentials() { - testDriverFailureOnWrongCredentials("neo4j://localhost:" + neo4j.boltPort()); + testDriverFailureOnWrongCredentials(neo4j.uri().toString()); } private void testDriverFailureOnWrongCredentials(String uri) { diff --git a/driver/src/test/java/org/neo4j/driver/integration/DirectDriverIT.java b/driver/src/test/java/org/neo4j/driver/integration/DirectDriverIT.java index 442ab39b56..ecd2d3c2d0 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/DirectDriverIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/DirectDriverIT.java @@ -25,14 +25,11 @@ import static org.neo4j.driver.internal.util.Matchers.directDriverWithAddress; import java.net.URI; -import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Result; -import org.neo4j.driver.Session; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.util.DatabaseExtension; import org.neo4j.driver.util.ParallelizableIT; @@ -87,17 +84,4 @@ void shouldRegisterSingleServer() { // Then assertThat(driver, is(directDriverWithAddress(address))); } - - @Test - void shouldConnectIPv6Uri() { - // Given - try (Driver driver = GraphDatabase.driver("bolt://[::1]:" + neo4j.boltPort(), neo4j.authToken()); - Session session = driver.session()) { - // When - Result result = session.run("RETURN 1"); - - // Then - assertThat(result.single().get(0).asInt(), CoreMatchers.equalTo(1)); - } - } } diff --git a/driver/src/test/java/org/neo4j/driver/integration/EncryptionIT.java b/driver/src/test/java/org/neo4j/driver/integration/EncryptionIT.java index 692fd1245c..2337b01aa9 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/EncryptionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/EncryptionIT.java @@ -25,6 +25,8 @@ import static org.neo4j.driver.Config.TrustStrategy.trustAllCertificates; import java.net.URI; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.neo4j.driver.Config; @@ -81,7 +83,9 @@ void shouldOperateWithoutEncryptionWhenItIsAlsoDisabledInTheDatabase() { } private void testMatchingEncryption(BoltTlsLevel tlsLevel, boolean driverEncrypted, String scheme) { - neo4j.restartDb(Neo4jSettings.TEST_SETTINGS.updateWith(Neo4jSettings.BOLT_TLS_LEVEL, tlsLevel.toString())); + Map tlsConfig = new HashMap<>(); + tlsConfig.put(Neo4jSettings.BOLT_TLS_LEVEL, tlsLevel.toString()); + neo4j.deleteAndStartNeo4j(tlsConfig); Config config; if (scheme.equals(Scheme.BOLT_LOW_TRUST_URI_SCHEME)) { @@ -107,7 +111,9 @@ private void testMatchingEncryption(BoltTlsLevel tlsLevel, boolean driverEncrypt } private void testMismatchingEncryption(BoltTlsLevel tlsLevel, boolean driverEncrypted) { - neo4j.restartDb(Neo4jSettings.TEST_SETTINGS.updateWith(Neo4jSettings.BOLT_TLS_LEVEL, tlsLevel.toString())); + Map tlsConfig = new HashMap<>(); + tlsConfig.put(Neo4jSettings.BOLT_TLS_LEVEL, tlsLevel.toString()); + neo4j.deleteAndStartNeo4j(tlsConfig); Config config = newConfig(driverEncrypted); ServiceUnavailableException e = assertThrows( diff --git a/driver/src/test/java/org/neo4j/driver/integration/ErrorIT.java b/driver/src/test/java/org/neo4j/driver/integration/ErrorIT.java index 43c3909879..6c3104cf09 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ErrorIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ErrorIT.java @@ -177,13 +177,12 @@ void shouldHandleFailureAtRunTime() { } @Test - void shouldGetHelpfulErrorWhenTryingToConnectToHttpPort() throws Throwable { - // the http server needs some time to start up - Thread.sleep(2000); - + void shouldGetHelpfulErrorWhenTryingToConnectToHttpPort() { Config config = Config.builder().withoutEncryption().build(); - final Driver driver = GraphDatabase.driver("bolt://localhost:" + session.httpPort(), config); + URI boltUri = session.uri(); + URI uri = URI.create(String.format("%s://%s:%d", boltUri.getScheme(), boltUri.getHost(), session.httpPort())); + final Driver driver = GraphDatabase.driver(uri, config); ClientException e = assertThrows(ClientException.class, driver::verifyConnectivity); assertEquals( "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " diff --git a/driver/src/test/java/org/neo4j/driver/integration/LoadCSVIT.java b/driver/src/test/java/org/neo4j/driver/integration/LoadCSVIT.java index 15a5f95471..44abb08f48 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/LoadCSVIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/LoadCSVIT.java @@ -31,14 +31,12 @@ import org.neo4j.driver.Result; import org.neo4j.driver.Session; import org.neo4j.driver.util.DatabaseExtension; -import org.neo4j.driver.util.Neo4jSettings; import org.neo4j.driver.util.ParallelizableIT; @ParallelizableIT class LoadCSVIT { @RegisterExtension - static final DatabaseExtension neo4j = new DatabaseExtension( - Neo4jSettings.TEST_SETTINGS.without(Neo4jSettings.IMPORT_DIR).without(Neo4jSettings.SERVER_IMPORT_DIR)); + static final DatabaseExtension neo4j = new DatabaseExtension(); @Test void shouldLoadCSV() throws Throwable { @@ -74,7 +72,7 @@ private String createLocalIrisData(Session session) throws IOException { session.run("CREATE (c:Class {name: $className}) RETURN c", parameters("className", className)); } - return neo4j.putTmpFile("iris", ".csv", IRIS_DATA).toExternalForm(); + return neo4j.addImportFile("iris", ".csv", IRIS_DATA); } private static String[] IRIS_CLASS_NAMES = new String[] {"Iris-setosa", "Iris-versicolor", "Iris-virginica"}; diff --git a/driver/src/test/java/org/neo4j/driver/integration/ServerKilledIT.java b/driver/src/test/java/org/neo4j/driver/integration/ServerKilledIT.java index b57b91a690..cc15f8df61 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/ServerKilledIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ServerKilledIT.java @@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import static org.neo4j.driver.Config.TrustStrategy.trustCustomCertificateSignedBy; -import static org.neo4j.driver.util.Neo4jRunner.DEFAULT_AUTH_TOKEN; import java.util.List; import java.util.concurrent.TimeUnit; @@ -69,11 +68,12 @@ private static Stream data() { @MethodSource("data") void shouldRecoverFromServerRestart(String name, Config.ConfigBuilder configBuilder) { // Given config with sessionLivenessCheckTimeout not set, i.e. turned off - try (Driver driver = GraphDatabase.driver(neo4j.uri(), DEFAULT_AUTH_TOKEN, configBuilder.build())) { + try (Driver driver = GraphDatabase.driver(neo4j.uri(), neo4j.authToken(), configBuilder.build())) { acquireAndReleaseConnections(4, driver); // When - neo4j.forceRestartDb(); + neo4j.stopProxy(); + neo4j.startProxy(); // Then we should be able to start using sessions again, at most O(numSessions) session calls later int toleratedFailures = 4; @@ -102,8 +102,9 @@ void shouldDropBrokenOldSessions(String name, Config.ConfigBuilder configBuilder try (Driver driver = createDriver(clock, configBuilder.build())) { acquireAndReleaseConnections(5, driver); - // restart database to invalidate all idle connections in the pool - neo4j.forceRestartDb(); + // restart database access to invalidate all idle connections in the pool + neo4j.stopProxy(); + neo4j.startProxy(); // move clock forward more than configured liveness check timeout clock.progress(TimeUnit.MINUTES.toMillis(livenessCheckTimeoutMinutes + 1)); @@ -131,6 +132,6 @@ private Driver createDriver(Clock clock, Config config) { RoutingSettings routingSettings = RoutingSettings.DEFAULT; RetrySettings retrySettings = RetrySettings.DEFAULT; return factory.newInstance( - neo4j.uri(), DEFAULT_AUTH_TOKEN, routingSettings, retrySettings, config, SecurityPlanImpl.insecure()); + neo4j.uri(), neo4j.authToken(), routingSettings, retrySettings, config, SecurityPlanImpl.insecure()); } } diff --git a/driver/src/test/java/org/neo4j/driver/integration/SessionIT.java b/driver/src/test/java/org/neo4j/driver/integration/SessionIT.java index 3ede881fe5..cc235c64a1 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/SessionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/SessionIT.java @@ -50,7 +50,6 @@ import static org.neo4j.driver.internal.util.Matchers.connectionAcquisitionTimeoutError; import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V4; import static org.neo4j.driver.util.DaemonThreadFactory.daemon; -import static org.neo4j.driver.util.Neo4jRunner.DEFAULT_AUTH_TOKEN; import java.util.HashSet; import java.util.List; @@ -1247,10 +1246,9 @@ private Driver newDriverWithoutRetries() { private Driver newDriverWithFixedRetries(int maxRetriesCount) { DriverFactory driverFactory = new DriverFactoryWithFixedRetryLogic(maxRetriesCount); - AuthToken auth = DEFAULT_AUTH_TOKEN; return driverFactory.newInstance( neo4j.uri(), - auth, + neo4j.authToken(), RoutingSettings.DEFAULT, RetrySettings.DEFAULT, noLoggingConfig(), diff --git a/driver/src/test/java/org/neo4j/driver/integration/TrustCustomCertificateIT.java b/driver/src/test/java/org/neo4j/driver/integration/TrustCustomCertificateIT.java index ca592292ab..b327ac1250 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/TrustCustomCertificateIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/TrustCustomCertificateIT.java @@ -24,6 +24,7 @@ import static org.neo4j.driver.Config.TrustStrategy.trustCustomCertificateSignedBy; import static org.neo4j.driver.util.CertificateUtil.createNewCertificateAndKey; import static org.neo4j.driver.util.CertificateUtil.createNewCertificateAndKeySignedBy; +import static org.neo4j.driver.util.DatabaseExtension.getDockerHostGeneralName; import java.io.File; import java.util.function.Supplier; @@ -35,7 +36,6 @@ import org.neo4j.driver.Result; import org.neo4j.driver.Session; import org.neo4j.driver.exceptions.SecurityException; -import org.neo4j.driver.util.CertificateExtension; import org.neo4j.driver.util.CertificateUtil.CertificateKeyPair; import org.neo4j.driver.util.DatabaseExtension; import org.neo4j.driver.util.ParallelizableIT; @@ -43,7 +43,7 @@ @ParallelizableIT class TrustCustomCertificateIT { @RegisterExtension - static final DatabaseExtension neo4j = new CertificateExtension(); + static final DatabaseExtension neo4j = new DatabaseExtension(); @Test void shouldAcceptServerWithCertificateSignedByDriverCertificate() throws Throwable { @@ -51,7 +51,7 @@ void shouldAcceptServerWithCertificateSignedByDriverCertificate() throws Throwab CertificateKeyPair root = createNewCertificateAndKey(); // When - CertificateKeyPair server = createNewCertificateAndKeySignedBy(root); + CertificateKeyPair server = createNewCertificateAndKeySignedBy(root, getDockerHostGeneralName()); neo4j.updateEncryptionKeyAndCert(server.key(), server.cert()); // Then @@ -59,7 +59,7 @@ void shouldAcceptServerWithCertificateSignedByDriverCertificate() throws Throwab } @Test - void shouldAcceptServerWithSameCertificate() throws Throwable { + void shouldAcceptServerWithSameCertificate() { shouldBeAbleToRunCypher(() -> createDriverWithCustomCertificate(neo4j.tlsCertFile())); } @@ -70,7 +70,7 @@ void shouldRejectServerWithUntrustedCertificate() throws Throwable { // When & Then final Driver driver = createDriverWithCustomCertificate(certificateAndKey.cert()); - SecurityException error = assertThrows(SecurityException.class, driver::verifyConnectivity); + assertThrows(SecurityException.class, driver::verifyConnectivity); } private void shouldBeAbleToRunCypher(Supplier driverSupplier) { diff --git a/driver/src/test/java/org/neo4j/driver/integration/async/AsyncSessionServerRestartIT.java b/driver/src/test/java/org/neo4j/driver/integration/async/AsyncSessionServerRestartIT.java index 7370eb0d0d..0db23e4ec6 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/async/AsyncSessionServerRestartIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/async/AsyncSessionServerRestartIT.java @@ -67,26 +67,26 @@ void shouldFailWhenServerIsRestarted() { ResultCursor cursor = await(session.runAsync(query)); if (i == 0) { - neo4j.stopDb(); + neo4j.stopProxy(); } List records = await(cursor.listAsync()); assertEquals(100, records.size()); } }); - neo4j.startDb(); + neo4j.startProxy(); } @Test void shouldRunAfterRunFailureToAcquireConnection() { - neo4j.stopDb(); + neo4j.stopProxy(); assertThrows(ServiceUnavailableException.class, () -> { ResultCursor cursor = await(session.runAsync("RETURN 42")); await(cursor.nextAsync()); }); - neo4j.startDb(); + neo4j.startProxy(); ResultCursor cursor2 = await(session.runAsync("RETURN 42")); Record record = await(cursor2.singleAsync()); @@ -95,14 +95,14 @@ void shouldRunAfterRunFailureToAcquireConnection() { @Test void shouldBeginTxAfterRunFailureToAcquireConnection() { - neo4j.stopDb(); + neo4j.stopProxy(); assertThrows(ServiceUnavailableException.class, () -> { ResultCursor cursor = await(session.runAsync("RETURN 42")); await(cursor.consumeAsync()); }); - neo4j.startDb(); + neo4j.startProxy(); AsyncTransaction tx = await(session.beginTransactionAsync()); ResultCursor cursor2 = await(tx.runAsync("RETURN 42")); diff --git a/driver/src/test/java/org/neo4j/driver/integration/async/AsyncTransactionIT.java b/driver/src/test/java/org/neo4j/driver/integration/async/AsyncTransactionIT.java index a9722344d2..753e04a819 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/async/AsyncTransactionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/async/AsyncTransactionIT.java @@ -508,7 +508,7 @@ void shouldFailToCommitWhenServerIsRestarted() { await(tx.runAsync("CREATE ()")); - neo4j.stopDb(); + neo4j.stopProxy(); assertThrows(ServiceUnavailableException.class, () -> await(tx.commitAsync())); } diff --git a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java index 5c5336a682..b77d9f7ddb 100644 --- a/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java +++ b/driver/src/test/java/org/neo4j/driver/integration/reactive/RxTransactionIT.java @@ -549,7 +549,7 @@ void shouldFailToCommitWhenServerIsRestarted() { assertThrows(ServiceUnavailableException.class, () -> { await(Flux.from(result.records()).doOnSubscribe(subscription -> { - neo4j.stopDb(); + neo4j.stopProxy(); })); await(tx.commit()); }); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java index ea974d06fd..c15022ffec 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/pool/NettyChannelPoolIT.java @@ -55,7 +55,6 @@ import org.neo4j.driver.internal.util.FakeClock; import org.neo4j.driver.internal.util.ImmediateSchedulingEventExecutor; import org.neo4j.driver.util.DatabaseExtension; -import org.neo4j.driver.util.Neo4jRunner; import org.neo4j.driver.util.ParallelizableIT; @ParallelizableIT @@ -122,7 +121,7 @@ void shouldAllowAcquireAfterFailures() throws Exception { AuthenticationException e = assertThrows(AuthenticationException.class, () -> acquire(pool)); } - authTokenMap.put("credentials", value(Neo4jRunner.PASSWORD)); + authTokenMap.put("credentials", value(neo4j.adminPassword())); assertNotNull(acquire(pool)); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/Neo4jWithFeatureCondition.java b/driver/src/test/java/org/neo4j/driver/internal/util/Neo4jWithFeatureCondition.java index 165e8b3c20..656c8fb16c 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/Neo4jWithFeatureCondition.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/Neo4jWithFeatureCondition.java @@ -30,8 +30,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.neo4j.driver.Driver; import org.neo4j.driver.Session; -import org.neo4j.driver.util.Neo4jRunner; -import org.neo4j.driver.util.Neo4jSettings; +import org.neo4j.driver.util.DatabaseExtension; public class Neo4jWithFeatureCondition implements ExecutionCondition { private static final ConditionEvaluationResult ENABLED_NOT_ANNOTATED = @@ -63,7 +62,7 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con } private static ConditionEvaluationResult checkFeatureAvailability(Neo4jFeature feature, boolean negated) { - Driver driver = getSharedNeo4jDriver(); + Driver driver = DatabaseExtension.getInstance().driver(); if (driver != null) { try (Session session = driver.session()) { String agent = session.readTransaction( @@ -88,7 +87,7 @@ private static ConditionEvaluationResult checkEditionAvailability( if (previousResult.isDisabled()) { return previousResult; } - Driver driver = getSharedNeo4jDriver(); + Driver driver = DatabaseExtension.getInstance().driver(); if (driver != null) { try (Session session = driver.session()) { String value = session.run("CALL dbms.components() YIELD edition") @@ -122,17 +121,4 @@ private static ConditionEvaluationResult createResult( : disabled("Disabled on neo4j " + version + " because it does not support " + feature); } } - - private static Driver getSharedNeo4jDriver() { - try { - Neo4jRunner runner = Neo4jRunner.getOrCreateGlobalRunner(); - // ensure database is running with default credentials - runner.ensureRunning(Neo4jSettings.TEST_SETTINGS); - return runner.driver(); - } catch (Throwable t) { - System.err.println("Failed to check database version in the test execution condition"); - t.printStackTrace(); - return null; - } - } } diff --git a/driver/src/test/java/org/neo4j/driver/util/CertificateExtension.java b/driver/src/test/java/org/neo4j/driver/util/CertificateExtension.java deleted file mode 100644 index c654729d37..0000000000 --- a/driver/src/test/java/org/neo4j/driver/util/CertificateExtension.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 org.neo4j.driver.util; - -import static org.neo4j.driver.util.Neo4jRunner.debug; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.Arrays; -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -public class CertificateExtension extends DatabaseExtension implements AfterEachCallback { - private Path originalKeyFile; - private Path originalCertFile; - - @Override - public void beforeEach(ExtensionContext context) throws Exception { - super.beforeEach(context); - - originalKeyFile = Files.createTempFile("key-file-", ""); - originalCertFile = Files.createTempFile("cert-file-", ""); - - Files.copy(tlsKeyFile().toPath(), originalKeyFile, StandardCopyOption.REPLACE_EXISTING); - Files.copy(tlsCertFile().toPath(), originalCertFile, StandardCopyOption.REPLACE_EXISTING); - } - - @Override - public void afterEach(ExtensionContext context) throws Exception { - // if the key and cert file changed, then we restore the file and restart the server. - if (!smallFileContentEquals(tlsKeyFile().toPath(), originalKeyFile) - || !smallFileContentEquals(tlsCertFile().toPath(), originalCertFile)) { - debug("Restoring original key and certificate file after certificate test."); - updateEncryptionKeyAndCert(originalKeyFile.toFile(), originalCertFile.toFile()); - } - Files.deleteIfExists(originalKeyFile); - Files.deleteIfExists(originalCertFile); - } - - private boolean smallFileContentEquals(Path path, Path pathAnother) throws IOException { - byte[] fileContent = Files.readAllBytes(path); - byte[] fileContentAnother = Files.readAllBytes(pathAnother); - return Arrays.equals(fileContent, fileContentAnother); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/util/CertificateUtil.java b/driver/src/test/java/org/neo4j/driver/util/CertificateUtil.java index f987b5bd5f..b84181c1c2 100644 --- a/driver/src/test/java/org/neo4j/driver/util/CertificateUtil.java +++ b/driver/src/test/java/org/neo4j/driver/util/CertificateUtil.java @@ -37,8 +37,11 @@ import java.security.Security; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Date; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.BasicConstraints; @@ -79,7 +82,7 @@ private static KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchA } private static X509Certificate generateCert( - X500Name issuer, X500Name subject, KeyPair issuerKeys, PublicKey publicKey) + X500Name issuer, X500Name subject, KeyPair issuerKeys, PublicKey publicKey, GeneralName... generalNames) throws GeneralSecurityException, OperatorCreationException, CertIOException { // Create x509 certificate Date startDate = new Date(System.currentTimeMillis()); @@ -89,7 +92,10 @@ private static X509Certificate generateCert( new JcaX509v3CertificateBuilder(issuer, serialNum, startDate, endDate, subject, publicKey); // Subject alternative name (part of SNI extension, used for hostname verification) - GeneralNames subjectAlternativeName = new GeneralNames(new GeneralName(GeneralName.dNSName, DEFAULT_HOST_NAME)); + Set names = new HashSet<>(); + names.add(new GeneralName(GeneralName.dNSName, DEFAULT_HOST_NAME)); + names.addAll(Arrays.asList(generalNames)); + GeneralNames subjectAlternativeName = new GeneralNames(names.toArray(new GeneralName[0])); certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAlternativeName); certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true)); @@ -107,7 +113,7 @@ public static class SelfSignedCertificateGenerator { private final KeyPair keyPair; private final X509Certificate certificate; - public SelfSignedCertificateGenerator() + public SelfSignedCertificateGenerator(GeneralName... generalNames) throws GeneralSecurityException, OperatorCreationException, CertIOException { // Create the public/private rsa key pair keyPair = generateKeyPair(); @@ -117,7 +123,8 @@ public SelfSignedCertificateGenerator() new X500Name("CN=" + DEFAULT_HOST_NAME), new X500Name("CN=" + DEFAULT_HOST_NAME), keyPair, - keyPair.getPublic()); + keyPair.getPublic(), + generalNames); } public void savePrivateKey(File saveTo) throws IOException { @@ -128,14 +135,15 @@ public void saveSelfSignedCertificate(File saveTo) throws CertificateEncodingExc writePem("CERTIFICATE", certificate.getEncoded(), saveTo); } - public X509Certificate sign(PKCS10CertificationRequest csr, PublicKey csrPublicKey) + public X509Certificate sign(PKCS10CertificationRequest csr, PublicKey csrPublicKey, GeneralName... generalNames) throws GeneralSecurityException, OperatorCreationException, CertIOException { return generateCert( X500Name.getInstance( this.certificate.getSubjectX500Principal().getEncoded()), csr.getSubject(), keyPair, - csrPublicKey); + csrPublicKey, + generalNames); } } @@ -195,26 +203,27 @@ private static void writePem(String type, byte[] encodedContent, File path) thro } } - public static CertificateKeyPair createNewCertificateAndKeySignedBy(CertificateKeyPair root) - throws Throwable { + public static CertificateKeyPair createNewCertificateAndKeySignedBy( + CertificateKeyPair root, GeneralName... generalNames) throws Throwable { Objects.requireNonNull(root.certGenerator); File cert = tempFile("driver", ".cert"); File key = tempFile("driver", ".key"); CertificateUtil.CertificateSigningRequestGenerator csrGenerator = new CertificateUtil.CertificateSigningRequestGenerator(); - X509Certificate signedCert = - root.certGenerator.sign(csrGenerator.certificateSigningRequest(), csrGenerator.publicKey()); + X509Certificate signedCert = root.certGenerator.sign( + csrGenerator.certificateSigningRequest(), csrGenerator.publicKey(), generalNames); csrGenerator.savePrivateKey(key); saveX509Cert(signedCert, cert); return new CertificateKeyPair<>(cert, key); } - public static CertificateKeyPair createNewCertificateAndKey() throws Throwable { + public static CertificateKeyPair createNewCertificateAndKey(GeneralName... ipAddresses) + throws Throwable { File cert = tempFile("driver", ".cert"); File key = tempFile("driver", ".key"); CertificateUtil.SelfSignedCertificateGenerator certGenerator = - new CertificateUtil.SelfSignedCertificateGenerator(); + new CertificateUtil.SelfSignedCertificateGenerator(ipAddresses); certGenerator.saveSelfSignedCertificate(cert); certGenerator.savePrivateKey(key); diff --git a/driver/src/test/java/org/neo4j/driver/util/DatabaseExtension.java b/driver/src/test/java/org/neo4j/driver/util/DatabaseExtension.java index 349a8c1faa..c0eea975f1 100644 --- a/driver/src/test/java/org/neo4j/driver/util/DatabaseExtension.java +++ b/driver/src/test/java/org/neo4j/driver/util/DatabaseExtension.java @@ -19,121 +19,227 @@ package org.neo4j.driver.util; import static java.lang.Integer.parseInt; -import static org.neo4j.driver.util.Neo4jRunner.DEFAULT_AUTH_TOKEN; -import static org.neo4j.driver.util.Neo4jRunner.HOME_DIR; -import static org.neo4j.driver.util.Neo4jRunner.debug; -import static org.neo4j.driver.util.Neo4jRunner.getOrCreateGlobalRunner; -import static org.neo4j.driver.util.Neo4jSettings.DEFAULT_TLS_CERT_PATH; -import static org.neo4j.driver.util.Neo4jSettings.DEFAULT_TLS_KEY_PATH; +import static org.neo4j.driver.util.Neo4jSettings.BOLT_TLS_LEVEL; +import static org.neo4j.driver.util.Neo4jSettings.BoltTlsLevel.OPTIONAL; +import static org.neo4j.driver.util.Neo4jSettings.BoltTlsLevel.REQUIRED; +import static org.neo4j.driver.util.Neo4jSettings.SSL_POLICY_BOLT_CLIENT_AUTH; +import static org.neo4j.driver.util.Neo4jSettings.SSL_POLICY_BOLT_ENABLED; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.URI; -import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.bouncycastle.asn1.x509.GeneralName; import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Config; import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; import org.neo4j.driver.Session; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.types.TypeSystem; +import org.neo4j.driver.util.CertificateUtil.CertificateKeyPair; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Neo4jContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; -public class DatabaseExtension implements BeforeEachCallback, AfterAllCallback { - static final String TEST_RESOURCE_FOLDER_PATH = "src/test/resources"; +public class DatabaseExtension implements ExecutionCondition, BeforeEachCallback, AfterEachCallback, AfterAllCallback { + private static final int BOLT_PORT = 7687; + private static final int HTTP_PORT = 7474; - private final Neo4jSettings settings; - private Neo4jRunner runner; + private static final boolean dockerAvailable; + private static final DatabaseExtension instance; + private static final URI boltUri; + private static final URI httpUri; + private static final AuthToken authToken; + private static final File cert; + private static final File key; + private static final Network network; + private static final GenericContainer nginx; + private static final Map defaultConfig; - public DatabaseExtension() { - this(Neo4jSettings.TEST_SETTINGS); + private static Neo4jContainer neo4jContainer; + private static Driver driver; + private static boolean nginxRunning; + + static { + dockerAvailable = isDockerAvailable(); + instance = new DatabaseExtension(); + defaultConfig = new HashMap<>(); + defaultConfig.put(SSL_POLICY_BOLT_ENABLED, "true"); + defaultConfig.put(SSL_POLICY_BOLT_CLIENT_AUTH, "NONE"); + defaultConfig.put(BOLT_TLS_LEVEL, OPTIONAL.toString()); + + if (dockerAvailable) { + CertificateKeyPair pair = generateCertificateAndKey(); + cert = pair.cert(); + key = pair.key(); + + network = Network.newNetwork(); + neo4jContainer = setupNeo4jContainer(cert, key, defaultConfig); + neo4jContainer.start(); + nginx = setupNginxContainer(); + nginx.start(); + nginxRunning = true; + + URI neo4jBoltUri = URI.create(neo4jContainer.getBoltUrl()); + URI neo4jHttpUri = URI.create(neo4jContainer.getHttpUrl()); + + boltUri = URI.create(String.format( + "%s://%s:%d", neo4jBoltUri.getScheme(), neo4jBoltUri.getHost(), nginx.getMappedPort(BOLT_PORT))); + httpUri = URI.create(String.format( + "%s://%s:%d", neo4jHttpUri.getScheme(), neo4jHttpUri.getHost(), nginx.getMappedPort(HTTP_PORT))); + + authToken = AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword()); + driver = GraphDatabase.driver(boltUri, authToken); + waitForBoltAvailability(); + } else { + // stub init, this is not usable when Docker is unavailable + boltUri = URI.create(""); + httpUri = URI.create(""); + authToken = AuthTokens.none(); + cert = new File(""); + key = new File(""); + network = null; + nginx = new GenericContainer<>(DockerImageName.parse("alpine:latest")); + } } - public DatabaseExtension(Neo4jSettings settings) { - this.settings = settings; + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + return dockerAvailable + ? ConditionEvaluationResult.enabled("Docker is available") + : ConditionEvaluationResult.disabled("Docker is unavailable"); } @Override public void beforeEach(ExtensionContext context) throws Exception { - runner = getOrCreateGlobalRunner(); - runner.ensureRunning(settings); - TestUtil.cleanDb(driver()); + TestUtil.cleanDb(driver); } @Override - public void afterAll(ExtensionContext context) { - if (runner != null) { - runner.stopNeo4j(); + public void afterEach(ExtensionContext context) throws Exception { + if (!nginxRunning) { + startProxy(); } } + @Override + public void afterAll(ExtensionContext context) { + deleteAndStartNeo4j(Collections.emptyMap()); + } + public Driver driver() { - return runner.driver(); + return driver; } public TypeSystem typeSystem() { - return driver().defaultTypeSystem(); + return driver.defaultTypeSystem(); } - public void forceRestartDb() { - runner.forceToRestart(); - } + public void deleteAndStartNeo4j(Map config) { + Map updatedConfig = new HashMap<>(defaultConfig); + updatedConfig.putAll(config); - public void restartDb(Neo4jSettings neo4jSettings) { - runner.restartNeo4j(neo4jSettings); + neo4jContainer.stop(); + neo4jContainer = setupNeo4jContainer(cert, key, updatedConfig); + neo4jContainer.start(); + if (REQUIRED.toString().equals(config.get(BOLT_TLS_LEVEL))) { + driver = GraphDatabase.driver( + boltUri, + authToken, + Config.builder() + .withTrustStrategy(Config.TrustStrategy.trustCustomCertificateSignedBy(cert)) + .withEncryption() + .build()); + } else { + driver = GraphDatabase.driver(boltUri, authToken); + } + waitForBoltAvailability(); } - public URL putTmpFile(String prefix, String suffix, String contents) throws IOException { + public String addImportFile(String prefix, String suffix, String contents) throws IOException { File tmpFile = File.createTempFile(prefix, suffix, null); tmpFile.deleteOnExit(); try (PrintWriter out = new PrintWriter(tmpFile)) { out.println(contents); } - return tmpFile.toURI().toURL(); + Path tmpFilePath = tmpFile.toPath(); + Path targetPath = + Paths.get("/var/lib/neo4j/import", tmpFilePath.getFileName().toString()); + neo4jContainer.copyFileToContainer(MountableFile.forHostPath(tmpFilePath), targetPath.toString()); + return String.format("file:///%s", tmpFile.getName()); } public URI uri() { - return runner.boltUri(); + return boltUri; } public int httpPort() { - return runner.httpPort(); + return httpUri.getPort(); } public int boltPort() { - return runner.boltPort(); + return boltUri.getPort(); } public AuthToken authToken() { - return DEFAULT_AUTH_TOKEN; + return authToken; } - public BoltServerAddress address() { - return runner.boltAddress(); + public String adminPassword() { + return neo4jContainer.getAdminPassword(); } - public void updateEncryptionKeyAndCert(File key, File cert) throws Exception { - FileTools.copyFile(key, tlsKeyFile()); - FileTools.copyFile(cert, tlsCertFile()); - debug("Updated neo4j key and certificate file."); - runner.forceToRestart(); // needs to force to restart as no configuration changed + public BoltServerAddress address() { + return new BoltServerAddress(boltUri); } - public File tlsCertFile() { - return new File(HOME_DIR, DEFAULT_TLS_CERT_PATH); + public void updateEncryptionKeyAndCert(File key, File cert) { + System.out.println("Updated neo4j key and certificate file."); + neo4jContainer.stop(); + neo4jContainer = setupNeo4jContainer(cert, key, defaultConfig); + neo4jContainer.start(); + driver = GraphDatabase.driver(boltUri, authToken); + waitForBoltAvailability(); } - public File tlsKeyFile() { - return new File(HOME_DIR, DEFAULT_TLS_KEY_PATH); + public File tlsCertFile() { + return cert; } - public void startDb() { - runner.startNeo4j(); + public void startProxy() { + try { + nginx.execInContainer("nginx"); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + nginxRunning = true; } - public void stopDb() { - runner.stopNeo4j(); + public void stopProxy() { + try { + nginx.execInContainer("nginx", "-s", "stop"); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + nginxRunning = false; } public boolean isNeo4j44OrEarlier() { @@ -144,12 +250,8 @@ public boolean isNeo4j43OrEarlier() { return isNeo4jVersionOrEarlier(4, 3); } - public void dumpLogs() { - runner.dumpDebugLog(); - } - private boolean isNeo4jVersionOrEarlier(int major, int minor) { - try (Session session = driver().session()) { + try (Session session = driver.session()) { String neo4jVersion = session.readTransaction( tx -> tx.run("CALL dbms.components() YIELD versions " + "RETURN versions[0] AS version") .single() @@ -159,4 +261,99 @@ private boolean isNeo4jVersionOrEarlier(int major, int minor) { return parseInt(versions[0]) <= major && parseInt(versions[1]) <= minor; } } + + public static DatabaseExtension getInstance() { + return instance; + } + + public static GeneralName getDockerHostGeneralName() { + String host = DockerClientFactory.instance().dockerHostIpAddress(); + GeneralName generalName; + try { + generalName = new GeneralName(GeneralName.iPAddress, host); + } catch (IllegalArgumentException e) { + generalName = new GeneralName(GeneralName.dNSName, host); + } + return generalName; + } + + private static CertificateKeyPair generateCertificateAndKey() { + try { + return CertificateUtil.createNewCertificateAndKey(getDockerHostGeneralName()); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + private static Neo4jContainer setupNeo4jContainer(File cert, File key, Map config) { + String neo4JVersion = + Optional.ofNullable(System.getenv("NEO4J_VERSION")).orElse("4.4"); + + ImageFromDockerfile extendedNeo4jImage = new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> builder.from(String.format("neo4j:%s-enterprise", neo4JVersion)) + .run("mkdir /var/lib/neo4j/certificates/bolt") + .copy("public.crt", "/var/lib/neo4j/certificates/bolt/") + .copy("private.key", "/var/lib/neo4j/certificates/bolt/") + .build()) + .withFileFromPath("public.crt", cert.toPath()) + .withFileFromPath("private.key", key.toPath()); + + DockerImageName extendedNeo4jImageAsSubstitute = + DockerImageName.parse(extendedNeo4jImage.get()).asCompatibleSubstituteFor("neo4j"); + + neo4jContainer = new Neo4jContainer<>(extendedNeo4jImageAsSubstitute) + .withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes") + .withNetwork(network) + .withNetworkAliases("neo4j"); + for (Map.Entry entry : config.entrySet()) { + neo4jContainer.withNeo4jConfig(entry.getKey(), entry.getValue()); + } + + return neo4jContainer; + } + + private static GenericContainer setupNginxContainer() { + ImageFromDockerfile extendedNginxImage = new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> builder.from("nginx:1.23.0-alpine") + .copy("nginx.conf", "/etc/nginx/") + .build()) + .withFileFromClasspath("nginx.conf", "nginx.conf"); + + //noinspection rawtypes + return new GenericContainer(extendedNginxImage.get()) + .withNetwork(network) + .withExposedPorts(BOLT_PORT, HTTP_PORT) + .withCommand("sh", "-c", "nginx && while sleep 3600; do :; done"); + } + + private static void waitForBoltAvailability() { + int maxAttempts = 600; + for (int attempt = 0; attempt < maxAttempts; attempt++) { + try { + driver.verifyConnectivity(); + return; + } catch (RuntimeException verificationException) { + if (attempt == maxAttempts - 1) { + throw new RuntimeException( + "Timed out waiting for Neo4j to become available over Bolt", verificationException); + } + try { + Thread.sleep(500); + } catch (InterruptedException interruptedException) { + interruptedException.addSuppressed(verificationException); + throw new RuntimeException( + "Interrupted while waiting for Neo4j to become available over Bolt", interruptedException); + } + } + } + } + + private static boolean isDockerAvailable() { + try { + DockerClientFactory.instance().client(); + return true; + } catch (Throwable ex) { + return false; + } + } } diff --git a/driver/src/test/java/org/neo4j/driver/util/Neo4jRunner.java b/driver/src/test/java/org/neo4j/driver/util/Neo4jRunner.java deleted file mode 100644 index 152038f71a..0000000000 --- a/driver/src/test/java/org/neo4j/driver/util/Neo4jRunner.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * 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 org.neo4j.driver.util; - -import static java.util.Arrays.asList; -import static java.util.logging.Level.INFO; -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.neo4j.driver.AuthTokens.basic; -import static org.neo4j.driver.Logging.console; -import static org.neo4j.driver.util.FileTools.moveFile; -import static org.neo4j.driver.util.FileTools.updateProperties; -import static org.neo4j.driver.util.Neo4jSettings.CURRENT_BOLT_PORT; -import static org.neo4j.driver.util.Neo4jSettings.CURRENT_HTTP_PORT; -import static org.neo4j.driver.util.Neo4jSettings.TEST_JVM_ID; -import static org.neo4j.driver.util.cc.CommandLineUtil.boltKitAvailable; -import static org.neo4j.driver.util.cc.CommandLineUtil.executeCommand; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.StandardSocketOptions; -import java.net.URI; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.Config; -import org.neo4j.driver.Driver; -import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.internal.util.ErrorUtil; - -/** - * This class wraps the neo4j stand-alone jar in some code to help pulling it in from a remote URL and then launching - * it in a separate process. - */ -public class Neo4jRunner { - private static Neo4jRunner globalInstance; - - private static final String DEFAULT_NEOCTRL_ARGS = "-e 3.5.11"; - private static final String ENV_NEOCTRL_ARGS = System.getenv("JAVA_DRIVER_NEOCTRL_ARGS"); - public static final String NEOCTRL_ARGS = - System.getProperty("neoctrl.args", ENV_NEOCTRL_ARGS == null ? DEFAULT_NEOCTRL_ARGS : ENV_NEOCTRL_ARGS); - public static final Config DEFAULT_CONFIG = - Config.builder().withLogging(console(INFO)).withoutEncryption().build(); - - public static final String USER = "neo4j"; - public static final String PASSWORD = "password"; - public static final AuthToken DEFAULT_AUTH_TOKEN = basic(USER, PASSWORD); - - private Neo4jSettings currentSettings = Neo4jSettings.TEST_SETTINGS; - - public static final String TARGET_DIR = new File("../target").getAbsolutePath(); - private static final String NEO4J_DIR = new File(TARGET_DIR, "test-server-" + TEST_JVM_ID).getAbsolutePath(); - public static final String HOME_DIR = new File(NEO4J_DIR, "neo4jHome").getAbsolutePath(); - - private Driver driver; - private boolean restartDriver; - - public int httpPort() { - return CURRENT_HTTP_PORT; - } - - public int boltPort() { - return CURRENT_BOLT_PORT; - } - - public BoltServerAddress boltAddress() { - return new BoltServerAddress(boltUri()); - } - - public URI boltUri() { - return URI.create("bolt://localhost:" + boltPort()); - } - - /** Global runner controlling a single server, used to avoid having to restart the server between tests */ - public static synchronized Neo4jRunner getOrCreateGlobalRunner() throws IOException { - assumeTrue(boltKitAvailable(), "BoltKit support unavailable"); - if (globalInstance == null) { - globalInstance = new Neo4jRunner(); - } - return globalInstance; - } - - public static synchronized boolean globalRunnerExists() { - return globalInstance != null; - } - - private Neo4jRunner() throws IOException { - try { - installNeo4j(); - updateServerSettingsFile(); - try { - startNeo4j(); - } catch (Exception e) { - debug("Failed to start server first time due to error: " - + ErrorUtil.getRootCause(e).getMessage()); - debug("Retry to start server again."); - startNeo4j(); - } - } finally { - // Make sure we stop on JVM exit even if start failed - installShutdownHook(); - } - } - - public void ensureRunning(Neo4jSettings neo4jSettings) { - ServerStatus status = serverStatus(); - switch (status) { - case OFFLINE: - updateServerSettings(neo4jSettings); - startNeo4j(); - break; - case ONLINE: - restartNeo4j(neo4jSettings); - break; - } - } - - public Driver driver() { - if (restartDriver) { - restartDriver = false; - if (driver != null) { - driver.close(); - driver = null; - } - } - - if (driver == null) { - driver = GraphDatabase.driver(boltUri(), DEFAULT_AUTH_TOKEN, DEFAULT_CONFIG); - } - return driver; - } - - private void installNeo4j() throws IOException { - File targetHomeFile = new File(HOME_DIR); - if (targetHomeFile.exists()) { - debug("Found and using server installed at `%s`. ", HOME_DIR); - } else { - List commands = new ArrayList<>(); - commands.add("neoctrl-install"); - String[] split = NEOCTRL_ARGS.trim().split("\\s+"); - commands.addAll(asList(split)); - commands.add(NEO4J_DIR); - - String tempHomeDir = executeCommand(commands).trim(); - debug("Downloaded server at `%s`, now renaming to `%s`.", tempHomeDir, HOME_DIR); - - moveFile(new File(tempHomeDir), targetHomeFile); - debug("Installed server at `%s`.", HOME_DIR); - executeCommand("neoctrl-set-initial-password", PASSWORD, HOME_DIR); - } - } - - public void startNeo4j() { - debug("Starting server..."); - executeCommand("neoctrl-start", HOME_DIR, "-v"); - debug("Server started."); - } - - public synchronized void stopNeo4j() { - if (serverStatus() == ServerStatus.OFFLINE) { - return; - } - restartDriver = true; - - debug("Stopping server..."); - executeCommand("neoctrl-stop", HOME_DIR); - debug("Server stopped."); - } - - public void killNeo4j() { - if (serverStatus() == ServerStatus.OFFLINE) { - return; - } - restartDriver = true; - - debug("Killing server..."); - executeCommand("neoctrl-stop", "-k", HOME_DIR); - debug("Server killed."); - } - - public void forceToRestart() { - stopNeo4j(); - startNeo4j(); - } - - /** - * Restart the server with default testing server configuration - */ - public void restartNeo4j() { - restartNeo4j(Neo4jSettings.TEST_SETTINGS); - } - - /** - * Will only restart the server if any configuration changes happens - * @param neo4jSettings - */ - public void restartNeo4j(Neo4jSettings neo4jSettings) { - if (updateServerSettings(neo4jSettings)) // needs to update server setting files - { - forceToRestart(); - } - } - - /** - * prints the debug log contents to stdOut - */ - public void dumpDebugLog() { - try { - System.out.println("Debug log for: " + HOME_DIR); - Scanner input = new Scanner(new File(HOME_DIR + "/logs/debug.log")); - - while (input.hasNextLine()) { - System.out.println(input.nextLine()); - } - } catch (FileNotFoundException e) { - System.out.println("Unable to find debug log file for: " + HOME_DIR); - e.printStackTrace(); - } - } - - private enum ServerStatus { - ONLINE, - OFFLINE - } - - private ServerStatus serverStatus() { - try { - SocketChannel soChannel = SocketChannel.open(); - soChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - BoltServerAddress address = boltAddress(); - soChannel.connect(new InetSocketAddress(address.connectionHost(), address.port())); - soChannel.close(); - return ServerStatus.ONLINE; - } catch (IOException e) { - return ServerStatus.OFFLINE; - } - } - - private boolean updateServerSettings(Neo4jSettings newSetting) { - if (currentSettings.equals(newSetting)) { - return false; - } else { - currentSettings = newSetting; - } - updateServerSettingsFile(); - return true; - } - - /** - * Write updated neo4j settings into neo4j-server.properties for use by the next start - */ - private void updateServerSettingsFile() { - Map propertiesMap = currentSettings.propertiesMap(); - - if (propertiesMap.isEmpty()) { - return; - } - - File oldFile = new File(HOME_DIR, "conf/neo4j.conf"); - try { - debug("Changing server properties file (for next start): %s", oldFile.getCanonicalPath()); - for (Map.Entry property : propertiesMap.entrySet()) { - String name = property.getKey(); - Object value = property.getValue(); - debug("%s=%s", name, value); - } - - updateProperties(oldFile, propertiesMap, currentSettings.excludes()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void installShutdownHook() { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - debug("Starting shutdown hook"); - if (driver != null) { - driver.close(); - } - stopNeo4j(); - debug("Finished shutdown hook"); - } catch (Exception e) { - e.printStackTrace(); - } - })); - } - - public static void debug(String text, Object... args) { - System.out.println(String.format(text, args)); - } - - public static void debug(String text) { - System.out.println(text); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/util/Neo4jSettings.java b/driver/src/test/java/org/neo4j/driver/util/Neo4jSettings.java index de3541bc8a..7c5ad16580 100644 --- a/driver/src/test/java/org/neo4j/driver/util/Neo4jSettings.java +++ b/driver/src/test/java/org/neo4j/driver/util/Neo4jSettings.java @@ -29,16 +29,14 @@ public class Neo4jSettings { public static final String DATA_DIR = "dbms.directories.data"; public static final String IMPORT_DIR = "dbms.directories.import"; + public static final String SSL_POLICY_BOLT_ENABLED = "dbms.ssl.policy.bolt.enabled"; + public static final String SSL_POLICY_BOLT_CLIENT_AUTH = "dbms.ssl.policy.bolt.client_auth"; // 5.0 - public static final String SERVER_IMPORT_DIR = "server.directories.import"; public static final String LISTEN_ADDR = "dbms.default_listen_address"; public static final String IPV6_ENABLED_ADDR = "::"; public static final String BOLT_TLS_LEVEL = "dbms.connector.bolt.tls_level"; private static final String DEFAULT_IMPORT_DIR = "import"; - private static final String DEFAULT_CERT_DIR = "certificates"; - public static final String DEFAULT_TLS_CERT_PATH = DEFAULT_CERT_DIR + "/neo4j.cert"; - public static final String DEFAULT_TLS_KEY_PATH = DEFAULT_CERT_DIR + "/neo4j.key"; public static final String DEFAULT_BOLT_TLS_LEVEL = BoltTlsLevel.OPTIONAL.toString(); public static final String DEFAULT_DATA_DIR = "data"; @@ -93,21 +91,6 @@ public Map propertiesMap() { return settings; } - public Neo4jSettings updateWith(String key, String value) { - return updateWith(map(key, value), excludes); - } - - private Neo4jSettings updateWith(Map updates, Set excludes) { - HashMap newSettings = new HashMap<>(settings); - for (Map.Entry entry : updates.entrySet()) { - newSettings.put(entry.getKey(), entry.getValue()); - } - for (String exclude : excludes) { - newSettings.remove(exclude); - } - return new Neo4jSettings(newSettings, excludes); - } - public Neo4jSettings without(String key) { Set newExcludes = new HashSet<>(excludes); newExcludes.add(key); diff --git a/driver/src/test/java/org/neo4j/driver/util/ProcessEnvConfigurator.java b/driver/src/test/java/org/neo4j/driver/util/ProcessEnvConfigurator.java deleted file mode 100644 index 7c60e7e02c..0000000000 --- a/driver/src/test/java/org/neo4j/driver/util/ProcessEnvConfigurator.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 org.neo4j.driver.util; - -public final class ProcessEnvConfigurator { - /** - * Name of environment variable used by the Neo4j database. - */ - private static final String JAVA_HOME = "JAVA_HOME"; - /** - * Name of environment variable to be used for the Neo4j database, defined by the build system. - */ - private static final String NEO4J_JAVA = "NEO4J_JAVA"; - /** - * Name of an optional environment variable containing file path to a local Neo4j package. - * This package is used by boltkit instead of downloading a package with the specified Neo4j version. - */ - private static final String BOLTKIT_LOCAL_PACKAGE = "NEOCTRL_LOCAL_PACKAGE"; - - private ProcessEnvConfigurator() {} - - public static void configure(ProcessBuilder processBuilder) { - processBuilder.environment().put(JAVA_HOME, determineJavaHome()); - - String localPackage = determineLocalPackage(); - if (localPackage != null) { - processBuilder.environment().put(BOLTKIT_LOCAL_PACKAGE, localPackage); - } - } - - /** - * This driver is built to work with multiple java versions. Neo4j, however, works with a specific version of - * Java. This allows specifying which Java version to use for Neo4j separately from which version to use for - * the driver tests. - *

- * This method determines which java home to use based on present environment variables. - * - * @return path to the java home. - */ - private static String determineJavaHome() { - return System.getenv().getOrDefault(NEO4J_JAVA, System.getProperties().getProperty("java.home")); - } - - private static String determineLocalPackage() { - String value = System.getenv().getOrDefault(BOLTKIT_LOCAL_PACKAGE, "").trim(); - return value.isEmpty() ? null : value; - } -} diff --git a/driver/src/test/java/org/neo4j/driver/util/cc/CommandLineException.java b/driver/src/test/java/org/neo4j/driver/util/cc/CommandLineException.java deleted file mode 100644 index d245aaaee0..0000000000 --- a/driver/src/test/java/org/neo4j/driver/util/cc/CommandLineException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 org.neo4j.driver.util.cc; - -class CommandLineException extends RuntimeException { - CommandLineException(String message) { - super(message); - } - - CommandLineException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/driver/src/test/java/org/neo4j/driver/util/cc/CommandLineUtil.java b/driver/src/test/java/org/neo4j/driver/util/cc/CommandLineUtil.java deleted file mode 100644 index b6027238ad..0000000000 --- a/driver/src/test/java/org/neo4j/driver/util/cc/CommandLineUtil.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 org.neo4j.driver.util.cc; - -import static java.lang.System.lineSeparator; -import static java.util.Arrays.asList; -import static java.util.concurrent.TimeUnit.MINUTES; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import org.neo4j.driver.util.DaemonThreadFactory; -import org.neo4j.driver.util.ProcessEnvConfigurator; - -public class CommandLineUtil { - private static final ExecutorService executor = - Executors.newCachedThreadPool(new DaemonThreadFactory("command-line-thread-")); - - public static boolean boltKitAvailable() { - try { - executeCommand("neoctrl-cluster", "--help"); - return true; - } catch (CommandLineException e) { - return false; - } - } - - public static String executeCommand(List commands) { - try { - ProcessBuilder processBuilder = new ProcessBuilder().command(commands); - ProcessEnvConfigurator.configure(processBuilder); - return executeAndGetStdOut(processBuilder); - } catch (IOException | CommandLineException e) { - throw new CommandLineException("Error running command " + commands, e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new CommandLineException("Interrupted while waiting for command " + commands, e); - } - } - - public static String executeCommand(String... command) { - return executeCommand(asList(command)); - } - - private static String executeAndGetStdOut(ProcessBuilder processBuilder) throws IOException, InterruptedException { - Process process = processBuilder.start(); - Future stdOutFuture = read(process.getInputStream()); - Future stdErrFuture = read(process.getErrorStream()); - int exitCode = process.waitFor(); - String stdOut = get(stdOutFuture); - String stdErr = get(stdErrFuture); - if (exitCode != 0) { - throw new CommandLineException("Non-zero exit code\nSTDOUT:\n" + stdOut + "\nSTDERR:\n" + stdErr); - } - return stdOut; - } - - private static Future read(final InputStream input) { - return executor.submit(new Callable() { - @Override - public String call() throws Exception { - return readToString(input); - } - }); - } - - private static String readToString(InputStream input) { - StringBuilder result = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) { - String line; - while ((line = reader.readLine()) != null) { - result.append(line).append(lineSeparator()); - } - } catch (IOException e) { - throw new CommandLineException("Unable to read from stream", e); - } - return result.toString(); - } - - private static T get(Future future) { - try { - return future.get(10, MINUTES); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/driver/src/test/resources/logback-test.xml b/driver/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..ea9a2a3fac --- /dev/null +++ b/driver/src/test/resources/logback-test.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/driver/src/test/resources/nginx.conf b/driver/src/test/resources/nginx.conf new file mode 100644 index 0000000000..4b8694ed11 --- /dev/null +++ b/driver/src/test/resources/nginx.conf @@ -0,0 +1,30 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + +stream { + server { + resolver 127.0.0.11 ipv6=off; + + set $upstream neo4j:7687; + + listen 7687; + proxy_pass $upstream; + } + + server { + resolver 127.0.0.11 ipv6=off; + + set $upstream neo4j:7474; + + listen 7474; + proxy_pass $upstream; + } +} \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index a18bd4456a..eddc9f5683 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -74,6 +74,10 @@ neo4j test + + org.bouncycastle + bcpkix-jdk15on + diff --git a/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java b/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java index b92fce2084..e62d97a2da 100644 --- a/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java +++ b/examples/src/test/java/org/neo4j/docs/driver/ExamplesIT.java @@ -36,8 +36,6 @@ import static org.neo4j.driver.Values.parameters; import static org.neo4j.driver.internal.util.Neo4jEdition.ENTERPRISE; import static org.neo4j.driver.internal.util.Neo4jFeature.BOLT_V4; -import static org.neo4j.driver.util.Neo4jRunner.PASSWORD; -import static org.neo4j.driver.util.Neo4jRunner.USER; import static org.neo4j.driver.util.TestUtil.await; import static org.neo4j.driver.util.TestUtil.createDatabase; import static org.neo4j.driver.util.TestUtil.dropDatabase; @@ -52,8 +50,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.parallel.Execution; -import org.junit.jupiter.api.parallel.ExecutionMode; import org.neo4j.driver.Config; import org.neo4j.driver.Driver; import org.neo4j.driver.Session; @@ -70,8 +66,9 @@ import org.neo4j.driver.util.TestUtil; @ParallelizableIT -@Execution(ExecutionMode.CONCURRENT) class ExamplesIT { + static final String USER = "neo4j"; + @RegisterExtension static final DatabaseExtension neo4j = new DatabaseExtension(); @@ -128,7 +125,8 @@ void setUp() { @Test void testShouldRunAutocommitTransactionExample() throws Exception { // Given - try (AutocommitTransactionExample example = new AutocommitTransactionExample(uri, USER, PASSWORD)) { + try (AutocommitTransactionExample example = + new AutocommitTransactionExample(uri, USER, neo4j.adminPassword())) { // When example.addPerson("Alice"); @@ -139,7 +137,8 @@ void testShouldRunAutocommitTransactionExample() throws Exception { @Test void testShouldRunAsyncAutocommitTransactionExample() throws Exception { - try (AsyncAutocommitTransactionExample example = new AsyncAutocommitTransactionExample(uri, USER, PASSWORD)) { + try (AsyncAutocommitTransactionExample example = + new AsyncAutocommitTransactionExample(uri, USER, neo4j.adminPassword())) { // create some 'Product' nodes try (Session session = neo4j.driver().session()) { session.run("UNWIND ['Tesseract', 'Orb', 'Eye of Agamotto'] AS item " @@ -157,7 +156,7 @@ void testShouldAsyncRunResultConsumeExample() throws Exception { // Given write("CREATE (a:Person {name: 'Alice'})"); write("CREATE (a:Person {name: 'Bob'})"); - try (AsyncResultConsumeExample example = new AsyncResultConsumeExample(uri, USER, PASSWORD)) { + try (AsyncResultConsumeExample example = new AsyncResultConsumeExample(uri, USER, neo4j.adminPassword())) { // When List names = await(example.getPeople()); @@ -171,7 +170,8 @@ void testShouldAsyncRunMultipleTransactionExample() throws Exception { // Given write("CREATE (a:Person {name: 'Alice'})"); write("CREATE (a:Person {name: 'Bob'})"); - try (AsyncRunMultipleTransactionExample example = new AsyncRunMultipleTransactionExample(uri, USER, PASSWORD)) { + try (AsyncRunMultipleTransactionExample example = + new AsyncRunMultipleTransactionExample(uri, USER, neo4j.adminPassword())) { // When Integer nodesCreated = await(example.addEmployees("Acme")); @@ -186,7 +186,7 @@ void testShouldAsyncRunMultipleTransactionExample() throws Exception { @Test void testShouldRunConfigConnectionPoolExample() throws Exception { // Given - try (ConfigConnectionPoolExample example = new ConfigConnectionPoolExample(uri, USER, PASSWORD)) { + try (ConfigConnectionPoolExample example = new ConfigConnectionPoolExample(uri, USER, neo4j.adminPassword())) { // Then assertTrue(example.canConnect()); } @@ -195,7 +195,7 @@ void testShouldRunConfigConnectionPoolExample() throws Exception { @Test void testShouldRunBasicAuthExample() throws Exception { // Given - try (BasicAuthExample example = new BasicAuthExample(uri, USER, PASSWORD)) { + try (BasicAuthExample example = new BasicAuthExample(uri, USER, neo4j.adminPassword())) { // Then assertTrue(example.canConnect()); } @@ -204,7 +204,8 @@ void testShouldRunBasicAuthExample() throws Exception { @Test void testShouldRunConfigConnectionTimeoutExample() throws Exception { // Given - try (ConfigConnectionTimeoutExample example = new ConfigConnectionTimeoutExample(uri, USER, PASSWORD)) { + try (ConfigConnectionTimeoutExample example = + new ConfigConnectionTimeoutExample(uri, USER, neo4j.adminPassword())) { // Then assertThat(example, instanceOf(ConfigConnectionTimeoutExample.class)); } @@ -213,7 +214,7 @@ void testShouldRunConfigConnectionTimeoutExample() throws Exception { @Test void testShouldRunConfigMaxRetryTimeExample() throws Exception { // Given - try (ConfigMaxRetryTimeExample example = new ConfigMaxRetryTimeExample(uri, USER, PASSWORD)) { + try (ConfigMaxRetryTimeExample example = new ConfigMaxRetryTimeExample(uri, USER, neo4j.adminPassword())) { // Then assertThat(example, instanceOf(ConfigMaxRetryTimeExample.class)); } @@ -222,7 +223,7 @@ void testShouldRunConfigMaxRetryTimeExample() throws Exception { @Test void testShouldRunConfigTrustExample() throws Exception { // Given - try (ConfigTrustExample example = new ConfigTrustExample(uri, USER, PASSWORD)) { + try (ConfigTrustExample example = new ConfigTrustExample(uri, USER, neo4j.adminPassword())) { // Then assertThat(example, instanceOf(ConfigTrustExample.class)); } @@ -231,7 +232,7 @@ void testShouldRunConfigTrustExample() throws Exception { @Test void testShouldRunConfigUnencryptedExample() throws Exception { // Given - try (ConfigUnencryptedExample example = new ConfigUnencryptedExample(uri, USER, PASSWORD)) { + try (ConfigUnencryptedExample example = new ConfigUnencryptedExample(uri, USER, neo4j.adminPassword())) { // Then assertThat(example, instanceOf(ConfigUnencryptedExample.class)); } @@ -240,7 +241,7 @@ void testShouldRunConfigUnencryptedExample() throws Exception { @Test void testShouldRunCypherErrorExample() throws Exception { // Given - try (CypherErrorExample example = new CypherErrorExample(uri, USER, PASSWORD)) { + try (CypherErrorExample example = new CypherErrorExample(uri, USER, neo4j.adminPassword())) { // When & Then StdIOCapture stdIO = new StdIOCapture(); try (AutoCloseable ignored = stdIO.capture()) { @@ -255,7 +256,7 @@ void testShouldRunCypherErrorExample() throws Exception { @Test void testShouldRunDriverLifecycleExample() throws Exception { // Given - try (DriverLifecycleExample example = new DriverLifecycleExample(uri, USER, PASSWORD)) { + try (DriverLifecycleExample example = new DriverLifecycleExample(uri, USER, neo4j.adminPassword())) { // Then assertThat(example, instanceOf(DriverLifecycleExample.class)); } @@ -264,7 +265,7 @@ void testShouldRunDriverLifecycleExample() throws Exception { @Test void testShouldRunHelloWorld() throws Exception { // Given - try (HelloWorldExample greeter = new HelloWorldExample(uri, USER, PASSWORD)) { + try (HelloWorldExample greeter = new HelloWorldExample(uri, USER, neo4j.adminPassword())) { // When StdIOCapture stdIO = new StdIOCapture(); @@ -285,7 +286,8 @@ void testShouldRunDriverIntroduction() throws Exception { .withEncryption() .withTrustStrategy(trustAllCertificates()) .build(); - try (DriverIntroductionExample intro = new DriverIntroductionExample(uri, USER, PASSWORD, config)) { + try (DriverIntroductionExample intro = + new DriverIntroductionExample(uri, USER, neo4j.adminPassword(), config)) { // When StdIOCapture stdIO = new StdIOCapture(); @@ -304,7 +306,7 @@ void testShouldRunDriverIntroduction() throws Exception { @Test void testShouldRunReadWriteTransactionExample() throws Exception { // Given - try (ReadWriteTransactionExample example = new ReadWriteTransactionExample(uri, USER, PASSWORD)) { + try (ReadWriteTransactionExample example = new ReadWriteTransactionExample(uri, USER, neo4j.adminPassword())) { // When long nodeID = example.addPerson("Alice"); @@ -318,7 +320,7 @@ void testShouldRunResultConsumeExample() throws Exception { // Given write("CREATE (a:Person {name: 'Alice'})"); write("CREATE (a:Person {name: 'Bob'})"); - try (ResultConsumeExample example = new ResultConsumeExample(uri, USER, PASSWORD)) { + try (ResultConsumeExample example = new ResultConsumeExample(uri, USER, neo4j.adminPassword())) { // When List names = example.getPeople(); @@ -332,7 +334,7 @@ void testShouldRunResultRetainExample() throws Exception { // Given write("CREATE (a:Person {name: 'Alice'})"); write("CREATE (a:Person {name: 'Bob'})"); - try (ResultRetainExample example = new ResultRetainExample(uri, USER, PASSWORD)) { + try (ResultRetainExample example = new ResultRetainExample(uri, USER, neo4j.adminPassword())) { // When example.addEmployees("Acme"); @@ -346,15 +348,15 @@ void testShouldRunResultRetainExample() throws Exception { @Test void testShouldRunServiceUnavailableExample() throws Exception { // Given - try (ServiceUnavailableExample example = new ServiceUnavailableExample(uri, USER, PASSWORD)) { + try (ServiceUnavailableExample example = new ServiceUnavailableExample(uri, USER, neo4j.adminPassword())) { try { // When - neo4j.stopDb(); + neo4j.stopProxy(); // Then assertThat(example.addItem(), equalTo(false)); } finally { - neo4j.startDb(); + neo4j.startProxy(); } } } @@ -362,7 +364,7 @@ void testShouldRunServiceUnavailableExample() throws Exception { @Test void testShouldRunSessionExample() throws Exception { // Given - try (SessionExample example = new SessionExample(uri, USER, PASSWORD)) { + try (SessionExample example = new SessionExample(uri, USER, neo4j.adminPassword())) { // When example.addPerson("Alice"); @@ -375,7 +377,7 @@ void testShouldRunSessionExample() throws Exception { @Test void testShouldRunTransactionFunctionExample() throws Exception { // Given - try (TransactionFunctionExample example = new TransactionFunctionExample(uri, USER, PASSWORD)) { + try (TransactionFunctionExample example = new TransactionFunctionExample(uri, USER, neo4j.adminPassword())) { // When example.addPerson("Alice"); @@ -387,7 +389,8 @@ void testShouldRunTransactionFunctionExample() throws Exception { @Test void testShouldConfigureTransactionTimeoutExample() throws Exception { // Given - try (TransactionTimeoutConfigExample example = new TransactionTimeoutConfigExample(uri, USER, PASSWORD)) { + try (TransactionTimeoutConfigExample example = + new TransactionTimeoutConfigExample(uri, USER, neo4j.adminPassword())) { // When example.addPerson("Alice"); @@ -399,7 +402,8 @@ void testShouldConfigureTransactionTimeoutExample() throws Exception { @Test void testShouldConfigureTransactionMetadataExample() throws Exception { // Given - try (TransactionMetadataConfigExample example = new TransactionMetadataConfigExample(uri, USER, PASSWORD)) { + try (TransactionMetadataConfigExample example = + new TransactionMetadataConfigExample(uri, USER, neo4j.adminPassword())) { // When example.addPerson("Alice"); @@ -410,7 +414,8 @@ void testShouldConfigureTransactionMetadataExample() throws Exception { @Test void testShouldRunAsyncTransactionFunctionExample() throws Exception { - try (AsyncTransactionFunctionExample example = new AsyncTransactionFunctionExample(uri, USER, PASSWORD)) { + try (AsyncTransactionFunctionExample example = + new AsyncTransactionFunctionExample(uri, USER, neo4j.adminPassword())) { // create some 'Product' nodes try (Session session = neo4j.driver().session()) { session.run( @@ -432,7 +437,7 @@ void testShouldRunAsyncTransactionFunctionExample() throws Exception { @Test void testPassBookmarksExample() throws Exception { - try (PassBookmarkExample example = new PassBookmarkExample(uri, USER, PASSWORD)) { + try (PassBookmarkExample example = new PassBookmarkExample(uri, USER, neo4j.adminPassword())) { // When example.addEmployAndMakeFriends(); @@ -458,7 +463,8 @@ void testPassBookmarksExample() throws Exception { @Test void testAsyncUnmanagedTransactionExample() throws Exception { - try (AsyncUnmanagedTransactionExample example = new AsyncUnmanagedTransactionExample(uri, USER, PASSWORD)) { + try (AsyncUnmanagedTransactionExample example = + new AsyncUnmanagedTransactionExample(uri, USER, neo4j.adminPassword())) { // create a 'Product' node try (Session session = neo4j.driver().session()) { session.run("CREATE (:Product {id: 0, title: 'Mind Gem'})"); @@ -491,7 +497,7 @@ void testSlf4jLogging() throws Exception { assertThat(logFileContent, is(emptyString())); String randomString = UUID.randomUUID().toString(); - try (Slf4jLoggingExample example = new Slf4jLoggingExample(uri, USER, PASSWORD)) { + try (Slf4jLoggingExample example = new Slf4jLoggingExample(uri, USER, neo4j.adminPassword())) { Object result = example.runReturnQuery(randomString); assertEquals(randomString, result); } @@ -505,7 +511,7 @@ void testSlf4jLogging() throws Exception { @Test void testHostnameVerificationExample() { try (HostnameVerificationExample example = - new HostnameVerificationExample(uri, USER, PASSWORD, neo4j.tlsCertFile())) { + new HostnameVerificationExample(uri, USER, neo4j.adminPassword(), neo4j.tlsCertFile())) { assertTrue(example.canConnect()); } } @@ -513,7 +519,8 @@ void testHostnameVerificationExample() { @Test @EnabledOnNeo4jWith(BOLT_V4) void testShouldRunRxAutocommitTransactionExample() throws Exception { - try (RxAutocommitTransactionExample example = new RxAutocommitTransactionExample(uri, USER, PASSWORD)) { + try (RxAutocommitTransactionExample example = + new RxAutocommitTransactionExample(uri, USER, neo4j.adminPassword())) { // create some 'Product' nodes try (Session session = neo4j.driver().session()) { session.run("UNWIND ['Tesseract', 'Orb', 'Eye of Agamotto'] AS item " @@ -532,7 +539,8 @@ void testShouldRunRxAutocommitTransactionExample() throws Exception { @Test @EnabledOnNeo4jWith(BOLT_V4) void testRxUnmanagedTransactionExample() throws Exception { - try (RxUnmanagedTransactionExample example = new RxUnmanagedTransactionExample(uri, USER, PASSWORD)) { + try (RxUnmanagedTransactionExample example = + new RxUnmanagedTransactionExample(uri, USER, neo4j.adminPassword())) { // create a 'Product' node try (Session session = neo4j.driver().session()) { session.run("CREATE (:Product {id: 0, title: 'Mind Gem'})"); @@ -551,7 +559,8 @@ void testRxUnmanagedTransactionExample() throws Exception { @Test @EnabledOnNeo4jWith(BOLT_V4) void testShouldRunRxTransactionFunctionExampleReactor() throws Exception { - try (RxTransactionFunctionExample example = new RxTransactionFunctionExample(uri, USER, PASSWORD)) { + try (RxTransactionFunctionExample example = + new RxTransactionFunctionExample(uri, USER, neo4j.adminPassword())) { // create some 'Product' nodes try (Session session = neo4j.driver().session()) { session.run( @@ -576,7 +585,8 @@ void testShouldRunRxTransactionFunctionExampleReactor() throws Exception { @Test @EnabledOnNeo4jWith(BOLT_V4) void testShouldRunRxTransactionFunctionExampleRxJava() throws Exception { - try (RxTransactionFunctionExample example = new RxTransactionFunctionExample(uri, USER, PASSWORD)) { + try (RxTransactionFunctionExample example = + new RxTransactionFunctionExample(uri, USER, neo4j.adminPassword())) { // create some 'Product' nodes try (Session session = neo4j.driver().session()) { session.run( @@ -604,7 +614,7 @@ void testShouldRunRxResultConsumeExampleReactor() throws Exception { // Given write("CREATE (a:Person {name: 'Alice'})"); write("CREATE (a:Person {name: 'Bob'})"); - try (RxResultConsumeExample example = new RxResultConsumeExample(uri, USER, PASSWORD)) { + try (RxResultConsumeExample example = new RxResultConsumeExample(uri, USER, neo4j.adminPassword())) { // When List names = await(example.getPeople()); @@ -619,7 +629,7 @@ void testShouldRunRxResultConsumeExampleRxJava() throws Exception { // Given write("CREATE (a:Person {name: 'Alice'})"); write("CREATE (a:Person {name: 'Bob'})"); - try (RxResultConsumeExample example = new RxResultConsumeExample(uri, USER, PASSWORD)) { + try (RxResultConsumeExample example = new RxResultConsumeExample(uri, USER, neo4j.adminPassword())) { // When List names = await(example.getPeopleRxJava()); @@ -635,7 +645,7 @@ void testUseAnotherDatabaseExample() throws Exception { dropDatabase(driver, "examples"); createDatabase(driver, "examples"); - try (DatabaseSelectionExample example = new DatabaseSelectionExample(uri, USER, PASSWORD)) { + try (DatabaseSelectionExample example = new DatabaseSelectionExample(uri, USER, neo4j.adminPassword())) { // When example.useAnotherDatabaseExample(); @@ -647,7 +657,7 @@ void testUseAnotherDatabaseExample() throws Exception { @Test void testReadingValuesExample() throws Exception { - try (ReadingValuesExample example = new ReadingValuesExample(uri, USER, PASSWORD)) { + try (ReadingValuesExample example = new ReadingValuesExample(uri, USER, neo4j.adminPassword())) { assertThat(example.integerFieldIsNull(), is(false)); assertThat(example.integerAsInteger(), is(4)); assertThat(example.integerAsLong(), is(4L)); diff --git a/pom.xml b/pom.xml index 2d8518ec9a..3b27e6d983 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ ${project.groupId}.${project.artifactId} ${project.basedir} - 1C + 2 parallelizableIT 3.0.0-M6 diff --git a/testkit-tests/pom.xml b/testkit-tests/pom.xml index 797f846888..7437177ad6 100644 --- a/testkit-tests/pom.xml +++ b/testkit-tests/pom.xml @@ -19,7 +19,7 @@ ${project.basedir}/.. https://github.com/neo4j-drivers/testkit.git - 4.4 + 5.0 --tests TESTKIT_TESTS INTEGRATION_TESTS STUB_TESTS STRESS_TESTS TLS_TESTS 7200000