Skip to content

Commit

Permalink
#420 - Introducing infrastructure to find and detect required databases.
Browse files Browse the repository at this point in the history
Tests using the new Rule can try multiple strategies to find a suitable database.
Typically first looking for a local one and alternatively creating one using Docker via Testcontainers.

If no database is found the tests get skipped by default.
If the tests are run with `-DignoreMissingInfrastructure=false` the first test with each Rule instance will fail, making the build fail.

Added the InfrastructureRule to ignore/fail tests depending on the presence of a required infrastructure.
Implemented that Rule for R2DBC and Solr.
  • Loading branch information
schauder committed Oct 29, 2018
1 parent 5d536b1 commit 51c7664
Show file tree
Hide file tree
Showing 19 changed files with 570 additions and 137 deletions.
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.springframework.data.examples</groupId>
Expand All @@ -16,6 +16,7 @@
</parent>

<modules>
<module>util</module>
<module>bom</module>
<module>couchbase</module>
<module>elasticsearch</module>
Expand All @@ -36,6 +37,7 @@
<properties>
<apt.version>1.1.3</apt.version>
<lombok.version>1.18.0</lombok.version>
<testcontainers.version>1.9.1</testcontainers.version>
</properties>

<profiles>
Expand Down
7 changes: 7 additions & 0 deletions r2dbc/example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@

<name>Spring Data R2DBC - Example</name>

<dependencies>
<dependency>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-examples-utils</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package example.springdata.r2dbc.basics;

import example.springdata.test.util.InfrastructureRule;
import reactor.core.publisher.Hooks;
import reactor.test.StepVerifier;

Expand All @@ -23,6 +24,7 @@
import java.util.List;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -31,7 +33,10 @@
import org.springframework.test.context.junit4.SpringRunner;

/**
* Tests demonstrating the use of R2DBC.
*
* @author Oliver Gierke
* @author Jens Schauder
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = InfrastructureConfiguration.class)
Expand All @@ -40,6 +45,8 @@ public class CustomerRepositoryIntegrationTests {
@Autowired CustomerRepository customers;
@Autowired DatabaseClient database;

@Rule @Autowired public InfrastructureRule requiresPostgres;

@Before
public void setUp() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,34 @@
*/
package example.springdata.r2dbc.basics;

import example.springdata.test.util.InfrastructureRule;
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;

import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.function.DatabaseClient;
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.testcontainers.containers.PostgreSQLContainer;

import javax.annotation.PreDestroy;
import reactor.test.StepVerifier;

