Skip to content

Commit 5bcbabd

Browse files
committed
#420 - Introducing infrastructure to find and detect required databases.
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.
1 parent 5d536b1 commit 5bcbabd

File tree

19 files changed

+497
-93
lines changed

19 files changed

+497
-93
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
</parent>
1717

1818
<modules>
19+
<module>util</module>
1920
<module>bom</module>
2021
<module>couchbase</module>
2122
<module>elasticsearch</module>

r2dbc/example/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,11 @@
1313

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

16+
<dependencies>
17+
<dependency>
18+
<groupId>org.springframework.data.examples</groupId>
19+
<artifactId>spring-data-examples-utils</artifactId>
20+
<version>${project.version}</version>
21+
</dependency>
22+
</dependencies>
1623
</project>

r2dbc/example/src/test/java/example/springdata/r2dbc/basics/CustomerRepositoryIntegrationTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package example.springdata.r2dbc.basics;
1717

18+
import example.springdata.test.util.InfrastructureRule;
19+
import org.junit.Rule;
1820
import reactor.core.publisher.Hooks;
1921
import reactor.test.StepVerifier;
2022

@@ -32,6 +34,7 @@
3234

3335
/**
3436
* @author Oliver Gierke
37+
* @author Jens Schauder
3538
*/
3639
@RunWith(SpringRunner.class)
3740
@SpringBootTest(classes = InfrastructureConfiguration.class)
@@ -40,6 +43,9 @@ public class CustomerRepositoryIntegrationTests {
4043
@Autowired CustomerRepository customers;
4144
@Autowired DatabaseClient database;
4245

46+
@Rule @Autowired
47+
public InfrastructureRule requiresPostgres;
48+
4349
@Before
4450
public void setUp() {
4551

r2dbc/example/src/test/java/example/springdata/r2dbc/basics/InfrastructureConfiguration.java

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,38 @@
1515
*/
1616
package example.springdata.r2dbc.basics;
1717

18+
import example.springdata.test.util.InfrastructureRule;
1819
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
1920
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
2021
import io.r2dbc.spi.ConnectionFactory;
2122

23+
import org.jetbrains.annotations.NotNull;
2224
import org.springframework.context.annotation.Bean;
2325
import org.springframework.context.annotation.Configuration;
2426
import org.springframework.data.r2dbc.function.DatabaseClient;
2527
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
2628
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
2729
import org.testcontainers.containers.PostgreSQLContainer;
30+
import reactor.test.StepVerifier;
2831

29-
import javax.annotation.PreDestroy;
32+
import java.sql.Connection;
33+
import java.sql.DriverManager;
34+
import java.sql.SQLException;
3035

3136
/**
3237
* @author Oliver Gierke
3338
*/
3439
@Configuration
3540
class InfrastructureConfiguration {
3641

37-
private PostgreSQLContainer postgres = new PostgreSQLContainer();
42+
@Bean
43+
InfrastructureRule<PostgresqlConnectionConfiguration> infrastructureRule() {
44+
45+
return new InfrastructureRule<>( //
46+
this::checkForlLocalPostgres, //
47+
this::startPostgresInDocker //
48+
);
49+
}
3850

3951
@Bean
4052
CustomerRepository customerRepository(R2dbcRepositoryFactory factory) {
@@ -61,22 +73,50 @@ DatabaseClient databaseClient(ConnectionFactory factory) {
6173
@Bean
6274
PostgresqlConnectionFactory connectionFactory() {
6375

76+
return new PostgresqlConnectionFactory(infrastructureRule().getInfo());
77+
}
78+
79+
@NotNull
80+
private InfrastructureRule.ResourceInfo<PostgresqlConnectionConfiguration> startPostgresInDocker() {
6481

82+
PostgreSQLContainer postgres = new PostgreSQLContainer();
6583
postgres.start();
6684

67-
PostgresqlConnectionConfiguration config = PostgresqlConnectionConfiguration.builder() //
85+
PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration.builder() //
6886
.host(postgres.getContainerIpAddress()) //
6987
.port(postgres.getFirstMappedPort()) //
7088
.database(postgres.getDatabaseName()) //
7189
.username(postgres.getUsername()) //
7290
.password(postgres.getPassword()) //
7391
.build();
7492

75-
return new PostgresqlConnectionFactory(config);
93+
return new InfrastructureRule.ResourceInfo<>(true, configuration, null, postgres::stop);
7694
}
7795

78-
@PreDestroy
79-
void shutdown() {
80-
postgres.stop();
96+
@NotNull
97+
private InfrastructureRule.ResourceInfo<PostgresqlConnectionConfiguration> checkForlLocalPostgres() {
98+
99+
PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration.builder() //
100+
.host("localhost") //
101+
.port(5432) //
102+
.database("postgres") //
103+
.username("postgres") //
104+
.password("") //
105+
.build();
106+
107+
try {
108+
109+
new PostgresqlConnectionFactory(configuration).create()
110+
.as(StepVerifier::create) //
111+
.assertNext(c -> {
112+
}) //
113+
.verifyComplete();
114+
} catch (AssertionError re) {
115+
116+
return new InfrastructureRule.ResourceInfo<>(false, null, re, () ->{});
117+
}
118+
119+
return new InfrastructureRule.ResourceInfo<>(true, configuration, null, () ->{});
81120
}
121+
82122
}

solr/example/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818
<version>${project.version}</version>
1919
<scope>test</scope>
2020
</dependency>
21+
<dependency>
22+
<groupId>org.springframework.data.examples</groupId>
23+
<artifactId>spring-data-examples-utils</artifactId>
24+
<version>${project.version}</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.testcontainers</groupId>
28+
<artifactId>testcontainers</artifactId>
29+
<version>1.7.3</version>
30+
<scope>test</scope>
31+
</dependency>
2132
</dependencies>
2233

2334
</project>

solr/example/src/main/java/example/springdata/solr/product/Product.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,16 @@
3838
@SolrDocument(collection = "techproducts")
3939
public class Product {
4040

41+
// debugging: testing the constructor does not change
42+
static {Product p = new Product("id", "name", null, null, null,true, null, null);
43+
}
44+
4145
private @Id String id;
4246
private @Indexed String name;
4347
private @Indexed(name = "cat") List<String> category;
4448
private @Indexed(name = "store") Point location;
4549
private @Indexed String description;
46-
private @Indexed boolean inStock;
50+
private @Indexed Boolean inStock;
4751
private @Indexed Integer popularity;
4852
private @Score Float score;
4953
}

solr/example/src/test/java/example/springdata/solr/AdvancedSolrRepositoryTests.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@
2222

2323
import example.springdata.solr.product.Product;
2424
import example.springdata.solr.product.ProductRepository;
25-
import example.springdata.solr.test.util.RequiresSolrServer;
25+
import example.springdata.test.util.InfrastructureRule;
2626

2727
import java.time.Duration;
2828
import java.util.Arrays;
2929
import java.util.Optional;
3030

31-
import org.junit.ClassRule;
31+
import org.junit.After;
32+
import org.junit.Before;
33+
import org.junit.Rule;
3234
import org.junit.Test;
3335
import org.junit.runner.RunWith;
3436
import org.springframework.beans.factory.annotation.Autowired;
3537
import org.springframework.boot.test.context.SpringBootTest;
36-
import org.springframework.context.annotation.Configuration;
3738
import org.springframework.data.domain.PageRequest;
38-
import org.springframework.data.repository.CrudRepository;
3939
import org.springframework.data.solr.core.SolrOperations;
4040
import org.springframework.data.solr.core.query.Function;
4141
import org.springframework.data.solr.core.query.Query;
@@ -48,33 +48,41 @@
4848
* @author Christoph Strobl
4949
* @author Oliver Gierke
5050
* @author Mark Paluch
51+
* @author Jens Schauder
5152
*/
5253
@RunWith(SpringRunner.class)
5354
@SpringBootTest
5455
public class AdvancedSolrRepositoryTests {
5556

56-
public static @ClassRule RequiresSolrServer requiresRunningServer = RequiresSolrServer.onLocalhost();
57+
@Rule @Autowired public InfrastructureRule requiresRunningServer;
5758

58-
@Configuration
59-
static class Config extends SolrTestConfiguration {
60-
61-
@Override
62-
protected void doInitTestData(CrudRepository<Product, String> repository) {
59+
@Autowired ProductRepository repository;
60+
@Autowired SolrOperations operations;
6361

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

71-
repository.saveAll(Arrays.asList(playstation, playstation2, superNES, nintendo64));
72-
}
63+
/**
64+
* Remove test data when context is shut down.
65+
*/
66+
public @After void deleteDocumentsOnShutdown() {
67+
repository.deleteAll();
7368
}
7469

75-
@Autowired ProductRepository repository;
76-
@Autowired SolrOperations operations;
70+
/**
71+
* Initialize Solr instance with test data once context has started.
72+
*/
73+
public @Before void initWithTestData() throws InterruptedException {
7774

75+
repository.deleteAll();
76+
77+
Product playstation = Product.builder().id("id-1").name("Playstation")
78+
.description("The Sony playstation was the top selling gaming system in 1994.").popularity(5).build();
79+
Product playstation2 = Product.builder().id("id-2").name("Playstation Two")
80+
.description("Playstation two is the successor of playstation in 2000.").build();
81+
Product superNES = Product.builder().id("id-3").name("Super Nintendo").popularity(3).build();
82+
Product nintendo64 = Product.builder().id("id-4").name("N64").description("Nintendo 64").popularity(2).build();
83+
84+
repository.saveAll(Arrays.asList(playstation, playstation2, superNES, nintendo64));
85+
}
7886
/**
7987
* {@link HighlightPage} holds next to the entities found also information about where a match was found within the
8088
* document. This allows to fine grained display snipplets of data containing the matching term in context.

solr/example/src/test/java/example/springdata/solr/BasicSolrRepositoryTests.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
*/
1616
package example.springdata.solr;
1717

18+
import example.springdata.solr.product.Product;
1819
import example.springdata.solr.product.ProductRepository;
19-
import example.springdata.solr.test.util.RequiresSolrServer;
20+
import example.springdata.test.util.InfrastructureRule;
2021

21-
import org.junit.ClassRule;
22+
import java.util.stream.IntStream;
23+
24+
import org.junit.After;
25+
import org.junit.Before;
26+
import org.junit.Rule;
2227
import org.junit.Test;
2328
import org.junit.runner.RunWith;
2429
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,15 +32,36 @@
2732

2833
/**
2934
* @author Christoph Strobl
35+
* @author Jens Schauder
3036
*/
3137
@RunWith(SpringRunner.class)
32-
@SpringBootTest(classes = SolrTestConfiguration.class)
38+
@SpringBootTest
3339
public class BasicSolrRepositoryTests {
3440

35-
public static @ClassRule RequiresSolrServer requiresRunningServer = RequiresSolrServer.onLocalhost();
41+
42+
43+
@Rule @Autowired public InfrastructureRule requiresRunningServer;
3644

3745
@Autowired ProductRepository repository;
3846

47+
/**
48+
* Remove test data when context is shut down.
49+
*/
50+
public @After void deleteDocumentsOnShutdown() {
51+
repository.deleteAll();
52+
}
53+
54+
/**
55+
* Initialize Solr instance with test data once context has started.
56+
*/
57+
public @Before void initWithTestData() {
58+
59+
repository.deleteAll();
60+
61+
IntStream.range(0, 100)
62+
.forEach(index -> repository.save(Product.builder().id("p-" + index).name("foobar").build()));
63+
}
64+
3965
/**
4066
* Finds all entries using a single request.
4167
*/

solr/example/src/test/java/example/springdata/solr/SolrTestConfiguration.java

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
package example.springdata.solr;
1717

1818
import example.springdata.solr.product.Product;
19-
20-
import java.util.stream.IntStream;
21-
22-
import javax.annotation.PostConstruct;
23-
import javax.annotation.PreDestroy;
19+
import example.springdata.solr.test.util.SolrInfrastructureRule;
20+
import example.springdata.test.util.InfrastructureRule;
2421

2522
import org.apache.solr.client.solrj.impl.HttpSolrClient;
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
2625
import org.springframework.beans.factory.annotation.Autowired;
2726
import org.springframework.boot.autoconfigure.SpringBootApplication;
2827
import org.springframework.context.annotation.Bean;
@@ -32,33 +31,22 @@
3231
/**
3332
* @author Christoph Strobl
3433
* @author Oliver Gierke
34+
* @author Jens Schauder
3535
*/
3636
@SpringBootApplication
3737
public class SolrTestConfiguration {
3838

3939
@Autowired CrudRepository<Product, String> repo;
4040

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

45-
/**
46-
* Remove test data when context is shut down.
47-
*/
48-
public @PreDestroy void deleteDocumentsOnShutdown() {
49-
repo.deleteAll();
50-
}
43+
public @Bean InfrastructureRule<String> infrastructureRule() {
5144

52-
/**
53-
* Initialize Solr instance with test data once context has started.
54-
*/
55-
public @PostConstruct void initWithTestData() {
56-
doInitTestData(repo);
45+
return new SolrInfrastructureRule("techproducts");
5746
}
5847

59-
protected void doInitTestData(CrudRepository<Product, String> repository) {
60-
61-
IntStream.range(0, 100)
62-
.forEach(index -> repository.save(Product.builder().id("p-" + index).name("foobar").build()));
48+
public @Bean SolrTemplate solrTemplate() {
49+
return new SolrTemplate(new HttpSolrClient.Builder().withBaseSolrUrl(infrastructureRule().getInfo()).build());
6350
}
51+
6452
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
logging.level.root=WARN
2+
logging.level.org.springframework.data.solr.core.SolrTemplate=DEBUG

0 commit comments

Comments
 (0)