diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc index 4fcf316c25a..3d2ff668e69 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc @@ -60,7 +60,7 @@ The following service connection factories are provided in the `spring-boot-test | Containers of type `JdbcDatabaseContainer` | `KafkaConnectionDetails` -| Containers of type `org.testcontainers.containers.KafkaContainer` or `RedpandaContainer` +| Containers of type `org.testcontainers.containers.KafkaContainer`, `org.testcontainers.kafka.KafkaContainer` or `RedpandaContainer` | `LiquibaseConnectionDetails` | Containers of type `JdbcDatabaseContainer` diff --git a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ApacheKafkaContainerConnectionDetailsFactoryIntegrationTests.java new file mode 100644 index 00000000000..516b854d7d8 --- /dev/null +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/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.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; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.boot.testsupport.container.TestImage; +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 ConfluentKafkaContainerConnectionDetailsFactory}. + * + * @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 = TestImage.container(KafkaContainer.class); + + @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-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/KafkaContainerConnectionDetailsFactoryIntegrationTests.java b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ConfluentKafkaContainerConnectionDetailsFactoryIntegrationTests.java similarity index 95% rename from spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/KafkaContainerConnectionDetailsFactoryIntegrationTests.java rename to spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ConfluentKafkaContainerConnectionDetailsFactoryIntegrationTests.java index c26eab67740..d02f9b93e3a 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/KafkaContainerConnectionDetailsFactoryIntegrationTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/dockerTest/java/org/springframework/boot/testcontainers/service/connection/kafka/ConfluentKafkaContainerConnectionDetailsFactoryIntegrationTests.java @@ -41,7 +41,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link KafkaContainerConnectionDetailsFactory}. + * Tests for {@link ConfluentKafkaContainerConnectionDetailsFactory}. * * @author Moritz Halbritter * @author Andy Wilkinson @@ -51,7 +51,7 @@ import static org.assertj.core.api.Assertions.assertThat; @Testcontainers(disabledWithoutDocker = true) @TestPropertySource(properties = { "spring.kafka.consumer.group-id=test-group", "spring.kafka.consumer.auto-offset-reset=earliest" }) -class KafkaContainerConnectionDetailsFactoryIntegrationTests { +class ConfluentKafkaContainerConnectionDetailsFactoryIntegrationTests { @Container @ServiceConnection 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 00000000000..5815e75ac82 --- /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 ApacheKafkaContainerConnectionDetails(source); + } + + /** + * {@link KafkaConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class ApacheKafkaContainerConnectionDetails extends ContainerConnectionDetails + implements KafkaConnectionDetails { + + private ApacheKafkaContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public List getBootstrapServers() { + return List.of(getContainer().getBootstrapServers()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/KafkaContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ConfluentKafkaContainerConnectionDetailsFactory.java similarity index 83% rename from spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/KafkaContainerConnectionDetailsFactory.java rename to spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ConfluentKafkaContainerConnectionDetailsFactory.java index 1f7b7f89a22..33bf8be95e7 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/KafkaContainerConnectionDetailsFactory.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/kafka/ConfluentKafkaContainerConnectionDetailsFactory.java @@ -33,21 +33,21 @@ import org.springframework.boot.testcontainers.service.connection.ServiceConnect * @author Andy Wilkinson * @author Phillip Webb */ -class KafkaContainerConnectionDetailsFactory +class ConfluentKafkaContainerConnectionDetailsFactory extends ContainerConnectionDetailsFactory { @Override protected KafkaConnectionDetails getContainerConnectionDetails(ContainerConnectionSource source) { - return new KafkaContainerConnectionDetails(source); + return new ConfluentKafkaContainerConnectionDetails(source); } /** * {@link KafkaConnectionDetails} backed by a {@link ContainerConnectionSource}. */ - private static final class KafkaContainerConnectionDetails extends ContainerConnectionDetails - implements KafkaConnectionDetails { + private static final class ConfluentKafkaContainerConnectionDetails + extends ContainerConnectionDetails implements KafkaConnectionDetails { - private KafkaContainerConnectionDetails(ContainerConnectionSource source) { + private ConfluentKafkaContainerConnectionDetails(ContainerConnectionSource source) { super(source); } 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 ac9e6aeae26..813e438b31f 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,7 +17,8 @@ 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.KafkaContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.kafka.ApacheKafkaContainerConnectionDetailsFactory,\ +org.springframework.boot.testcontainers.service.connection.kafka.ConfluentKafkaContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.ldap.OpenLdapContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\ org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\ diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java index 3c9d179bca8..baa04c18fb8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support-docker/src/main/java/org/springframework/boot/testsupport/container/TestImage.java @@ -64,6 +64,11 @@ public enum TestImage { */ ACTIVE_MQ_CLASSIC("apache/activemq-classic", "5.18.3", () -> ActiveMQContainer.class), + /** + * A container image suitable for testing Apache Kafka. + */ + APACHE_KAFKA("apache/kafka", "3.7.0", () -> org.testcontainers.kafka.KafkaContainer.class), + /** * A container image suitable for testing Artemis. */ @@ -96,9 +101,9 @@ public enum TestImage { ELASTICSEARCH_8("elasticsearch", "8.6.1"), /** - * A container image suitable for testing Kafka. + * A container image suitable for testing Confluent's distribution of Kafka. */ - KAFKA("confluentinc/cp-kafka", "7.4.0", () -> KafkaContainer.class), + CONFLUENT_KAFKA("confluentinc/cp-kafka", "7.4.0", () -> KafkaContainer.class), /** * A container image suitable for testing OpenLDAP.