/**
* @author Oliver Gierke
*/
@Configuration
class InfrastructureConfiguration {

private PostgreSQLContainer postgres = new PostgreSQLContainer();
@Bean
InfrastructureRule<PostgresqlConnectionConfiguration> infrastructureRule() {

return new InfrastructureRule<>( //
this::checkForlLocalPostgres, //
this::startPostgresInDocker //
);
}

@Bean
CustomerRepository customerRepository(R2dbcRepositoryFactory factory) {
Expand All @@ -61,22 +69,50 @@ DatabaseClient databaseClient(ConnectionFactory factory) {
@Bean
PostgresqlConnectionFactory connectionFactory() {

return new PostgresqlConnectionFactory(infrastructureRule().getInfo());
}

@NotNull
private InfrastructureRule.InfrastructureInfo<PostgresqlConnectionConfiguration> startPostgresInDocker() {

PostgreSQLContainer postgres = new PostgreSQLContainer();
postgres.start();

PostgresqlConnectionConfiguration config = PostgresqlConnectionConfiguration.builder() //
PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration.builder() //
.host(postgres.getContainerIpAddress()) //
.port(postgres.getFirstMappedPort()) //
.database(postgres.getDatabaseName()) //
.username(postgres.getUsername()) //
.password(postgres.getPassword()) //
.build();

return new PostgresqlConnectionFactory(config);
return new InfrastructureRule.InfrastructureInfo<>(true, configuration, null, postgres::stop);
}

@PreDestroy
void shutdown() {
postgres.stop();
@NotNull
private InfrastructureRule.InfrastructureInfo<PostgresqlConnectionConfiguration> checkForlLocalPostgres() {

PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration.builder() //
.host("localhost") //
.port(5432) //
.database("postgres") //
.username("postgres") //
.password("") //
.build();

try {

new PostgresqlConnectionFactory(configuration).create()
.as(StepVerifier::create) //
.assertNext(c -> {
}) //
.verifyComplete();
} catch (AssertionError re) {

return new InfrastructureRule.InfrastructureInfo<>(false, null, re, () ->{});
}

return new InfrastructureRule.InfrastructureInfo<>(true, configuration, null, () ->{});
}

}
11 changes: 11 additions & 0 deletions solr/example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-examples-utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class Product {
private @Indexed(name = "cat") List<String> category;
private @Indexed(name = "store") Point location;
private @Indexed String description;
private @Indexed boolean inStock;
private @Indexed Boolean inStock;
private @Indexed Integer popularity;
private @Score Float score;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@

import example.springdata.solr.product.Product;
import example.springdata.solr.product.ProductRepository;
import example.springdata.solr.test.util.RequiresSolrServer;
import example.springdata.test.util.InfrastructureRule;

import java.time.Duration;
import java.util.Arrays;
import java.util.Optional;

import org.junit.ClassRule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.solr.core.SolrOperations;
import org.springframework.data.solr.core.query.Function;
import org.springframework.data.solr.core.query.Query;
Expand All @@ -48,33 +48,41 @@
* @author Christoph Strobl
* @author Oliver Gierke
* @author Mark Paluch
* @author Jens Schauder
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AdvancedSolrRepositoryTests {

public static @ClassRule RequiresSolrServer requiresRunningServer = RequiresSolrServer.onLocalhost();
@Rule @Autowired public InfrastructureRule requiresRunningServer;

@Configuration
static class Config extends SolrTestConfiguration {

@Override
protected void doInitTestData(CrudRepository<Product, String> repository) {
@Autowired ProductRepository repository;
@Autowired SolrOperations operations;

Product playstation = Product.builder().id("id-1").name("Playstation")
.description("The Sony playstation was the top selling gaming system in 1994.").popularity(5).build();
Product playstation2 = Product.builder().id("id-2").name("Playstation Two")
.description("Playstation two is the successor of playstation in 2000.").build();
Product superNES = Product.builder().id("id-3").name("Super Nintendo").popularity(3).build();
Product nintendo64 = Product.builder().id("id-4").name("N64").description("Nintendo 64").popularity(2).build();

repository.saveAll(Arrays.asList(playstation, playstation2, superNES, nintendo64));
}
/**
* Remove test data when context is shut down.
*/
public @After void deleteDocumentsOnShutdown() {
repository.deleteAll();
}

@Autowired ProductRepository repository;
@Autowired SolrOperations operations;
/**
* Initialize Solr instance with test data once context has started.
*/
public @Before void initWithTestData() throws InterruptedException {

repository.deleteAll();

Product playstation = Product.builder().id("id-1").name("Playstation")
.description("The Sony playstation was the top selling gaming system in 1994.").popularity(5).build();
Product playstation2 = Product.builder().id("id-2").name("Playstation Two")
.description("Playstation two is the successor of playstation in 2000.").build();
Product superNES = Product.builder().id("id-3").name("Super Nintendo").popularity(3).build();
Product nintendo64 = Product.builder().id("id-4").name("N64").description("Nintendo 64").popularity(2).build();

repository.saveAll(Arrays.asList(playstation, playstation2, superNES, nintendo64));
}
/**
* {@link HighlightPage} holds next to the entities found also information about where a match was found within the
* document. This allows to fine grained display snipplets of data containing the matching term in context.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
*/
package example.springdata.solr;

import example.springdata.solr.product.Product;
import example.springdata.solr.product.ProductRepository;
import example.springdata.solr.test.util.RequiresSolrServer;
import example.springdata.test.util.InfrastructureRule;

import org.junit.ClassRule;
import java.util.stream.IntStream;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -27,15 +32,36 @@

/**
* @author Christoph Strobl
* @author Jens Schauder
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SolrTestConfiguration.class)
@SpringBootTest
public class BasicSolrRepositoryTests {

public static @ClassRule RequiresSolrServer requiresRunningServer = RequiresSolrServer.onLocalhost();


@Rule @Autowired public InfrastructureRule requiresRunningServer;

@Autowired ProductRepository repository;

/**
* Remove test data when context is shut down.
*/
public @After void deleteDocumentsOnShutdown() {
repository.deleteAll();
}

/**
* Initialize Solr instance with test data once context has started.
*/
public @Before void initWithTestData() {

repository.deleteAll();

IntStream.range(0, 100)
.forEach(index -> repository.save(Product.builder().id("p-" + index).name("foobar").build()));
}

/**
* Finds all entries using a single request.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
package example.springdata.solr;

import example.springdata.solr.product.Product;

import java.util.stream.IntStream;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import example.springdata.solr.test.util.SolrInfrastructureRule;
import example.springdata.test.util.InfrastructureRule;

import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
Expand All @@ -32,33 +31,22 @@
/**
* @author Christoph Strobl
* @author Oliver Gierke
* @author Jens Schauder
*/
@SpringBootApplication
public class SolrTestConfiguration {

@Autowired CrudRepository<Product, String> repo;

public @Bean SolrTemplate solrTemplate() {
return new SolrTemplate(new HttpSolrClient.Builder().withBaseSolrUrl("http://localhost:8983/solr").build());
}
private static final Logger LOG = LoggerFactory.getLogger(SolrTestConfiguration.class);

/**
* Remove test data when context is shut down.
*/
public @PreDestroy void deleteDocumentsOnShutdown() {
repo.deleteAll();
}
public @Bean InfrastructureRule<String> infrastructureRule() {

/**
* Initialize Solr instance with test data once context has started.
*/
public @PostConstruct void initWithTestData() {
doInitTestData(repo);
return new SolrInfrastructureRule("techproducts");
}

protected void doInitTestData(CrudRepository<Product, String> repository) {

IntStream.range(0, 100)
.forEach(index -> repository.save(Product.builder().id("p-" + index).name("foobar").build()));
public @Bean SolrTemplate solrTemplate() {
return new SolrTemplate(new HttpSolrClient.Builder().withBaseSolrUrl(infrastructureRule().getInfo()).build());
}

}
Loading

0 comments on commit 51c7664

Please sign in to comment.