collectionOptions() {
BsonBinary key1 = new BsonBinary(UUID.randomUUID(), UuidRepresentation.STANDARD);
BsonBinary key2 = new BsonBinary(UUID.randomUUID(), UuidRepresentation.STANDARD);
+ BsonBinary key3 = new BsonBinary(UUID.randomUUID(), UuidRepresentation.STANDARD);
+ BsonBinary key4 = new BsonBinary(UUID.randomUUID(), UuidRepresentation.STANDARD);
CollectionOptions manualOptions = CollectionOptions.encryptedCollection(options -> options //
- .queryable(encrypted(int32("encryptedInt")).keys(key1), range().min(5).max(100).contention(1)) //
- .queryable(encrypted(JsonSchemaProperty.int64("nested.encryptedLong")).keys(key2),
- range().min(-1L).max(1L).contention(0)));
-
- CollectionOptions schemaOptions = CollectionOptions.encryptedCollection(MongoJsonSchema.builder()
+ .encrypted(int32("encrypted-but-not-queryable"), key1) //
+ .queryable(encrypted(int32("encryptedInt")).keyId(key2), range().min(5).max(100).contention(1)) //
+ .queryable(encrypted(JsonSchemaProperty.int64("nested.encryptedLong")).keyId(key3),
+ range().min(-1L).max(1L).contention(0)) //
+ .queryable(encrypted(JsonSchemaProperty.float64("encryptedDouble")).keys(key4),
+ range().min(-1.123D).max(1.123D).precision(5).contention(1)));
+
+ CollectionOptions schemaOptions = CollectionOptions.encryptedCollection(MongoJsonSchema.builder() //
+ .property(encrypted(int32("encrypted-but-not-queryable")).keyId(key1)) //
.property(
- queryable(encrypted(int32("encryptedInt")).keyId(key1), List.of(range().min(5).max(100).contention(1))))
- .property(queryable(encrypted(int64("nested.encryptedLong")).keyId(key2),
+ queryable(encrypted(int32("encryptedInt")).keyId(key2), List.of(range().min(5).max(100).contention(1))))
+ .property(queryable(encrypted(int64("nested.encryptedLong")).keyId(key3),
List.of(range().min(-1L).max(1L).contention(0))))
+ .property(queryable(encrypted(float64("encryptedDouble")).keyId(key4),
+ List.of(range().min(-1.123D).max(1.123D).precision(5).contention(1))))
.build());
return Stream.of(Arguments.of(manualOptions), Arguments.of(schemaOptions));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java
index e4e760cc91..5369d0d70d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java
@@ -32,9 +32,7 @@
import org.bson.BsonInt32;
import org.bson.BsonString;
import org.bson.Document;
-import org.junit.Before;
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -61,7 +59,6 @@
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion;
import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.data.mongodb.util.MongoClientVersion;
import org.springframework.data.util.Lazy;
import org.springframework.test.context.ContextConfiguration;
@@ -92,7 +89,7 @@
* @author Ross Lawley
* @author Christoph Strobl
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@EnableIfMongoServerVersion(isGreaterThanEqual = "8.0")
@EnableIfReplicaSetAvailable
@ContextConfiguration(classes = RangeEncryptionTests.EncryptionConfig.class)
@@ -125,11 +122,11 @@ void manuallyEncryptedValuesCanBeSavedAndRetrievedCorrectly() {
EncryptOptions equalityEncOptions = new EncryptOptions("Indexed").contentionFactor(0L)
.keyId(keyHolder.getEncryptionKey("age"));
- ;
EncryptOptions equalityEncOptionsString = new EncryptOptions("Indexed").contentionFactor(0L)
.keyId(keyHolder.getEncryptionKey("name"));
- ;
+
+ EncryptOptions justEncryptOptions = new EncryptOptions("Unindexed").keyId(keyHolder.getEncryptionKey("ssn"));
Document source = new Document("_id", "id-1");
@@ -137,6 +134,7 @@ void manuallyEncryptedValuesCanBeSavedAndRetrievedCorrectly() {
clientEncryption.getClientEncryption().encrypt(new BsonString("It's a Me, Mario!"), equalityEncOptionsString));
source.put("age", clientEncryption.getClientEncryption().encrypt(new BsonInt32(101), equalityEncOptions));
source.put("encryptedInt", clientEncryption.getClientEncryption().encrypt(new BsonInt32(101), encryptOptions));
+ source.put("ssn", clientEncryption.getClientEncryption().encrypt(new BsonString("6-4-20"), justEncryptOptions));
source.put("_class", Person.class.getName());
template.execute(Person.class, col -> col.insertOne(source));
@@ -151,6 +149,8 @@ void manuallyEncryptedValuesCanBeSavedAndRetrievedCorrectly() {
});
assertThat(result).containsEntry("encryptedInt", 101);
+ assertThat(result).containsEntry("age", 101);
+ assertThat(result).containsEntry("ssn", "6-4-20");
}
@Test // GH-4185
@@ -283,6 +283,7 @@ private Person createPerson() {
source.encryptedLong = 1001L;
source.nested = new NestedWithQEFields();
source.nested.value = "Luigi time!";
+ source.ssn = "6-4-20";
return source;
}
@@ -480,6 +481,10 @@ static class Person {
rangeOptions = "{\"min\": {\"$numberLong\": \"1000\"}, \"max\": {\"$numberLong\": \"9999\"}, \"trimFactor\": 1, \"sparsity\": 1}") //
Long encryptedLong;
+ @ValueConverter(MongoEncryptionConverter.class)
+ @Encrypted(algorithm = "Unindexed") // encrypted, nothing else!
+ String ssn;
+
NestedWithQEFields nested;
public String getId() {
@@ -514,6 +519,14 @@ public void setEncryptedLong(Long encryptedLong) {
this.encryptedLong = encryptedLong;
}
+ public String getSsn() {
+ return ssn;
+ }
+
+ public void setSsn(String ssn) {
+ this.ssn = ssn;
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) {
@@ -525,18 +538,20 @@ public boolean equals(Object o) {
Person person = (Person) o;
return Objects.equals(id, person.id) && Objects.equals(unencryptedValue, person.unencryptedValue)
&& Objects.equals(name, person.name) && Objects.equals(age, person.age)
- && Objects.equals(encryptedInt, person.encryptedInt) && Objects.equals(encryptedLong, person.encryptedLong);
+ && Objects.equals(encryptedInt, person.encryptedInt) && Objects.equals(encryptedLong, person.encryptedLong)
+ && Objects.equals(ssn, person.ssn);
}
@Override
public int hashCode() {
- return Objects.hash(id, unencryptedValue, name, age, encryptedInt, encryptedLong);
+ return Objects.hash(id, unencryptedValue, name, age, encryptedInt, encryptedLong, ssn);
}
@Override
public String toString() {
return "Person{" + "id='" + id + '\'' + ", unencryptedValue='" + unencryptedValue + '\'' + ", name='" + name
- + '\'' + ", age=" + age + ", encryptedInt=" + encryptedInt + ", encryptedLong=" + encryptedLong + '}';
+ + '\'' + ", age=" + age + ", encryptedInt=" + encryptedInt + ", encryptedLong=" + encryptedLong + ", ssn="
+ + ssn + '}';
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
index b81b51abd5..6a792a40f6 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
@@ -29,6 +29,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
@@ -49,7 +50,6 @@
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -62,7 +62,7 @@
* @author Mark Paluch
* @author Ivan Volzhev
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@ContextConfiguration
public class GeoJsonTests {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java
index caa40e96c0..c8b11b8ef5 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java
@@ -31,6 +31,7 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -48,7 +49,6 @@
import org.springframework.data.mongodb.core.mapping.TimeSeries;
import org.springframework.data.mongodb.test.util.Client;
import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -64,7 +64,7 @@
* @author Mark Paluch
* @author Ben Foster
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@ContextConfiguration
public class IndexingIntegrationTests {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java
index aa37b8bced..0abb357bfd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java
@@ -21,13 +21,12 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Language;
import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
@@ -35,7 +34,6 @@
* @author Christoph Strobl
* @author Mark Paluch
*/
-@ExtendWith(MongoTemplateExtension.class)
public class TextIndexTests {
@Template(initialEntitySet = TextIndexedDocumentRoot.class)
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/VectorIndexIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/VectorIndexIntegrationTests.java
index dcd447f81a..fcf4cc328d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/VectorIndexIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/VectorIndexIntegrationTests.java
@@ -17,8 +17,10 @@
import static org.assertj.core.api.Assertions.*;
import static org.awaitility.Awaitility.*;
+import static org.springframework.data.mongodb.test.util.Assertions.*;
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
+import java.time.Duration;
import java.util.List;
import org.bson.Document;
@@ -32,6 +34,7 @@
import org.springframework.data.mongodb.core.index.VectorIndex.SimilarityFunction;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.test.util.AtlasContainer;
+import org.springframework.data.mongodb.test.util.EnableIfVectorSearchAvailable;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.MongoTestUtils;
import org.springframework.lang.Nullable;
@@ -66,19 +69,22 @@ class VectorIndexIntegrationTests {
@BeforeEach
void init() {
- template.createCollection(Movie.class);
+
+ template.createCollectionIfNotExists(Movie.class);
indexOps = template.searchIndexOps(Movie.class);
}
@AfterEach
void cleanup() {
+ template.flush(Movie.class);
template.searchIndexOps(Movie.class).dropAllIndexes();
- template.dropCollection(Movie.class);
+ template.awaitNoSearchIndexAvailable(Movie.class, Duration.ofSeconds(30));
}
@ParameterizedTest // GH-4706
@ValueSource(strings = { "euclidean", "cosine", "dotProduct" })
+ @EnableIfVectorSearchAvailable(collection = Movie.class)
void createsSimpleVectorIndex(String similarityFunction) {
VectorIndex idx = new VectorIndex("vector_index").addVector("plotEmbedding",
@@ -98,6 +104,7 @@ void createsSimpleVectorIndex(String similarityFunction) {
}
@Test // GH-4706
+ @EnableIfVectorSearchAvailable(collection = Movie.class)
void dropIndex() {
VectorIndex idx = new VectorIndex("vector_index").addVector("plotEmbedding",
@@ -105,7 +112,7 @@ void dropIndex() {
indexOps.createIndex(idx);
- template.awaitIndexCreation(Movie.class, idx.getName());
+ template.awaitSearchIndexCreation(Movie.class, idx.getName());
indexOps.dropIndex(idx.getName());
@@ -113,6 +120,7 @@ void dropIndex() {
}
@Test // GH-4706
+ @EnableIfVectorSearchAvailable(collection = Movie.class)
void statusChanges() throws InterruptedException {
String indexName = "vector_index";
@@ -131,6 +139,7 @@ void statusChanges() throws InterruptedException {
}
@Test // GH-4706
+ @EnableIfVectorSearchAvailable(collection = Movie.class)
void exists() throws InterruptedException {
String indexName = "vector_index";
@@ -148,6 +157,7 @@ void exists() throws InterruptedException {
}
@Test // GH-4706
+ @EnableIfVectorSearchAvailable(collection = Movie.class)
void updatesVectorIndex() throws InterruptedException {
String indexName = "vector_index";
@@ -177,6 +187,7 @@ void updatesVectorIndex() throws InterruptedException {
}
@Test // GH-4706
+ @EnableIfVectorSearchAvailable(collection = Movie.class)
void createsVectorIndexWithFilters() throws InterruptedException {
VectorIndex idx = new VectorIndex("vector_index")
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java
index eee407701c..82be189f70 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java
@@ -31,7 +31,7 @@
import org.bson.types.ObjectId;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.annotation.Id;
@@ -46,7 +46,6 @@
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
@@ -60,7 +59,6 @@
* @author Thomas Darimont
* @author Mark Paluch
*/
-@ExtendWith(MongoTemplateExtension.class)
public class MappingTests {
static final String DB_NAME = "mapping-tests";
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java
index 9bc1dc78aa..cb92e3bae7 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java
@@ -28,7 +28,7 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.annotation.Id;
@@ -42,7 +42,6 @@
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.test.annotation.DirtiesContext;
import com.mongodb.WriteConcern;
@@ -57,7 +56,6 @@
* @author Jordi Llach
* @author Mark Paluch
*/
-@ExtendWith({ MongoClientExtension.class })
public class ApplicationContextEventTests {
private static final String COLLECTION_NAME = "personPojoStringId";
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java
index 53d093897e..62195239e3 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java
@@ -38,8 +38,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.RepeatFailedTest;
+
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.ChangeStreamOptions;
import org.springframework.data.mongodb.core.CollectionOptions;
@@ -51,7 +51,6 @@
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion;
import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.MongoVersion;
import org.springframework.data.mongodb.test.util.Template;
@@ -68,7 +67,6 @@
* @author Mark Paluch
* @author Myroslav Kosinskyi
*/
-@ExtendWith({ MongoTemplateExtension.class })
@EnableIfReplicaSetAvailable
class ChangeStreamTests {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java
index 9373845a89..bcae19d32a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java
@@ -28,7 +28,7 @@
import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.dao.DataAccessException;
import org.springframework.data.annotation.Id;
@@ -40,8 +40,6 @@
import org.springframework.data.mongodb.test.util.Client;
import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion;
import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
-import org.springframework.data.mongodb.test.util.MongoServerCondition;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestUtils;
import org.springframework.data.mongodb.test.util.Template;
import org.springframework.util.ErrorHandler;
@@ -56,7 +54,6 @@
*
* @author Christoph Strobl
*/
-@ExtendWith({ MongoTemplateExtension.class, MongoServerCondition.class })
public class DefaultMessageListenerContainerTests {
static final String DATABASE_NAME = "change-stream-events";
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java
index 60d9153212..8b41f9120d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java
@@ -31,7 +31,7 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -39,7 +39,6 @@
import org.springframework.data.mongodb.core.messaging.Message.MessageProperties;
import org.springframework.data.mongodb.core.messaging.TailableCursorRequest.TailableCursorRequestOptions;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import com.mongodb.client.MongoClient;
@@ -50,7 +49,6 @@
* @author Christoph Strobl
* @author Mark Paluch
*/
-@ExtendWith({ MongoClientExtension.class })
public class TailableCursorTests {
static final String COLLECTION_NAME = "user";
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
index 72f42db9b5..869d6d67c8 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
@@ -25,9 +25,8 @@
import org.bson.types.Binary;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.data.annotation.Id;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
@@ -37,7 +36,6 @@
* @author Christoph Strobl
* @author Andreas Zink
*/
-@ExtendWith(MongoTemplateExtension.class)
class CriteriaTests {
@Template(initialEntitySet = { DocumentWithBitmask.class }) //
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java
index 6ea0f5aa9c..6aa97f53bc 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java
@@ -23,7 +23,7 @@
import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.index.IndexDefinition;
@@ -32,7 +32,6 @@
import org.springframework.data.mongodb.core.mapping.Language;
import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.query.TextQueryTests.FullTextDoc.FullTextDocBuilder;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
import org.springframework.lang.Nullable;
@@ -41,7 +40,6 @@
* @author Christoph Strobl
* @author Mark Paluch
*/
-@ExtendWith(MongoTemplateExtension.class)
public class TextQueryTests {
private static final FullTextDoc BAKE = new FullTextDocBuilder().headline("bake").build();
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java
index a7cf75366f..2dc7cd0f5f 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java
@@ -24,6 +24,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
@@ -33,7 +34,6 @@
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -49,7 +49,7 @@
*
* @author Christoph Strobl
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@ContextConfiguration
public class MongoJsonSchemaTests {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java
index 4615568d10..20cbbe9dfd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java
@@ -28,6 +28,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataRetrievalFailureException;
@@ -36,7 +37,6 @@
import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.data.mongodb.test.util.ReactiveMongoClientClosingTestConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -48,7 +48,7 @@
*
* @author Mark Paluch
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@ContextConfiguration
public class ReactiveMongoJsonSchemaTests {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java
index e70b398f7f..4cc99b0f27 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java
@@ -20,9 +20,8 @@
import java.net.UnknownHostException;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import com.mongodb.client.MongoClient;
@@ -33,7 +32,6 @@
* @author Thomas Darimont
* @author Mark Paluch
*/
-@ExtendWith(MongoClientExtension.class)
public class MongoMonitorIntegrationTests {
static @Client MongoClient mongoClient;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java
index 5c2cb0b701..35536e3921 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerTests.java
@@ -20,6 +20,7 @@
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.Observation;
@@ -50,6 +51,7 @@
* @author Greg Turnquist
* @author Mark Paluch
* @author François Kha
+ * @author Michal Domagala
*/
class MongoObservationCommandListenerTests {
@@ -98,6 +100,19 @@ void commandStartedShouldNotInstrumentWhenNoParentSampleInRequestContext() {
assertThat(meterRegistry).hasMeterWithName("spring.data.mongodb.command.active");
}
+ @Test // GH-4994
+ void commandStartedShouldAlwaysIncludeCollection() {
+
+ // when
+ listener.commandStarted(new CommandStartedEvent(new MapRequestContext(), 0, 0, null, "some name", "hello", null));
+
+ // then
+ // although command 'hello' is collection-less, metric must have tag "db.mongodb.collection"
+ assertThat(meterRegistry).hasMeterWithNameAndTags(
+ "spring.data.mongodb.command.active",
+ Tags.of("db.mongodb.collection", "none"));
+ }
+
@Test
void successfullyCompletedCommandShouldCreateTimerWhenParentSampleInRequestContext() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java
index a4f533f0be..15776510fd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java
@@ -26,6 +26,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
@@ -34,7 +35,6 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.mongodb.client.MongoClient;
@@ -44,7 +44,7 @@
* @author Oliver Gierke
* @author Mark Paluch
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
public class ComplexIdRepositoryIntegrationTests {
static @Client MongoClient mongoClient;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java
index c41abf4aa1..d598c8a207 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java
@@ -23,7 +23,7 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -35,7 +35,6 @@
import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
import org.springframework.lang.Nullable;
@@ -48,7 +47,6 @@
* @author Oliver Gierke
* @author Mark Paluch
*/
-@ExtendWith(MongoTemplateExtension.class)
class MongoRepositoryTextSearchIntegrationTests {
private static final FullTextDocument PASSENGER_57 = new FullTextDocument("1", "Passenger 57",
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java
index 0af684b9c1..ae4a3c57f1 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java
@@ -30,6 +30,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter;
@@ -44,7 +45,6 @@
import org.springframework.data.mongodb.test.util.AfterTransactionAssertion;
import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion;
import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.data.mongodb.test.util.ReplSetClient;
import org.springframework.lang.Nullable;
import org.springframework.test.annotation.Rollback;
@@ -63,7 +63,7 @@
* @author Christoph Strobl
* @currentRead Shadow's Edge - Brent Weeks
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@EnableIfReplicaSetAvailable
@EnableIfMongoServerVersion(isGreaterThanEqual = "4.0")
@Transactional(transactionManager = "txManager")
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPersonRepositoryIntegrationTests.java
index f4e1e0282e..d0b89795ca 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPersonRepositoryIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPersonRepositoryIntegrationTests.java
@@ -31,7 +31,6 @@
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
import org.springframework.data.mongodb.test.util.MongoTestUtils;
import org.springframework.data.repository.CrudRepository;
import org.springframework.lang.Nullable;
@@ -45,7 +44,7 @@
*
* @author Christoph Strobl
*/
-@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
+@ExtendWith({ SpringExtension.class })
@ContextConfiguration
class VersionedPersonRepositoryIntegrationTests {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java
index 60c02ee775..3dbb0b3890 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java
@@ -32,6 +32,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
+
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
@@ -53,8 +54,6 @@
import org.springframework.data.mongodb.test.util.DirtiesStateExtension;
import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion;
import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
-import org.springframework.data.mongodb.test.util.MongoServerCondition;
-import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
import org.springframework.data.repository.query.FluentQuery;
@@ -68,7 +67,7 @@
* @author Mark Paluch
* @author Jens Schauder
*/
-@ExtendWith({ MongoTemplateExtension.class, MongoServerCondition.class, DirtiesStateExtension.class })
+@ExtendWith({ DirtiesStateExtension.class })
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SimpleMongoRepositoryTests implements StateFunctions {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AtlasContainer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AtlasContainer.java
index c3a97a03bc..c9c23c16ab 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AtlasContainer.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AtlasContainer.java
@@ -17,11 +17,16 @@
import org.springframework.core.env.StandardEnvironment;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
import org.testcontainers.mongodb.MongoDBAtlasLocalContainer;
import org.testcontainers.utility.DockerImageName;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+
/**
- * Extension to MongoDBAtlasLocalContainer.
+ * Extension to {@link MongoDBAtlasLocalContainer}. Registers mapped host an port as system properties
+ * ({@link #ATLAS_HOST}, {@link #ATLAS_PORT}).
*
* @author Christoph Strobl
*/
@@ -31,12 +36,21 @@ public class AtlasContainer extends MongoDBAtlasLocalContainer {
private static final String DEFAULT_TAG = "8.0.0";
private static final String LATEST = "latest";
+ public static final String ATLAS_HOST = "docker.mongodb.atlas.host";
+ public static final String ATLAS_PORT = "docker.mongodb.atlas.port";
+
+ public static final WaitAllStrategy WAIT_STRATEGY = new WaitAllStrategy(
+ WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT).withStrategy(Wait.forListeningPort())
+ .withStrategy(Wait.forSuccessfulCommand("runner healthcheck"));
+
private AtlasContainer(String dockerImageName) {
super(DockerImageName.parse(dockerImageName));
+ this.waitingFor(WAIT_STRATEGY);
}
private AtlasContainer(DockerImageName dockerImageName) {
super(dockerImageName);
+ this.waitingFor(WAIT_STRATEGY);
}
public static AtlasContainer bestMatch() {
@@ -55,4 +69,20 @@ public static AtlasContainer tagged(String tag) {
return new AtlasContainer(DEFAULT_IMAGE_NAME.withTag(tag));
}
+ @Override
+ protected void containerIsStarted(InspectContainerResponse containerInfo) {
+
+ super.containerIsStarted(containerInfo);
+
+ System.setProperty(ATLAS_HOST, getHost());
+ System.setProperty(ATLAS_PORT, getMappedPort(27017).toString());
+ }
+
+ @Override
+ protected void containerIsStopping(InspectContainerResponse containerInfo) {
+
+ System.clearProperty(ATLAS_HOST);
+ System.clearProperty(ATLAS_PORT);
+ super.containerIsStopping(containerInfo);
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfVectorSearchAvailable.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfVectorSearchAvailable.java
index da008d9ee4..7570d6fc50 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfVectorSearchAvailable.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfVectorSearchAvailable.java
@@ -25,13 +25,32 @@
import org.junit.jupiter.api.extension.ExtendWith;
/**
+ * {@link EnableIfVectorSearchAvailable} indicates a specific method can only be run in an environment that has a search
+ * server available. This means that not only the mongodb instance needs to have a
+ * {@literal searchIndexManagementHostAndPort} configured, but also that the search index sever is actually up and
+ * running, responding to a {@literal $listSearchIndexes} aggregation.
+ *
+ * Using this annotation will wait up to {@code 60 seconds} for the search index to become available.
+ *
* @author Christoph Strobl
+ * @since 4.5.3
+ * @see Tag
*/
-@Target({ ElementType.TYPE, ElementType.METHOD })
+@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Tag("vector-search")
@ExtendWith(MongoServerCondition.class)
public @interface EnableIfVectorSearchAvailable {
+ /**
+ * @return the name of the collection used to run the {@literal $listSearchIndexes} aggregation.
+ */
+ String collectionName() default "";
+
+ /**
+ * @return the type for resolving the name of the collection used to run the {@literal $listSearchIndexes}
+ * aggregation. The {@link #collectionName()} has precedence over the type.
+ */
+ Class> collection() default Object.class;
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java
index 357a87168e..6e2c694ed7 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java
@@ -47,7 +47,7 @@
* @see Client
* @see ReplSetClient
*/
-public class MongoClientExtension implements Extension, BeforeAllCallback, AfterAllCallback, ParameterResolver {
+class MongoClientExtension implements Extension, BeforeAllCallback, AfterAllCallback, ParameterResolver {
private static final Log LOGGER = LogFactory.getLog(MongoClientExtension.class);
@@ -157,7 +157,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
return getMongoClient(parameterType, extensionContext, replSet);
}
- static class SyncClientHolder implements Store.CloseableResource {
+ static class SyncClientHolder implements AutoCloseable {
final MongoClient client;
@@ -175,7 +175,7 @@ public void close() {
}
}
- static class ReactiveClientHolder implements Store.CloseableResource {
+ static class ReactiveClientHolder implements AutoCloseable {
final com.mongodb.reactivestreams.client.MongoClient client;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java
index c90f7e999b..864bb6aa5d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java
@@ -31,7 +31,7 @@ static class Client {
static final String REACTIVE_REPLSET_KEY = "mongo.client.replset.reactive";
}
- static class Termplate {
+ static class Template {
static final Namespace NAMESPACE = Namespace.create(MongoTemplateExtension.class);
static final String SYNC = "mongo.template.sync";
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java
index d811e0a1ef..a1536d01d2 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java
@@ -15,17 +15,26 @@
*/
package org.springframework.data.mongodb.test.util;
+import java.time.Duration;
+
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.data.mongodb.MongoCollectionUtils;
import org.springframework.data.util.Version;
+import org.springframework.util.NumberUtils;
+import org.springframework.util.StringUtils;
+import org.testcontainers.shaded.org.awaitility.Awaitility;
+
+import com.mongodb.Function;
+import com.mongodb.client.MongoClient;
/**
* @author Christoph Strobl
*/
-public class MongoServerCondition implements ExecutionCondition {
+class MongoServerCondition implements ExecutionCondition {
private static final Namespace NAMESPACE = Namespace.create("mongodb", "server");
@@ -42,10 +51,15 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con
}
}
- if(context.getTags().contains("vector-search")) {
- if(!atlasEnvironment(context)) {
+ if (context.getTags().contains("vector-search")) {
+
+ if (!atlasEnvironment(context)) {
return ConditionEvaluationResult.disabled("Disabled for servers not supporting Vector Search.");
}
+
+ if (!isSearchIndexAvailable(context)) {
+ return ConditionEvaluationResult.disabled("Search index unavailable.");
+ }
}
if (context.getTags().contains("version-specific") && context.getElement().isPresent()) {
@@ -90,8 +104,55 @@ private Version serverVersion(ExtensionContext context) {
Version.class);
}
+ private boolean isSearchIndexAvailable(ExtensionContext context) {
+
+ EnableIfVectorSearchAvailable vectorSearchAvailable = AnnotatedElementUtils
+ .findMergedAnnotation(context.getElement().get(), EnableIfVectorSearchAvailable.class);
+
+ if (vectorSearchAvailable == null) {
+ return true;
+ }
+
+ String collectionName = StringUtils.hasText(vectorSearchAvailable.collectionName())
+ ? vectorSearchAvailable.collectionName()
+ : MongoCollectionUtils.getPreferredCollectionName(vectorSearchAvailable.collection());
+
+ return context.getStore(NAMESPACE).getOrComputeIfAbsent("search-index-%s-available".formatted(collectionName),
+ (key) -> {
+ try {
+ doWithClient(client -> {
+ Awaitility.await().atMost(Duration.ofSeconds(60)).pollInterval(Duration.ofMillis(200)).until(() -> {
+ return MongoTestUtils.isSearchIndexReady(client, null, collectionName);
+ });
+ return "done waiting for search index";
+ });
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }, Boolean.class);
+
+ }
+
private boolean atlasEnvironment(ExtensionContext context) {
- return context.getStore(NAMESPACE).getOrComputeIfAbsent(Version.class, (key) -> MongoTestUtils.isVectorSearchEnabled(),
- Boolean.class);
+
+ return context.getStore(NAMESPACE).getOrComputeIfAbsent("mongodb-atlas",
+ (key) -> doWithClient(MongoTestUtils::isVectorSearchEnabled), Boolean.class);
+ }
+
+ private T doWithClient(Function function) {
+
+ String host = System.getProperty(AtlasContainer.ATLAS_HOST);
+ String port = System.getProperty(AtlasContainer.ATLAS_PORT);
+
+ if (StringUtils.hasText(host) && StringUtils.hasText(port)) {
+ try (MongoClient client = MongoTestUtils.client(host, NumberUtils.parseNumber(port, Integer.class))) {
+ return function.apply(client);
+ }
+ }
+
+ try (MongoClient client = MongoTestUtils.client()) {
+ return function.apply(client);
+ }
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java
index 301d1ef499..c91bdb4d5d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java
@@ -33,7 +33,7 @@
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
-import org.springframework.data.mongodb.test.util.MongoExtensions.Termplate;
+import org.springframework.data.mongodb.test.util.MongoExtensions.Template;
import org.springframework.data.util.ParsingUtils;
import org.springframework.util.ClassUtils;
@@ -41,11 +41,11 @@
* JUnit {@link Extension} providing parameter resolution for synchronous and reactive MongoDB Template API objects.
*
* @author Christoph Strobl
- * @see Template
+ * @see org.springframework.data.mongodb.test.util.Template
* @see MongoTestTemplate
* @see ReactiveMongoTestTemplate
*/
-public class MongoTemplateExtension extends MongoClientExtension implements TestInstancePostProcessor {
+class MongoTemplateExtension extends MongoClientExtension implements TestInstancePostProcessor {
private static final String DEFAULT_DATABASE = "database";
@@ -65,32 +65,32 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
- return super.supportsParameter(parameterContext, extensionContext) || parameterContext.isAnnotated(Template.class);
+ return super.supportsParameter(parameterContext, extensionContext) || parameterContext.isAnnotated(org.springframework.data.mongodb.test.util.Template.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
- if (parameterContext.getParameter().getAnnotation(Template.class) == null) {
+ if (parameterContext.getParameter().getAnnotation(org.springframework.data.mongodb.test.util.Template.class) == null) {
return super.resolveParameter(parameterContext, extensionContext);
}
Class> parameterType = parameterContext.getParameter().getType();
- return getMongoTemplate(parameterType, parameterContext.getParameter().getAnnotation(Template.class),
+ return getMongoTemplate(parameterType, parameterContext.getParameter().getAnnotation(org.springframework.data.mongodb.test.util.Template.class),
extensionContext);
}
private void injectFields(ExtensionContext context, Object testInstance, Predicate predicate) {
- AnnotationUtils.findAnnotatedFields(context.getRequiredTestClass(), Template.class, predicate).forEach(field -> {
+ AnnotationUtils.findAnnotatedFields(context.getRequiredTestClass(), org.springframework.data.mongodb.test.util.Template.class, predicate).forEach(field -> {
assertValidFieldCandidate(field);
try {
ReflectionUtils.makeAccessible(field).set(testInstance,
- getMongoTemplate(field.getType(), field.getAnnotation(Template.class), context));
+ getMongoTemplate(field.getType(), field.getAnnotation(org.springframework.data.mongodb.test.util.Template.class), context));
} catch (Throwable t) {
ExceptionUtils.throwAsUncheckedException(t);
}
@@ -107,14 +107,14 @@ private void assertSupportedType(String target, Class> type) {
if (!ClassUtils.isAssignable(MongoOperations.class, type)
&& !ClassUtils.isAssignable(ReactiveMongoOperations.class, type)) {
throw new ExtensionConfigurationException(
- String.format("Can only resolve @%s %s of type %s or %s but was: %s", Template.class.getSimpleName(), target,
+ String.format("Can only resolve @%s %s of type %s or %s but was: %s", org.springframework.data.mongodb.test.util.Template.class.getSimpleName(), target,
MongoOperations.class.getName(), ReactiveMongoOperations.class.getName(), type.getName()));
}
}
- private Object getMongoTemplate(Class> type, Template options, ExtensionContext extensionContext) {
+ private Object getMongoTemplate(Class> type, org.springframework.data.mongodb.test.util.Template options, ExtensionContext extensionContext) {
- Store templateStore = extensionContext.getStore(MongoExtensions.Termplate.NAMESPACE);
+ Store templateStore = extensionContext.getStore(Template.NAMESPACE);
boolean replSetClient = holdsReplSetClient(extensionContext) || options.replicaSet();
@@ -126,7 +126,7 @@ private Object getMongoTemplate(Class> type, Template options, ExtensionContex
if (ClassUtils.isAssignable(MongoOperations.class, type)) {
- String key = Termplate.SYNC + "-" + dbName;
+ String key = Template.SYNC + "-" + dbName;
return templateStore.getOrComputeIfAbsent(key, it -> {
com.mongodb.client.MongoClient client = (com.mongodb.client.MongoClient) getMongoClient(
@@ -137,7 +137,7 @@ private Object getMongoTemplate(Class> type, Template options, ExtensionContex
if (ClassUtils.isAssignable(ReactiveMongoOperations.class, type)) {
- String key = Termplate.REACTIVE + "-" + dbName;
+ String key = Template.REACTIVE + "-" + dbName;
return templateStore.getOrComputeIfAbsent(key, it -> {
com.mongodb.reactivestreams.client.MongoClient client = (com.mongodb.reactivestreams.client.MongoClient) getMongoClient(
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java
index 40948a0e22..b296623062 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java
@@ -32,8 +32,12 @@
import org.testcontainers.shaded.org.awaitility.Awaitility;
import com.mongodb.MongoWriteException;
+import com.mongodb.ReadPreference;
+import com.mongodb.WriteConcern;
import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
/**
* A {@link MongoTemplate} with configuration hooks and extension suitable for tests.
@@ -133,6 +137,21 @@ public void flush(Object... objects) {
}).collect(Collectors.toList()));
}
+ public void createCollectionIfNotExists(Class> type) {
+ createCollectionIfNotExists(getCollectionName(type));
+ }
+
+ public void createCollectionIfNotExists(String collectionName) {
+
+ MongoDatabase database = getDb().withWriteConcern(WriteConcern.MAJORITY)
+ .withReadPreference(ReadPreference.primary());
+
+ boolean collectionExists = database.listCollections().filter(new Document("name", collectionName)).first() != null;
+ if (!collectionExists) {
+ createCollection(collectionName);
+ }
+ }
+
public void dropDatabase() {
getDb().drop();
}
@@ -156,11 +175,11 @@ public void doInCollection(Class> entityClass, Consumer type, String indexName) {
- awaitIndexCreation(getCollectionName(type), indexName, Duration.ofSeconds(10));
+ public void awaitSearchIndexCreation(Class> type, String indexName) {
+ awaitSearchIndexCreation(getCollectionName(type), indexName, Duration.ofSeconds(30));
}
- public void awaitIndexCreation(String collectionName, String indexName, Duration timeout) {
+ public void awaitSearchIndexCreation(String collectionName, String indexName, Duration timeout) {
Awaitility.await().atMost(timeout).pollInterval(Duration.ofMillis(200)).until(() -> {
@@ -176,4 +195,35 @@ public void awaitIndexCreation(String collectionName, String indexName, Duration
return false;
});
}
+
+ public void awaitIndexDeletion(String collectionName, String indexName, Duration timeout) {
+
+ Awaitility.await().atMost(timeout).pollInterval(Duration.ofMillis(200)).until(() -> {
+
+ List execute = this.execute(collectionName,
+ coll -> coll
+ .aggregate(List.of(Document.parse("{'$listSearchIndexes': { 'name' : '%s'}}".formatted(indexName))))
+ .into(new ArrayList<>()));
+ for (Document doc : execute) {
+ if (doc.getString("name").equals(indexName)) {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+ public void awaitNoSearchIndexAvailable(String collectionName, Duration timeout) {
+
+ Awaitility.await().atMost(timeout).pollInterval(Duration.ofMillis(200)).until(() -> {
+
+ return this.execute(collectionName, coll -> coll.aggregate(List.of(Document.parse("{'$listSearchIndexes': {}}")))
+ .into(new ArrayList<>()).isEmpty());
+
+ });
+ }
+
+ public void awaitNoSearchIndexAvailable(Class> type, Duration timeout) {
+ awaitNoSearchIndexAvailable(getCollectionName(type), timeout);
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java
index f88caf80dd..93993f5769 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java
@@ -21,15 +21,20 @@
import java.time.Duration;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import org.bson.Document;
+
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.data.util.Version;
+import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
import com.mongodb.ConnectionString;
+import com.mongodb.MongoClientSettings;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoClient;
@@ -68,6 +73,10 @@ public static MongoClient client(String host, int port) {
}
public static MongoClient client(ConnectionString connectionString) {
+ MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(connectionString)
+ .applyToSocketSettings(builder -> {
+ builder.connectTimeout(120, TimeUnit.SECONDS);
+ }).build();
return com.mongodb.client.MongoClients.create(connectionString, SpringDataMongoDB.driverInformation());
}
@@ -176,11 +185,10 @@ public static void dropCollectionNow(String dbName, String collectionName,
* @param collectionName must not be {@literal null}.
* @param client must not be {@literal null}.
*/
- public static void dropCollectionNow(String dbName, String collectionName,
- com.mongodb.client.MongoClient client) {
+ public static void dropCollectionNow(String dbName, String collectionName, com.mongodb.client.MongoClient client) {
- com.mongodb.client.MongoDatabase database = client.getDatabase(dbName)
- .withWriteConcern(WriteConcern.MAJORITY).withReadPreference(ReadPreference.primary());
+ com.mongodb.client.MongoDatabase database = client.getDatabase(dbName).withWriteConcern(WriteConcern.MAJORITY)
+ .withReadPreference(ReadPreference.primary());
database.getCollection(collectionName).drop();
}
@@ -205,11 +213,10 @@ public static void flushCollection(String dbName, String collectionName,
.verifyComplete();
}
- public static void flushCollection(String dbName, String collectionName,
- com.mongodb.client.MongoClient client) {
+ public static void flushCollection(String dbName, String collectionName, com.mongodb.client.MongoClient client) {
- com.mongodb.client.MongoDatabase database = client.getDatabase(dbName)
- .withWriteConcern(WriteConcern.MAJORITY).withReadPreference(ReadPreference.primary());
+ com.mongodb.client.MongoDatabase database = client.getDatabase(dbName).withWriteConcern(WriteConcern.MAJORITY)
+ .withReadPreference(ReadPreference.primary());
database.getCollection(collectionName).deleteMany(new Document());
}
@@ -267,17 +274,34 @@ public static boolean serverIsReplSet() {
@SuppressWarnings("unchecked")
public static boolean isVectorSearchEnabled() {
try (MongoClient client = MongoTestUtils.client()) {
+ return isVectorSearchEnabled(client);
+ }
+ }
+ public static boolean isVectorSearchEnabled(MongoClient client) {
+ try {
return client.getDatabase("admin").runCommand(new Document("getCmdLineOpts", "1")).get("argv", List.class)
- .stream().anyMatch(it -> {
- if(it instanceof String cfgString) {
- return cfgString.startsWith("searchIndexManagementHostAndPort");
- }
- return false;
- });
+ .stream().anyMatch(it -> {
+ if (it instanceof String cfgString) {
+ return cfgString.startsWith("searchIndexManagementHostAndPort");
+ }
+ return false;
+ });
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static boolean isSearchIndexReady(MongoClient client, @Nullable String database, String collectionName) {
+
+ try {
+ MongoCollection collection = client.getDatabase(StringUtils.hasText(database) ? database : "test").getCollection(collectionName);
+ collection.aggregate(List.of(new Document("$listSearchIndexes", new Document())));
} catch (Exception e) {
return false;
}
+ return true;
+
}
public static Duration getTimeout() {
@@ -297,10 +321,11 @@ private static void giveTheServerALittleTimeToThink() {
public static CollectionInfo readCollectionInfo(MongoDatabase db, String collectionName) {
- List list = db.runCommand(new Document().append("listCollections", 1).append("filter", new Document("name", collectionName)))
+ List list = db
+ .runCommand(new Document().append("listCollections", 1).append("filter", new Document("name", collectionName)))
.get("cursor", Document.class).get("firstBatch", List.class);
- if(list.isEmpty()) {
+ if (list.isEmpty()) {
throw new IllegalStateException(String.format("Collection %s not found.", collectionName));
}
return CollectionInfo.from(list.get(0));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java
index 8342c5b5ee..ede3687f70 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java
@@ -21,6 +21,8 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+
/**
* Marks a field or method as to be autowired by JUnit's dependency injection facilities for injection of a MongoDB
* client instance connected to a replica set. Depends on {@link MongoClientExtension}.
@@ -34,6 +36,7 @@
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
+@ExtendWith(MongoClientExtension.class)
public @interface ReplSetClient {
}
diff --git a/src/main/antora/modules/ROOT/pages/mongodb/mongo-encryption.adoc b/src/main/antora/modules/ROOT/pages/mongodb/mongo-encryption.adoc
index 14e866cf14..e55c584ce0 100644
--- a/src/main/antora/modules/ROOT/pages/mongodb/mongo-encryption.adoc
+++ b/src/main/antora/modules/ROOT/pages/mongodb/mongo-encryption.adoc
@@ -140,10 +140,17 @@ Manual Collection Setup::
====
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
+
+BsonBinary pinDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
+BsonBinary ssnDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
+BsonBinary ageDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
+BsonBinary signDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
+
CollectionOptions collectionOptions = CollectionOptions.encryptedCollection(options -> options
- .queryable(encrypted(string("ssn")).algorithm("Indexed"), equality().contention(0))
- .queryable(encrypted(int32("age")).algorithm("Range"), range().contention(8).min(0).max(150))
- .queryable(encrypted(int64("address.sign")).algorithm("Range"), range().contention(2).min(-10L).max(10L))
+ .encrypted(string("pin"), pinDK)
+ .queryable(encrypted(string("ssn")).algorithm("Indexed").keyId(ssnDK.asUuid()), equality().contention(0))
+ .queryable(encrypted(int32("age")).algorithm("Range").keyId(ageDK.asUuid()), range().contention(8).min(0).max(150))
+ .queryable(encrypted(int64("address.sign")).algorithm("Range").keyId(signDK.asUuid()), range().contention(2).min(-10L).max(10L))
);
mongoTemplate.createCollection(Patient.class, collectionOptions); <1>
@@ -160,13 +167,16 @@ class Patient {
@Id String id;
- @Encrypted(algorithm = "Indexed") //
+ @Encrypted(algorithm = "Indexed")
@Queryable(queryType = "equality", contentionFactor = 0)
String ssn;
@RangeEncrypted(contentionFactor = 8, rangeOptions = "{ 'min' : 0, 'max' : 150 }")
Integer age;
+ @Encrypted(algorithm = "Unindexed")
+ String pin;
+
Address address;
}
@@ -210,6 +220,11 @@ MongoDB Collection Info::
bsonType: 'int',
queries: [ { queryType: 'range', contention: Long('8'), min: 0, max: 150 } ]
},
+ {
+ keyId: ...,
+ path: 'pin',
+ bsonType: 'string'
+ },
{
keyId: ...,
path: 'address.sign',
diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt
index 3b6fc5c998..659f332b22 100644
--- a/src/main/resources/notice.txt
+++ b/src/main/resources/notice.txt
@@ -1,4 +1,4 @@
-Spring Data MongoDB 4.5 GA (2025.0.0)
+Spring Data MongoDB 4.5.1 (2025.0.1)
Copyright (c) [2010-2019] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -61,5 +61,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
+