Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusable testcontainers #15

Merged
merged 14 commits into from
Aug 9, 2023
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
Loading
Loading