diff --git a/src/test/java/org/springframework/data/elasticsearch/ElasticsearchTestConfiguration.java b/src/test/java/org/springframework/data/elasticsearch/ElasticsearchTestConfiguration.java index 8a307f204..172c305e3 100644 --- a/src/test/java/org/springframework/data/elasticsearch/ElasticsearchTestConfiguration.java +++ b/src/test/java/org/springframework/data/elasticsearch/ElasticsearchTestConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; +import org.springframework.data.elasticsearch.junit.junit4.TestNodeResource; /** * configuration class for the classic ElasticsearchTemplate. Needs a {@link TestNodeResource} bean that should be set up in diff --git a/src/test/java/org/springframework/data/elasticsearch/JUnit5ClusterConnectionTests.java b/src/test/java/org/springframework/data/elasticsearch/JUnit5ClusterConnectionTests.java new file mode 100644 index 000000000..a2a04cae4 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/JUnit5ClusterConnectionTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.data.elasticsearch.junit.jupiter.ClusterConnectionInfo; +import org.springframework.data.elasticsearch.junit.jupiter.IntegrationTest; +import org.springframework.data.elasticsearch.junit.jupiter.SpringDataElasticsearchExtension; + +/** + * Testing the setup and parameter injection of the CusterConnectionInfo. + * + * @author Peter-Josef Meisch + */ +@IntegrationTest +@DisplayName("a sample JUnit 5 test with a bare cluster connection") +public class JUnit5ClusterConnectionTests { + + @Test + @DisplayName("should have the connection info injected") + void shouldHaveTheConnectionInfoInjected(ClusterConnectionInfo clusterConnectionInfo) { + assertThat(clusterConnectionInfo).isNotNull(); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/JUnit5SampleRestClientBasedTests.java b/src/test/java/org/springframework/data/elasticsearch/JUnit5SampleRestClientBasedTests.java new file mode 100644 index 000000000..d708faa04 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/JUnit5SampleRestClientBasedTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019 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; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration; +import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest; +import org.springframework.test.context.ContextConfiguration; + +/** + * class demonstrating the setup of a JUnit 5 test in Spring Data Elasticsearch that uses the rest client. The + * ContextConfiguration must include the {@link ElasticsearchRestTemplateConfiguration} class. + * + * @author Peter-Josef Meisch + */ +@SpringIntegrationTest +@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class}) +@DisplayName("a sample JUnit 5 test with rest client") +public class JUnit5SampleRestClientBasedTests { + + @Autowired private ElasticsearchOperations elasticsearchOperations; + + @Test + @DisplayName("should have a ElasticsearchRestTemplate") + void shouldHaveARestTemplate() { + assertThat(elasticsearchOperations).isNotNull().isInstanceOf(ElasticsearchRestTemplate.class); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/JUnit5SampleTransportClientBasedTests.java b/src/test/java/org/springframework/data/elasticsearch/JUnit5SampleTransportClientBasedTests.java new file mode 100644 index 000000000..fdd6e9234 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/JUnit5SampleTransportClientBasedTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019 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; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration; +import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest; +import org.springframework.test.context.ContextConfiguration; + +/** + * class demonstrating the setup of a JUnit 5 test in Spring Data Elasticsearch that uses the transport client. The + * ContextConfiguration must include the {@link ElasticsearchTemplateConfiguration} class + * + * @author Peter-Josef Meisch + */ +@SpringIntegrationTest +@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class }) +@DisplayName("a sample JUnit 5 test with transport client") +public class JUnit5SampleTransportClientBasedTests { + + @Autowired private ElasticsearchOperations elasticsearchOperations; + + @Test + @DisplayName("should have a ElasticsearchTemplate") + void shouldHaveATemplate() { + assertThat(elasticsearchOperations).isNotNull().isInstanceOf(ElasticsearchTemplate.class); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java index d55d95ef3..74c8c5a26 100644 --- a/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClientTests.java @@ -53,8 +53,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.data.elasticsearch.ElasticsearchVersion; -import org.springframework.data.elasticsearch.ElasticsearchVersionRule; +import org.springframework.data.elasticsearch.junit.junit4.ElasticsearchVersion; +import org.springframework.data.elasticsearch.junit.junit4.ElasticsearchVersionRule; import org.springframework.data.elasticsearch.TestUtils; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.http.HttpHeaders; diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplateTests.java index 82f197a82..a6784f2da 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchTemplateTests.java @@ -53,8 +53,8 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.elasticsearch.ElasticsearchVersion; -import org.springframework.data.elasticsearch.ElasticsearchVersionRule; +import org.springframework.data.elasticsearch.junit.junit4.ElasticsearchVersion; +import org.springframework.data.elasticsearch.junit.junit4.ElasticsearchVersionRule; import org.springframework.data.elasticsearch.TestUtils; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; diff --git a/src/test/java/org/springframework/data/elasticsearch/ElasticsearchVersion.java b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/ElasticsearchVersion.java similarity index 95% rename from src/test/java/org/springframework/data/elasticsearch/ElasticsearchVersion.java rename to src/test/java/org/springframework/data/elasticsearch/junit/junit4/ElasticsearchVersion.java index 0b4d29f0e..85ebfd6da 100644 --- a/src/test/java/org/springframework/data/elasticsearch/ElasticsearchVersion.java +++ b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/ElasticsearchVersion.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch; +package org.springframework.data.elasticsearch.junit.junit4; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/src/test/java/org/springframework/data/elasticsearch/ElasticsearchVersionRule.java b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/ElasticsearchVersionRule.java similarity index 97% rename from src/test/java/org/springframework/data/elasticsearch/ElasticsearchVersionRule.java rename to src/test/java/org/springframework/data/elasticsearch/junit/junit4/ElasticsearchVersionRule.java index 5932abc03..aa68dc142 100644 --- a/src/test/java/org/springframework/data/elasticsearch/ElasticsearchVersionRule.java +++ b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/ElasticsearchVersionRule.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch; +package org.springframework.data.elasticsearch.junit.junit4; import java.util.concurrent.atomic.AtomicReference; @@ -23,6 +23,7 @@ import org.junit.runners.model.Statement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.data.elasticsearch.TestUtils; import org.springframework.data.util.Version; /** diff --git a/src/test/java/org/springframework/data/elasticsearch/TestNodeResource.java b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/TestNodeResource.java similarity index 90% rename from src/test/java/org/springframework/data/elasticsearch/TestNodeResource.java rename to src/test/java/org/springframework/data/elasticsearch/junit/junit4/TestNodeResource.java index 4f9698f00..b6c1c07aa 100644 --- a/src/test/java/org/springframework/data/elasticsearch/TestNodeResource.java +++ b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/TestNodeResource.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.elasticsearch; +package org.springframework.data.elasticsearch.junit.junit4; import java.io.IOException; import org.elasticsearch.client.Client; import org.elasticsearch.node.Node; import org.junit.rules.ExternalResource; +import org.springframework.data.elasticsearch.Utils; import org.springframework.util.Assert; /** @@ -29,7 +30,7 @@ */ public class TestNodeResource extends ExternalResource { - private static Node node; + private Node node; @Override protected void before() throws Throwable { diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/junit4/package-info.java b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/package-info.java new file mode 100644 index 000000000..aacd0ea7f --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/junit4/package-info.java @@ -0,0 +1,5 @@ +/** + * interfaces, annotations and classes related to JUnit 4 test handling. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.elasticsearch.junit.junit4; 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 new file mode 100644 index 000000000..541a0191e --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnection.java @@ -0,0 +1,125 @@ +/* + * Copyright 2019 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; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import org.elasticsearch.node.Node; +import org.elasticsearch.node.NodeValidationException; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.elasticsearch.Utils; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +/** + * This class manages the connection to an Elasticsearch Cluster, starting a local one if necessary. The information + * about the ClusterConnection is stored botha s a varaible in the instance for direct aaces from JUnit 5 and in a + * static ThreadLocal acessible with the {@link ClusterConnection#clusterConnectionInfo()} method + * to be integrated in the Spring setup + * + * @author Peter-Josef Meisch + */ +class ClusterConnection implements ExtensionContext.Store.CloseableResource { + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterConnection.class); + + private static final ThreadLocal clusterConnectionInfoThreadLocal = new ThreadLocal<>(); + + private Node node; + private final ClusterConnectionInfo clusterConnectionInfo; + + /** + * creates the ClusterConnection, starting a local node if necessary. + * + * @param clusterUrl if null or empty a local cluster is tarted + */ + public ClusterConnection(@Nullable String clusterUrl) { + clusterConnectionInfo = StringUtils.isEmpty(clusterUrl) ? startLocalNode() : parseUrl(clusterUrl); + + if (clusterConnectionInfo != null) { + LOGGER.debug(clusterConnectionInfo.toString()); + clusterConnectionInfoThreadLocal.set(clusterConnectionInfo); + } else { + LOGGER.error("could not create ClusterConnectionInfo"); + } + } + + /** + * @return the {@link ClusterConnectionInfo} from the ThreadLocal storage. + */ + @Nullable + public static ClusterConnectionInfo clusterConnectionInfo() { + return clusterConnectionInfoThreadLocal.get(); + } + + public ClusterConnectionInfo getClusterConnectionInfo() { + return clusterConnectionInfo; + } + + /** + * @param clusterUrl the URL to parse + * @return the connection information + */ + private ClusterConnectionInfo parseUrl(String clusterUrl) { + try { + URL url = new URL(clusterUrl); + + if (!url.getProtocol().startsWith("http") || url.getPort() <= 0) { + throw new ClusterConnectionException("invalid url " + clusterUrl); + } + + return ClusterConnectionInfo.builder() // + .withHostAndPort(url.getHost(), url.getPort()) // + .useSsl(url.getProtocol().equals("https")) // + .build(); + } catch (MalformedURLException e) { + throw new ClusterConnectionException(e); + } + + } + + private @Nullable ClusterConnectionInfo startLocalNode() { + LOGGER.debug("starting local node"); + + try { + node = Utils.getNode(); + node.start(); + return ClusterConnectionInfo.builder() // + .withHostAndPort("localhost", 9200) // + .withClient(node.client()) // + .build(); + } catch (NodeValidationException e) { + LOGGER.error("could not start local node", e); + } + + return null; + } + + @Override + public void close() throws Exception { + + if (node != null) { + LOGGER.debug("closing node"); + try { + node.close(); + } catch (IOException ignored) {} + } + LOGGER.debug("closed"); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionException.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionException.java new file mode 100644 index 000000000..83661b25d --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 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 class ClusterConnectionException extends RuntimeException { + public ClusterConnectionException(String message) { + super(message); + } + + public ClusterConnectionException(Throwable cause) { + super(cause); + } +} 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 new file mode 100644 index 000000000..26d67d06d --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ClusterConnectionInfo.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019 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; + +import org.elasticsearch.client.Client; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +/** + * The information about the ClusterConnection. the {@link #client} field is only set if a local node is started, + * otherwise it is null.
+ * The {@link #host}, {@link #httpPort} and {@link #useSsl} values specify the values needed to connect to the cluster + * 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 boolean useSsl; + private final String host; + private final int httpPort; + private final Client client; + + public static Builder builder() { + return new Builder(); + } + + private ClusterConnectionInfo(String host, int httpPort, boolean useSsl, Client client) { + this.host = host; + this.httpPort = httpPort; + this.useSsl = useSsl; + this.client = client; + } + + @Override + public String toString() { + return "ClusterConnectionInfo{" + "useSsl=" + useSsl + ", host='" + host + '\'' + ", httpPort=" + httpPort + + ", client=" + client + '}'; + } + + public String getHost() { + return host; + } + + public int getHttpPort() { + return httpPort; + } + + public boolean isUseSsl() { + return useSsl; + } + + @Nullable + public Client getClient() { + return client; + } + + public static class Builder { + boolean useSsl = false; + private String host; + private int httpPort; + private Client client = null; + + public Builder withHostAndPort(String host, int httpPort) { + Assert.hasLength(host, "host must not be empty"); + this.host = host; + this.httpPort = httpPort; + return this; + } + + public Builder useSsl(boolean useSsl) { + this.useSsl = useSsl; + return this; + } + + public Builder withClient(Client client) { + this.client = client; + return this; + } + + public ClusterConnectionInfo build() { + return new ClusterConnectionInfo(host, httpPort, useSsl, client); + } + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ElasticsearchRestTemplateConfiguration.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ElasticsearchRestTemplateConfiguration.java new file mode 100644 index 000000000..8e55e790a --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ElasticsearchRestTemplateConfiguration.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019 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; + +import java.time.Duration; + +import org.elasticsearch.client.RestHighLevelClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.client.ClientConfiguration; +import org.springframework.data.elasticsearch.client.RestClients; +import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; +import org.springframework.util.Assert; + +/** + * Configuration for Spring Data Elasticsearch using + * {@link org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate}. The required + * {@link ClusterConnectionInfo} bean must be provided by the testclass. + * + * @author Peter-Josef Meisch + */ +@Configuration +public class ElasticsearchRestTemplateConfiguration extends AbstractElasticsearchConfiguration { + + @Autowired + private ClusterConnectionInfo clusterConnectionInfo; + + @Override + @Bean + public RestHighLevelClient elasticsearchClient() { + + String elasticsearchHostPort = clusterConnectionInfo.getHost() + ':' + clusterConnectionInfo.getHttpPort(); + + ClientConfiguration.TerminalClientConfigurationBuilder configurationBuilder = ClientConfiguration.builder() + .connectedTo(elasticsearchHostPort); + + if (clusterConnectionInfo.isUseSsl()) { + configurationBuilder = ((ClientConfiguration.MaybeSecureClientConfigurationBuilder) configurationBuilder) + .usingSsl(); + } + + return RestClients.create(configurationBuilder // + .withConnectTimeout(Duration.ofSeconds(5)) // + .withSocketTimeout(Duration.ofSeconds(3)) // + .build()) // + .rest(); + } +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ElasticsearchTemplateConfiguration.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ElasticsearchTemplateConfiguration.java new file mode 100644 index 000000000..6e24cbc37 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/ElasticsearchTemplateConfiguration.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019 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; + +import org.elasticsearch.client.Client; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; +import org.springframework.util.Assert; + +/** + * Configuration for Spring Data Elasticsearch using + * {@link org.springframework.data.elasticsearch.core.ElasticsearchTemplate}. + * + * @author Peter-Josef Meisch + */ +@Configuration +public class ElasticsearchTemplateConfiguration extends ElasticsearchConfigurationSupport { + + @Bean + public Client elasticsearchClient(ClusterConnectionInfo clusterConnectionInfo) { + return clusterConnectionInfo.getClient(); + } + + @Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" }) + public ElasticsearchTemplate elasticsearchTemplate(Client elasticsearchClient, + MappingElasticsearchConverter entityMapper) { + return new ElasticsearchTemplate(elasticsearchClient, entityMapper); + } + +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationTest.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationTest.java new file mode 100644 index 000000000..7725648e3 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/IntegrationTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2019 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; + +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.extension.ExtendWith; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Wraps the {@link SpringDataElasticsearchExtension}. + * + * @author Peter-Josef Meisch + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@ExtendWith(SpringDataElasticsearchExtension.class) +public @interface IntegrationTest { +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/SpringDataElasticsearchExtension.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/SpringDataElasticsearchExtension.java new file mode 100644 index 000000000..456556ae6 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/SpringDataElasticsearchExtension.java @@ -0,0 +1,112 @@ +/* + * Copyright 2019 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; + +import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.test.context.MergedContextConfiguration; + +/** + * This extension class check in the {@link #beforeAll(ExtensionContext)} call if there is already a Elasticsearch + * cluster connection defined in the root store. If no, the connection to the cluster is defined according to the + * configuration, starting a local node if necessary. The connection is stored and will be closed when the store is + * shutdown at the end of all tests. + *

+ * A ParameterResolver is implemented which enables resolving the ClusterConnectionInfo to test methods, and if a Spring + * context is used, the ClusterConnectionInfo can be autowired. + * + * @author Peter-Josef Meisch + */ +public class SpringDataElasticsearchExtension + implements BeforeAllCallback, ParameterResolver, ContextCustomizerFactory { + + public static final String SPRING_DATA_ELASTICSEARCH_TEST_CLUSTER_URL = "SPRING_DATA_ELASTICSEARCH_TEST_CLUSTER_URL"; + private static final Logger LOGGER = LoggerFactory.getLogger(SpringDataElasticsearchExtension.class); + + private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace + .create(SpringDataElasticsearchExtension.class.getName()); + private static final String STORE_KEY_CLUSTER_CONNECTION = ClusterConnection.class.getSimpleName(); + private static final String STORE_KEY_CLUSTER_CONNECTION_INFO = ClusterConnectionInfo.class.getSimpleName(); + + private static final Lock initLock = new ReentrantLock(); + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + initLock.lock(); + try { + ExtensionContext.Store store = getStore(extensionContext); + ClusterConnection clusterConnection = store.getOrComputeIfAbsent(STORE_KEY_CLUSTER_CONNECTION, key -> { + LOGGER.debug("creating ClusterConnection"); + return createClusterConnection(); + }, ClusterConnection.class); + store.getOrComputeIfAbsent(STORE_KEY_CLUSTER_CONNECTION_INFO, + key -> clusterConnection.getClusterConnectionInfo()); + } finally { + initLock.unlock(); + } + } + + private ExtensionContext.Store getStore(ExtensionContext extensionContext) { + return extensionContext.getRoot().getStore(NAMESPACE); + } + + private ClusterConnection createClusterConnection() { + return new ClusterConnection(System.getenv(SPRING_DATA_ELASTICSEARCH_TEST_CLUSTER_URL)); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + Class parameterType = parameterContext.getParameter().getType(); + return parameterType.isAssignableFrom(ClusterConnectionInfo.class); + } + + /* + * (non javadoc) + * no need to check the paramaterContext and extensionContext here, this was done before in supportsParameter. + */ + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return getStore(extensionContext).get(STORE_KEY_CLUSTER_CONNECTION_INFO, ClusterConnectionInfo.class); + } + + @Override + public ContextCustomizer createContextCustomizer(Class testClass, + List configAttributes) { + return this::customizeContext; + } + + private void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { + + context.getBeanFactory().registerResolvableDependency(ClusterConnectionInfo.class, + ClusterConnection.clusterConnectionInfo()); + } + +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/SpringIntegrationTest.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/SpringIntegrationTest.java new file mode 100644 index 000000000..387507aeb --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/SpringIntegrationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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; + +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.extension.ExtendWith; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Combines the {@link SpringDataElasticsearchExtension} and the {@link SpringExtension}. + * + * @author Peter-Josef Meisch + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@ExtendWith(SpringDataElasticsearchExtension.class) +@ExtendWith(SpringExtension.class) +public @interface SpringIntegrationTest { +} diff --git a/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/package-info.java b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/package-info.java new file mode 100644 index 000000000..e6e754267 --- /dev/null +++ b/src/test/java/org/springframework/data/elasticsearch/junit/jupiter/package-info.java @@ -0,0 +1,5 @@ +/** + * interfaces, annotations and classes related to JUnit 5 test handling. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.elasticsearch.junit.jupiter; diff --git a/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRepositoryTests.java b/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRepositoryTests.java index b881d5ce0..7c8bbe3e2 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRepositoryTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRepositoryTests.java @@ -19,7 +19,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.ElasticsearchTestConfiguration; -import org.springframework.data.elasticsearch.TestNodeResource; +import org.springframework.data.elasticsearch.junit.junit4.TestNodeResource; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import org.springframework.test.context.ContextConfiguration; diff --git a/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRestRepositoryTests.java b/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRestRepositoryTests.java index 4eb508207..382840378 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRestRepositoryTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/repository/query/keywords/QueryKeywordsRestRepositoryTests.java @@ -16,13 +16,11 @@ package org.springframework.data.elasticsearch.repository.query.keywords; import org.junit.ClassRule; -import org.junit.runner.RunWith; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.RestElasticsearchTestConfiguration; -import org.springframework.data.elasticsearch.TestNodeResource; +import org.springframework.data.elasticsearch.junit.junit4.TestNodeResource; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; /** * {@link QueryKeywordsTests} using a Repository backed by an ElasticsearchRestTemplate. diff --git a/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryTests.java b/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryTests.java index 94d24f7f9..6e447725b 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryTests.java @@ -52,7 +52,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; -import org.springframework.data.elasticsearch.TestNodeResource; +import org.springframework.data.elasticsearch.junit.junit4.TestNodeResource; import org.springframework.data.elasticsearch.TestUtils; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; diff --git a/src/test/java/org/springframework/data/elasticsearch/repository/support/simple/SimpleElasticsearchRepositoryTests.java b/src/test/java/org/springframework/data/elasticsearch/repository/support/simple/SimpleElasticsearchRepositoryTests.java index c1845a291..2f1572888 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repository/support/simple/SimpleElasticsearchRepositoryTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/repository/support/simple/SimpleElasticsearchRepositoryTests.java @@ -45,7 +45,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.data.elasticsearch.RestElasticsearchTestConfiguration; -import org.springframework.data.elasticsearch.TestNodeResource; +import org.springframework.data.elasticsearch.junit.junit4.TestNodeResource; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; diff --git a/src/test/resources/META-INF/spring.factories b/src/test/resources/META-INF/spring.factories new file mode 100644 index 000000000..36d262d79 --- /dev/null +++ b/src/test/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.test.context.ContextCustomizerFactory=org.springframework.data.elasticsearch.junit.jupiter.SpringDataElasticsearchExtension diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index ace5ad382..f28758447 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -1,17 +1,18 @@ - - - %d %5p %40.40c:%4L - %m%n - - + + + %d %5p %40.40c:%4L - %m%n + + - + + - - - + + +