From 4ef5c4d6bc0b710018557c7364dfc5655dd0f6b3 Mon Sep 17 00:00:00 2001
From: puppylpg <shininglhb@163.com>
Date: Tue, 23 Jan 2024 15:58:56 +0800
Subject: [PATCH] Add elasticsearch repository test

---
 .../conference/ApplicationConfiguration.java  | 18 +++---
 .../elasticsearch/conference/Conference.java  |  6 +-
 .../conference/ConferenceRepository.java      | 10 ++-
 .../conference/AbstractContainerBaseTest.java | 62 ++++++++++++++++++
 .../ElasticsearchOperationsTest.java          | 47 +++-----------
 .../ElasticsearchRepositoryTest.java          | 52 +++++++++++++++
 .../conference/ApplicationConfiguration.java  | 17 +++--
 .../elasticsearch/conference/Conference.java  |  6 +-
 .../conference/ConferenceRepository.java      |  7 ++-
 .../conference/AbstractContainerBaseTest.java | 63 +++++++++++++++++++
 .../ReactiveElasticsearchOperationsTest.java  | 56 +++--------------
 .../ReactiveElasticsearchRepositoryTest.java  | 56 +++--------------
 12 files changed, 243 insertions(+), 157 deletions(-)
 create mode 100644 elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java
 create mode 100644 elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchRepositoryTest.java
 create mode 100644 elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java

diff --git a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java
index c725211a8..32ec0a536 100644
--- a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java
+++ b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-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.
@@ -15,8 +15,9 @@
  */
 package example.springdata.elasticsearch.conference;
 
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
-import java.util.List;
 
 import jakarta.annotation.PostConstruct;
 import jakarta.annotation.PreDestroy;
@@ -31,10 +32,13 @@
  * @author Oliver Gierke
  * @author Christoph Strobl
  * @author Prakhar Gupta
