From d89180a0d306b72b639ba040a927950f1411ceee Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Tue, 27 Jul 2021 22:11:40 +0200 Subject: [PATCH] Make integration tests configurable to use different containers. --- pom.xml | 56 ++++++++++--------- .../data/elasticsearch/Foobar.java | 19 +++++++ .../elasticsearch/FoobarIntegrationTest.java | 28 ++++++++++ .../TransportFoobarIntegrationTest.java | 17 ++++++ .../config/AuditingIntegrationTest.java | 2 +- .../junit/jupiter/ClusterConnection.java | 55 ++++++++++++++---- .../junit/jupiter/ClusterConnectionInfo.java | 24 ++++++-- .../jupiter/IntegrationtestEnvironment.java | 39 +++++++++++++ .../testcontainers-elasticsearch.properties | 10 +++- .../testcontainers-opensearch.properties | 10 ++++ 10 files changed, 214 insertions(+), 46 deletions(-) create mode 100644 src/test/java/org/springframework/data/elasticsearch/Foobar.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/FoobarIntegrationTest.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/TransportFoobarIntegrationTest.java create mode 100644 src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationtestEnvironment.java create mode 100644 src/test/resources/testcontainers-opensearch.properties diff --git a/pom.xml b/pom.xml index 124e702b1..63925dcd4 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,15 @@ 1.15.3 1.0.6.RELEASE spring.data.elasticsearch + + + test + integration-test + none @@ -264,25 +273,6 @@ test - - org.skyscreamer jsonassert @@ -365,9 +355,6 @@ - org.apache.maven.plugins maven-surefire-plugin @@ -386,7 +373,7 @@ default-test - test + ${mvn.unit-test.goal} test @@ -394,15 +381,32 @@ integration-test - + + + integration-test-elasticsearch + ${mvn.integration-test-elasticsearch.goal} + + test + + + integration-test + + elasticsearch + + + + - integration-test - integration-test + integration-test-opensearch + ${mvn.integration-test-opensearch.goal} test integration-test + + opensearch + diff --git a/src/test/java/org/springframework/data/elasticsearch/Foobar.java b/src/test/java/org/springframework/data/elasticsearch/Foobar.java new file mode 100644 index 000000000..3f9d28f02 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/Foobar.java @@ -0,0 +1,19 @@ +package org.springframework.data.elasticsearch; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.Tag; + +/** + * @author Peter-Josef Meisch + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Tag("foobar") +public @interface Foobar { +} diff --git a/src/test/java/org/springframework/data/elasticsearch/FoobarIntegrationTest.java b/src/test/java/org/springframework/data/elasticsearch/FoobarIntegrationTest.java new file mode 100644 index 000000000..409bf4349 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/FoobarIntegrationTest.java @@ -0,0 +1,28 @@ +package org.springframework.data.elasticsearch; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest; + +/** + * @author Peter-Josef Meisch + */ +@SpringIntegrationTest +@Foobar +public abstract class FoobarIntegrationTest { + + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); + + @Test + @DisplayName("should run test") + void shouldRunTest() { + + int answer = 42; + + assertThat(answer).isEqualTo(42); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/TransportFoobarIntegrationTest.java b/src/test/java/org/springframework/data/elasticsearch/TransportFoobarIntegrationTest.java new file mode 100644 index 000000000..bdc39c75c --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/TransportFoobarIntegrationTest.java @@ -0,0 +1,17 @@ +package org.springframework.data.elasticsearch; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration; +import org.springframework.data.elasticsearch.junit.jupiter.IntegrationtestEnvironment; +import org.springframework.test.context.ContextConfiguration; + +/** + * This class should only run when the cluster is an Elasticsearch cluster. + * + * @author Peter-Josef Meisch + */ +@EnabledIfSystemProperty(named = IntegrationtestEnvironment.SYSTEM_PROPERTY, matches = "(?i)elasticsearch") +@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class }) +@DisplayName("foobar integration with transport client") +public class TransportFoobarIntegrationTest extends FoobarIntegrationTest {} diff --git a/src/test/java/org/springframework/data/elasticsearch/config/AuditingIntegrationTest.java b/src/test/java/org/springframework/data/elasticsearch/config/AuditingIntegrationTest.java index e14647448..e0bc69f89 100644 --- a/src/test/java/org/springframework/data/elasticsearch/config/AuditingIntegrationTest.java +++ b/src/test/java/org/springframework/data/elasticsearch/config/AuditingIntegrationTest.java @@ -73,7 +73,7 @@ void shouldEnableAuditingAndSetAuditingDates() throws InterruptedException { assertThat(entity.getCreatedBy()).isEqualTo("Auditor 1"); assertThat(entity.getModifiedBy()).isEqualTo("Auditor 1"); - Thread.sleep(10); + Thread.sleep(50); entity = callbacks.callback(BeforeConvertCallback.class, entity, IndexCoordinates.of("index")); diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnection.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnection.java index e0d2bc430..5f16814a3 100644 --- a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnection.java +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnection.java @@ -24,9 +24,9 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.data.elasticsearch.support.VersionInfo; import org.springframework.lang.Nullable; import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; /** * This class manages the connection to an Elasticsearch Cluster, starting a containerized one if necessary. The @@ -40,9 +40,10 @@ public class ClusterConnection implements ExtensionContext.Store.CloseableResour private static final Logger LOGGER = LoggerFactory.getLogger(ClusterConnection.class); + private static final String SDE_TESTCONTAINER_IMAGE_NAME = "sde.testcontainers.image-name"; + private static final String SDE_TESTCONTAINER_IMAGE_VERSION = "sde.testcontainers.image-version"; private static final int ELASTICSEARCH_DEFAULT_PORT = 9200; private static final int ELASTICSEARCH_DEFAULT_TRANSPORT_PORT = 9300; - private static final String ELASTICSEARCH_DEFAULT_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch"; private static final ThreadLocal clusterConnectionInfoThreadLocal = new ThreadLocal<>(); @@ -78,20 +79,27 @@ public ClusterConnectionInfo getClusterConnectionInfo() { @Nullable private ClusterConnectionInfo startElasticsearchContainer() { - LOGGER.debug("Starting Elasticsearch Container"); + LOGGER.info("Starting Elasticsearch Container..."); try { - String elasticsearchVersion = VersionInfo.versionProperties() - .getProperty(VersionInfo.VERSION_ELASTICSEARCH_CLIENT); - Map elasticsearchProperties = elasticsearchProperties(); + IntegrationtestEnvironment integrationtestEnvironment = IntegrationtestEnvironment.get(); + LOGGER.info("Integration test environment: {}", integrationtestEnvironment); + if (integrationtestEnvironment == IntegrationtestEnvironment.UNDEFINED) { + throw new IllegalArgumentException(IntegrationtestEnvironment.SYSTEM_PROPERTY + " property not set"); + } + + String testcontainersConfiguration = integrationtestEnvironment.name().toLowerCase(); + Map testcontainersProperties = testcontainersProperties( + "testcontainers-" + testcontainersConfiguration + ".properties"); - String dockerImageName = ELASTICSEARCH_DEFAULT_IMAGE + ':' + elasticsearchVersion; - LOGGER.debug("Docker image: {}", dockerImageName); + DockerImageName dockerImageName = getDockerImageName(testcontainersProperties); - ElasticsearchContainer elasticsearchContainer = new ElasticsearchContainer(dockerImageName); - elasticsearchContainer.withEnv(elasticsearchProperties); + ElasticsearchContainer elasticsearchContainer = new ElasticsearchContainer(dockerImageName) + .withEnv(testcontainersProperties); elasticsearchContainer.start(); + return ClusterConnectionInfo.builder() // + .withIntegrationtestEnvironment(integrationtestEnvironment) .withHostAndPort(elasticsearchContainer.getHost(), elasticsearchContainer.getMappedPort(ELASTICSEARCH_DEFAULT_PORT)) // .withTransportPort(elasticsearchContainer.getMappedPort(ELASTICSEARCH_DEFAULT_TRANSPORT_PORT)) // @@ -104,9 +112,32 @@ private ClusterConnectionInfo startElasticsearchContainer() { return null; } - private Map elasticsearchProperties() { + private DockerImageName getDockerImageName(Map testcontainersProperties) { + + String imageName = testcontainersProperties.get(SDE_TESTCONTAINER_IMAGE_NAME); + String imageVersion = testcontainersProperties.get(SDE_TESTCONTAINER_IMAGE_VERSION); + + if (imageName == null) { + throw new IllegalArgumentException("property " + SDE_TESTCONTAINER_IMAGE_NAME + " not configured"); + } + testcontainersProperties.remove(SDE_TESTCONTAINER_IMAGE_NAME); + + if (imageVersion == null) { + throw new IllegalArgumentException("property " + SDE_TESTCONTAINER_IMAGE_VERSION + " not configured"); + } + testcontainersProperties.remove(SDE_TESTCONTAINER_IMAGE_VERSION); + + String configuredImageName = imageName + ':' + imageVersion; + DockerImageName dockerImageName = DockerImageName.parse(configuredImageName) + .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); + LOGGER.info("Docker image: {}", dockerImageName); + return dockerImageName; + } + + private Map testcontainersProperties(String propertiesFile) { + + LOGGER.info("load configuration from {}", propertiesFile); - String propertiesFile = "testcontainers-elasticsearch.properties"; try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(propertiesFile)) { Properties props = new Properties(); diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionInfo.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionInfo.java index ee4217809..307e5e033 100644 --- a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionInfo.java +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionInfo.java @@ -25,10 +25,11 @@ * with a rest client for both a local started cluster and for one defined by the cluster URL when creating the * {@link ClusterConnection}.
* The object must be created by using a {@link ClusterConnectionInfo.Builder}. - * + * * @author Peter-Josef Meisch */ public final class ClusterConnectionInfo { + private final IntegrationtestEnvironment integrationtestEnvironment; private final boolean useSsl; private final String host; private final int httpPort; @@ -40,8 +41,9 @@ public static Builder builder() { return new Builder(); } - private ClusterConnectionInfo(String host, int httpPort, boolean useSsl, int transportPort, - @Nullable ElasticsearchContainer elasticsearchContainer) { + private ClusterConnectionInfo(IntegrationtestEnvironment integrationtestEnvironment, String host, int httpPort, + boolean useSsl, int transportPort, @Nullable ElasticsearchContainer elasticsearchContainer) { + this.integrationtestEnvironment = integrationtestEnvironment; this.host = host; this.httpPort = httpPort; this.useSsl = useSsl; @@ -53,7 +55,8 @@ private ClusterConnectionInfo(String host, int httpPort, boolean useSsl, int tra @Override public String toString() { return "ClusterConnectionInfo{" + // - "useSsl=" + useSsl + // + "configuration: " + integrationtestEnvironment + // + ", useSsl=" + useSsl + // ", host='" + host + '\'' + // ", httpPort=" + httpPort + // ", transportPort=" + transportPort + // @@ -86,14 +89,22 @@ public ElasticsearchContainer getElasticsearchContainer() { } public static class Builder { - boolean useSsl = false; + private IntegrationtestEnvironment integrationtestEnvironment; + private boolean useSsl = false; private String host; private int httpPort; private int transportPort; @Nullable private ElasticsearchContainer elasticsearchContainer; + public Builder withIntegrationtestEnvironment(IntegrationtestEnvironment integrationtestEnvironment) { + this.integrationtestEnvironment = integrationtestEnvironment; + return this; + } + public Builder withHostAndPort(String host, int httpPort) { + Assert.hasLength(host, "host must not be empty"); + this.host = host; this.httpPort = httpPort; return this; @@ -115,7 +126,8 @@ public Builder withElasticsearchContainer(ElasticsearchContainer elasticsearchCo } public ClusterConnectionInfo build() { - return new ClusterConnectionInfo(host, httpPort, useSsl, transportPort, elasticsearchContainer); + return new ClusterConnectionInfo(integrationtestEnvironment, host, httpPort, useSsl, transportPort, + elasticsearchContainer); } } } diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationtestEnvironment.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationtestEnvironment.java new file mode 100644 index 000000000..5db8bc7fc --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationtestEnvironment.java @@ -0,0 +1,39 @@ +/* + * Copyright 2021 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.data.elasticsearch.junit.jupiter; + +/** + * @author Peter-Josef Meisch + */ +public enum IntegrationtestEnvironment { + + ELASTICSEARCH, OPENSEARCH, UNDEFINED; + + public static final String SYSTEM_PROPERTY = "sde.integration-test.environment"; + + public static IntegrationtestEnvironment get() { + + String property = System.getProperty(SYSTEM_PROPERTY, "elasticsearch"); + switch (property.toUpperCase()) { + case "ELASTICSEARCH": + return ELASTICSEARCH; + case "OPENSEARCH": + return OPENSEARCH; + default: + return UNDEFINED; + } + } +} diff --git a/src/test/resources/testcontainers-elasticsearch.properties b/src/test/resources/testcontainers-elasticsearch.properties index 5bef8c62b..2ca3d43e3 100644 --- a/src/test/resources/testcontainers-elasticsearch.properties +++ b/src/test/resources/testcontainers-elasticsearch.properties @@ -1,2 +1,10 @@ -# needed as we do a DELETE /* at the end of the tests, will be requited from 8.0 on, produces a warning since 7.13 +# +# properties defining the image, these are not passed to the container on startup +# +sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch +sde.testcontainers.image-version=7.13.3 +# +# +# needed as we do a DELETE /* at the end of the tests, will be required from 8.0 on, produces a warning since 7.13 +# action.destructive_requires_name=false diff --git a/src/test/resources/testcontainers-opensearch.properties b/src/test/resources/testcontainers-opensearch.properties new file mode 100644 index 000000000..147af3ce5 --- /dev/null +++ b/src/test/resources/testcontainers-opensearch.properties @@ -0,0 +1,10 @@ +# +# properties defining the image, these are not passed to the container on startup +# +sde.testcontainers.image-name=opensearchproject/opensearch +sde.testcontainers.image-version=1.0.0 +# +# +# Opensearch as default has TLS and Basic auth enabled, we do not want this here, Testcontainers cannot ignore self signed certificates +# +plugins.security.disabled=true