From 5a1f33348b023aee000679c8e3c91dcb9d5a6550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Sat, 11 May 2024 22:02:04 +0200 Subject: [PATCH 1/2] Add support for org.testcontainers.kafka.KafkaContainer Testcontainers 1.19.8 provides `org.testcontainers.kafka.KafkaContainer`, which relies on `apache/kafka` image. --- .../spring-boot-dependencies/build.gradle | 2 +- ...afkaContainerConnectionDetailsFactory.java | 62 ++++++++++++ .../main/resources/META-INF/spring.factories | 1 + ...nectionDetailsFactoryIntegrationTests.java | 96 +++++++++++++++++++ .../testcontainers/DockerImageNames.java | 10 ++ 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactory.java create mode 100644 spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index f9c34d541afe..b7a03ef971de 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2116,7 +2116,7 @@ bom { releaseNotes("https://github.com/xerial/sqlite-jdbc/releases/tag/{version}") } } - library("Testcontainers", "1.19.7") { + library("Testcontainers", "1.19.8") { group("org.testcontainers") { imports = [ "testcontainers-bom" diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactory.java new file mode 100644 index 000000000000..31a0c94dff1e --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.testcontainers.service.connection.kafka; + +import java.util.List; + +import org.testcontainers.kafka.KafkaContainer; + +import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; + +/** + * {@link ContainerConnectionDetailsFactory} to create {@link KafkaConnectionDetails} from + * a {@link ServiceConnection @ServiceConnection}-annotated {@link KafkaContainer}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @author Eddú Meléndez + */ +class ApacheKafkaContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected KafkaConnectionDetails getContainerConnectionDetails(ContainerConnectionSource source) { + return new KafkaContainerConnectionDetails(source); + } + + /** + * {@link KafkaConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class KafkaContainerConnectionDetails extends ContainerConnectionDetails + implements KafkaConnectionDetails { + + private KafkaContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public List getBootstrapServers() { + return List.of(getContainer().getBootstrapServers()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories index ac9e6aeae26b..04a9ad9b0650 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories @@ -17,6 +17,7 @@ org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseCo org.springframework.boot.testcontainers.service.connection.flyway.FlywayContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.kafka.ApacheKafkaContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.kafka.KafkaContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.ldap.OpenLdapContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 000000000000..7471618885c9 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java @@ -0,0 +1,96 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.testcontainers.service.connection.kafka; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.testcontainers.kafka.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link KafkaContainerConnectionDetailsFactory}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @author Eddú Meléndez + */ +@SpringJUnitConfig +@Testcontainers(disabledWithoutDocker = true) +@TestPropertySource(properties = { "spring.kafka.consumer.group-id=test-group", + "spring.kafka.consumer.auto-offset-reset=earliest" }) +class ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests { + + @Container + @ServiceConnection + static final KafkaContainer kafka = new KafkaContainer(DockerImageNames.apacheKafka()); + + @Autowired + private KafkaTemplate kafkaTemplate; + + @Autowired + private TestListener listener; + + @Test + void connectionCanBeMadeToKafkaContainer() { + this.kafkaTemplate.send("test-topic", "test-data"); + Awaitility.waitAtMost(Duration.ofMinutes(4)) + .untilAsserted(() -> assertThat(this.listener.messages).containsExactly("test-data")); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(KafkaAutoConfiguration.class) + static class TestConfiguration { + + @Bean + TestListener testListener() { + return new TestListener(); + } + + } + + static class TestListener { + + private final List messages = new ArrayList<>(); + + @KafkaListener(topics = "test-topic") + void processMessage(String message) { + this.messages.add(message); + } + + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java index a5b5365ab1b8..70297e5d41b3 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java @@ -30,6 +30,8 @@ public final class DockerImageNames { private static final String ACTIVE_MQ_VERSION = "5.18.3"; + private static final String APACHE_KAFKA_VERSION = "3.7.0"; + private static final String ARTEMIS_VERSION = "2.31.2"; private static final String CASSANDRA_VERSION = "3.11.10"; @@ -91,6 +93,14 @@ public static DockerImageName activeMqClassic() { return DockerImageName.parse("apache/activemq-classic").withTag(ACTIVE_MQ_VERSION); } + /** + * Return a {@link DockerImageName} suitable for running Apache Kafka. + * @return a docker image name for running Apache Kafka + */ + public static DockerImageName apacheKafka() { + return DockerImageName.parse("apache/kafka").withTag(APACHE_KAFKA_VERSION); + } + /** * Return a {@link DockerImageName} suitable for running Artemis. * @return a docker image name for running artemis From 94d90c3b589467abab6cb3e74b5afc58e117a817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Mon, 13 May 2024 10:53:01 -0500 Subject: [PATCH 2/2] Fix checkstyle --- ...eKafkaContainerConnectionDetailsFactoryIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java index 7471618885c9..f7e2a75dcefc 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java @@ -22,9 +22,9 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; -import org.testcontainers.kafka.KafkaContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.kafka.KafkaContainer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;