diff --git a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt index b4cbad3b9dd..435964d4ac0 100644 --- a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt +++ b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt @@ -31,6 +31,7 @@ import kotlinx.serialization.modules.SerializersModule import org.bson.AbstractBsonReader import org.bson.BsonInvalidOperationException import org.bson.BsonReader +import org.bson.BsonReaderMark import org.bson.BsonType import org.bson.BsonValue import org.bson.codecs.BsonValueCodec @@ -68,6 +69,20 @@ internal open class DefaultBsonDecoder( val validKeyKinds = setOf(PrimitiveKind.STRING, PrimitiveKind.CHAR, SerialKind.ENUM) val bsonValueCodec = BsonValueCodec() const val UNKNOWN_INDEX = -10 + fun validateCurrentBsonType( + reader: AbstractBsonReader, + expectedType: BsonType, + descriptor: SerialDescriptor, + actualType: (descriptor: SerialDescriptor) -> String = { it.kind.toString() } + ) { + reader.currentBsonType?.let { + if (it != expectedType) { + throw SerializationException( + "Invalid data for `${actualType(descriptor)}` expected a bson " + + "${expectedType.name.lowercase()} found: ${reader.currentBsonType}") + } + } + } } private fun initElementMetadata(descriptor: SerialDescriptor) { @@ -119,29 +134,14 @@ internal open class DefaultBsonDecoder( @Suppress("ReturnCount") override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder { - when (descriptor.kind) { - is StructureKind.LIST -> { - reader.readStartArray() - return BsonArrayDecoder(reader, serializersModule, configuration) - } - is PolymorphicKind -> { - reader.readStartDocument() - return PolymorphicDecoder(reader, serializersModule, configuration) - } + return when (descriptor.kind) { + is StructureKind.LIST -> BsonArrayDecoder(descriptor, reader, serializersModule, configuration) + is PolymorphicKind -> PolymorphicDecoder(descriptor, reader, serializersModule, configuration) is StructureKind.CLASS, - StructureKind.OBJECT -> { - val current = reader.currentBsonType - if (current == null || current == BsonType.DOCUMENT) { - reader.readStartDocument() - } - } - is StructureKind.MAP -> { - reader.readStartDocument() - return BsonDocumentDecoder(reader, serializersModule, configuration) - } + StructureKind.OBJECT -> BsonDocumentDecoder(descriptor, reader, serializersModule, configuration) + is StructureKind.MAP -> MapDecoder(descriptor, reader, serializersModule, configuration) else -> throw SerializationException("Primitives are not supported at top-level") } - return DefaultBsonDecoder(reader, serializersModule, configuration) } override fun endStructure(descriptor: SerialDescriptor) { @@ -194,10 +194,17 @@ internal open class DefaultBsonDecoder( @OptIn(ExperimentalSerializationApi::class) private class BsonArrayDecoder( + descriptor: SerialDescriptor, reader: AbstractBsonReader, serializersModule: SerializersModule, configuration: BsonConfiguration ) : DefaultBsonDecoder(reader, serializersModule, configuration) { + + init { + validateCurrentBsonType(reader, BsonType.ARRAY, descriptor) + reader.readStartArray() + } + private var index = 0 override fun decodeElementIndex(descriptor: SerialDescriptor): Int { val nextType = reader.readBsonType() @@ -208,18 +215,46 @@ private class BsonArrayDecoder( @OptIn(ExperimentalSerializationApi::class) private class PolymorphicDecoder( + descriptor: SerialDescriptor, reader: AbstractBsonReader, serializersModule: SerializersModule, configuration: BsonConfiguration ) : DefaultBsonDecoder(reader, serializersModule, configuration) { private var index = 0 + private var mark: BsonReaderMark? - override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = - deserializer.deserialize(DefaultBsonDecoder(reader, serializersModule, configuration)) + init { + mark = reader.mark + validateCurrentBsonType(reader, BsonType.DOCUMENT, descriptor) { it.serialName } + reader.readStartDocument() + } + + override fun decodeSerializableValue(deserializer: DeserializationStrategy): T { + mark?.let { + it.reset() + mark = null + } + return deserializer.deserialize(DefaultBsonDecoder(reader, serializersModule, configuration)) + } override fun decodeElementIndex(descriptor: SerialDescriptor): Int { + var found = false return when (index) { - 0 -> index++ + 0 -> { + while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { + if (reader.readName() == configuration.classDiscriminator) { + found = true + break + } + reader.skipValue() + } + if (!found) { + throw SerializationException( + "Missing required discriminator field `${configuration.classDiscriminator}` " + + "for polymorphic class: `${descriptor.serialName}`.") + } + index++ + } 1 -> index++ else -> DECODE_DONE } @@ -228,6 +263,20 @@ private class PolymorphicDecoder( @OptIn(ExperimentalSerializationApi::class) private class BsonDocumentDecoder( + descriptor: SerialDescriptor, + reader: AbstractBsonReader, + serializersModule: SerializersModule, + configuration: BsonConfiguration +) : DefaultBsonDecoder(reader, serializersModule, configuration) { + init { + validateCurrentBsonType(reader, BsonType.DOCUMENT, descriptor) { it.serialName } + reader.readStartDocument() + } +} + +@OptIn(ExperimentalSerializationApi::class) +private class MapDecoder( + descriptor: SerialDescriptor, reader: AbstractBsonReader, serializersModule: SerializersModule, configuration: BsonConfiguration @@ -236,6 +285,11 @@ private class BsonDocumentDecoder( private var index = 0 private var isKey = false + init { + validateCurrentBsonType(reader, BsonType.DOCUMENT, descriptor) + reader.readStartDocument() + } + override fun decodeString(): String { return if (isKey) { reader.readName() diff --git a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecProviderTest.kt b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecProviderTest.kt index 8d4fa304bc8..5a912e7bb3a 100644 --- a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecProviderTest.kt +++ b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecProviderTest.kt @@ -35,8 +35,12 @@ import org.bson.codecs.kotlinx.samples.DataClassOpen import org.bson.codecs.kotlinx.samples.DataClassOpenA import org.bson.codecs.kotlinx.samples.DataClassOpenB import org.bson.codecs.kotlinx.samples.DataClassParameterized +import org.bson.codecs.kotlinx.samples.DataClassSealedInterface import org.bson.codecs.kotlinx.samples.DataClassWithSimpleValues +import org.bson.codecs.kotlinx.samples.SealedInterface import org.bson.conversions.Bson +import org.bson.json.JsonReader +import org.bson.types.ObjectId import org.junit.jupiter.api.Test class KotlinSerializerCodecProviderTest { @@ -75,6 +79,41 @@ class KotlinSerializerCodecProviderTest { assertEquals(DataClassWithSimpleValues::class.java, codec.encoderClass) } + @Test + fun testDataClassWithSimpleValuesFieldOrdering() { + val codec = MongoClientSettings.getDefaultCodecRegistry().get(DataClassWithSimpleValues::class.java) + val expected = DataClassWithSimpleValues('c', 0, 1, 22, 42L, 4.0f, 4.2, true, "String") + + val numberLong = "\$numberLong" + val actual = + codec.decode( + JsonReader( + """{"boolean": true, "byte": 0, "char": "c", "double": 4.2, "float": 4.0, "int": 22, + |"long": {"$numberLong": "42"}, "short": 1, "string": "String"}""" + .trimMargin()), + DecoderContext.builder().build()) + + assertEquals(expected, actual) + } + + @Test + fun testDataClassSealedFieldOrdering() { + val codec = MongoClientSettings.getDefaultCodecRegistry().get(SealedInterface::class.java) + + val objectId = ObjectId("111111111111111111111111") + val oid = "\$oid" + val expected = DataClassSealedInterface(objectId, "string") + val actual = + codec.decode( + JsonReader( + """{"name": "string", "_id": {$oid: "${objectId.toHexString()}"}, + |"_t": "org.bson.codecs.kotlinx.samples.DataClassSealedInterface"}""" + .trimMargin()), + DecoderContext.builder().build()) + + assertEquals(expected, actual) + } + @OptIn(ExperimentalSerializationApi::class) @Test fun shouldAllowOverridingOfSerializersModuleAndBsonConfigurationInConstructor() { diff --git a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt index 146e897c59b..14fcfa8a01c 100644 --- a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt +++ b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt @@ -84,20 +84,23 @@ import org.bson.codecs.kotlinx.samples.DataClassWithSequence import org.bson.codecs.kotlinx.samples.DataClassWithSimpleValues import org.bson.codecs.kotlinx.samples.DataClassWithTriple import org.bson.codecs.kotlinx.samples.Key +import org.bson.codecs.kotlinx.samples.SealedInterface import org.bson.codecs.kotlinx.samples.ValueClass import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @OptIn(ExperimentalSerializationApi::class) +@Suppress("LargeClass") class KotlinSerializerCodecTest { private val numberLong = "\$numberLong" + private val oid = "\$oid" private val emptyDocument = "{}" private val altConfiguration = BsonConfiguration(encodeDefaults = false, classDiscriminator = "_t", explicitNulls = true) private val allBsonTypesJson = """{ - | "id": {"${'$'}oid": "111111111111111111111111"}, + | "id": {"$oid": "111111111111111111111111"}, | "arrayEmpty": [], | "arraySimple": [{"${'$'}numberInt": "1"}, {"${'$'}numberInt": "2"}, {"${'$'}numberInt": "3"}], | "arrayComplex": [{"a": {"${'$'}numberInt": "1"}}, {"a": {"${'$'}numberInt": "2"}}], @@ -668,17 +671,49 @@ class KotlinSerializerCodecTest { codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) } - assertThrows("Invalid complex types") { - val data = BsonDocument.parse("""{"_id": "myId", "embedded": 123}""") - val codec = KotlinSerializerCodec.create() - codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) - } - assertThrows("Failing init") { val data = BsonDocument.parse("""{"id": "myId"}""") val codec = KotlinSerializerCodec.create() codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) } + + var exception = + assertThrows("Invalid complex types - document") { + val data = BsonDocument.parse("""{"_id": "myId", "embedded": 123}""") + val codec = KotlinSerializerCodec.create() + codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) + } + assertEquals( + "Invalid data for `org.bson.codecs.kotlinx.samples.DataClassEmbedded` " + + "expected a bson document found: INT32", + exception.message) + + exception = + assertThrows("Invalid complex types - list") { + val data = BsonDocument.parse("""{"_id": "myId", "nested": 123}""") + val codec = KotlinSerializerCodec.create() + codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) + } + assertEquals("Invalid data for `LIST` expected a bson array found: INT32", exception.message) + + exception = + assertThrows("Invalid complex types - map") { + val data = BsonDocument.parse("""{"_id": "myId", "nested": 123}""") + val codec = KotlinSerializerCodec.create() + codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) + } + assertEquals("Invalid data for `MAP` expected a bson document found: INT32", exception.message) + + exception = + assertThrows("Missing discriminator") { + val data = BsonDocument.parse("""{"_id": {"$oid": "111111111111111111111111"}, "name": "string"}""") + val codec = KotlinSerializerCodec.create() + codec?.decode(BsonDocumentReader(data), DecoderContext.builder().build()) + } + assertEquals( + "Missing required discriminator field `_t` for polymorphic class: " + + "`org.bson.codecs.kotlinx.samples.SealedInterface`.", + exception.message) } @Test diff --git a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt index ea5e3fea3cd..0326827d4a7 100644 --- a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt +++ b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt @@ -245,6 +245,15 @@ data class DataClassOptionalBsonValues( @Serializable @SerialName("C") data class DataClassSealedC(val c: String) : DataClassSealed() +@Serializable +sealed interface SealedInterface { + val name: String +} + +@Serializable +data class DataClassSealedInterface(@Contextual @SerialName("_id") val id: ObjectId, override val name: String) : + SealedInterface + @Serializable data class DataClassListOfSealed(val items: List) interface DataClassOpen diff --git a/build.gradle b/build.gradle index 5af3afdf53e..b7233752fba 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,7 @@ configure(coreProjects) { apply plugin: 'idea' group = 'org.mongodb' - version = '5.0.0' + version = '5.0.1' repositories { mavenLocal() diff --git a/driver-core/src/main/com/mongodb/internal/bulk/DeleteRequest.java b/driver-core/src/main/com/mongodb/internal/bulk/DeleteRequest.java index 587b24293f1..bcbf43142c1 100644 --- a/driver-core/src/main/com/mongodb/internal/bulk/DeleteRequest.java +++ b/driver-core/src/main/com/mongodb/internal/bulk/DeleteRequest.java @@ -19,7 +19,6 @@ import com.mongodb.client.model.Collation; import com.mongodb.lang.Nullable; import org.bson.BsonDocument; -import org.bson.conversions.Bson; import static com.mongodb.assertions.Assertions.notNull; @@ -32,7 +31,7 @@ public final class DeleteRequest extends WriteRequest { private final BsonDocument filter; private boolean isMulti = true; private Collation collation; - private Bson hint; + private BsonDocument hint; private String hintString; public DeleteRequest(final BsonDocument filter) { @@ -63,11 +62,11 @@ public DeleteRequest collation(@Nullable final Collation collation) { } @Nullable - public Bson getHint() { + public BsonDocument getHint() { return hint; } - public DeleteRequest hint(@Nullable final Bson hint) { + public DeleteRequest hint(@Nullable final BsonDocument hint) { this.hint = hint; return this; } diff --git a/driver-core/src/main/com/mongodb/internal/bulk/UpdateRequest.java b/driver-core/src/main/com/mongodb/internal/bulk/UpdateRequest.java index 454d0b3ba0d..5a7df089641 100644 --- a/driver-core/src/main/com/mongodb/internal/bulk/UpdateRequest.java +++ b/driver-core/src/main/com/mongodb/internal/bulk/UpdateRequest.java @@ -20,7 +20,6 @@ import com.mongodb.lang.Nullable; import org.bson.BsonDocument; import org.bson.BsonValue; -import org.bson.conversions.Bson; import java.util.List; @@ -39,7 +38,7 @@ public final class UpdateRequest extends WriteRequest { private boolean isUpsert = false; private Collation collation; private List arrayFilters; - @Nullable private Bson hint; + @Nullable private BsonDocument hint; @Nullable private String hintString; public UpdateRequest(final BsonDocument filter, @Nullable final BsonValue update, final Type updateType) { @@ -111,11 +110,11 @@ public List getArrayFilters() { } @Nullable - public Bson getHint() { + public BsonDocument getHint() { return hint; } - public UpdateRequest hint(@Nullable final Bson hint) { + public UpdateRequest hint(@Nullable final BsonDocument hint) { this.hint = hint; return this; } diff --git a/driver-core/src/main/com/mongodb/internal/connection/ServerAddressHelper.java b/driver-core/src/main/com/mongodb/internal/connection/ServerAddressHelper.java index de004b748ab..5230c4d7c18 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/ServerAddressHelper.java +++ b/driver-core/src/main/com/mongodb/internal/connection/ServerAddressHelper.java @@ -21,17 +21,27 @@ import com.mongodb.MongoSocketException; import com.mongodb.ServerAddress; import com.mongodb.UnixServerAddress; +import com.mongodb.lang.Nullable; import com.mongodb.spi.dns.InetAddressResolver; +import com.mongodb.spi.dns.InetAddressResolverProvider; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.List; +import java.util.ServiceLoader; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; /** *

This class is not part of the public API and may be removed or changed at any time

*/ public final class ServerAddressHelper { + @Nullable + private static final InetAddressResolver LOCATED_INET_ADDRESS_RESOLVER = StreamSupport.stream( + ServiceLoader.load(InetAddressResolverProvider.class).spliterator(), false) + .findFirst() + .map(InetAddressResolverProvider::create) + .orElse(null); public static ServerAddress createServerAddress(final String host) { return createServerAddress(host, ServerAddress.defaultPort()); @@ -46,8 +56,14 @@ public static ServerAddress createServerAddress(final String host, final int por } public static InetAddressResolver getInetAddressResolver(final MongoClientSettings settings) { - InetAddressResolver inetAddressResolver = settings.getInetAddressResolver(); - return inetAddressResolver == null ? new DefaultInetAddressResolver() : inetAddressResolver; + InetAddressResolver explicitInetAddressResolver = settings.getInetAddressResolver(); + if (explicitInetAddressResolver != null) { + return explicitInetAddressResolver; + } else if (LOCATED_INET_ADDRESS_RESOLVER != null) { + return LOCATED_INET_ADDRESS_RESOLVER; + } else { + return new DefaultInetAddressResolver(); + } } public static List getSocketAddresses(final ServerAddress serverAddress, final InetAddressResolver resolver) { diff --git a/driver-core/src/main/com/mongodb/internal/connection/SplittablePayload.java b/driver-core/src/main/com/mongodb/internal/connection/SplittablePayload.java index 50300ceeac0..a71f7a940f0 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/SplittablePayload.java +++ b/driver-core/src/main/com/mongodb/internal/connection/SplittablePayload.java @@ -246,8 +246,8 @@ public void encode(final BsonWriter writer, final WriteRequestWithIndex writeReq } if (update.getHint() != null) { writer.writeName("hint"); - BsonDocument hint = assertNotNull(update.getHint()).toBsonDocument(BsonDocument.class, null); - getCodec(hint).encode(writer, hint, EncoderContext.builder().build()); + getCodec(assertNotNull(update.getHint())).encode(writer, assertNotNull(update.getHint()), + EncoderContext.builder().build()); } else if (update.getHintString() != null) { writer.writeString("hint", update.getHintString()); } @@ -265,7 +265,7 @@ public void encode(final BsonWriter writer, final WriteRequestWithIndex writeReq } if (deleteRequest.getHint() != null) { writer.writeName("hint"); - BsonDocument hint = assertNotNull(deleteRequest.getHint()).toBsonDocument(BsonDocument.class, null); + BsonDocument hint = assertNotNull(deleteRequest.getHint()); getCodec(hint).encode(writer, hint, EncoderContext.builder().build()); } else if (deleteRequest.getHintString() != null) { writer.writeString("hint", deleteRequest.getHintString()); diff --git a/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java b/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java index e3ae79fa589..5179d3096b3 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java @@ -31,7 +31,6 @@ import org.bson.BsonValue; import org.bson.FieldNameValidator; import org.bson.codecs.Decoder; -import org.bson.conversions.Bson; import java.util.concurrent.TimeUnit; @@ -62,7 +61,7 @@ public abstract class BaseFindAndModifyOperation implements AsyncWriteOperati private BsonDocument sort; private long maxTimeMS; private Collation collation; - private Bson hint; + private BsonDocument hint; private String hintString; private BsonValue comment; private BsonDocument variables; @@ -151,11 +150,11 @@ public Collation getCollation() { } @Nullable - public Bson getHint() { + public BsonDocument getHint() { return hint; } - public BaseFindAndModifyOperation hint(@Nullable final Bson hint) { + public BaseFindAndModifyOperation hint(@Nullable final BsonDocument hint) { this.hint = hint; return this; } @@ -216,7 +215,7 @@ private CommandCreator getCommandCreator(final SessionContext sessionContext) { if (getHint() != null || getHintString() != null) { validateHintForFindAndModify(connectionDescription, getWriteConcern()); if (getHint() != null) { - commandDocument.put("hint", getHint().toBsonDocument(BsonDocument.class, null)); + commandDocument.put("hint", getHint()); } else { commandDocument.put("hint", new BsonString(getHintString())); } diff --git a/driver-core/src/main/com/mongodb/internal/operation/FindAndDeleteOperation.java b/driver-core/src/main/com/mongodb/internal/operation/FindAndDeleteOperation.java index ede7ee51628..928173ba2fb 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/FindAndDeleteOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/FindAndDeleteOperation.java @@ -27,7 +27,6 @@ import org.bson.BsonValue; import org.bson.FieldNameValidator; import org.bson.codecs.Decoder; -import org.bson.conversions.Bson; import java.util.concurrent.TimeUnit; @@ -68,7 +67,7 @@ public FindAndDeleteOperation sort(@Nullable final BsonDocument sort) { } @Override - public FindAndDeleteOperation hint(@Nullable final Bson hint) { + public FindAndDeleteOperation hint(@Nullable final BsonDocument hint) { super.hint(hint); return this; } diff --git a/driver-core/src/main/com/mongodb/internal/operation/FindAndReplaceOperation.java b/driver-core/src/main/com/mongodb/internal/operation/FindAndReplaceOperation.java index de988c963a4..303d9c0e208 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/FindAndReplaceOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/FindAndReplaceOperation.java @@ -29,7 +29,6 @@ import org.bson.BsonValue; import org.bson.FieldNameValidator; import org.bson.codecs.Decoder; -import org.bson.conversions.Bson; import java.util.HashMap; import java.util.Map; @@ -111,7 +110,7 @@ public FindAndReplaceOperation sort(@Nullable final BsonDocument sort) { } @Override - public FindAndReplaceOperation hint(@Nullable final Bson hint) { + public FindAndReplaceOperation hint(@Nullable final BsonDocument hint) { super.hint(hint); return this; } diff --git a/driver-core/src/main/com/mongodb/internal/operation/FindAndUpdateOperation.java b/driver-core/src/main/com/mongodb/internal/operation/FindAndUpdateOperation.java index 17ce879102d..2c2a00ff437 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/FindAndUpdateOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/FindAndUpdateOperation.java @@ -30,7 +30,6 @@ import org.bson.BsonValue; import org.bson.FieldNameValidator; import org.bson.codecs.Decoder; -import org.bson.conversions.Bson; import java.util.HashMap; import java.util.List; @@ -139,7 +138,7 @@ public FindAndUpdateOperation sort(@Nullable final BsonDocument sort) { } @Override - public FindAndUpdateOperation hint(@Nullable final Bson hint) { + public FindAndUpdateOperation hint(@Nullable final BsonDocument hint) { super.hint(hint); return this; } diff --git a/driver-core/src/main/com/mongodb/internal/operation/Operations.java b/driver-core/src/main/com/mongodb/internal/operation/Operations.java index b6765b7cc36..b32beda747b 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/Operations.java +++ b/driver-core/src/main/com/mongodb/internal/operation/Operations.java @@ -322,7 +322,7 @@ FindAndDeleteOperation findOneAndDelete(final Bson filter, final Find .sort(toBsonDocument(options.getSort())) .maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS) .collation(options.getCollation()) - .hint(options.getHint()) + .hint(toBsonDocument(options.getHint())) .hintString(options.getHintString()) .comment(options.getComment()) .let(toBsonDocument(options.getLet())); @@ -340,7 +340,7 @@ FindAndReplaceOperation findOneAndReplace(final Bson filter, final TD .maxTime(options.getMaxTime(MILLISECONDS), MILLISECONDS) .bypassDocumentValidation(options.getBypassDocumentValidation()) .collation(options.getCollation()) - .hint(options.getHint()) + .hint(toBsonDocument(options.getHint())) .hintString(options.getHintString()) .comment(options.getComment()) .let(toBsonDocument(options.getLet())); @@ -358,7 +358,7 @@ FindAndUpdateOperation findOneAndUpdate(final Bson filter, final Bson .bypassDocumentValidation(options.getBypassDocumentValidation()) .collation(options.getCollation()) .arrayFilters(toBsonDocumentList(options.getArrayFilters())) - .hint(options.getHint()) + .hint(toBsonDocument(options.getHint())) .hintString(options.getHintString()) .comment(options.getComment()) .let(toBsonDocument(options.getLet())); @@ -377,7 +377,7 @@ FindAndUpdateOperation findOneAndUpdate(final Bson filter, final List .bypassDocumentValidation(options.getBypassDocumentValidation()) .collation(options.getCollation()) .arrayFilters(toBsonDocumentList(options.getArrayFilters())) - .hint(options.getHint()) + .hint(toBsonDocument(options.getHint())) .hintString(options.getHintString()) .comment(options.getComment()) .let(toBsonDocument(options.getLet())); @@ -470,7 +470,7 @@ MixedBulkWriteOperation bulkWrite(final List updateOneModel = (UpdateOneModel) writeModel; @@ -481,7 +481,7 @@ MixedBulkWriteOperation bulkWrite(final List updateManyModel = (UpdateManyModel) writeModel; @@ -492,19 +492,19 @@ MixedBulkWriteOperation bulkWrite(final List deleteOneModel = (DeleteOneModel) writeModel; writeRequest = new DeleteRequest(assertNotNull(toBsonDocument(deleteOneModel.getFilter()))).multi(false) .collation(deleteOneModel.getOptions().getCollation()) - .hint(deleteOneModel.getOptions().getHint()) + .hint(toBsonDocument(deleteOneModel.getOptions().getHint())) .hintString(deleteOneModel.getOptions().getHintString()); } else if (writeModel instanceof DeleteManyModel) { DeleteManyModel deleteManyModel = (DeleteManyModel) writeModel; writeRequest = new DeleteRequest(assertNotNull(toBsonDocument(deleteManyModel.getFilter()))).multi(true) .collation(deleteManyModel.getOptions().getCollation()) - .hint(deleteManyModel.getOptions().getHint()) + .hint(toBsonDocument(deleteManyModel.getOptions().getHint())) .hintString(deleteManyModel.getOptions().getHintString()); } else { throw new UnsupportedOperationException(format("WriteModel of type %s is not supported", writeModel.getClass())); diff --git a/driver-core/src/main/resources/META-INF/native-image/org.mongodb/bson/native-image.properties b/driver-core/src/main/resources/META-INF/native-image/org.mongodb/bson/native-image.properties index 20f30cb0bd2..74579722773 100644 --- a/driver-core/src/main/resources/META-INF/native-image/org.mongodb/bson/native-image.properties +++ b/driver-core/src/main/resources/META-INF/native-image/org.mongodb/bson/native-image.properties @@ -13,4 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # -Args = --initialize-at-run-time=com.mongodb.UnixServerAddress,com.mongodb.internal.connection.SnappyCompressor,org.bson.types.ObjectId,com.mongodb.internal.connection.ClientMetadataHelper +Args =\ + --initialize-at-run-time=\ + com.mongodb.UnixServerAddress,\ + com.mongodb.internal.connection.SnappyCompressor,\ + com.mongodb.internal.connection.ClientMetadataHelper,\ + com.mongodb.internal.connection.ServerAddressHelper diff --git a/driver-core/src/main/resources/META-INF/native-image/resource-config.json b/driver-core/src/main/resources/META-INF/native-image/resource-config.json new file mode 100644 index 00000000000..8c008c9938d --- /dev/null +++ b/driver-core/src/main/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,7 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\QMETA-INF/services/com.mongodb.spi.dns.InetAddressResolverProvider\\E" + }]}, + "bundles":[] +} diff --git a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java index fe76ef68668..934c83f113b 100644 --- a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java +++ b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java @@ -198,10 +198,11 @@ public static List getVersionList(final String versionString) { } public static boolean hasEncryptionTestsEnabled() { - List requiredSystemProperties = asList("awsAccessKeyId", "awsSecretAccessKey", "azureTenantId", "azureClientId", - "azureClientSecret", "gcpEmail", "gcpPrivateKey", "tmpAwsAccessKeyId", "tmpAwsSecretAccessKey", "tmpAwsSessionToken"); + List requiredSystemProperties = asList("AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AZURE_TENANT_ID", "AZURE_CLIENT_ID", + "AZURE_CLIENT_SECRET", "GCP_EMAIL", "GCP_PRIVATE_KEY", "AWS_TEMP_ACCESS_KEY_ID", "AWS_TEMP_SECRET_ACCESS_KEY", + "AWS_TEMP_SESSION_TOKEN"); return requiredSystemProperties.stream() - .map(name -> getEnv("org.mongodb.test." + name, "")) + .map(name -> getEnv(name, "")) .filter(s -> !s.isEmpty()) .count() == requiredSystemProperties.size(); } diff --git a/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java b/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java index 9bf435d013e..0e58ef638b7 100644 --- a/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java +++ b/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java @@ -109,6 +109,11 @@ public static void dropDatabase(final String name, final WriteConcern writeConce } } + public static BsonDocument getCurrentClusterTime() { + return new CommandReadOperation("admin", new BsonDocument("ping", new BsonInt32(1)), new BsonDocumentCodec()) + .execute(getBinding()).getDocument("$clusterTime", null); + } + public MongoNamespace getNamespace() { return namespace; } diff --git a/driver-core/src/test/functional/com/mongodb/internal/operation/ChangeStreamOperationSpecification.groovy b/driver-core/src/test/functional/com/mongodb/internal/operation/ChangeStreamOperationSpecification.groovy index 2325061b25b..129289bfbba 100644 --- a/driver-core/src/test/functional/com/mongodb/internal/operation/ChangeStreamOperationSpecification.groovy +++ b/driver-core/src/test/functional/com/mongodb/internal/operation/ChangeStreamOperationSpecification.groovy @@ -519,7 +519,13 @@ class ChangeStreamOperationSpecification extends OperationFunctionalSpecificatio def cursor = execute(operation, async) when: - def expected = insertDocuments(helper, [1, 2]) + // split into two insert commands, because starting in MongoDB 8.0 the same clusterTime is applied to all documents in a bulk + // write operation, and the test relies on the clusterTime values being both ascending _and_ unique. + def expectedOne = insertDocuments(helper, [1]) + def expectedTwo = insertDocuments(helper, [2]) + def expected = [] + expected.addAll(expectedOne) + expected.addAll(expectedTwo) def result = next(cursor, async, 2) then: diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Aggregate.json index dea821bd1e8..9eaabe0d71a 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Aggregate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Correctness.json index 9e4f5258776..fa887e08928 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Correctness.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Delete.json index 7f4094f50cc..cce4faf1887 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Delete.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-FindOneAndUpdate.json index 5ec0601603d..4392b676860 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-FindOneAndUpdate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-InsertFind.json index efce1511c06..27ce7881df1 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-InsertFind.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Update.json index 7f9fadcda45..f7d5a6af665 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Date-Update.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Aggregate.json index fb129392b18..401ee34e3f2 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Aggregate.json @@ -2,10 +2,10 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Correctness.json index 5120aecb7a0..758d3e57325 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Correctness.json @@ -2,10 +2,10 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Delete.json index de81159b43e..24a08f318ce 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Delete.json @@ -2,10 +2,10 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-FindOneAndUpdate.json index 36cf91c88c2..2a8070ecf9d 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-FindOneAndUpdate.json @@ -2,10 +2,10 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-InsertFind.json index 6b5a642aa82..2ef63f42b99 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-InsertFind.json @@ -2,10 +2,10 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Update.json index 8cfb7b525b6..8064eb1b189 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Decimal-Update.json @@ -2,10 +2,10 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Aggregate.json index 801beefe180..8cf143c0945 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Aggregate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Correctness.json index b8a6953611d..a4b06998f7e 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Correctness.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Delete.json index 1abb59bfd19..fad8234838a 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Delete.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json index 8d763431fac..fb8f4f4140d 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-InsertFind.json index 5407fba18b6..79562802e6f 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-InsertFind.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Update.json index e5d1a4e0592..cc93b76948c 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DecimalPrecision-Update.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Aggregate.json index d8c9cacdccf..79f26660f24 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Aggregate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Correctness.json index 65594bcb110..117e56af620 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Correctness.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Delete.json index 392e722f1f0..40d8ed5bb2e 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Delete.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-FindOneAndUpdate.json index bbcfb321f50..f0893ce6612 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-FindOneAndUpdate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-InsertFind.json index 9f2c7c99115..d3dc2f830c0 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-InsertFind.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Update.json index ce03576f883..9d6a1fbfdd1 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Double-Update.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Aggregate.json index b121c72f149..4188685a2c0 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Aggregate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Correctness.json index 6b42ecfe82a..60f1ea7a333 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Correctness.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Delete.json index a5c397d0be4..4ed591d3f88 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Delete.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json index b6df9463e8e..d8fbbfae73b 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-FindOneAndUpdate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-InsertFind.json index 1cea25545ba..4213b066d1c 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-InsertFind.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Update.json index 7703c9057d2..89eb4c338d7 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-DoublePrecision-Update.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Aggregate.json index 9c2536264d8..686f0241bae 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Aggregate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Correctness.json index 58ccf3efc89..2964624f22b 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Correctness.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Delete.json index b20b2750bbb..531b3e7590c 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Delete.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-FindOneAndUpdate.json index f9c189ace90..402086cdb6b 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-FindOneAndUpdate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-InsertFind.json index 874d4760c86..965b8a55163 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-InsertFind.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Update.json index c2b62b4d1c5..6cf44ac782d 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Int-Update.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Aggregate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Aggregate.json index afc0f97be19..6edb38a800c 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Aggregate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Aggregate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Correctness.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Correctness.json index cda941de8a7..3d33f7381bb 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Correctness.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Correctness.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Delete.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Delete.json index ad344e21b48..1b327820108 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Delete.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Delete.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-FindOneAndUpdate.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-FindOneAndUpdate.json index d4472004681..b8e3b888a8e 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-FindOneAndUpdate.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-FindOneAndUpdate.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-InsertFind.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-InsertFind.json index 4eb837f28bc..d637fcf9e73 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-InsertFind.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-InsertFind.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Update.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Update.json index 3ba7f17c14b..1b76019a4cf 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Update.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-Long-Update.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-WrongType.json b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-WrongType.json index e5e9ddc8212..704a693b8fd 100644 --- a/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-WrongType.json +++ b/driver-core/src/test/resources/client-side-encryption/legacy/fle2v2-Range-WrongType.json @@ -2,12 +2,12 @@ "runOn": [ { "minServerVersion": "7.0.0", - "serverless": "forbid", "topology": [ "replicaset", "sharded", "load-balanced" - ] + ], + "maxServerVersion": "7.99.99" } ], "database_name": "default", diff --git a/driver-core/src/test/unit/com/mongodb/connection/ServerAddressHelperTest.java b/driver-core/src/test/unit/com/mongodb/connection/ServerAddressHelperTest.java new file mode 100644 index 00000000000..51e5996eb6f --- /dev/null +++ b/driver-core/src/test/unit/com/mongodb/connection/ServerAddressHelperTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.connection; + +import com.mongodb.MongoClientSettings; +import com.mongodb.internal.connection.DefaultInetAddressResolver; +import com.mongodb.internal.connection.ServerAddressHelper; +import com.mongodb.spi.dns.InetAddressResolver; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +final class ServerAddressHelperTest { + @Test + void getInetAddressResolver() { + assertAll( + () -> assertEquals( + DefaultInetAddressResolver.class, + ServerAddressHelper.getInetAddressResolver(MongoClientSettings.builder().build()).getClass()), + () -> { + InetAddressResolver resolver = new DefaultInetAddressResolver(); + assertSame( + resolver, + ServerAddressHelper.getInetAddressResolver(MongoClientSettings.builder().inetAddressResolver(resolver).build())); + } + ); + } +} diff --git a/driver-kotlin-coroutine/build.gradle.kts b/driver-kotlin-coroutine/build.gradle.kts index 1467c832abe..abd81fcd1d3 100644 --- a/driver-kotlin-coroutine/build.gradle.kts +++ b/driver-kotlin-coroutine/build.gradle.kts @@ -75,6 +75,7 @@ dependencies { testImplementation("io.github.classgraph:classgraph:4.8.154") integrationTestImplementation("org.jetbrains.kotlin:kotlin-test-junit") + integrationTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test") integrationTestImplementation(project(path = ":driver-sync")) integrationTestImplementation(project(path = ":driver-core")) integrationTestImplementation(project(path = ":bson")) diff --git a/driver-kotlin-coroutine/src/integration/kotlin/com/mongodb/kotlin/client/coroutine/SmokeTests.kt b/driver-kotlin-coroutine/src/integration/kotlin/com/mongodb/kotlin/client/coroutine/SmokeTests.kt new file mode 100644 index 00000000000..db51912d17c --- /dev/null +++ b/driver-kotlin-coroutine/src/integration/kotlin/com/mongodb/kotlin/client/coroutine/SmokeTests.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.kotlin.client.coroutine + +import com.mongodb.client.Fixture.getDefaultDatabaseName +import com.mongodb.client.Fixture.getMongoClientSettings +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.flow.toSet +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.bson.Document +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class SmokeTests { + + @AfterEach + fun afterEach() { + runBlocking { database?.drop() } + } + + @Test + @DisplayName("distinct and return nulls") + fun testDistinctNullable() = runTest { + collection!!.insertMany( + listOf( + Document.parse("{_id: 1, a: 0}"), + Document.parse("{_id: 2, a: 1}"), + Document.parse("{_id: 3, a: 0}"), + Document.parse("{_id: 4, a: null}"))) + + // nulls are auto excluded in reactive streams! + val actual = collection!!.distinct("a").toSet() + assertEquals(setOf(0, 1), actual) + } + + @Test + @DisplayName("mapping can return nulls") + fun testMongoIterableMap() = runTest { + collection!!.insertMany( + listOf( + Document.parse("{_id: 1, a: 0}"), + Document.parse("{_id: 2, a: 1}"), + Document.parse("{_id: 3, a: 0}"), + Document.parse("{_id: 4, a: null}"))) + + val actual = collection!!.find().map { it["a"] as Int? }.toList() + assertContentEquals(listOf(0, 1, 0, null), actual) + } + + companion object { + + private var mongoClient: MongoClient? = null + private var database: MongoDatabase? = null + private var collection: MongoCollection? = null + + @BeforeAll + @JvmStatic + internal fun beforeAll() { + runBlocking { + mongoClient = MongoClient.create(getMongoClientSettings()) + database = mongoClient?.getDatabase(getDefaultDatabaseName()) + database?.drop() + collection = database?.getCollection("SmokeTests") + } + } + + @AfterAll + @JvmStatic + internal fun afterAll() { + runBlocking { + collection = null + database?.drop() + database = null + mongoClient?.close() + mongoClient = null + } + } + } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/SmokeTests.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/SmokeTests.kt new file mode 100644 index 00000000000..3fc601d6425 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/SmokeTests.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.Fixture.getDefaultDatabaseName +import com.mongodb.client.Fixture.getMongoClientSettings +import kotlin.test.assertContentEquals +import org.bson.Document +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class SmokeTests { + + @AfterEach + fun afterEach() { + database?.drop() + } + + @Test + @DisplayName("distinct and return nulls") + fun testDistinctNullable() { + collection!!.insertMany( + listOf( + Document.parse("{_id: 1, a: 0}"), + Document.parse("{_id: 2, a: 1}"), + Document.parse("{_id: 3, a: 0}"), + Document.parse("{_id: 4, a: null}"))) + + val actual = collection!!.distinct("a").toList().toSet() + assertEquals(setOf(null, 0, 1), actual) + } + + @Test + @DisplayName("mapping can return nulls") + fun testMongoIterableMap() { + collection!!.insertMany( + listOf( + Document.parse("{_id: 1, a: 0}"), + Document.parse("{_id: 2, a: 1}"), + Document.parse("{_id: 3, a: 0}"), + Document.parse("{_id: 4, a: null}"))) + + val actual = collection!!.find().map { it["a"] }.toList() + assertContentEquals(listOf(0, 1, 0, null), actual) + } + + companion object { + + private var mongoClient: MongoClient? = null + private var database: MongoDatabase? = null + private var collection: MongoCollection? = null + + @BeforeAll + @JvmStatic + internal fun beforeAll() { + mongoClient = MongoClient.create(getMongoClientSettings()) + database = mongoClient?.getDatabase(getDefaultDatabaseName()) + database?.drop() + collection = database?.getCollection("SmokeTests") + } + + @AfterAll + @JvmStatic + internal fun afterAll() { + collection = null + database?.drop() + database = null + mongoClient?.close() + mongoClient = null + } + } +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt index b630af52517..de77215d033 100644 --- a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt @@ -27,7 +27,7 @@ import org.bson.conversions.Bson * @param T The type of the result. * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) */ -public class DistinctIterable(private val wrapped: JDistinctIterable) : MongoIterable(wrapped) { +public class DistinctIterable(private val wrapped: JDistinctIterable) : MongoIterable(wrapped) { /** * Sets the number of documents to return per batch. * diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt index 1529af7eaba..786140caf12 100644 --- a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt @@ -219,7 +219,7 @@ public class MongoCollection(private val wrapped: JMongoCollection) * @return an iterable of distinct values * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) */ - public fun distinct( + public fun distinct( fieldName: String, filter: Bson = BsonDocument(), resultClass: Class @@ -236,7 +236,7 @@ public class MongoCollection(private val wrapped: JMongoCollection) * @return an iterable of distinct values * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) */ - public fun distinct( + public fun distinct( clientSession: ClientSession, fieldName: String, filter: Bson = BsonDocument(), @@ -252,7 +252,7 @@ public class MongoCollection(private val wrapped: JMongoCollection) * @return an iterable of distinct values * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) */ - public inline fun distinct( + public inline fun distinct( fieldName: String, filter: Bson = BsonDocument() ): DistinctIterable = distinct(fieldName, filter, R::class.java) @@ -267,7 +267,7 @@ public class MongoCollection(private val wrapped: JMongoCollection) * @return an iterable of distinct values * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) */ - public inline fun distinct( + public inline fun distinct( clientSession: ClientSession, fieldName: String, filter: Bson = BsonDocument() diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt index 5c757bf5e65..b407195b079 100644 --- a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt @@ -36,7 +36,7 @@ import org.bson.BsonDocument * * @param T The type of documents the cursor contains */ -public sealed interface MongoCursor : Iterator, Closeable { +public sealed interface MongoCursor : Iterator, Closeable { /** * Gets the number of results available locally without blocking, which may be 0. @@ -90,7 +90,7 @@ public sealed interface MongoChangeStreamCursor : MongoCursor { public val resumeToken: BsonDocument? } -internal class MongoCursorImpl(private val wrapped: JMongoCursor) : MongoCursor { +internal class MongoCursorImpl(private val wrapped: JMongoCursor) : MongoCursor { override fun hasNext(): Boolean = wrapped.hasNext() diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt index fffcea2ce76..b3c37d05d43 100644 --- a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt @@ -23,7 +23,7 @@ import com.mongodb.client.MongoIterable as JMongoIterable * * @param T The type that this iterable will decode documents to. */ -public open class MongoIterable(private val delegate: JMongoIterable) { +public open class MongoIterable(private val delegate: JMongoIterable) { /** * Returns a cursor used for iterating over elements of type `T. The cursor is primarily used for change streams. @@ -71,7 +71,7 @@ public open class MongoIterable(private val delegate: JMongoIterable * @param transform a function that maps from the source to the target document type * @return an iterable which maps T to U */ - public fun map(transform: (T) -> R): MongoIterable = MongoIterable(delegate.map(transform)) + public fun map(transform: (T) -> R): MongoIterable = MongoIterable(delegate.map(transform)) /** Performs the given [action] on each element and safely closes the cursor. */ public fun forEach(action: (T) -> Unit): Unit = use { it.forEach(action) } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionRangeExplicitEncryptionTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionRangeExplicitEncryptionTest.java index efd630fabbb..6bb5d1d5120 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionRangeExplicitEncryptionTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionRangeExplicitEncryptionTest.java @@ -60,6 +60,7 @@ import static com.mongodb.ClusterFixture.isServerlessTest; import static com.mongodb.ClusterFixture.isStandalone; import static com.mongodb.ClusterFixture.serverVersionAtLeast; +import static com.mongodb.ClusterFixture.serverVersionLessThan; import static com.mongodb.client.Fixture.getDefaultDatabase; import static com.mongodb.client.Fixture.getDefaultDatabaseName; import static com.mongodb.client.Fixture.getMongoClient; @@ -91,6 +92,7 @@ public abstract class AbstractClientSideEncryptionRangeExplicitEncryptionTest { @BeforeEach public void setUp(final Type type) { + assumeTrue(serverVersionLessThan(8, 0)); assumeTrue(serverVersionAtLeast(6, 2)); assumeFalse(isStandalone()); assumeFalse(isServerlessTest()); diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index ba1ed53cd83..87b6bf0c145 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -23,8 +23,6 @@ import com.mongodb.ReadConcernLevel; import com.mongodb.ServerApi; import com.mongodb.ServerApiVersion; -import com.mongodb.internal.connection.TestClusterListener; -import com.mongodb.logging.TestLoggingInterceptor; import com.mongodb.TransactionOptions; import com.mongodb.WriteConcern; import com.mongodb.assertions.Assertions; @@ -56,11 +54,13 @@ import com.mongodb.event.ConnectionPoolListener; import com.mongodb.event.ConnectionPoolReadyEvent; import com.mongodb.event.ConnectionReadyEvent; +import com.mongodb.internal.connection.TestClusterListener; import com.mongodb.internal.connection.TestCommandListener; import com.mongodb.internal.connection.TestConnectionPoolListener; import com.mongodb.internal.connection.TestServerListener; import com.mongodb.internal.logging.LogMessage; import com.mongodb.lang.NonNull; +import com.mongodb.logging.TestLoggingInterceptor; import org.bson.BsonArray; import org.bson.BsonBoolean; import org.bson.BsonDocument; @@ -300,6 +300,7 @@ private void putEntity(final String id, final T entity, final Map } public void init(final BsonArray entitiesArray, + final BsonDocument startingClusterTime, final boolean waitForPoolAsyncWorkManagerStart, final Function mongoClientSupplier, final Function gridFSBucketSupplier, @@ -324,7 +325,7 @@ public void init(final BsonArray entitiesArray, break; } case "session": { - initSession(entity, id); + initSession(entity, id, startingClusterTime); break; } case "bucket": { @@ -596,7 +597,7 @@ private void initCollection(final BsonDocument entity, final String id) { putEntity(id, collection, collections); } - private void initSession(final BsonDocument entity, final String id) { + private void initSession(final BsonDocument entity, final String id, final BsonDocument startingClusterTime) { MongoClient client = clients.get(entity.getString("client").getValue()); ClientSessionOptions.Builder optionsBuilder = ClientSessionOptions.builder(); if (entity.containsKey("sessionOptions")) { @@ -614,6 +615,7 @@ private void initSession(final BsonDocument entity, final String id) { } } ClientSession session = client.startSession(optionsBuilder.build()); + session.advanceClusterTime(startingClusterTime); putEntity(id, session, sessions); putEntity(id + "-identifier", session.getServerSession().getIdentifier(), sessionIdentifiers); } diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java index 8b76f426dbc..fb99a28621d 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java @@ -73,6 +73,7 @@ import static com.mongodb.ClusterFixture.getServerVersion; import static com.mongodb.client.Fixture.getMongoClient; import static com.mongodb.client.Fixture.getMongoClientSettings; +import static com.mongodb.client.test.CollectionHelper.getCurrentClusterTime; import static com.mongodb.client.unified.RunOnRequirementsMatcher.runOnRequirementsMet; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; @@ -105,6 +106,7 @@ public abstract class UnifiedTest { private final UnifiedClientEncryptionHelper clientEncryptionHelper = new UnifiedClientEncryptionHelper(entities); private final List failPoints = new ArrayList<>(); private final UnifiedTestContext rootContext = new UnifiedTestContext(); + private BsonDocument startingClusterTime; private class UnifiedTestContext { private final AssertionContext context = new AssertionContext(); @@ -212,12 +214,12 @@ public void setUp() { if (definition.containsKey("skipReason")) { throw new AssumptionViolatedException(definition.getString("skipReason").getValue()); } - entities.init(entitiesArray, + startingClusterTime = addInitialDataAndGetClusterTime(); + entities.init(entitiesArray, startingClusterTime, fileDescription != null && PRESTART_POOL_ASYNC_WORK_MANAGER_FILE_DESCRIPTIONS.contains(fileDescription), this::createMongoClient, this::createGridFSBucket, this::createClientEncryption); - addInitialData(); } @After @@ -561,6 +563,7 @@ protected boolean terminateLoop() { private OperationResult executeCreateEntities(final BsonDocument operation) { entities.init(operation.getDocument("arguments").getArray("entities"), + startingClusterTime, false, this::createMongoClient, this::createGridFSBucket, @@ -890,7 +893,7 @@ private List lastTwoCommandEvents(final TestCommandListener listen return events.subList(events.size() - 2, events.size()); } - private void addInitialData() { + private BsonDocument addInitialDataAndGetClusterTime() { for (BsonValue cur : initialData.getValues()) { BsonDocument curDataSet = cur.asDocument(); CollectionHelper helper = new CollectionHelper<>(new BsonDocumentCodec(), @@ -905,5 +908,6 @@ private void addInitialData() { WriteConcern.MAJORITY); } } + return getCurrentClusterTime(); } }