Skip to content

Commit

Permalink
Merge pull request #15 from infobip/reuse-container
Browse files Browse the repository at this point in the history
Reusable testcontainers
  • Loading branch information
lpandzic committed Aug 9, 2023
2 parents 1c54a1b + ec87ca8 commit aa0ab91
Show file tree
Hide file tree
Showing 54 changed files with 585 additions and 342 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 4.2.0
* Added reusable support

### 4.1.0

* Added the testcontainers.db-name.init-script property to all the database starters
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Usual use cases include:

* [Changelog](#Changelog)
* [Usage](#Usage)
* [General](#General)
* [Reusable](#Reusable)
* [MSSQL](#MSSQL)
* [Tests](#MSSQLTests)
* [Local development](#MSSQLLocalDevelopment)
Expand Down Expand Up @@ -59,6 +61,10 @@ Generally, in cases where port placeholders are used (`<port>`), the library wil
a randomly selected open port and that the selected value will be used by the configuration in the runtime.
You can use a concrete value instead of the placeholder - in that case the library will attempt to start the container on the specified port.

<a name="Reusable"></a>
### Reusable
If [reuse is enabled](https://java.testcontainers.org/features/reuse/) this project automatically marks all created containers for reuse.

<a name="MSSQL"></a>
### MSSQL

Expand Down
10 changes: 9 additions & 1 deletion infobip-clickhouse-testcontainers-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>com.infobip</groupId>
<artifactId>infobip-testcontainers-spring-boot-starter</artifactId>
<version>4.1.1-SNAPSHOT</version>
<version>4.2.0-SNAPSHOT</version>
</parent>

<artifactId>infobip-clickhouse-testcontainers-spring-boot-starter</artifactId>
Expand Down Expand Up @@ -42,5 +42,13 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!--TEST-->
<dependency>
<groupId>com.infobip</groupId>
<artifactId>infobip-testcontainers-test-common</artifactId>
<scope>test</scope>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
package com.infobip.testcontainers.spring.clickhouse;

import java.util.Objects;
import java.util.Optional;

import com.infobip.testcontainers.InitializerBase;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

import java.util.Objects;
import java.util.Optional;

public class ClickhouseContainerInitializer extends InitializerBase<ClickhouseContainerWrapper> {

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Environment environment = applicationContext.getEnvironment();
Optional<String> customPropertyPath = Optional.ofNullable(environment.getProperty("testcontainers.clickhouse.custom-path"));
String jdbcUrlPropertyPath = customPropertyPath.orElse("spring.datasource") + ".jdbc-url";
String jdbcUrlValue = Objects.requireNonNull(environment.getProperty(jdbcUrlPropertyPath));
ClickhouseContainerWrapper container = Optional.ofNullable(
environment.getProperty("testcontainers.clickhouse.docker.image.version"))
.map(ClickhouseContainerWrapper::new)
.orElseGet(ClickhouseContainerWrapper::new);

Optional.ofNullable(environment.getProperty("testcontainers.clickhouse.init-script")).ifPresent(container::withInitScript);
var environment = applicationContext.getEnvironment();
var customPropertyPath = Optional.ofNullable(environment.getProperty("testcontainers.clickhouse.custom-path"));
var jdbcUrlPropertyPath = customPropertyPath.orElse("spring.datasource") + ".jdbc-url";
var jdbcUrlValue = Objects.requireNonNull(environment.getProperty(jdbcUrlPropertyPath));
var wrapper = Optional.ofNullable(environment.getProperty("testcontainers.clickhouse.docker.image.version"))
.map(ClickhouseContainerWrapper::new)
.orElseGet(ClickhouseContainerWrapper::new);
var container = handleReusable(wrapper);

Optional.ofNullable(environment.getProperty("testcontainers.clickhouse.init-script"))
.ifPresent(container::withInitScript);

resolveStaticPort(jdbcUrlValue, GENERIC_URL_WITH_PORT_GROUP_PATTERN)
.ifPresent(staticPort -> bindPort(container, staticPort, ClickhouseContainerWrapper.HTTP_PORT));
.ifPresent(staticPort -> bindPort(container, staticPort, ClickhouseContainerWrapper.HTTP_PORT));

start(container);

String url = replaceHostAndPortPlaceholders(jdbcUrlValue, container, ClickhouseContainerWrapper.HTTP_PORT);
var url = replaceHostAndPortPlaceholders(jdbcUrlValue, container, ClickhouseContainerWrapper.HTTP_PORT);

TestPropertyValues values = TestPropertyValues.of(String.format("%s=%s", jdbcUrlPropertyPath, url));
var values = TestPropertyValues.of(String.format("%s=%s", jdbcUrlPropertyPath, url));
values.applyTo(applicationContext);
}

registerContainerAsBean(applicationContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import javax.sql.DataSource;

import com.infobip.testcontainers.TestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
Expand All @@ -13,9 +14,7 @@

@AllArgsConstructor
@ActiveProfiles("test")
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@SpringBootTest(classes = Main.class)
class ClickhouseContainerInitializerTest {
class ClickhouseContainerInitializerTest extends TestBase {

private DataSource dataSource;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package com.infobip.testcontainers.spring.clickhouse;

import com.infobip.testcontainers.TestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestConstructor;

import javax.sql.DataSource;

import static org.assertj.core.api.BDDAssertions.then;

@AllArgsConstructor
@ActiveProfiles("init-script")
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@SpringBootTest(classes = Main.class)
class ClickhouseContainerInitializerWithInitScriptTest {
class ClickhouseContainerInitializerWithInitScriptTest extends TestBase {

private DataSource dataSource;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.infobip.testcontainers.spring.clickhouse;

import com.infobip.testcontainers.ReusableTestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.ActiveProfiles;
import org.testcontainers.containers.ClickHouseContainer;

import static org.assertj.core.api.BDDAssertions.then;

@AllArgsConstructor
@ActiveProfiles("reusable")
class ClickhouseContainerInitializerWithReusableTest extends ReusableTestBase {

private final ClickhouseContainerWrapper wrapper;
private final int port = ClickHouseContainer.NATIVE_PORT;

@Test
void shouldReuseContainer() {
// given
var givenContainer = new ClickhouseContainerWrapper();
givenContainer.withReuse(true);

// when
givenContainer.start();

// then
then(givenContainer.getMappedPort(port)).isEqualTo(wrapper.getMappedPort(port));
}

}
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
package com.infobip.testcontainers.spring.clickhouse;

import static com.infobip.testcontainers.spring.clickhouse.DataSourceConfig.CLICKHOUSE_URL_PROPERTY_NAME;
import static org.assertj.core.api.BDDAssertions.then;

import javax.sql.DataSource;

import com.infobip.testcontainers.TestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestConstructor;

import javax.sql.DataSource;

import static com.infobip.testcontainers.spring.clickhouse.DataSourceConfig.CLICKHOUSE_URL_PROPERTY_NAME;
import static org.assertj.core.api.BDDAssertions.then;

@AllArgsConstructor
@ActiveProfiles("static-port")
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@SpringBootTest(classes = Main.class)
class ClickhouseContainerInitializerWithStaticPortTest {
class ClickhouseContainerInitializerWithStaticPortTest extends TestBase {

private Environment environment;
private DataSource dataSource;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
spring:
datasource:
clickhouse:
jdbc-url: jdbc:clickhouse://<host>:<port>

testcontainers:
clickhouse:
custom-path: "spring.datasource.clickhouse"
9 changes: 8 additions & 1 deletion infobip-kafka-testcontainers-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>com.infobip</groupId>
<artifactId>infobip-testcontainers-spring-boot-starter</artifactId>
<version>4.1.1-SNAPSHOT</version>
<version>4.2.0-SNAPSHOT</version>
</parent>

<artifactId>infobip-kafka-testcontainers-spring-boot-starter</artifactId>
Expand Down Expand Up @@ -39,5 +39,12 @@
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.infobip</groupId>
<artifactId>infobip-testcontainers-test-common</artifactId>
<scope>test</scope>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,66 +1,84 @@
package com.infobip.testcontainers.spring.kafka;

import static java.lang.Integer.parseInt;
import static java.lang.Short.parseShort;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.infobip.testcontainers.InitializerBase;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

import java.util.*;
import java.util.concurrent.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.lang.Integer.parseInt;
import static java.lang.Short.parseShort;

public class KafkaContainerInitializer extends InitializerBase<KafkaContainerWrapper> {

private static final Pattern KAFKA_SERVER_PATTERN = Pattern.compile("^.*:(\\d+).*");

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Environment environment = applicationContext.getEnvironment();
String bootstrapServers = Objects.requireNonNull(environment.getProperty("spring.kafka.bootstrap-servers"));
KafkaContainerWrapper container = Optional.ofNullable(
environment.getProperty("testcontainers.kafka.docker.image.version"))
.map(KafkaContainerWrapper::new)
.orElseGet(KafkaContainerWrapper::new);
var environment = applicationContext.getEnvironment();
var bootstrapServers = Objects.requireNonNull(environment.getProperty("spring.kafka.bootstrap-servers"));
var wrapper = Optional.ofNullable(environment.getProperty("testcontainers.kafka.docker.image.version"))
.map(KafkaContainerWrapper::new)
.orElseGet(KafkaContainerWrapper::new);
var container = handleReusable(wrapper);

resolveStaticPort(bootstrapServers, KAFKA_SERVER_PATTERN)
.ifPresent(staticPort -> bindPort(container, staticPort, KafkaContainerWrapper.KAFKA_PORT));
.ifPresent(staticPort -> bindPort(container, staticPort, KafkaContainerWrapper.KAFKA_PORT));

start(container);
String url = replaceHostAndPortPlaceholders(bootstrapServers, container, KafkaContainerWrapper.KAFKA_PORT);
var url = replaceHostAndPortPlaceholders(bootstrapServers, container, KafkaContainerWrapper.KAFKA_PORT);

Optional.ofNullable(environment.getProperty("testcontainers.kafka.topics", String[].class))
.ifPresent(topics -> createTestKafkaTopics(url, topics));
TestPropertyValues values = TestPropertyValues.of(
"spring.kafka.bootstrap-servers=" + url);
.ifPresent(topics -> createTestKafkaTopics(container, url, topics));
var values = TestPropertyValues.of(
"spring.kafka.bootstrap-servers=" + url);
values.applyTo(applicationContext);
}

private static void createTestKafkaTopics(String bootstrapServers, String[] topics) {
registerContainerAsBean(applicationContext);
}

List<NewTopic> newTopics = Stream.of(topics)
.map(topic -> topic.split(":"))
.map(topicParts -> new NewTopic(topicParts[0], parseInt(topicParts[1]),
parseShort(topicParts[2])))
.collect(Collectors.toList());
private static void createTestKafkaTopics(KafkaContainerWrapper container, String bootstrapServers, String[] topics) {

try {
AdminClient.create(Collections.singletonMap("bootstrap.servers", bootstrapServers))
.createTopics(newTopics)
.all()
.get(60, TimeUnit.SECONDS);
try (var client = AdminClient.create(Collections.singletonMap("bootstrap.servers", bootstrapServers))) {
if(container.isShouldBeReused()) {
deleteTopics(client, topics);
}
createTopics(client, topics);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static void deleteTopics(AdminClient client, String[] topics) throws ExecutionException,
InterruptedException, TimeoutException {
var existingTopics = client.listTopics().names().get(60, TimeUnit.SECONDS);
var deleteTopics = Stream.of(topics)
.map(topic -> topic.split(":"))
.filter(topic -> !existingTopics.contains(topic[0]))
.map(topicParts -> topicParts[0])
.toList();

client.deleteTopics(deleteTopics);
}

private static void createTopics(AdminClient client, String[] topics) throws InterruptedException,
ExecutionException, TimeoutException {
var existingTopics = client.listTopics().names().get(60, TimeUnit.SECONDS);
var newTopics = Stream.of(topics)
.map(topic -> topic.split(":"))
.filter(topic -> !existingTopics.contains(topic[0]))
.map(topicParts -> new NewTopic(topicParts[0], parseInt(topicParts[1]),
parseShort(topicParts[2])))
.toList();

client.createTopics(newTopics)
.all()
.get(60, TimeUnit.SECONDS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.infobip.testcontainers.TestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
Expand All @@ -19,9 +20,7 @@

@AllArgsConstructor
@ActiveProfiles("test")
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@SpringBootTest(classes = Main.class)
class KafkaContainerInitializerTest {
class KafkaContainerInitializerTest extends TestBase {

private final KafkaTemplate<String, String> kafkaTemplate;
private final Listener listener;
Expand Down

0 comments on commit aa0ab91

Please sign in to comment.