Skip to content

Commit

Permalink
Polish "Upgrade to Testcontainers 1.20.2"
Browse files Browse the repository at this point in the history
This commit review the original upgrade to retain compatiblity with the
deprecated Cassandra and ConfluentKafka containers.

This commit also fixes the SSL Cassandra tests. The new container uses
a custom wait strategy that uses plain text and does not work with an
SSL container.

Closes gh-42670

Co-authored-by: Moritz Halbritter <[email protected]>
  • Loading branch information
snicoll and mhalbritter committed Oct 15, 2024
1 parent a7fb369 commit e015209
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.cassandra;

import com.datastax.oss.driver.api.core.CqlSession;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.CassandraContainer;
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.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.boot.testsupport.container.TestImage;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link DeprecatedCassandraContainerConnectionDetailsFactory}.
*
* @author Andy Wilkinson
* @deprecated since 3.4.0 for removal in 3.6.0
*/
@SpringJUnitConfig
@Testcontainers(disabledWithoutDocker = true)
@Deprecated(since = "3.4.0", forRemoval = true)
class DeprecatedCassandraContainerConnectionDetailsFactoryTests {

@Container
@ServiceConnection
static final CassandraContainer<?> cassandra = TestImage.container(CassandraContainer.class);

@Autowired(required = false)
private CassandraConnectionDetails connectionDetails;

@Autowired
private CqlSession cqlSession;

@Test
void connectionCanBeMadeToCassandraContainer() {
assertThat(this.connectionDetails).isNotNull();
assertThat(this.cqlSession.getMetadata().getNodes()).hasSize(1);
}

@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration(CassandraAutoConfiguration.class)
static class TestConfiguration {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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.containers.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.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 DeprecatedConfluentKafkaContainerConnectionDetailsFactory}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @deprecated since 3.4.0 for removal in 3.6.0
*/
@SpringJUnitConfig
@Testcontainers(disabledWithoutDocker = true)
@TestPropertySource(properties = { "spring.kafka.consumer.group-id=test-group",
"spring.kafka.consumer.auto-offset-reset=earliest" })
@Deprecated(since = "3.4.0", forRemoval = true)
class DeprecatedConfluentKafkaContainerConnectionDetailsFactoryIntegrationTests {

@Container
@ServiceConnection
static final KafkaContainer kafka = TestImage.container(KafkaContainer.class);

@Autowired
private KafkaTemplate<String, String> 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<String> messages = new ArrayList<>();

@KafkaListener(topics = "test-topic")
void processMessage(String message) {
this.messages.add(message);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2012-2023 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.cassandra;

import java.net.InetSocketAddress;
import java.util.List;

import org.testcontainers.containers.CassandraContainer;

import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
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 CassandraConnectionDetails}
* from a {@link ServiceConnection @ServiceConnection}-annotated
* {@link CassandraContainer}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of
* {@link CassandraContainerConnectionDetailsFactory}.
*/
@Deprecated(since = "3.4.0", forRemoval = true)
class DeprecatedCassandraContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<CassandraContainer<?>, CassandraConnectionDetails> {

@Override
protected CassandraConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<CassandraContainer<?>> source) {
return new CassandraContainerConnectionDetails(source);
}

/**
* {@link CassandraConnectionDetails} backed by a {@link ContainerConnectionSource}.
*/
private static final class CassandraContainerConnectionDetails
extends ContainerConnectionDetails<CassandraContainer<?>> implements CassandraConnectionDetails {

private CassandraContainerConnectionDetails(ContainerConnectionSource<CassandraContainer<?>> source) {
super(source);
}

@Override
public List<Node> getContactPoints() {
InetSocketAddress contactPoint = getContainer().getContactPoint();
return List.of(new Node(contactPoint.getHostString(), contactPoint.getPort()));
}

@Override
public String getUsername() {
return getContainer().getUsername();
}

@Override
public String getPassword() {
return getContainer().getPassword();
}

@Override
public String getLocalDatacenter() {
return getContainer().getLocalDatacenter();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.containers.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
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of
* {@link ConfluentKafkaContainerConnectionDetailsFactory}.
*/
@Deprecated(since = "3.4.0", forRemoval = true)
class DeprecatedConfluentKafkaContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<KafkaContainer, KafkaConnectionDetails> {

@Override
protected KafkaConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<KafkaContainer> source) {
return new ConfluentKafkaContainerConnectionDetails(source);
}

/**
* {@link KafkaConnectionDetails} backed by a {@link ContainerConnectionSource}.
*/
private static final class ConfluentKafkaContainerConnectionDetails
extends ContainerConnectionDetails<KafkaContainer> implements KafkaConnectionDetails {

private ConfluentKafkaContainerConnectionDetails(ContainerConnectionSource<KafkaContainer> source) {
super(source);
}

@Override
public List<String> getBootstrapServers() {
return List.of(getContainer().getBootstrapServers());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ org.springframework.boot.testcontainers.service.connection.activemq.ActiveMQCont
org.springframework.boot.testcontainers.service.connection.activemq.ArtemisContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.amqp.RabbitContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.cassandra.CassandraContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.cassandra.DeprecatedCassandraContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.flyway.FlywayContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.hazelcast.HazelcastContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.kafka.ApacheKafkaContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.kafka.ConfluentKafkaContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.kafka.DeprecatedConfluentKafkaContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.ldap.OpenLdapContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseContainerConnectionDetailsFactory,\
org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ public enum TestImage {
CASSANDRA("cassandra", "3.11.10", () -> CassandraContainer.class,
(container) -> ((CassandraContainer) container).withStartupTimeout(Duration.ofMinutes(10))),

/**
* A container image suitable for testing Cassandra using the deprecated
* {@link org.testcontainers.containers.CassandraContainer}.
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of {@link #CASSANDRA}
*/
@Deprecated(since = "3.4.0", forRemoval = true)
CASSANDRA_DEPRECATED("cassandra", "3.11.10", () -> org.testcontainers.containers.CassandraContainer.class,
(container) -> ((org.testcontainers.containers.CassandraContainer<?>) container)
.withStartupTimeout(Duration.ofMinutes(10))),

/**
* A container image suitable for testing Couchbase.
*/
Expand Down Expand Up @@ -119,6 +129,15 @@ public enum TestImage {
*/
CONFLUENT_KAFKA("confluentinc/cp-kafka", "7.4.0", () -> ConfluentKafkaContainer.class),

/**
* A container image suitable for testing Confluent's distribution of Kafka using the
* deprecated {@link org.testcontainers.containers.KafkaContainer}.
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of {@link #CONFLUENT_KAFKA}
*/
@Deprecated(since = "3.4.0", forRemoval = true)
CONFLUENT_KAFKA_DEPRECATED("confluentinc/cp-kafka", "7.4.0",
() -> org.testcontainers.containers.KafkaContainer.class),

/**
* A container image suitable for testing OpenLDAP.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package smoketest.data.cassandra;

import org.testcontainers.cassandra.CassandraContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.MountableFile;

Expand All @@ -29,6 +30,7 @@ class SecureCassandraContainer extends CassandraContainer {

SecureCassandraContainer(DockerImageName dockerImageName) {
super(dockerImageName);
setWaitStrategy(Wait.defaultWaitStrategy()); // default strategy uses plain text
withCopyFileToContainer(MountableFile.forClasspathResource("/ssl/cassandra.yaml"),
"/etc/cassandra/cassandra.yaml");
withCopyFileToContainer(MountableFile.forClasspathResource("/ssl/test-server.p12"),
Expand Down

0 comments on commit e015209

Please sign in to comment.