+ * @author Haibo Liu
  */
 @SpringBootApplication
 class ApplicationConfiguration {
 
+	private final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+
 	@Autowired ElasticsearchOperations operations;
 	@Autowired ConferenceRepository repository;
 
@@ -51,16 +55,16 @@ public void insertDataSample() {
 		// Save data sample
 
 		var documents = Arrays.asList(
-				Conference.builder().date("2014-11-06").name("Spring eXchange 2014 - London")
+				Conference.builder().date(LocalDate.parse("2014-11-06", FORMAT)).name("Spring eXchange 2014 - London")
 						.keywords(Arrays.asList("java", "spring")).location(new GeoPoint(51.500152D, -0.126236D)).build(), //
-				Conference.builder().date("2014-12-07").name("Scala eXchange 2014 - London")
+				Conference.builder().date(LocalDate.parse("2014-12-07", FORMAT)).name("Scala eXchange 2014 - London")
 						.keywords(Arrays.asList("scala", "play", "java")).location(new GeoPoint(51.500152D, -0.126236D)).build(), //
-				Conference.builder().date("2014-11-20").name("Elasticsearch 2014 - Berlin")
+				Conference.builder().date(LocalDate.parse("2014-11-20", FORMAT)).name("Elasticsearch 2014 - Berlin")
 						.keywords(Arrays.asList("java", "elasticsearch", "kibana")).location(new GeoPoint(52.5234051D, 13.4113999))
 						.build(), //
-				Conference.builder().date("2014-11-12").name("AWS London 2014").keywords(Arrays.asList("cloud", "aws"))
+				Conference.builder().date(LocalDate.parse("2014-11-12", FORMAT)).name("AWS London 2014").keywords(Arrays.asList("cloud", "aws"))
 						.location(new GeoPoint(51.500152D, -0.126236D)).build(), //
-				Conference.builder().date("2014-10-04").name("JDD14 - Cracow").keywords(Arrays.asList("java", "spring"))
+				Conference.builder().date(LocalDate.parse("2014-10-04", FORMAT)).name("JDD14 - Cracow").keywords(Arrays.asList("java", "spring"))
 						.location(new GeoPoint(50.0646501D, 19.9449799)).build());
 
 		repository.saveAll(documents);
diff --git a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/Conference.java b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/Conference.java
index ac2161c29..4c89b7f84 100644
--- a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/Conference.java
+++ b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/Conference.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-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.
@@ -20,6 +20,7 @@
 import lombok.Builder;
 import lombok.Data;
 
+import java.time.LocalDate;
 import java.util.List;
 
 import org.springframework.data.annotation.Id;
@@ -31,6 +32,7 @@
  * @author Artur Konczak
  * @author Oliver Gierke
  * @author Christoph Strobl
+ * @author Haibo Liu
  */
 @Data
 @Builder
@@ -39,7 +41,7 @@ public class Conference {
 
 	private @Id String id;
 	private String name;
-	private @Field(type = Date) String date;
+	private @Field(type = Date) LocalDate date;
 	private GeoPoint location;
 	private List<String> keywords;
 
diff --git a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java
index 78024ab02..ab5d4a6fd 100644
--- a/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java
+++ b/elasticsearch/example/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2021 the original author or authors.
+ * Copyright 2014-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.
@@ -15,10 +15,16 @@
  */
 package example.springdata.elasticsearch.conference;
 
+import java.time.LocalDate;
+
 import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
 
 /**
  * @author Artur Konczak
  * @author Oliver Gierke
+ * @author Haibo Liu
  */
-interface ConferenceRepository extends ElasticsearchRepository<Conference, String> {}
+interface ConferenceRepository extends ElasticsearchRepository<Conference, String> {
+
+    Iterable<Conference> findAllByKeywordsContainsAndDateAfter(String keyword, LocalDate Date);
+}
diff --git a/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java
new file mode 100644
index 000000000..f5b65c656
--- /dev/null
+++ b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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
+ *
+ *      http://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 example.springdata.elasticsearch.conference;
+
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.elasticsearch.client.ClientConfiguration;
+import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+import org.testcontainers.elasticsearch.ElasticsearchContainer;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * singleton container
+ *
+ * @author Haibo Liu
+ */
+@SpringBootTest(classes = {ApplicationConfiguration.class, AbstractContainerBaseTest.TestConfiguration.class})
+public class AbstractContainerBaseTest {
+
+	protected static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+
+	private static final ElasticsearchContainer CONTAINER = new ElasticsearchContainer(
+			DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:8.7.0")) //
+			.withPassword("foobar");
+
+	static {
+		CONTAINER.start();
+	}
+
+	@Configuration
+	static class TestConfiguration extends ElasticsearchConfiguration {
+		@Override
+		@NonNull
+		public ClientConfiguration clientConfiguration() {
+
+			Assert.notNull(CONTAINER, "TestContainer is not initialized!");
+
+			return ClientConfiguration.builder() //
+					.connectedTo(CONTAINER.getHttpHostAddress()) //
+					.usingSsl(CONTAINER.createSslContextFromCa()) //
+					.withBasicAuth("elastic", "foobar") //
+					.build();
+		}
+	}
+}
diff --git a/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchOperationsTest.java b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchOperationsTest.java
index 4a52ba8a8..719866f4d 100644
--- a/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchOperationsTest.java
+++ b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchOperationsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-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.
@@ -17,25 +17,15 @@
 
 import static org.assertj.core.api.Assertions.*;
 
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.LocalDate;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.elasticsearch.client.ClientConfiguration;
-import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
 import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
 import org.springframework.data.elasticsearch.core.geo.GeoPoint;
 import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
 import org.springframework.data.elasticsearch.core.query.Criteria;
 import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
-import org.springframework.util.Assert;
-import org.testcontainers.elasticsearch.ElasticsearchContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.DockerImageName;
 
 /**
  * Test case to show Spring Data Elasticsearch functionality.
@@ -45,39 +35,16 @@
  * @author Christoph Strobl
  * @author Prakhar Gupta
  * @author Peter-Josef Meisch
+ * @author Haibo Liu
  */
-@SpringBootTest(classes = { ApplicationConfiguration.class, ElasticsearchOperationsTest.TestConfiguration.class })
-@Testcontainers
-class ElasticsearchOperationsTest {
-
-	private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
-	@Container //
-	private static final ElasticsearchContainer container = new ElasticsearchContainer(
-			DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:8.7.0")) //
-			.withPassword("foobar") //
-			.withReuse(true);
-
-	@Configuration
-	static class TestConfiguration extends ElasticsearchConfiguration {
-		@Override
-		public ClientConfiguration clientConfiguration() {
-
-			Assert.notNull(container, "TestContainer is not initialized!");
-
-			return ClientConfiguration.builder() //
-					.connectedTo(container.getHttpHostAddress()) //
-					.usingSsl(container.createSslContextFromCa()) //
-					.withBasicAuth("elastic", "foobar") //
-					.build();
-		}
-	}
+class ElasticsearchOperationsTest extends AbstractContainerBaseTest {
 
 	@Autowired ElasticsearchOperations operations;
 
 	@Test
-	void textSearch() throws ParseException {
+	void textSearch() {
 
-		var expectedDate = "2014-10-29";
+		var expectedDate = LocalDate.parse( "2014-10-29", FORMAT);
 		var expectedWord = "java";
 		var query = new CriteriaQuery(
 				new Criteria("keywords").contains(expectedWord).and(new Criteria("date").greaterThanEqual(expectedDate)));
@@ -88,7 +55,7 @@ void textSearch() throws ParseException {
 
 		for (var conference : result) {
 			assertThat(conference.getContent().getKeywords()).contains(expectedWord);
-			assertThat(format.parse(conference.getContent().getDate())).isAfter(format.parse(expectedDate));
+			assertThat(conference.getContent().getDate()).isAfter(expectedDate);
 		}
 	}
 
diff --git a/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchRepositoryTest.java b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchRepositoryTest.java
new file mode 100644
index 000000000..a202d6a67
--- /dev/null
+++ b/elasticsearch/example/src/test/java/example/springdata/elasticsearch/conference/ElasticsearchRepositoryTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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
+ *
+ *      http://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 example.springdata.elasticsearch.conference;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.time.LocalDate;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Test case to show Spring Data Elasticsearch Repository functionality.
+ *
+ * @author Haibo Liu
+ */
+class ElasticsearchRepositoryTest extends AbstractContainerBaseTest {
+
+	@Autowired ConferenceRepository repository;
+
+	@Test
+	void textSearch() {
+
+		var expectedDate = LocalDate.parse("2014-10-29", FORMAT);
+		var expectedWord = "java";
+
+		var result = repository.findAllByKeywordsContainsAndDateAfter(expectedWord, expectedDate);
+
+		assertThat(result).hasSize(3);
+
+		result.forEach(it -> verify(it, expectedWord, expectedDate));
+	}
+
+	private void verify(Conference it, String expectedWord, LocalDate expectedDate) {
+
+		assertThat(it.getKeywords()).contains(expectedWord);
+		assertThat(it.getDate()).isAfter(expectedDate);
+	}
+}
diff --git a/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java b/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java
index 0d852edbb..a8246f1f6 100644
--- a/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java
+++ b/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ApplicationConfiguration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-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.
@@ -15,6 +15,8 @@
  */
 package example.springdata.elasticsearch.conference;
 
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 
 import jakarta.annotation.PostConstruct;
@@ -27,10 +29,13 @@
 
 /**
  * @author Christoph Strobl
+ * @author Haibo Liu
  */
 @SpringBootApplication
 class ApplicationConfiguration {
 
+	private final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+
 	@Autowired ElasticsearchOperations operations;
 	@Autowired ConferenceRepository repository;
 
@@ -47,16 +52,16 @@ public void insertDataSample() {
 		// Save data sample
 
 		var documents = Arrays.asList(
-				Conference.builder().date("2014-11-06").name("Spring eXchange 2014 - London")
+				Conference.builder().date(LocalDate.parse("2014-11-06", FORMAT)).name("Spring eXchange 2014 - London")
 						.keywords(Arrays.asList("java", "spring")).location(new GeoPoint(51.500152D, -0.126236D)).build(), //
-				Conference.builder().date("2014-12-07").name("Scala eXchange 2014 - London")
+				Conference.builder().date(LocalDate.parse("2014-12-07", FORMAT)).name("Scala eXchange 2014 - London")
 						.keywords(Arrays.asList("scala", "play", "java")).location(new GeoPoint(51.500152D, -0.126236D)).build(), //
-				Conference.builder().date("2014-11-20").name("Elasticsearch 2014 - Berlin")
+				Conference.builder().date(LocalDate.parse("2014-11-20", FORMAT)).name("Elasticsearch 2014 - Berlin")
 						.keywords(Arrays.asList("java", "elasticsearch", "kibana")).location(new GeoPoint(52.5234051D, 13.4113999))
 						.build(), //
-				Conference.builder().date("2014-11-12").name("AWS London 2014").keywords(Arrays.asList("cloud", "aws"))
+				Conference.builder().date(LocalDate.parse("2014-11-12", FORMAT)).name("AWS London 2014").keywords(Arrays.asList("cloud", "aws"))
 						.location(new GeoPoint(51.500152D, -0.126236D)).build(), //
-				Conference.builder().date("2014-10-04").name("JDD14 - Cracow").keywords(Arrays.asList("java", "spring"))
+				Conference.builder().date(LocalDate.parse("2014-10-04", FORMAT)).name("JDD14 - Cracow").keywords(Arrays.asList("java", "spring"))
 						.location(new GeoPoint(50.0646501D, 19.9449799)).build());
 
 		operations.save(documents);
diff --git a/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/Conference.java b/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/Conference.java
index de395f560..9b9eccc75 100644
--- a/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/Conference.java
+++ b/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/Conference.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-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.
@@ -20,6 +20,7 @@
 import lombok.Builder;
 import lombok.Data;
 
+import java.time.LocalDate;
 import java.util.List;
 
 import org.springframework.data.annotation.Id;
@@ -29,6 +30,7 @@
 
 /**
  * @author Christoph Strobl
+ * @author Haibo Liu
  */
 @Data
 @Builder
@@ -37,7 +39,7 @@ public class Conference {
 
 	private @Id String id;
 	private String name;
-	private @Field(type = Date) String date;
+	private @Field(type = Date) LocalDate date;
 	private GeoPoint location;
 	private List<String> keywords;
 }
diff --git a/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java b/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java
index f5c5e468e..74746d6e7 100644
--- a/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java
+++ b/elasticsearch/reactive/src/main/java/example/springdata/elasticsearch/conference/ConferenceRepository.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 the original author or authors.
+ * Copyright 2019-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.
@@ -15,13 +15,16 @@
  */
 package example.springdata.elasticsearch.conference;
 
+import java.time.LocalDate;
+
 import org.springframework.data.repository.reactive.ReactiveCrudRepository;
 import reactor.core.publisher.Flux;
 
 /**
  * @author Christoph Strobl
+ * @author Haibo Liu
  */
 interface ConferenceRepository extends ReactiveCrudRepository<Conference, String> {
 
-	Flux<Conference> findAllByKeywordsContainsAndDateAfter(String keyword, String Date);
+	Flux<Conference> findAllByKeywordsContainsAndDateAfter(String keyword, LocalDate Date);
 }
diff --git a/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java b/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java
new file mode 100644
index 000000000..5107deb91
--- /dev/null
+++ b/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/AbstractContainerBaseTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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
+ *
+ *      http://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 example.springdata.elasticsearch.conference;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.elasticsearch.client.ClientConfiguration;
+import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+import org.testcontainers.elasticsearch.ElasticsearchContainer;
+import org.testcontainers.utility.DockerImageName;
+
+import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * singleton container
+ *
+ * @author Haibo Liu
+ */
+@SpringBootTest(classes = {ApplicationConfiguration.class, AbstractContainerBaseTest.TestConfiguration.class})
+public class AbstractContainerBaseTest {
+
+	protected static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;
+
+	private static final ElasticsearchContainer CONTAINER = new ElasticsearchContainer(
+			DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:8.7.0")) //
+			.withPassword("foobar");
+
+	static {
+		CONTAINER.start();
+	}
+
+	@Configuration
+	static class TestConfiguration extends ElasticsearchConfiguration {
+		@Override
+		@NonNull
+		public ClientConfiguration clientConfiguration() {
+
+			Assert.notNull(CONTAINER, "TestContainer is not initialized!");
+
+			return ClientConfiguration.builder() //
+					.connectedTo(CONTAINER.getHttpHostAddress()) //
+					.usingSsl(CONTAINER.createSslContextFromCa()) //
+					.withBasicAuth("elastic", "foobar") //
+					.build();
+		}
+	}
+}
diff --git a/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchOperationsTest.java b/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchOperationsTest.java
index 8b885d389..6aeb8f64d 100644
--- a/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchOperationsTest.java
+++ b/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchOperationsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-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.
@@ -17,26 +17,15 @@
 
 import static org.assertj.core.api.Assertions.*;
 
-import reactor.test.StepVerifier;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.LocalDate;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.elasticsearch.client.ClientConfiguration;
-import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
 import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
 import org.springframework.data.elasticsearch.core.SearchHit;
 import org.springframework.data.elasticsearch.core.query.Criteria;
 import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
-import org.springframework.util.Assert;
-import org.testcontainers.elasticsearch.ElasticsearchContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.DockerImageName;
+import reactor.test.StepVerifier;
 
 /**
  * Test case to show Spring Data Elasticsearch functionality.
@@ -44,41 +33,16 @@
  * @author Christoph Strobl
  * @author Prakhar Gupta
  * @author Peter-Josef Meisch
+ * @author Haibo Liu
  */
-@SpringBootTest(
-		classes = { ApplicationConfiguration.class, ReactiveElasticsearchOperationsTest.TestConfiguration.class })
-@Testcontainers
-class ReactiveElasticsearchOperationsTest {
-
-	private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
-
-	@Container //
-	private static final ElasticsearchContainer container = new ElasticsearchContainer(
-			DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:8.7.0")) //
-			.withPassword("foobar") //
-			.withReuse(true);
-
-	@Configuration
-	static class TestConfiguration extends ReactiveElasticsearchConfiguration {
-		@Override
-		public ClientConfiguration clientConfiguration() {
-
-			Assert.notNull(container, "TestContainer is not initialized!");
-
-			return ClientConfiguration.builder() //
-					.connectedTo(container.getHttpHostAddress()) //
-					.usingSsl(container.createSslContextFromCa()) //
-					.withBasicAuth("elastic", "foobar") //
-					.build();
-		}
-	}
+class ReactiveElasticsearchOperationsTest extends AbstractContainerBaseTest {
 
 	@Autowired ReactiveElasticsearchOperations operations;
 
 	@Test
 	void textSearch() {
 
-		var expectedDate = "2014-10-29";
+		var expectedDate = LocalDate.parse( "2014-10-29", FORMAT);
 		var expectedWord = "java";
 		var query = new CriteriaQuery(
 				new Criteria("keywords").contains(expectedWord).and(new Criteria("date").greaterThanEqual(expectedDate)));
@@ -91,13 +55,9 @@ void textSearch() {
 				.verifyComplete();
 	}
 
-	private void verify(SearchHit<Conference> hit, String expectedWord, String expectedDate) {
+	private void verify(SearchHit<Conference> hit, String expectedWord, LocalDate expectedDate) {
 
 		assertThat(hit.getContent().getKeywords()).contains(expectedWord);
-		try {
-			assertThat(format.parse(hit.getContent().getDate())).isAfter(format.parse(expectedDate));
-		} catch (ParseException e) {
-			fail("o_O", e);
-		}
+		assertThat(hit.getContent().getDate()).isAfter(expectedDate);
 	}
 }
diff --git a/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchRepositoryTest.java b/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchRepositoryTest.java
index a48d338cf..1c290e9f8 100644
--- a/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchRepositoryTest.java
+++ b/elasticsearch/reactive/src/test/java/example/springdata/elasticsearch/conference/ReactiveElasticsearchRepositoryTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 the original author or authors.
+ * Copyright 2019-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.
@@ -17,22 +17,11 @@
 
 import static org.assertj.core.api.Assertions.*;
 
-import reactor.test.StepVerifier;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.LocalDate;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.elasticsearch.client.ClientConfiguration;
-import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
-import org.springframework.util.Assert;
-import org.testcontainers.elasticsearch.ElasticsearchContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.DockerImageName;
+import reactor.test.StepVerifier;
 
 /**
  * Test case to show reactive Spring Data Elasticsearch repository functionality.
@@ -40,41 +29,16 @@
  * @author Christoph Strobl
  * @author Prakhar Gupta
  * @author Peter-Josef Meisch
+ * @author Haibo Liu
  */
-@SpringBootTest(
-		classes = { ApplicationConfiguration.class, ReactiveElasticsearchRepositoryTest.TestConfiguration.class })
-@Testcontainers
-class ReactiveElasticsearchRepositoryTest {
-
-	private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
-
-	@Container //
-	private static final ElasticsearchContainer container = new ElasticsearchContainer(
-			DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:8.7.0")) //
-			.withPassword("foobar") //
-			.withReuse(true);
-
-	@Configuration
-	static class TestConfiguration extends ReactiveElasticsearchConfiguration {
-		@Override
-		public ClientConfiguration clientConfiguration() {
-
-			Assert.notNull(container, "TestContainer is not initialized!");
-
-			return ClientConfiguration.builder() //
-					.connectedTo(container.getHttpHostAddress()) //
-					.usingSsl(container.createSslContextFromCa()) //
-					.withBasicAuth("elastic", "foobar") //
-					.build();
-		}
-	}
+class ReactiveElasticsearchRepositoryTest extends AbstractContainerBaseTest {
 
 	@Autowired ConferenceRepository repository;
 
 	@Test
 	void textSearch() {
 
-		var expectedDate = "2014-10-29";
+		var expectedDate = LocalDate.parse("2014-10-29", FORMAT);
 		var expectedWord = "java";
 
 		repository.findAllByKeywordsContainsAndDateAfter(expectedWord, expectedDate) //
@@ -85,13 +49,9 @@ void textSearch() {
 				.verifyComplete();
 	}
 
-	private void verify(Conference it, String expectedWord, String expectedDate) {
+	private void verify(Conference it, String expectedWord, LocalDate expectedDate) {
 
 		assertThat(it.getKeywords()).contains(expectedWord);
-		try {
-			assertThat(format.parse(it.getDate())).isAfter(format.parse(expectedDate));
-		} catch (ParseException e) {
-			fail("o_O", e);
-		}
+		assertThat(it.getDate()).isAfter(expectedDate);
 	}
 }