diff --git a/pom.xml b/pom.xml index 665d56cc..e00e6b70 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-r2dbc - 1.0.0.BUILD-SNAPSHOT + 1.0.0.gh-29-SNAPSHOT Spring Data R2DBC Spring Data module for R2DBC. diff --git a/src/test/java/org/springframework/data/r2dbc/testing/ExternalDatabase.java b/src/test/java/org/springframework/data/r2dbc/testing/ExternalDatabase.java index 6a1832ae..a538f630 100644 --- a/src/test/java/org/springframework/data/r2dbc/testing/ExternalDatabase.java +++ b/src/test/java/org/springframework/data/r2dbc/testing/ExternalDatabase.java @@ -24,6 +24,8 @@ import org.junit.AssumptionViolatedException; import org.junit.rules.ExternalResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * {@link ExternalResource} wrapper to encapsulate {@link ProvidedDatabase} and @@ -33,6 +35,8 @@ */ public abstract class ExternalDatabase extends ExternalResource { + private static Logger LOG = LoggerFactory.getLogger(ExternalDatabase.class); + /** * @return the post of the database service. */ @@ -53,16 +57,35 @@ public abstract class ExternalDatabase extends ExternalResource { */ public abstract String getUsername(); + /** + * Throws an {@link AssumptionViolatedException} if the database cannot be reached. + */ @Override protected void before() { + if (!checkValidity()) { + throw new AssumptionViolatedException( + String.format("Cannot connect to %s:%d. Skipping tests.", getHostname(), getPort())); + } + } + + /** + * performs a test if the database can actually be reached. + * + * @return true, if the database could be reached. + */ + boolean checkValidity() { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(getHostname(), getPort()), Math.toIntExact(TimeUnit.SECONDS.toMillis(5))); + return true; } catch (IOException e) { - throw new AssumptionViolatedException( - String.format("Cannot connect to %s:%d. Skipping tests.", getHostname(), getPort())); + LOG.debug("external database not available.", e); } + + return false; } /** @@ -122,4 +145,42 @@ public String getPassword() { return password; } } + + /** + * An {@link ExternalDatabase} that couldn't get constructed. + * + * @author Jens Schauder + */ + static class NoSuchDatabase extends ExternalDatabase { + + @Override + boolean checkValidity() { + return false; + } + + @Override + public int getPort() { + throw new UnsupportedOperationException(); + } + + @Override + public String getHostname() { + throw new UnsupportedOperationException(); + } + + @Override + public String getDatabase() { + throw new UnsupportedOperationException(); + } + + @Override + public String getUsername() { + throw new UnsupportedOperationException(); + } + + @Override + public String getPassword() { + throw new UnsupportedOperationException(); + } + } } diff --git a/src/test/java/org/springframework/data/r2dbc/testing/PostgresTestSupport.java b/src/test/java/org/springframework/data/r2dbc/testing/PostgresTestSupport.java index d7608f09..74159cb8 100644 --- a/src/test/java/org/springframework/data/r2dbc/testing/PostgresTestSupport.java +++ b/src/test/java/org/springframework/data/r2dbc/testing/PostgresTestSupport.java @@ -4,18 +4,24 @@ import io.r2dbc.postgresql.PostgresqlConnectionFactory; import io.r2dbc.spi.ConnectionFactory; +import java.util.function.Supplier; + import javax.sql.DataSource; import org.postgresql.ds.PGSimpleDataSource; import org.springframework.data.r2dbc.testing.ExternalDatabase.ProvidedDatabase; +import org.testcontainers.containers.PostgreSQLContainer; /** * Utility class for testing against Postgres. * * @author Mark Paluch + * @author Jens Schauder */ public class PostgresTestSupport { + private static ExternalDatabase testContainerDatabase; + public static String CREATE_TABLE_LEGOSET = "CREATE TABLE legoset (\n" // + " id integer CONSTRAINT id PRIMARY KEY,\n" // + " name varchar(255) NOT NULL,\n" // @@ -31,30 +37,91 @@ public class PostgresTestSupport { public static String INSERT_INTO_LEGOSET = "INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)"; /** - * Returns a locally provided database at {@code postgres:@localhost:5432/postgres}. + * Returns a database either hosted locally at {@code postgres:@localhost:5432/postgres} or running inside Docker. * - * @return + * @return information about the database. Guaranteed to be not {@literal null}. */ public static ExternalDatabase database() { - return local(); + + if (Boolean.getBoolean("spring.data.r2dbc.test.preferLocalDatabase")) { + + return getFirstWorkingDatabase( // + PostgresTestSupport::local, // + PostgresTestSupport::testContainer // + ); + } else { + + return getFirstWorkingDatabase( // + PostgresTestSupport::testContainer, // + PostgresTestSupport::local // + ); + } + } + + private static ExternalDatabase getFirstWorkingDatabase(Supplier first, + Supplier second) { + + ExternalDatabase database = first.get(); + if (database.checkValidity()) { + return database; + } else { + return second.get(); + } } /** * Returns a locally provided database at {@code postgres:@localhost:5432/postgres}. - * - * @return */ private static ExternalDatabase local() { - return ProvidedDatabase.builder().hostname("localhost").port(5432).database("postgres").username("postgres") + + return ProvidedDatabase.builder() // + .hostname("localhost") // + .port(5432) // + .database("postgres") // + .username("postgres") // .password("").build(); } + /** + * Returns a database provided via Testcontainers. + */ + private static ExternalDatabase testContainer() { + + if (testContainerDatabase == null) { + + try { + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(); + postgreSQLContainer.start(); + + testContainerDatabase = ProvidedDatabase.builder() // + .hostname("localhost") // + .port(postgreSQLContainer.getFirstMappedPort()) // + .database(postgreSQLContainer.getDatabaseName()) // + .username(postgreSQLContainer.getUsername()) // + .password(postgreSQLContainer.getPassword()).build(); + + } catch (IllegalStateException ise) { + // docker is not available. + testContainerDatabase = new ExternalDatabase.NoSuchDatabase(); + } + + } + + return testContainerDatabase; + } + /** * Creates a new {@link ConnectionFactory} configured from the {@link ExternalDatabase}.. */ public static ConnectionFactory createConnectionFactory(ExternalDatabase database) { - return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder().host(database.getHostname()) - .database(database.getDatabase()).username(database.getUsername()).password(database.getPassword()).build()); + + return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder() // + .host(database.getHostname()) // + .database(database.getDatabase()) // + .port(database.getPort()) // + .username(database.getUsername()) // + .password(database.getPassword()) // + .build()); } /** @@ -72,4 +139,5 @@ public static DataSource createDataSource(ExternalDatabase database) { return dataSource; } + } diff --git a/src/test/java/org/springframework/data/r2dbc/testing/SqlServerTestSupport.java b/src/test/java/org/springframework/data/r2dbc/testing/SqlServerTestSupport.java index 3fb08ad7..d52c8476 100644 --- a/src/test/java/org/springframework/data/r2dbc/testing/SqlServerTestSupport.java +++ b/src/test/java/org/springframework/data/r2dbc/testing/SqlServerTestSupport.java @@ -33,31 +33,35 @@ public class SqlServerTestSupport { /** * Returns a locally provided database at {@code sqlserver:@localhost:1433/master}. - * - * @return */ public static ExternalDatabase database() { return local(); } /** - * Returns a locally provided database at {@code postgres:@localhost:5432/postgres}. - * - * @return + * Returns a locally provided database at {@code sqlserver:@localhost:1433/master}. */ private static ExternalDatabase local() { - return ProvidedDatabase.builder().hostname("localhost").port(1433).database("master").username("sa") - .password("A_Str0ng_Required_Password").build(); + + return ProvidedDatabase.builder() // + .hostname("localhost") // + .port(1433) // + .database("master") // + .username("sa") // + .password("A_Str0ng_Required_Password") // + .build(); } /** * Creates a new {@link ConnectionFactory} configured from the {@link ExternalDatabase}.. */ public static ConnectionFactory createConnectionFactory(ExternalDatabase database) { + return new MssqlConnectionFactory(MssqlConnectionConfiguration.builder().host(database.getHostname()) // .database(database.getDatabase()) // .username(database.getUsername()) // .password(database.getPassword()) // + .port(database.getPort()) // .build()); }