From f710063d317dced704d120a8675707924f3eb3c2 Mon Sep 17 00:00:00 2001 From: BorisDog Date: Fri, 28 Jun 2024 09:15:51 -0700 Subject: [PATCH 01/21] Update Release Notes v2.25.0.md --- Release Notes/Release Notes v2.25.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release Notes/Release Notes v2.25.0.md b/Release Notes/Release Notes v2.25.0.md index ddc3b28ef61..d172ba58fe4 100644 --- a/Release Notes/Release Notes v2.25.0.md +++ b/Release Notes/Release Notes v2.25.0.md @@ -17,4 +17,4 @@ The main new features in 2.25.0 include: The full list of issues resolved in this release is available at [CSHARP JIRA project](https://jira.mongodb.org/issues/?jql=project%20%3D%20CSHARP%20AND%20fixVersion%20%3D%202.25.0%20ORDER%20BY%20key%20ASC). -Documentation on the .NET driver can be found [here](https://www.mongodb.com/docs/drivers/csharp/v2.25.0}/). +Documentation on the .NET driver can be found [here](https://www.mongodb.com/docs/drivers/csharp/v2.25/). From da64949553546b3fbe43c1d90c4a143d2f6315ef Mon Sep 17 00:00:00 2001 From: BorisDog Date: Fri, 28 Jun 2024 09:16:39 -0700 Subject: [PATCH 02/21] Update Release Notes v2.26.0.md --- Release Notes/Release Notes v2.26.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release Notes/Release Notes v2.26.0.md b/Release Notes/Release Notes v2.26.0.md index c48d4d466c6..469b6874846 100644 --- a/Release Notes/Release Notes v2.26.0.md +++ b/Release Notes/Release Notes v2.26.0.md @@ -16,4 +16,4 @@ The main new features in 2.26.0 include: The full list of issues resolved in this release is available at [CSHARP JIRA project](https://jira.mongodb.org/issues/?jql=project%20%3D%20CSHARP%20AND%20fixVersion%20%3D%202.26.0%20ORDER%20BY%20key%20ASC). -Documentation on the .NET driver can be found [here](https://www.mongodb.com/docs/drivers/csharp/v2.26.0}/). +Documentation on the .NET driver can be found [here](https://www.mongodb.com/docs/drivers/csharp/v2.26/). From 16526fc26aca98b40a812428c42c3f360faa201a Mon Sep 17 00:00:00 2001 From: BorisDog Date: Tue, 2 Jul 2024 13:09:41 -0700 Subject: [PATCH 03/21] CSHARP-5080: Add analyzer variant to driver EG (#1376) --- evergreen/evergreen.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index f8e7d82a27d..7a96695fad9 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -1933,6 +1933,22 @@ tasks: DOTNET_SDK_PATH="${DOTNET_SDK_PATH}" bash ./evergreen/install-dependencies.sh DRIVER_VERSION="${PACKAGE_VERSION}" bash ./evergreen/run-tests.sh + - name: test-analyzer + commands: + - command: expansions.update + params: + updates: + - key: TEST_RESULTS_PATH + value: "./analyzer/build/test-results/TEST-*.xml" + - func: run-external-script + vars: + GIT_REPO: "https://github.com/mongodb/mongo-csharp-analyzer.git" + LOCAL_PATH: ${workdir}/analyzer + SCRIPT: | + ${PREPARE_SHELL} + DOTNET_SDK_PATH="${DOTNET_SDK_PATH}" bash ./evergreen/install-dependencies.sh + DRIVER_VERSION="${PACKAGE_VERSION}" TARGET_FRAMEWORK=netcoreapp3.1 bash ./evergreen/run-tests.sh + axes: - id: version display_name: MongoDB Version @@ -2810,3 +2826,14 @@ buildvariants: depends_on: - name: push-packages-myget variant: ".push-packages-myget" + +- matrix_name: analyzer-tests + batchtime: 720 # 12 hours + matrix_spec: + os: ["ubuntu-2004", "windows-64"] + display_name: "Analyzer tests on ${os}" + tasks: + - name: test-analyzer + depends_on: + - name: push-packages-myget + variant: ".push-packages-myget" From ddf36cfff8a5c392fba2b6b37f7870bed4fcc7a1 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:26:46 -0700 Subject: [PATCH 04/21] CSHARP-5029: Flaky RoundTimeTrip_test (#1377) --- src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs index 52e68c40018..aa06eca19bd 100644 --- a/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs +++ b/src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs @@ -369,11 +369,8 @@ private void Heartbeat(CancellationToken cancellationToken) ServerDescription newDescription; if (heartbeatHelloResult != null) { - var averageRoundTripTime = _roundTripTimeMonitor.Average; - var averageRoundTripTimeRounded = TimeSpan.FromMilliseconds(Math.Round(averageRoundTripTime.TotalMilliseconds)); - newDescription = _baseDescription.With( - averageRoundTripTime: averageRoundTripTimeRounded, + averageRoundTripTime: _roundTripTimeMonitor.Average, canonicalEndPoint: heartbeatHelloResult.Me, electionId: heartbeatHelloResult.ElectionId, helloOk: heartbeatHelloResult.HelloOk, From 941a018a26c94d797a14844e984b147f0169c792 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Thu, 4 Jul 2024 17:04:42 -0700 Subject: [PATCH 05/21] CSHARP-4017: Fix flaky Aggregate_with__out_includes_read_preference_for_5.0__server error (#1379) --- .../UnifiedTestRunner.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs index b479b2c3648..ac971fd64a7 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs @@ -22,6 +22,7 @@ using MongoDB.Bson; using MongoDB.Bson.TestHelpers.JsonDrivenTests; using MongoDB.Driver.Core; +using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Logging; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers; @@ -195,6 +196,15 @@ private void AddInitialData(IMongoClient client, BsonArray initialData, UnifiedE } #pragma warning restore CS0618 // Type or member is obsolete + var writeConcern = WriteConcern.WMajority; + if (client.Cluster.Description.Type == ClusterType.ReplicaSet) + { + // Makes server to wait for ack from all data nodes to make sure the test data availability before running the test itself. + // It's limited to replica set only because there is no simple way to calculate proper w for sharded cluster. + var dataBearingServersCount = client.Cluster.Description.Servers.Count(s => s.IsDataBearing); + writeConcern = WriteConcern.Acknowledged.With(w: dataBearingServersCount, journal:true); + } + BsonDocument serverTime = null; foreach (var dataItem in initialData) { @@ -202,22 +212,19 @@ private void AddInitialData(IMongoClient client, BsonArray initialData, UnifiedE var databaseName = dataItem["databaseName"].AsString; var documents = dataItem["documents"].AsBsonArray.Cast().ToList(); - var database = client.GetDatabase(databaseName); - var collection = database - .GetCollection(collectionName, mongoCollectionSettings) - .WithWriteConcern(WriteConcern.WMajority); + var database = client.GetDatabase(databaseName).WithWriteConcern(writeConcern); + var collection = database.GetCollection(collectionName, mongoCollectionSettings); _logger.LogDebug("Dropping {0}", collectionName); - - database.DropCollection(collectionName); var session = client.StartSession(); + database.DropCollection(session, collectionName); if (documents.Any()) { collection.InsertMany(session, documents); } else { - database.WithWriteConcern(WriteConcern.WMajority).CreateCollection(session, collectionName); + database.CreateCollection(session, collectionName); } serverTime = session.ClusterTime; From bf8c4e49caa5a69bd3132bbf2c19662460cf91f7 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:34:13 -0700 Subject: [PATCH 06/21] CSHARP-4017: Fix flaky Aggregate_with__out_includes_read_preference_for_5.0__server error (#1381) --- .../Jira/CSharp3397Tests.cs | 30 ------------------- .../UnifiedTestRunner.cs | 4 +-- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs b/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs index 9de4ddb7eef..49a24a71359 100644 --- a/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs +++ b/tests/MongoDB.Driver.Tests/Jira/CSharp3397Tests.cs @@ -26,36 +26,6 @@ namespace MongoDB.Driver.Tests.Jira { public class CSharp3397Tests { - [Fact] - public void Aggregate_out_to_collection_should_work() - { - var client = DriverTestConfiguration.Client; - var database = client.GetDatabase("test"); - var collection = database.GetCollection("test"); - var outCollection = database.GetCollection("out"); - - var writeConcern = WriteConcern.WMajority; - if (DriverTestConfiguration.IsReplicaSet(client)) - { - var n = DriverTestConfiguration.GetReplicaSetNumberOfDataBearingMembers(client); - writeConcern = new WriteConcern(n); - } - - database.DropCollection("test"); - database.DropCollection("out"); - collection - .WithWriteConcern(writeConcern) - .InsertOne(new BsonDocument("_id", 1)); - - var pipeline = new EmptyPipelineDefinition() - .Count() - .Out(outCollection) - .As(); - var results = collection.WithReadPreference(ReadPreference.SecondaryPreferred).Aggregate(pipeline).ToList(); - - results.Single().Count.Should().Be(1); - } - [Fact] public void Aggregate_out_to_time_series_collection_on_secondary_should_work() { diff --git a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs index ac971fd64a7..a74accd4f57 100644 --- a/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs +++ b/tests/MongoDB.Driver.Tests/UnifiedTestOperations/UnifiedTestRunner.cs @@ -197,11 +197,11 @@ private void AddInitialData(IMongoClient client, BsonArray initialData, UnifiedE #pragma warning restore CS0618 // Type or member is obsolete var writeConcern = WriteConcern.WMajority; - if (client.Cluster.Description.Type == ClusterType.ReplicaSet) + if (DriverTestConfiguration.IsReplicaSet(client)) { // Makes server to wait for ack from all data nodes to make sure the test data availability before running the test itself. // It's limited to replica set only because there is no simple way to calculate proper w for sharded cluster. - var dataBearingServersCount = client.Cluster.Description.Servers.Count(s => s.IsDataBearing); + var dataBearingServersCount = DriverTestConfiguration.GetReplicaSetNumberOfDataBearingMembers(client); writeConcern = WriteConcern.Acknowledged.With(w: dataBearingServersCount, journal:true); } From f9f582ea718d67710743bd4b0e5bc2181b926b35 Mon Sep 17 00:00:00 2001 From: rstam Date: Fri, 28 Jun 2024 15:44:42 -0700 Subject: [PATCH 07/21] CSHARP-5043: Support conversion between enum types. --- ...essionToAggregationExpressionTranslator.cs | 55 +++ .../Jira/CSharp5043Tests.cs | 345 ++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5043Tests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs index a510e17be8a..8596f64e2b0 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs @@ -54,6 +54,11 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE return TranslateConvertUnderlyingTypeToEnum(expression, operandTranslation); } + if (IsConvertEnumToEnum(expression)) + { + return TranslateConvertEnumToEnum(expression, operandTranslation); + } + if (IsConvertToBaseType(sourceType: operandExpression.Type, targetType: expressionType)) { return TranslateConvertToBaseType(expression, operandTranslation); @@ -111,6 +116,16 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE throw new ExpressionNotSupportedException(expression); } + private static bool IsConvertEnumToEnum(UnaryExpression expression) + { + var sourceType = expression.Operand.Type; + var targetType = expression.Type; + + return + sourceType.IsEnumOrNullableEnum(out _, out _) && + targetType.IsEnumOrNullableEnum(out _, out _); + } + private static bool IsConvertEnumToUnderlyingType(UnaryExpression expression) { var sourceType = expression.Operand.Type; @@ -173,6 +188,46 @@ private static AggregationExpression TranslateConvertToBsonValue(TranslationCont return new AggregationExpression(expression, operandTranslation.Ast, BsonValueSerializer.Instance); } + private static AggregationExpression TranslateConvertEnumToEnum(UnaryExpression expression, AggregationExpression operandTranslation) + { + var sourceType = expression.Operand.Type; + var targetType = expression.Type; + + if (!sourceType.IsEnumOrNullableEnum(out var sourceEnumType, out _)) + { + throw new ExpressionNotSupportedException(expression, because: "source type is not an enum or nullable enum"); + } + if (!targetType.IsEnumOrNullableEnum(out var targetEnumType, out _)) + { + throw new ExpressionNotSupportedException(expression, because: "target type is not an enum or nullable enum"); + } + + var sourceSerializer = operandTranslation.Serializer; + IBsonSerializer targetEnumSerializer; + if (targetEnumType == sourceEnumType) + { + targetEnumSerializer = sourceSerializer is INullableSerializer sourceNullableSerializer ? + sourceNullableSerializer.ValueSerializer : + sourceSerializer; + } + else + { + if (sourceSerializer is IHasRepresentationSerializer sourceHasRepresentationSerializer && + !SerializationHelper.IsNumericRepresentation(sourceHasRepresentationSerializer.Representation)) + { + throw new ExpressionNotSupportedException(expression, because: "source enum is not represented as a number"); + } + + targetEnumSerializer = EnumSerializer.Create(targetEnumType); + } + + var targetSerializer = targetType.IsNullable() ? + NullableSerializer.Create(targetEnumSerializer) : + targetEnumSerializer; + + return new AggregationExpression(expression, operandTranslation.Ast, targetSerializer); + } + private static AggregationExpression TranslateConvertEnumToUnderlyingType(UnaryExpression expression, AggregationExpression operandTranslation) { var sourceType = expression.Operand.Type; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5043Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5043Tests.cs new file mode 100644 index 00000000000..88a7fca1f5b --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5043Tests.cs @@ -0,0 +1,345 @@ +/* Copyright 2010-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. +*/ + +using System; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp5043Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void Convert_E1_to_E1_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E1)x.E1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { E1 : '$E1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$E1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E1.A, E1.B); + } + + [Theory] + [ParameterAttributeData] + public void Convert_E1_to_E2_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E2)x.E1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$E1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$E1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E2.C, E2.D); + } + + [Theory] + [ParameterAttributeData] + public void Convert_E1_to_nullable_E1_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E1?)x.E1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$E1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$E1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E1.A, E1.B); + } + + [Theory] + [ParameterAttributeData] + public void Convert_E1_to_nullable_E2_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E2?)x.E1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$E1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$E1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E2.C, E2.D); + } + + [Theory] + [ParameterAttributeData] + public void Convert_nullable_E1_to_E1_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E1)x.NE1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NE1 : '$NE1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NE1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E1.A, E1.B); + } + + [Theory] + [ParameterAttributeData] + public void Convert_nullable_E1_to_E2_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E2)x.NE1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NE1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NE1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E2.C, E2.D); + } + + [Theory] + [ParameterAttributeData] + public void Convert_nullable_E1_to_nullable_E1_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E1?)x.NE1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NE1 : '$NE1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NE1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E1.A, E1.B); + } + + [Theory] + [ParameterAttributeData] + public void Convert_nullable_E1_to_nullable_E2_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E2?)x.NE1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NE1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NE1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E2.C, E2.D); + } + + [Theory] + [ParameterAttributeData] + public void Convert_ES1_to_E1_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E1)x.ES1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { ES1 : '$ES1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$ES1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E1.A, E1.B); + } + + [Theory] + [ParameterAttributeData] + public void Convert_ES1_to_E2_should_throw( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E2)x.ES1); + + if (linqProvider == LinqProvider.V2) + { + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { __fld0 : '$ES1', _id : 0 } }"); + + // LINQ2 fails during deserialization because E1 and E2 do not have compatible representations + var exception = Record.Exception(() => queryable.ToList()); + exception.Should().BeOfType(); + } + else + { + var exception = Record.Exception(() => Translate(collection, queryable)); + exception.Should().BeOfType(); + exception.Message.Should().Contain("because source enum is not represented as a number"); + } + } + + [Theory] + [ParameterAttributeData] + public void Convert_ES1_to_nullable_E1_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E1?)x.ES1); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$ES1', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$ES1', _id : 0 } }"); + } + + var results = queryable.ToList(); + results.Should().Equal(E1.A, E1.B); + } + + [Theory] + [ParameterAttributeData] + public void Convert_ES1_to_nullable_E2_should_throw( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (E2?)x.ES1); + + if (linqProvider == LinqProvider.V2) + { + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { __fld0 : '$ES1', _id : 0 } }"); + + // LINQ2 fails during deserialization because E1 and E2 do not have compatible representations + var exception = Record.Exception(() => queryable.ToList()); + exception.Should().BeOfType(); + } + else + { + var exception = Record.Exception(() => Translate(collection, queryable)); + exception.Should().BeOfType(); + exception.Message.Should().Contain("because source enum is not represented as a number"); + } + } + + private IMongoCollection GetCollection(LinqProvider linqProvider) + { + var collection = GetCollection("test", linqProvider); + CreateCollection( + collection, + new C { Id = 1, E1 = E1.A, NE1 = E1.A, ES1 = E1.A, NES1 = E1.A }, + new C { Id = 2, E1 = E1.B, NE1 = E1.B, ES1 = E1.B, NES1 = E1.B }); + return collection; + } + + private class C + { + public int Id { get; set; } + public E1 E1 { get; set; } + public E1? NE1 { get; set; } + [BsonRepresentation(BsonType.String)] public E1 ES1 { get; set; } + [BsonRepresentation(BsonType.String)] public E1? NES1 { get; set; } + } + + private enum E1 { A, B } + private enum E2 { C, D } + } +} From 4971ea120a7d93e837fafb84dae7be5a7f0e80a5 Mon Sep 17 00:00:00 2001 From: rstam Date: Fri, 15 Mar 2024 14:49:36 -0700 Subject: [PATCH 08/21] CSHARP-4985: Verify that operands to numeric operators in LINQ expressions are represented as numbers on the server. --- .../Misc/SerializationHelper.cs | 48 ++++++ ...essionToAggregationExpressionTranslator.cs | 33 +++-- ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 9 +- ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 2 + ...MethodToAggregationExpressionTranslator.cs | 14 +- ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 2 + ...MethodToAggregationExpressionTranslator.cs | 6 +- ...MethodToAggregationExpressionTranslator.cs | 2 + ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 1 + ...essionToAggregationExpressionTranslator.cs | 2 +- .../Support/ReflectionExtensions.cs | 15 ++ .../Jira/CSharp4985Tests.cs | 137 ++++++++++++++++++ 19 files changed, 254 insertions(+), 24 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4985Tests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs index bb5a5f4f601..3f0d27eafdd 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs @@ -21,6 +21,7 @@ using MongoDB.Bson.Serialization.Options; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators; namespace MongoDB.Driver.Linq.Linq3Implementation.Misc { @@ -35,6 +36,11 @@ public static void EnsureRepresentationIsArray(Expression expression, IBsonSeria } } + public static void EnsureRepresentationIsNumeric(Expression expression, AggregationExpression translation) + { + EnsureRepresentationIsNumeric(expression, translation.Serializer); + } + public static void EnsureRepresentationIsNumeric(Expression expression, IBsonSerializer serializer) { var representation = GetRepresentation(serializer); @@ -56,6 +62,11 @@ public static BsonType GetRepresentation(IBsonSerializer serializer) return GetRepresentation(downcastingSerializer.DerivedSerializer); } + if (serializer is IEnumUnderlyingTypeSerializer enumUnderlyingTypeSerializer) + { + return GetRepresentation(enumUnderlyingTypeSerializer.EnumSerializer); + } + if (serializer is IImpliedImplementationInterfaceSerializer impliedImplementationSerializer) { return GetRepresentation(impliedImplementationSerializer.ImplementationSerializer); @@ -82,6 +93,11 @@ public static BsonType GetRepresentation(IBsonSerializer serializer) return keyValuePairSerializer.Representation; } + if (serializer is INullableSerializer nullableSerializer) + { + return GetRepresentation(nullableSerializer.ValueSerializer); + } + // for backward compatibility assume that any remaining implementers of IBsonDocumentSerializer are represented as documents if (serializer is IBsonDocumentSerializer) { @@ -97,6 +113,15 @@ public static BsonType GetRepresentation(IBsonSerializer serializer) return BsonType.Undefined; } + public static bool IsIntegerRepresentation(BsonType representation) + { + return representation switch + { + BsonType.Int32 or BsonType.Int64 => true, + _ => false + }; + } + public static bool IsNumericRepresentation(BsonType representation) { return representation switch @@ -111,6 +136,29 @@ public static bool IsRepresentedAsDocument(IBsonSerializer serializer) return SerializationHelper.GetRepresentation(serializer) == BsonType.Document; } + public static bool IsRepresentedAsInteger(IBsonSerializer serializer) + { + var representation = GetRepresentation(serializer); + return IsIntegerRepresentation(representation); + } + + public static bool IsRepresentedAsIntegerOrNullableInteger(AggregationExpression translation) + { + return IsRepresentedAsIntegerOrNullableInteger(translation.Serializer); + } + + public static bool IsRepresentedAsIntegerOrNullableInteger(IBsonSerializer serializer) + { + if (serializer is INullableSerializer nullableSerializer) + { + return IsRepresentedAsInteger(nullableSerializer.ValueSerializer); + } + else + { + return IsRepresentedAsInteger(serializer); + } + } + public static BsonValue SerializeValue(IBsonSerializer serializer, ConstantExpression constantExpression, Expression containingExpression) { var value = constantExpression.Value; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs index a8a45caa232..c6105136c47 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/BinaryExpressionToAggregationExpressionTranslator.cs @@ -15,7 +15,6 @@ using System; using System.Linq.Expressions; -using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; @@ -82,6 +81,12 @@ public static AggregationExpression Translate(TranslationContext context, Binary rightTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, rightExpression); } + if (IsArithmeticExpression(expression)) + { + SerializationHelper.EnsureRepresentationIsNumeric(leftExpression, leftTranslation); + SerializationHelper.EnsureRepresentationIsNumeric(rightExpression, rightTranslation); + } + var ast = expression.NodeType switch { ExpressionType.Add => AstExpression.Add(leftTranslation.Ast, rightTranslation.Ast), @@ -184,7 +189,7 @@ private static bool IsAddOrSubtractExpression(Expression expression) private static bool IsArithmeticExpression(BinaryExpression expression) { - return expression.Type.IsNumeric() && IsArithmeticOperator(expression.NodeType); + return expression.Type.IsNumericOrNullableNumeric() && IsArithmeticOperator(expression.NodeType); } private static bool IsArithmeticOperator(ExpressionType nodeType) @@ -304,31 +309,29 @@ private static AggregationExpression TranslateEnumExpression(TranslationContext leftTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, leftExpression); rightTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, rightExpression); + AggregationExpression enumTranslation, operandTranslation; if (IsEnumOrConvertEnumToUnderlyingType(leftExpression)) { - serializer = leftTranslation.Serializer; + enumTranslation = leftTranslation; + operandTranslation = rightTranslation; } else { - serializer = rightTranslation.Serializer; + enumTranslation = rightTranslation; + operandTranslation = leftTranslation; } - var representation = BsonType.Int32; // assume an integer representation unless we can determine otherwise - var valueSerializer = serializer; - if (valueSerializer is INullableSerializer nullableSerializer) + if (!SerializationHelper.IsRepresentedAsIntegerOrNullableInteger(enumTranslation)) { - valueSerializer = nullableSerializer.ValueSerializer; - } - if (valueSerializer is IEnumUnderlyingTypeSerializer enumUnderlyingTypeSerializer && - enumUnderlyingTypeSerializer.EnumSerializer is IHasRepresentationSerializer withRepresentationSerializer) - { - representation = withRepresentationSerializer.Representation; + throw new ExpressionNotSupportedException(expression, because: "arithmetic on enums is only allowed when the enum is represented as an integer"); } - if (representation != BsonType.Int32 && representation != BsonType.Int64) + if (!SerializationHelper.IsRepresentedAsIntegerOrNullableInteger(operandTranslation)) { - throw new ExpressionNotSupportedException(expression, because: "arithmetic on enums is only allowed when the enum is represented as an integer"); + throw new ExpressionNotSupportedException(expression, because: "the value being added to or subtracted from an enum must be represented as an integer"); } + + serializer = enumTranslation.Serializer; } else { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/AbsMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/AbsMethodToAggregationExpressionTranslator.cs index 9d63b0f1a90..3b2a183d810 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/AbsMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/AbsMethodToAggregationExpressionTranslator.cs @@ -43,6 +43,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var valueExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, valueExpression); + SerializationHelper.EnsureRepresentationIsNumeric(valueExpression, valueTranslation); var ast = AstExpression.Abs(valueTranslation.Ast); return new AggregationExpression(expression, ast, valueTranslation.Serializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs index e604bf79991..cd3a1865969 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs @@ -32,6 +32,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); var ast = AstExpression.Ceil(argumentTranslation.Ast); var serializer = BsonSerializer.LookupSerializer(expression.Type); return new AggregationExpression(expression, ast, serializer); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateTimeAddOrSubtractMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateTimeAddOrSubtractMethodToAggregationExpressionTranslator.cs index 5ead3fb7023..d17117353cf 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateTimeAddOrSubtractMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateTimeAddOrSubtractMethodToAggregationExpressionTranslator.cs @@ -144,12 +144,9 @@ public static AggregationExpression Translate(TranslationContext context, Method { throw new ExpressionNotSupportedException(valueExpression, expression); } - var representation = timeSpanSerializer.Representation; + SerializationHelper.EnsureRepresentationIsNumeric(valueExpression, timeSpanSerializer); + var serializerUnits = timeSpanSerializer.Units; - if (representation != BsonType.Int32 && representation != BsonType.Int64 && representation != BsonType.Double) - { - throw new ExpressionNotSupportedException(valueExpression, expression); - } (unit, amount) = serializerUnits switch { TimeSpanUnits.Ticks => ("millisecond", AstExpression.Divide(valueTranslation.Ast, (double)TimeSpan.TicksPerMillisecond)), @@ -174,6 +171,8 @@ public static AggregationExpression Translate(TranslationContext context, Method else { var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, valueExpression); + SerializationHelper.EnsureRepresentationIsNumeric(valueExpression, valueTranslation); + (unit, amount) = method.Name switch { "AddTicks" => ("millisecond", AstExpression.Divide(valueTranslation.Ast, (double)TimeSpan.TicksPerMillisecond)), diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ExpMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ExpMethodToAggregationExpressionTranslator.cs index c126005d10a..724562db255 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ExpMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ExpMethodToAggregationExpressionTranslator.cs @@ -32,6 +32,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); var ast = AstExpression.Exp(argumentTranslation.Ast); return new AggregationExpression(expression, ast, new DoubleSerializer()); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs index 11c79b38e86..2f290643c9a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs @@ -32,6 +32,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); var ast = AstExpression.Floor(argumentTranslation.Ast); var serializer = BsonSerializer.LookupSerializer(expression.Type); return new AggregationExpression(expression, ast, serializer); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfAnyMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfAnyMethodToAggregationExpressionTranslator.cs index 810c4a4ab33..f9864e35ba3 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfAnyMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfAnyMethodToAggregationExpressionTranslator.cs @@ -115,6 +115,7 @@ string TranslateAnyOf(ReadOnlyCollection arguments) var startIndexExpression = arguments[1]; var startIndexTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, startIndexExpression); + SerializationHelper.EnsureRepresentationIsNumeric(startIndexExpression, startIndexTranslation); return AstExpression.UseVarIfNotSimple("startIndex", startIndexTranslation.Ast); } @@ -127,6 +128,7 @@ string TranslateAnyOf(ReadOnlyCollection arguments) var countExpression = arguments[2]; var countTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, countExpression); + SerializationHelper.EnsureRepresentationIsNumeric(countExpression, countTranslation); return AstExpression.UseVarIfNotSimple("count", countTranslation.Ast); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfMethodToAggregationExpressionTranslator.cs index b4854d099c1..dfdb3aeb339 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/IndexOfMethodToAggregationExpressionTranslator.cs @@ -83,8 +83,18 @@ public static AggregationExpression Translate(TranslationContext context, Method { var objectTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, objectExpression); var valueTranslation = TranslateValue(); - var startIndexTranslation = startIndexExpression == null ? null : ExpressionToAggregationExpressionTranslator.Translate(context, startIndexExpression); - var countTranslation = countExpression == null ? null : ExpressionToAggregationExpressionTranslator.Translate(context, countExpression); + AggregationExpression startIndexTranslation = null; + if (startIndexExpression != null) + { + startIndexTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, startIndexExpression); + SerializationHelper.EnsureRepresentationIsNumeric(startIndexExpression, startIndexTranslation); + } + AggregationExpression countTranslation = null; + if (countExpression != null) + { + countTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, countExpression); + SerializationHelper.EnsureRepresentationIsNumeric(countExpression, countTranslation); + } var ordinal = GetOrdinalFromComparisonType(); var endAst = CreateEndAst(startIndexTranslation?.Ast, countTranslation?.Ast); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/LogMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/LogMethodToAggregationExpressionTranslator.cs index c9b623eceec..1194e4fc3f0 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/LogMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/LogMethodToAggregationExpressionTranslator.cs @@ -32,6 +32,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); AstExpression ast; if (method.Is(MathMethod.LogWithNewBase)) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PowMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PowMethodToAggregationExpressionTranslator.cs index 1ffa08fb6ea..5d5c34db33f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PowMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PowMethodToAggregationExpressionTranslator.cs @@ -32,8 +32,10 @@ public static AggregationExpression Translate(TranslationContext context, Method { var xExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var xTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, xExpression); + SerializationHelper.EnsureRepresentationIsNumeric(xExpression, xTranslation); var yExpression = ConvertHelper.RemoveWideningConvert(arguments[1]); var yTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, yExpression); + SerializationHelper.EnsureRepresentationIsNumeric(yExpression, yTranslation); var ast = AstExpression.Pow(xTranslation.Ast, yTranslation.Ast); return new AggregationExpression(expression, ast, new DoubleSerializer()); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RangeMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RangeMethodToAggregationExpressionTranslator.cs index b4acb22943c..f52d0b50dc9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RangeMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RangeMethodToAggregationExpressionTranslator.cs @@ -34,10 +34,14 @@ public static AggregationExpression Translate(TranslationContext context, Method { var startExpression = arguments[0]; var startTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, startExpression); - var (startVar, startAst) = AstExpression.UseVarIfNotSimple("start", startTranslation.Ast); + SerializationHelper.EnsureRepresentationIsNumeric(startExpression, startTranslation); var countExpression = arguments[1]; var countTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, countExpression); + SerializationHelper.EnsureRepresentationIsNumeric(countExpression, countTranslation); + + var (startVar, startAst) = AstExpression.UseVarIfNotSimple("start", startTranslation.Ast); var (countVar, countAst) = AstExpression.UseVarIfNotSimple("count", countTranslation.Ast); + var ast = AstExpression.Let( startVar, countVar, diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RoundMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RoundMethodToAggregationExpressionTranslator.cs index 605bef61dce..aad8d8138c7 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RoundMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/RoundMethodToAggregationExpressionTranslator.cs @@ -46,12 +46,14 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); AstExpression ast; if (method.IsOneOf(__roundWithPlaceMethods)) { var placeExpression = arguments[1]; var placeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, placeExpression); + SerializationHelper.EnsureRepresentationIsNumeric(placeExpression, placeTranslation); ast = AstExpression.Round(argumentTranslation.Ast, placeTranslation.Ast); } else diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SqrtMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SqrtMethodToAggregationExpressionTranslator.cs index 3e0a6437a4c..32e493abff9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SqrtMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SqrtMethodToAggregationExpressionTranslator.cs @@ -32,6 +32,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); var ast = AstExpression.Sqrt(argumentTranslation.Ast); return new AggregationExpression(expression, ast, new DoubleSerializer()); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SubstringMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SubstringMethodToAggregationExpressionTranslator.cs index 1506aadf067..e542e04faf0 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SubstringMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SubstringMethodToAggregationExpressionTranslator.cs @@ -53,6 +53,7 @@ private static AggregationExpression TranslateHelper(TranslationContext context, { var stringTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, stringExpression); var startIndexTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, startIndexExpression); + SerializationHelper.EnsureRepresentationIsNumeric(startIndexExpression, startIndexTranslation); AstExpression ast; if (lengthExpression == null) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TruncateMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TruncateMethodToAggregationExpressionTranslator.cs index 2d3ec2565a3..ecf8c9c7932 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TruncateMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/TruncateMethodToAggregationExpressionTranslator.cs @@ -73,6 +73,7 @@ public static AggregationExpression Translate(TranslationContext context, Method { var argumentExpression = ConvertHelper.RemoveWideningConvert(arguments[0]); var argumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, argumentExpression); + SerializationHelper.EnsureRepresentationIsNumeric(argumentExpression, argumentTranslation); var ast = AstExpression.Trunc(argumentTranslation.Ast); return new AggregationExpression(expression, ast, argumentTranslation.Serializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NegateExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NegateExpressionToAggregationExpressionTranslator.cs index 376bcb7ebbb..df053de8c22 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NegateExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NegateExpressionToAggregationExpressionTranslator.cs @@ -26,7 +26,7 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE if (expression.NodeType == ExpressionType.Negate) { var operandTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, expression.Operand); - SerializationHelper.EnsureRepresentationIsNumeric(expression, operandTranslation.Serializer); + SerializationHelper.EnsureRepresentationIsNumeric(expression, operandTranslation); var ast = AstExpression.Subtract(0, operandTranslation.Ast); return new AggregationExpression(expression, ast, operandTranslation.Serializer); diff --git a/src/MongoDB.Driver/Support/ReflectionExtensions.cs b/src/MongoDB.Driver/Support/ReflectionExtensions.cs index 6cfce8c0bb7..7922f49a770 100644 --- a/src/MongoDB.Driver/Support/ReflectionExtensions.cs +++ b/src/MongoDB.Driver/Support/ReflectionExtensions.cs @@ -72,6 +72,21 @@ public static bool IsNumeric(this Type type) type == typeof(Decimal128); } + public static bool IsNumericOrNullableNumeric(this Type type) + { + if (type.IsConstructedGenericType && + type.GetGenericTypeDefinition() is var genericTypeDefinition && + genericTypeDefinition == typeof(Nullable<>)) + { + var valueType = type.GetGenericArguments()[0]; + return IsNumeric(valueType); + } + else + { + return IsNumeric(type); + } + } + public static bool IsConvertibleToEnum(this Type type) { return diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4985Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4985Tests.cs new file mode 100644 index 00000000000..f738f0e6654 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4985Tests.cs @@ -0,0 +1,137 @@ +/* Copyright 2010-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. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp4985Tests : Linq3IntegrationTest + { + [Theory] + [InlineData(1, "{ $project : { _v : '$X', _id : 0 } }", new[] { 1, 3 })] + [InlineData(2, "{ $project : { _v : { $add : ['$X', 2] }, _id : 0 } }", new[] { 3, 5 })] + [InlineData(3, "{ $project : { _v : { $add : ['$X', '$Y'] }, _id : 0 } }", new[] { 3, 7 })] + [InlineData(4, "{ $project : { _v : { $add : ['$X', '$NY'] }, _id : 0 } }", null)] + [InlineData(5, "{ $project : { _v : '$NX', _id : 0 } }", null)] + [InlineData(6, "{ $project : { _v : { $add : ['$NX', 2] }, _id : 0 } }", null)] + [InlineData(7, "{ $project : { _v : { $add : ['$NX', '$Y'] }, _id : 0 } }", null)] + [InlineData(8, "{ $project : { _v : { $add : ['$NX', '$NY'] }, _id : 0 } }", null)] + [InlineData(9, "{ $project : { _v : { $add : [1, '$Y'] }, _id : 0 } }", new[] { 3, 5 })] + [InlineData(10, "{ $project : { _v : { $add : [1, '$NY'] }, _id : 0 } }", null)] + public void Select_with_int_result_should_work( + int test, + string expectedStage, + int[] expectedResults) + { + var collection = GetCollection(); + Expression> selector = test switch + { + 1 => x => x.X, + 2 => x => x.X + 2, + 3 => x => x.X + x.Y, + 4 => x => x.X + x.NY.Value, + 5 => x => x.NX.Value, + 6 => x => x.NX.Value + 2, + 7 => x => x.NX.Value + x.Y, + 8 => x => x.NX.Value + x.NY.Value, + 9 => x => 1 + x.Y, + 10 => x => 1 + x.NY.Value, + _ => throw new Exception() + }; + var queryable = collection.AsQueryable().Select(selector); + + var stages = Translate(collection, queryable); + AssertStages(stages, expectedStage); + + List results = null; + var exception = Record.Exception(() => { results = queryable.ToList(); }); + if (expectedResults != null) + { + exception.Should().BeNull(); + results.Should().Equal(expectedResults); + } + else + { + exception.Should().BeOfType(); + exception.Message.Should().Contain("Cannot deserialize a 'Int32' from BsonType 'Null'"); + } + } + + [Theory] + [InlineData(1, "{ $project : { _v : '$X', _id : 0 } }", new object[] { 1, 3 })] + [InlineData(2, "{ $project : { _v : { $add : ['$X', 2] }, _id : 0 } }", new object[] { 3, 5 })] + [InlineData(3, "{ $project : { _v : { $add : ['$X', '$Y'] }, _id : 0 } }", new object[] { 3, 7 })] + [InlineData(4, "{ $project : { _v : { $add : ['$X', '$NY'] }, _id : 0 } }", new object[] { 3, null })] + [InlineData(5, "{ $project : { _v : '$NX', _id : 0 } }", new object[] { 1, null })] + [InlineData(6, "{ $project : { _v : { $add : ['$NX', 2] }, _id : 0 } }", new object[] { 3, null })] + [InlineData(7, "{ $project : { _v : { $add : ['$NX', '$Y'] }, _id : 0 } }", new object[] { 3, null })] + [InlineData(8, "{ $project : { _v : { $add : ['$NX', '$NY'] }, _id : 0 } }", new object[] { 3, null })] + [InlineData(9, "{ $project : { _v : { $add : [1, '$Y'] }, _id : 0 } }", new object[] { 3, 5 })] + [InlineData(10, "{ $project : { _v : { $add : [1, '$NY'] }, _id : 0 } }", new object[] { 3, null })] + public void Select_with_nullable_int_result_should_work( + int test, + string expectedStage, + object[] expectedResults) + { + var collection = GetCollection(); + Expression> selector = test switch + { + 1 => x => x.X, + 2 => x => x.X + 2, + 3 => x => x.X + x.Y, + 4 => x => x.X + x.NY, + 5 => x => x.NX, + 6 => x => x.NX + 2, + 7 => x => x.NX + x.Y, + 8 => x => x.NX + x.NY, + 9 => x => 1 + x.Y, + 10 => x => 1 + x.NY, + _ => throw new Exception() + }; + var queryable = collection.AsQueryable().Select(selector); + + var stages = Translate(collection, queryable); + AssertStages(stages, expectedStage); + + var results = queryable.ToList(); + results.Should().Equal(expectedResults.Select(r => r == null ? null : (int?)r)); + } + + private IMongoCollection GetCollection() + { + var collection = GetCollection("test"); + CreateCollection( + collection, + new C { Id = 1, X = 1, Y = 2, NX = 1, NY = 2 }, + new C { Id = 2, X = 3, Y = 4, NX = null, NY = null }); + return collection; + } + + private class C + { + public int Id { get; set; } + public int X { get; set; } + public int Y { get; set; } + public int? NX { get; set; } + public int? NY { get; set; } + } + } +} From 1c7315c33d0dd0fbb1304e87d23dd1f3bb9312cd Mon Sep 17 00:00:00 2001 From: BorisDog Date: Fri, 12 Jul 2024 12:20:01 -0700 Subject: [PATCH 09/21] CSHARP-4941: CSFLE/QE KMIP support "delegated" protocol (#1380) --- .../tests/legacy/azureKMS.json | 459 +-- .../tests/legacy/azureKMS.yml | 90 +- .../tests/legacy/gcpKMS.json | 463 +-- .../tests/legacy/gcpKMS.yml | 90 +- .../tests/legacy/kmipKMS.json | 585 +-- .../tests/legacy/kmipKMS.yml | 129 +- .../tests/unified/createDataKey.json | 1486 +++---- .../tests/unified/createDataKey.yml | 647 ++-- .../tests/unified/rewrapManyDataKey.json | 3415 ++++++++++------- .../tests/unified/rewrapManyDataKey.yml | 967 ++--- 10 files changed, 4571 insertions(+), 3760 deletions(-) diff --git a/specifications/client-side-encryption/tests/legacy/azureKMS.json b/specifications/client-side-encryption/tests/legacy/azureKMS.json index afecf40b0a7..be27bb99529 100644 --- a/specifications/client-side-encryption/tests/legacy/azureKMS.json +++ b/specifications/client-side-encryption/tests/legacy/azureKMS.json @@ -1,224 +1,235 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string_aws": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_azure": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_gcp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_local": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_kmip": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1601573901680" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1601573901680" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyAltNames": [ - "altname", - "azure_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using Azure KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "azure": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string_azure": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string_azure": { - "$binary": { - "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_azure": { - "$binary": { - "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1601573901680" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1601573901680" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyAltNames": [ + "altname", + "azure_altname" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using Azure KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "azure": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_azure": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_azure": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_azure": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/azureKMS.yml b/specifications/client-side-encryption/tests/legacy/azureKMS.yml index b3c1f694720..82f442d9299 100644 --- a/specifications/client-side-encryption/tests/legacy/azureKMS.yml +++ b/specifications/client-side-encryption/tests/legacy/azureKMS.yml @@ -1,46 +1,46 @@ -runOn: - - minServerVersion: "4.1.10" -database_name: &database_name "default" -collection_name: &collection_name "default" - -data: [] -json_schema: {'properties': {'encrypted_string_aws': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_azure': {'encrypt': {'keyId': [{'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_gcp': {'encrypt': {'keyId': [{'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_local': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip': {'encrypt': {'keyId': [{'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} -key_vault_data: [{'_id': {'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1601573901680'}}, 'updateDate': {'$date': {'$numberLong': '1601573901680'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'azure', 'keyVaultEndpoint': 'key-vault-csfle.vault.azure.net', 'keyName': 'key-name-csfle'}, 'keyAltNames': ['altname', 'azure_altname']}] - -tests: - - description: "Insert a document with auto encryption using Azure KMS provider" - clientOptions: - autoEncryptOpts: - kmsProviders: - azure: {} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 1, encrypted_string_azure: "string0" } - expectations: - # Auto encryption will request the collection info. - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - # Then key is fetched from the key vault. - - command_started_event: - command: - find: datakeys - filter: { $or: [ { _id: { $in: [ {'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}} ] } }, { keyAltNames: { $in: [] } } ] } - $db: keyvault - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { _id: 1, encrypted_string_azure: {'$binary': {'base64': 'AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==', 'subType': '06'}} } - ordered: true - command_name: insert - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'encrypted_string_aws': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_azure': {'encrypt': {'keyId': [{'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_gcp': {'encrypt': {'keyId': [{'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_local': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip': {'encrypt': {'keyId': [{'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip_delegated': {'encrypt': {'keyId': [{'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1601573901680'}}, 'updateDate': {'$date': {'$numberLong': '1601573901680'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'azure', 'keyVaultEndpoint': 'key-vault-csfle.vault.azure.net', 'keyName': 'key-name-csfle'}, 'keyAltNames': ['altname', 'azure_altname']}] + +tests: + - description: "Insert a document with auto encryption using Azure KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + azure: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_azure: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_azure: {'$binary': {'base64': 'AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: - *doc0_encrypted \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/gcpKMS.json b/specifications/client-side-encryption/tests/legacy/gcpKMS.json index c2c08b8a232..61479d42d09 100644 --- a/specifications/client-side-encryption/tests/legacy/gcpKMS.json +++ b/specifications/client-side-encryption/tests/legacy/gcpKMS.json @@ -1,226 +1,237 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string_aws": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_azure": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_gcp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_local": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_kmip": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1601574333107" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1601574333107" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyAltNames": [ - "altname", - "gcp_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using GCP KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "gcp": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string_gcp": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string_gcp": { - "$binary": { - "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_gcp": { - "$binary": { - "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1601574333107" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1601574333107" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyAltNames": [ + "altname", + "gcp_altname" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using GCP KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "gcp": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_gcp": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_gcp": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_gcp": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/gcpKMS.yml b/specifications/client-side-encryption/tests/legacy/gcpKMS.yml index 50b6a40bec8..7f7dea72893 100644 --- a/specifications/client-side-encryption/tests/legacy/gcpKMS.yml +++ b/specifications/client-side-encryption/tests/legacy/gcpKMS.yml @@ -1,46 +1,46 @@ -runOn: - - minServerVersion: "4.1.10" -database_name: &database_name "default" -collection_name: &collection_name "default" - -data: [] -json_schema: {'properties': {'encrypted_string_aws': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_azure': {'encrypt': {'keyId': [{'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_gcp': {'encrypt': {'keyId': [{'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_local': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip': {'encrypt': {'keyId': [{'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} -key_vault_data: [{'_id': {'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1601574333107'}}, 'updateDate': {'$date': {'$numberLong': '1601574333107'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'gcp', 'projectId': 'devprod-drivers', 'location': 'global', 'keyRing': 'key-ring-csfle', 'keyName': 'key-name-csfle'}, 'keyAltNames': ['altname', 'gcp_altname']}] - -tests: - - description: "Insert a document with auto encryption using GCP KMS provider" - clientOptions: - autoEncryptOpts: - kmsProviders: - gcp: {} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 1, encrypted_string_gcp: "string0" } - expectations: - # Auto encryption will request the collection info. - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - # Then key is fetched from the key vault. - - command_started_event: - command: - find: datakeys - filter: { $or: [ { _id: { $in: [ {'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] } }, { keyAltNames: { $in: [] } } ] } - $db: keyvault - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { _id: 1, encrypted_string_gcp: {'$binary': {'base64': 'ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==', 'subType': '06'}} } - ordered: true - command_name: insert - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'encrypted_string_aws': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_azure': {'encrypt': {'keyId': [{'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_gcp': {'encrypt': {'keyId': [{'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_local': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip': {'encrypt': {'keyId': [{'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip_delegated': {'encrypt': {'keyId': [{'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1601574333107'}}, 'updateDate': {'$date': {'$numberLong': '1601574333107'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'gcp', 'projectId': 'devprod-drivers', 'location': 'global', 'keyRing': 'key-ring-csfle', 'keyName': 'key-name-csfle'}, 'keyAltNames': ['altname', 'gcp_altname']}] + +tests: + - description: "Insert a document with auto encryption using GCP KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + gcp: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_gcp: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_gcp: {'$binary': {'base64': 'ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: - *doc0_encrypted \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/legacy/kmipKMS.json b/specifications/client-side-encryption/tests/legacy/kmipKMS.json index 5749d21ab81..9c242dcf018 100644 --- a/specifications/client-side-encryption/tests/legacy/kmipKMS.json +++ b/specifications/client-side-encryption/tests/legacy/kmipKMS.json @@ -1,223 +1,362 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string_aws": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_azure": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_gcp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_local": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_kmip": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1634220190041" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1634220190041" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "kmip", - "keyId": "1" - }, - "keyAltNames": [ - "altname", - "kmip_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using KMIP KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "kmip": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string_kmip": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string_kmip": { - "$binary": { - "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_kmip": { - "$binary": { - "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "keyId": "1" + }, + "keyAltNames": [ + "altname", + "kmip_altname" + ] + }, + { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + }, + "keyMaterial": { + "$binary": { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": "11" + }, + "keyAltNames": [ + "delegated" + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using KMIP KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "kmip": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_kmip": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_kmip": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_kmip": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Insert a document with auto encryption using KMIP delegated KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "kmip": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_kmip_delegated": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_kmip_delegated": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_kmip_delegated": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/specifications/client-side-encryption/tests/legacy/kmipKMS.yml b/specifications/client-side-encryption/tests/legacy/kmipKMS.yml index 874a92bf3bb..21dd9d49e2b 100644 --- a/specifications/client-side-encryption/tests/legacy/kmipKMS.yml +++ b/specifications/client-side-encryption/tests/legacy/kmipKMS.yml @@ -1,46 +1,83 @@ -runOn: - - minServerVersion: "4.1.10" -database_name: &database_name "default" -collection_name: &collection_name "default" - -data: [] -json_schema: {'properties': {'encrypted_string_aws': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_azure': {'encrypt': {'keyId': [{'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_gcp': {'encrypt': {'keyId': [{'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_local': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip': {'encrypt': {'keyId': [{'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} -key_vault_data: [{'_id': {'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1634220190041'}}, 'updateDate': {'$date': {'$numberLong': '1634220190041'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'kmip', 'keyId': '1'}, 'keyAltNames': ['altname', 'kmip_altname']}] - -tests: - - description: "Insert a document with auto encryption using KMIP KMS provider" - clientOptions: - autoEncryptOpts: - kmsProviders: - kmip: {} - operations: - - name: insertOne - arguments: - document: &doc0 { _id: 1, encrypted_string_kmip: "string0" } - expectations: - # Auto encryption will request the collection info. - - command_started_event: - command: - listCollections: 1 - filter: - name: *collection_name - command_name: listCollections - # Then key is fetched from the key vault. - - command_started_event: - command: - find: datakeys - filter: { $or: [ { _id: { $in: [ {'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}} ] } }, { keyAltNames: { $in: [] } } ] } - $db: keyvault - command_name: find - - command_started_event: - command: - insert: *collection_name - documents: - - &doc0_encrypted { _id: 1, encrypted_string_kmip: {'$binary': {'base64': 'AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==', 'subType': '06'}} } - ordered: true - command_name: insert - outcome: - collection: - # Outcome is checked using a separate MongoClient without auto encryption. - data: - - *doc0_encrypted \ No newline at end of file +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'encrypted_string_aws': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_azure': {'encrypt': {'keyId': [{'$binary': {'base64': 'AZURE+AAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_gcp': {'encrypt': {'keyId': [{'$binary': {'base64': 'GCP+AAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_local': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip': {'encrypt': {'keyId': [{'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'encrypted_string_kmip_delegated': {'encrypt': {'keyId': [{'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1634220190041'}}, 'updateDate': {'$date': {'$numberLong': '1634220190041'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'kmip', 'keyId': '1'}, 'keyAltNames': ['altname', 'kmip_altname']},{'_id': {'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}, 'keyMaterial': {'$binary': {'base64': '5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1634220190041'}}, 'updateDate': {'$date': {'$numberLong': '1634220190041'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'kmip', 'delegated': True, 'keyId': '11'}, 'keyAltNames': ['delegated']}] + +tests: + - description: "Insert a document with auto encryption using KMIP KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + kmip: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_kmip: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {'$binary': {'base64': 'dBHpr8aITfeBQ15grpbLpQ==', 'subType': '04'}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_kmip: {'$binary': {'base64': 'AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + + - description: "Insert a document with auto encryption using KMIP delegated KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + kmip: {} + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, encrypted_string_kmip_delegated: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { _id: 1, encrypted_string_kmip_delegated: {'$binary': {'base64': 'AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted \ No newline at end of file diff --git a/specifications/client-side-encryption/tests/unified/createDataKey.json b/specifications/client-side-encryption/tests/unified/createDataKey.json index 110c726f9a2..ce54f12beab 100644 --- a/specifications/client-side-encryption/tests/unified/createDataKey.json +++ b/specifications/client-side-encryption/tests/unified/createDataKey.json @@ -1,711 +1,775 @@ -{ - "description": "createDataKey", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [] - } - ], - "tests": [ - { - "description": "create data key with AWS KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "aws", - "opts": { - "masterKey": { - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with Azure KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "azure", - "opts": { - "masterKey": { - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with GCP KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "gcp", - "opts": { - "masterKey": { - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with KMIP KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "kmip" - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with local KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local" - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "local" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with no keyAltName", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [] - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": { - "$$exists": false - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with single keyAltName", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [ - "local_key" - ] - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with multiple keyAltNames", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [ - "abc", - "def" - ] - } - }, - "expectResult": { - "$$type": "binData" - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 0, - "keyAltNames": 1 - } - }, - { - "$unwind": "$keyAltNames" - }, - { - "$sort": { - "keyAltNames": 1 - } - } - ] - }, - "expectResult": [ - { - "keyAltNames": "abc" - }, - { - "keyAltNames": "def" - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": { - "$$type": "array" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "aggregate" - } - } - ] - } - ] - }, - { - "description": "create datakey with custom key material", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyMaterial": { - "$binary": { - "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", - "subType": "00" - } - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with invalid custom key material (too short)", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyMaterial": { - "$binary": { - "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", - "subType": "00" - } - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} +{ + "description": "createDataKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "create data key with AWS KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with Azure KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "azure", + "opts": { + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with GCP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "gcp", + "opts": { + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with KMIP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with KMIP delegated KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip", + "opts": { + "masterKey": { + "delegated": true + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + }, + "delegated": true + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with local KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "local" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with no keyAltName", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [] + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": { + "$$exists": false + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with single keyAltName", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "local_key" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with multiple keyAltNames", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyAltNames": [ + "abc", + "def" + ] + } + }, + "expectResult": { + "$$type": "binData" + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 0, + "keyAltNames": 1 + } + }, + { + "$unwind": "$keyAltNames" + }, + { + "$sort": { + "keyAltNames": 1 + } + } + ] + }, + "expectResult": [ + { + "keyAltNames": "abc" + }, + { + "keyAltNames": "def" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyAltNames": { + "$$type": "array" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "create datakey with custom key material", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyMaterial": { + "$binary": { + "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", + "subType": "00" + } + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$type": "int" + }, + "masterKey": { + "$$type": "object" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with invalid custom key material (too short)", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local", + "opts": { + "keyMaterial": { + "$binary": { + "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", + "subType": "00" + } + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/unified/createDataKey.yml b/specifications/client-side-encryption/tests/unified/createDataKey.yml index dd146386375..501c263964c 100644 --- a/specifications/client-side-encryption/tests/unified/createDataKey.yml +++ b/specifications/client-side-encryption/tests/unified/createDataKey.yml @@ -1,309 +1,338 @@ -description: createDataKey - -schemaVersion: "1.8" - -runOnRequirements: - - csfle: true - -createEntities: - - client: - id: &client0 client0 - observeEvents: - - commandStartedEvent - - clientEncryption: - id: &clientEncryption0 clientEncryption0 - clientEncryptionOpts: - keyVaultClient: *client0 - keyVaultNamespace: keyvault.datakeys - kmsProviders: - aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } - azure: { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } - gcp: { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } - kmip: { endpoint: { $$placeholder: 1 } } - local: { key: { $$placeholder: 1 } } - - database: - id: &database0 database0 - client: *client0 - databaseName: &database0Name keyvault - - collection: - id: &collection0 collection0 - database: *database0 - collectionName: &collection0Name datakeys - -initialData: - - databaseName: *database0Name - collectionName: *collection0Name - documents: [] - -tests: - - description: create data key with AWS KMS provider - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: aws - opts: - masterKey: &new_aws_masterkey - key: arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 - region: us-east-1 - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$exists: true } - masterKey: - provider: aws - <<: *new_aws_masterkey - writeConcern: { w: majority } - - - description: create datakey with Azure KMS provider - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: azure - opts: - masterKey: &new_azure_masterkey - keyVaultEndpoint: key-vault-csfle.vault.azure.net - keyName: key-name-csfle - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$exists: true } - masterKey: - provider: azure - <<: *new_azure_masterkey - writeConcern: { w: majority } - - - description: create datakey with GCP KMS provider - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: gcp - opts: - masterKey: &new_gcp_masterkey - projectId: devprod-drivers - location: global - keyRing: key-ring-csfle - keyName: key-name-csfle - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$exists: true } - masterKey: - provider: gcp - <<: *new_gcp_masterkey - writeConcern: { w: majority } - - - description: create datakey with KMIP KMS provider - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: kmip - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$exists: true } - masterKey: - provider: kmip - keyId: { $$type: string } - writeConcern: { w: majority } - - - description: create datakey with local KMS provider - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: local - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$exists: true } - masterKey: - provider: local - writeConcern: { w: majority } - - - description: create datakey with no keyAltName - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: local - opts: - keyAltNames: [] - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - # keyAltNames field should not exist if no keyAltNames are given. - keyAltNames: { $$exists: false } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$type: int } - masterKey: { $$type: object } - writeConcern: { w: majority } - - - description: create datakey with single keyAltName - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: local - opts: - keyAltNames: ["local_key"] - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyAltNames: [local_key] - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$type: int } - masterKey: { $$type: object } - writeConcern: { w: majority } - - - description: create datakey with multiple keyAltNames - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: local - opts: - keyAltNames: ["abc", "def"] - expectResult: { $$type: binData } - - name: aggregate - object: *collection0 - arguments: - # Need to use pipeline to sort keyAltNames for deterministic matching - # because keyAltNames is not required to be sorted. - pipeline: - - $project: { _id: 0, keyAltNames: 1 } - - $unwind: $keyAltNames - - $sort: { keyAltNames: 1 } - expectResult: - - keyAltNames: abc - - keyAltNames: def - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - keyAltNames: { $$type: array } - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$type: int } - masterKey: { $$type: object } - writeConcern: { w: majority } - - commandStartedEvent: { commandName: aggregate } - - - description: create datakey with custom key material - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: local - opts: - # "key_material" repeated 8 times. - keyMaterial: &custom_key_material { $binary: { base64: a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs, subType: "00" } } - expectResult: { $$type: binData } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - insert: *collection0Name - documents: - - _id: { $$type: binData } - # Cannot match exact value of encrypted key material. - keyMaterial: { $$type: binData } - creationDate: { $$type: date } - updateDate: { $$type: date } - status: { $$type: int } - masterKey: { $$type: object } - writeConcern: { w: majority } - - - description: create datakey with invalid custom key material (too short) - operations: - - name: createDataKey - object: *clientEncryption0 - arguments: - kmsProvider: local - opts: - # "key_material" repeated only 7 times (key material length == 84). - keyMaterial: { $binary: { base64: a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs, subType: "00" } } - expectError: - isClientError: true - expectEvents: - - client: *client0 - events: [] +description: createDataKey + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + azure: { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + gcp: { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + kmip: { endpoint: { $$placeholder: 1 } } + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: create data key with AWS KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: aws + opts: + masterKey: &new_aws_masterkey + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: aws + <<: *new_aws_masterkey + writeConcern: { w: majority } + + - description: create datakey with Azure KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: azure + opts: + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: azure + <<: *new_azure_masterkey + writeConcern: { w: majority } + + - description: create datakey with GCP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: gcp + opts: + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: gcp + <<: *new_gcp_masterkey + writeConcern: { w: majority } + + - description: create datakey with KMIP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: kmip + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: kmip + keyId: { $$type: string } + writeConcern: { w: majority } + + - description: create datakey with KMIP delegated KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: kmip + opts: + masterKey: &new_kmip_masterkey + delegated: true + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: kmip + keyId: { $$type: string } + delegated: true + writeConcern: { w: majority } + + - description: create datakey with local KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: local + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: local + writeConcern: { w: majority } + + - description: create datakey with no keyAltName + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: local + opts: + keyAltNames: [] + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + # keyAltNames field should not exist if no keyAltNames are given. + keyAltNames: { $$exists: false } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$type: int } + masterKey: { $$type: object } + writeConcern: { w: majority } + + - description: create datakey with single keyAltName + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: local + opts: + keyAltNames: ["local_key"] + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyAltNames: [local_key] + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$type: int } + masterKey: { $$type: object } + writeConcern: { w: majority } + + - description: create datakey with multiple keyAltNames + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: local + opts: + keyAltNames: ["abc", "def"] + expectResult: { $$type: binData } + - name: aggregate + object: *collection0 + arguments: + # Need to use pipeline to sort keyAltNames for deterministic matching + # because keyAltNames is not required to be sorted. + pipeline: + - $project: { _id: 0, keyAltNames: 1 } + - $unwind: $keyAltNames + - $sort: { keyAltNames: 1 } + expectResult: + - keyAltNames: abc + - keyAltNames: def + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyAltNames: { $$type: array } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$type: int } + masterKey: { $$type: object } + writeConcern: { w: majority } + - commandStartedEvent: { commandName: aggregate } + + - description: create datakey with custom key material + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: local + opts: + # "key_material" repeated 8 times. + keyMaterial: &custom_key_material { $binary: { base64: a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs, subType: "00" } } + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + # Cannot match exact value of encrypted key material. + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$type: int } + masterKey: { $$type: object } + writeConcern: { w: majority } + + - description: create datakey with invalid custom key material (too short) + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: local + opts: + # "key_material" repeated only 7 times (key material length == 84). + keyMaterial: { $binary: { base64: a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs, subType: "00" } } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] diff --git a/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json index 6b3c9664a97..ae7b0a37772 100644 --- a/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json +++ b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.json @@ -1,1493 +1,1922 @@ -{ - "description": "rewrapManyDataKey", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "YXp1cmVhenVyZWF6dXJlYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "azure_key" - ], - "keyMaterial": { - "$binary": { - "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", - "subType": "04" - } - }, - "keyAltNames": [ - "gcp_key" - ], - "keyMaterial": { - "$binary": { - "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "a21pcGttaXBrbWlwa21pcA==", - "subType": "04" - } - }, - "keyAltNames": [ - "kmip_key" - ], - "keyMaterial": { - "$binary": { - "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "kmip", - "keyId": "1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "no keys to rewrap due to no filter matches", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": "no_matching_keys" - }, - "opts": { - "provider": "local" - } - }, - "expectResult": { - "bulkWriteResult": { - "$$exists": false - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": "no_matching_keys" - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new AWS KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "aws_key" - } - }, - "opts": { - "provider": "aws", - "masterKey": { - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - } - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {}, - "insertedIds": { - "$$unsetOrMatches": {} - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "aws_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new Azure KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "azure_key" - } - }, - "opts": { - "provider": "azure", - "masterKey": { - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {}, - "insertedIds": { - "$$unsetOrMatches": {} - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "azure_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new GCP KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "gcp_key" - } - }, - "opts": { - "provider": "gcp", - "masterKey": { - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {}, - "insertedIds": { - "$$unsetOrMatches": {} - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "gcp_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new KMIP KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "kmip_key" - } - }, - "opts": { - "provider": "kmip" - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {}, - "insertedIds": { - "$$unsetOrMatches": {} - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "kmip_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new local KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "local_key" - } - }, - "opts": { - "provider": "local" - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {}, - "insertedIds": { - "$$unsetOrMatches": {} - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "local_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with current KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": {} - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 5, - "modifiedCount": 5, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {}, - "insertedIds": { - "$$unsetOrMatches": {} - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "projection": { - "masterKey": 1 - }, - "sort": { - "keyAltNames": 1 - } - }, - "expectResult": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "YXp1cmVhenVyZWF6dXJlYQ==", - "subType": "04" - } - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", - "subType": "04" - } - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "a21pcGttaXBrbWlwa21pcA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "kmip", - "keyId": "1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "find" - } - } - ] - } - ] - } - ] -} +{ + "description": "rewrapManyDataKey", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "azure_key" + ], + "keyMaterial": { + "$binary": { + "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "keyAltNames": [ + "gcp_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "keyAltNames": [ + "kmip_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + }, + { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba5" + }, + "keyAltNames": [ + "kmip_delegated_key" + ], + "keyMaterial": { + "$binary": { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip", + "keyId": "11", + "delegated": true + } + } + ] + } + ], + "tests": [ + { + "description": "no keys to rewrap due to no filter matches", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": "no_matching_keys" + }, + "opts": { + "provider": "local" + } + }, + "expectResult": { + "bulkWriteResult": { + "$$exists": false + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": "no_matching_keys" + }, + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new AWS KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "aws_key" + } + }, + "opts": { + "provider": "aws", + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "aws_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new Azure KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "azure_key" + } + }, + "opts": { + "provider": "azure", + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "azure_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new GCP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "gcp_key" + } + }, + "opts": { + "provider": "gcp", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "gcp_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new KMIP KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip_key" + } + }, + "opts": { + "provider": "kmip" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new KMIP delegated KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip_delegated_key" + } + }, + "opts": { + "provider": "kmip", + "masterKey": { + "delegated": true + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip_delegated_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with new local KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "local_key" + } + }, + "opts": { + "provider": "local" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "local_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap with current KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": {} + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 6, + "modifiedCount": 6, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "projection": { + "masterKey": 1 + }, + "sort": { + "keyAltNames": 1 + } + }, + "expectResult": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "aws", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba5" + }, + "masterKey": { + "provider": "kmip", + "keyId": "11", + "delegated": true + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "kmip", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": {}, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "$$type": "object" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml index cc20e1b1710..f231def489d 100644 --- a/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml +++ b/specifications/client-side-encryption/tests/unified/rewrapManyDataKey.yml @@ -1,438 +1,529 @@ -# To ensure consistent ordering for expectResult matching purposes, find -# commands sort the resulting documents in ascending order by the single-element -# keyAltNames array to ensure alphabetic order by original KMS provider as -# defined in initialData. -description: rewrapManyDataKey - -schemaVersion: "1.8" - -runOnRequirements: - - csfle: true - -createEntities: - - client: - id: &client0 client0 - observeEvents: - - commandStartedEvent - - clientEncryption: - id: &clientEncryption0 clientEncryption0 - clientEncryptionOpts: - keyVaultClient: *client0 - keyVaultNamespace: keyvault.datakeys - kmsProviders: - aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } - azure: { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } - gcp: { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } - kmip: { endpoint: { $$placeholder: 1 } } - local: { key: { $$placeholder: 1 } } - - database: - id: &database0 database0 - client: *client0 - databaseName: &database0Name keyvault - - collection: - id: &collection0 collection0 - database: *database0 - collectionName: &collection0Name datakeys - -initialData: - - databaseName: *database0Name - collectionName: *collection0Name - documents: - - _id: &aws_key_id { $binary: { base64: YXdzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } - keyAltNames: ["aws_key"] - keyMaterial: { $binary: { base64: AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L, subType: "00" } } - creationDate: { $date: { $numberLong: "1641024000000" } } - updateDate: { $date: { $numberLong: "1641024000000" } } - status: 1 - masterKey: &aws_masterkey - provider: aws - key: arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0 - region: us-east-1 - - _id: &azure_key_id { $binary: { base64: YXp1cmVhenVyZWF6dXJlYQ==, subType: "04" } } - keyAltNames: ["azure_key"] - keyMaterial: { $binary: { base64: pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==, subType: "00" } } - creationDate: { $date: { $numberLong: "1641024000000" } } - updateDate: { $date: { $numberLong: "1641024000000" } } - status: 1 - masterKey: &azure_masterkey - provider: azure - keyVaultEndpoint: key-vault-csfle.vault.azure.net - keyName: key-name-csfle - - _id: &gcp_key_id { $binary: { base64: Z2NwZ2NwZ2NwZ2NwZ2NwZw==, subType: "04" } } - keyAltNames: ["gcp_key"] - keyMaterial: { $binary: { base64: CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==, subType: "00" } } - creationDate: { $date: { $numberLong: "1641024000000" } } - updateDate: { $date: { $numberLong: "1641024000000" } } - status: 1 - masterKey: &gcp_masterkey - provider: gcp - projectId: devprod-drivers - location: global - keyRing: key-ring-csfle - keyName: key-name-csfle - - _id: &kmip_key_id { $binary: { base64: a21pcGttaXBrbWlwa21pcA==, subType: "04" } } - keyAltNames: ["kmip_key"] - keyMaterial: { $binary: { base64: CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==, subType: "00" } } - creationDate: { $date: { $numberLong: "1641024000000" } } - updateDate: { $date: { $numberLong: "1641024000000" } } - status: 1 - masterKey: &kmip_masterkey - provider: kmip - keyId: "1" - - _id: &local_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } - keyAltNames: ["local_key"] - keyMaterial: { $binary: { base64: ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==, subType: "00" } } - creationDate: { $date: { $numberLong: "1641024000000" } } - updateDate: { $date: { $numberLong: "1641024000000" } } - status: 1 - masterKey: &local_masterkey - provider: local - -tests: - - description: "no keys to rewrap due to no filter matches" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: { keyAltNames: no_matching_keys } - opts: - provider: local - expectResult: - # If no bulk write operation, then no bulk write result. - bulkWriteResult: { $$exists: false } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: { keyAltNames: no_matching_keys } - readConcern: { level: majority } - - - description: "rewrap with new AWS KMS provider" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: { keyAltNames: { $ne: aws_key } } - opts: - provider: aws - # Different key: 89fcc2c4-08b0-4bd9-9f25-e30687b580d0 -> 061334ae-07a8-4ceb-a813-8135540e837d. - masterKey: &new_aws_masterkey - key: arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d - region: us-east-1 - expectResult: - bulkWriteResult: - insertedCount: 0 - matchedCount: 4 - modifiedCount: 4 - deletedCount: 0 - upsertedCount: 0 - upsertedIds: {} - insertedIds: { $$unsetOrMatches: {} } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: { keyAltNames: { $ne: aws_key } } - readConcern: { level: majority } - - commandStartedEvent: - databaseName: *database0Name - command: - update: *collection0Name - ordered: true - updates: - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - writeConcern: { w: majority } - - - description: "rewrap with new Azure KMS provider" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: { keyAltNames: { $ne: azure_key } } - opts: - provider: azure - masterKey: &new_azure_masterkey - keyVaultEndpoint: key-vault-csfle.vault.azure.net - keyName: key-name-csfle - expectResult: - bulkWriteResult: - insertedCount: 0 - matchedCount: 4 - modifiedCount: 4 - deletedCount: 0 - upsertedCount: 0 - upsertedIds: {} - insertedIds: { $$unsetOrMatches: {} } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: { keyAltNames: { $ne: azure_key } } - readConcern: { level: majority } - - commandStartedEvent: - databaseName: *database0Name - command: - update: *collection0Name - ordered: true - updates: - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - writeConcern: { w: majority } - - - description: "rewrap with new GCP KMS provider" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: { keyAltNames: { $ne: gcp_key } } - opts: - provider: gcp - masterKey: &new_gcp_masterkey - projectId: devprod-drivers - location: global - keyRing: key-ring-csfle - keyName: key-name-csfle - expectResult: - bulkWriteResult: - insertedCount: 0 - matchedCount: 4 - modifiedCount: 4 - deletedCount: 0 - upsertedCount: 0 - upsertedIds: {} - insertedIds: { $$unsetOrMatches: {} } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: { keyAltNames: { $ne: gcp_key } } - readConcern: { level: majority } - - commandStartedEvent: - databaseName: *database0Name - command: - update: *collection0Name - ordered: true - updates: - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - writeConcern: { w: majority } - - - description: "rewrap with new KMIP KMS provider" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: { keyAltNames: { $ne: kmip_key } } - opts: - provider: kmip - expectResult: - bulkWriteResult: - insertedCount: 0 - matchedCount: 4 - modifiedCount: 4 - deletedCount: 0 - upsertedCount: 0 - upsertedIds: {} - insertedIds: { $$unsetOrMatches: {} } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: { keyAltNames: { $ne: kmip_key } } - readConcern: { level: majority } - - commandStartedEvent: - databaseName: *database0Name - command: - update: *collection0Name - ordered: true - updates: - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - writeConcern: { w: majority } - - - description: "rewrap with new local KMS provider" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: { keyAltNames: { $ne: local_key } } - opts: - provider: local - expectResult: - bulkWriteResult: - insertedCount: 0 - matchedCount: 4 - modifiedCount: 4 - deletedCount: 0 - upsertedCount: 0 - upsertedIds: {} - insertedIds: { $$unsetOrMatches: {} } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: { keyAltNames: { $ne: local_key } } - readConcern: { level: majority } - - commandStartedEvent: - databaseName: *database0Name - command: - update: *collection0Name - ordered: true - updates: - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - writeConcern: { w: majority } - - - description: "rewrap with current KMS provider" - operations: - - name: rewrapManyDataKey - object: *clientEncryption0 - arguments: - filter: {} - expectResult: - bulkWriteResult: - insertedCount: 0 - matchedCount: 5 - modifiedCount: 5 - deletedCount: 0 - upsertedCount: 0 - upsertedIds: {} - insertedIds: { $$unsetOrMatches: {} } - - name: find - object: *collection0 - arguments: - filter: {} - projection: { masterKey: 1 } - sort: { keyAltNames: 1 } - expectResult: - - { _id: *aws_key_id, masterKey: *aws_masterkey } - - { _id: *azure_key_id, masterKey: *azure_masterkey } - - { _id: *gcp_key_id, masterKey: *gcp_masterkey } - - { _id: *kmip_key_id, masterKey: *kmip_masterkey } - - { _id: *local_key_id, masterKey: *local_masterkey } - expectEvents: - - client: *client0 - events: - - commandStartedEvent: - databaseName: *database0Name - command: - find: *collection0Name - filter: {} - readConcern: { level: majority } - - commandStartedEvent: - databaseName: *database0Name - command: - update: *collection0Name - ordered: true - updates: - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - - q: { _id: { $$type: binData } } - u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } - multi: { $$unsetOrMatches: false } - upsert: { $$unsetOrMatches: false } - writeConcern: { w: majority } - - commandStartedEvent: { commandName: find } +# To ensure consistent ordering for expectResult matching purposes, find +# commands sort the resulting documents in ascending order by the single-element +# keyAltNames array to ensure alphabetic order by original KMS provider as +# defined in initialData. +description: rewrapManyDataKey + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + azure: { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + gcp: { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + kmip: { endpoint: { $$placeholder: 1 } } + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - _id: &aws_key_id { $binary: { base64: YXdzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } + keyAltNames: ["aws_key"] + keyMaterial: { $binary: { base64: AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &aws_masterkey + provider: aws + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + - _id: &azure_key_id { $binary: { base64: YXp1cmVhenVyZWF6dXJlYQ==, subType: "04" } } + keyAltNames: ["azure_key"] + keyMaterial: { $binary: { base64: pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &azure_masterkey + provider: azure + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + - _id: &gcp_key_id { $binary: { base64: Z2NwZ2NwZ2NwZ2NwZ2NwZw==, subType: "04" } } + keyAltNames: ["gcp_key"] + keyMaterial: { $binary: { base64: CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &gcp_masterkey + provider: gcp + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + - _id: &kmip_key_id { $binary: { base64: a21pcGttaXBrbWlwa21pcA==, subType: "04" } } + keyAltNames: ["kmip_key"] + keyMaterial: { $binary: { base64: CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &kmip_masterkey + provider: kmip + keyId: "1" + - _id: &local_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } + keyAltNames: ["local_key"] + keyMaterial: { $binary: { base64: ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &local_masterkey + provider: local + - _id: &kmip_delegated_key_id {$uuid: "7411e9af-c688-4df7-8143-5e60ae96cba5"} + keyAltNames: ["kmip_delegated_key"] + keyMaterial: { $binary: { base64: 5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &kmip_delegated_masterkey + provider: kmip + keyId: "11" + delegated: true + +tests: + - description: "no keys to rewrap due to no filter matches" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: no_matching_keys } + opts: + provider: local + expectResult: + # If no bulk write operation, then no bulk write result. + bulkWriteResult: { $$exists: false } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: no_matching_keys } + readConcern: { level: majority } + + - description: "rewrap with new AWS KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: aws_key } } + opts: + provider: aws + # Different key: 89fcc2c4-08b0-4bd9-9f25-e30687b580d0 -> 061334ae-07a8-4ceb-a813-8135540e837d. + masterKey: &new_aws_masterkey + key: "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d" + region: us-east-1 + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: aws_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap with new Azure KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: azure_key } } + opts: + provider: azure + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: azure_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: azure, <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap with new GCP KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: gcp_key } } + opts: + provider: gcp + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: gcp_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap with new KMIP KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: kmip_key } } + opts: + provider: kmip + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: kmip_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap with new KMIP delegated KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: kmip_delegated_key } } + opts: + provider: kmip + masterKey: + delegated: true + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: kmip_delegated_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap with new local KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: local_key } } + opts: + provider: local + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: local_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: local }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap with current KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: {} + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 6 + modifiedCount: 6 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + - name: find + object: *collection0 + arguments: + filter: {} + projection: { masterKey: 1 } + sort: { keyAltNames: 1 } + expectResult: + - { _id: *aws_key_id, masterKey: *aws_masterkey } + - { _id: *azure_key_id, masterKey: *azure_masterkey } + - { _id: *gcp_key_id, masterKey: *gcp_masterkey } + - { _id: *kmip_delegated_key_id, masterKey: *kmip_delegated_masterkey } + - { _id: *kmip_key_id, masterKey: *kmip_masterkey } + - { _id: *local_key_id, masterKey: *local_masterkey } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { $$type: object }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + - commandStartedEvent: { commandName: find } From a6a98c70a21735305afb102aadf934d1cfff63cf Mon Sep 17 00:00:00 2001 From: BorisDog Date: Fri, 12 Jul 2024 15:41:13 -0700 Subject: [PATCH 10/21] CSHARP-4920: Remove *.slnf filters (#1384) --- CSharpDriver.Bson.slnf | 10 ---------- CSharpDriver.Driver.Core.slnf | 12 ------------ CSharpDriver.Driver.slnf | 15 --------------- 3 files changed, 37 deletions(-) delete mode 100644 CSharpDriver.Bson.slnf delete mode 100644 CSharpDriver.Driver.Core.slnf delete mode 100644 CSharpDriver.Driver.slnf diff --git a/CSharpDriver.Bson.slnf b/CSharpDriver.Bson.slnf deleted file mode 100644 index 56265b22f69..00000000000 --- a/CSharpDriver.Bson.slnf +++ /dev/null @@ -1,10 +0,0 @@ -{ - "solution": { - "path": "CSharpDriver.sln", - "projects": [ - "src\\MongoDB.Bson\\MongoDB.Bson.csproj", - "tests\\MongoDB.Bson.TestHelpers\\MongoDB.Bson.TestHelpers.csproj", - "tests\\MongoDB.Bson.Tests\\MongoDB.Bson.Tests.csproj" - ] - } -} diff --git a/CSharpDriver.Driver.Core.slnf b/CSharpDriver.Driver.Core.slnf deleted file mode 100644 index 8ad13e9d183..00000000000 --- a/CSharpDriver.Driver.Core.slnf +++ /dev/null @@ -1,12 +0,0 @@ -{ - "solution": { - "path": "CSharpDriver.sln", - "projects": [ - "src\\MongoDB.Bson\\MongoDB.Bson.csproj", - "src\\MongoDB.Driver.Core\\MongoDB.Driver.Core.csproj", - "tests\\MongoDB.Bson.TestHelpers\\MongoDB.Bson.TestHelpers.csproj", - "tests\\MongoDB.Driver.Core.TestHelpers\\MongoDB.Driver.Core.TestHelpers.csproj", - "tests\\MongoDB.Driver.Core.Tests\\MongoDB.Driver.Core.Tests.csproj" - ] - } -} diff --git a/CSharpDriver.Driver.slnf b/CSharpDriver.Driver.slnf deleted file mode 100644 index 59d218d11f0..00000000000 --- a/CSharpDriver.Driver.slnf +++ /dev/null @@ -1,15 +0,0 @@ -{ - "solution": { - "path": "CSharpDriver.sln", - "projects": [ - "src\\MongoDB.Bson\\MongoDB.Bson.csproj", - "src\\MongoDB.Driver.Core\\MongoDB.Driver.Core.csproj", - "src\\MongoDB.Driver.GridFS\\MongoDB.Driver.GridFS.csproj", - "src\\MongoDB.Driver\\MongoDB.Driver.csproj", - "tests\\MongoDB.Bson.TestHelpers\\MongoDB.Bson.TestHelpers.csproj", - "tests\\MongoDB.Driver.Core.TestHelpers\\MongoDB.Driver.Core.TestHelpers.csproj", - "tests\\MongoDB.Driver.TestHelpers\\MongoDB.Driver.TestHelpers.csproj", - "tests\\MongoDB.Driver.Tests\\MongoDB.Driver.Tests.csproj" - ] - } -} From 0c5c09e5d98a3db927778dbf4a2e73f0109c4472 Mon Sep 17 00:00:00 2001 From: BorisDog Date: Mon, 15 Jul 2024 10:02:41 -0700 Subject: [PATCH 11/21] CSHARP-4921: Improve Nuget packages hygiene (#1386) --- README.md | 94 +------------------------------------- packageIcon.png | Bin 28235 -> 3810 bytes src/Directory.Build.props | 3 ++ 3 files changed, 4 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 52f1f370da3..e2495368ff1 100644 --- a/README.md +++ b/README.md @@ -76,96 +76,4 @@ Contributing Please see our [guidelines](CONTRIBUTING.md) for contributing to the driver. -### Maintainers: -* Boris Dogadov boris.dogadov@mongodb.com -* James Kovacs james.kovacs@mongodb.com -* Oleksandr Poliakov oleksandr.poliakov@mongodb.com -* Robert Stam robert@mongodb.com - -### Contributors: -* Alexander Aramov https://github.com/alex687 -* Bar Arnon https://github.com/I3arnon -* Wan Bachtiar https://github.com/sindbach -* Mark Benvenuto https://github.com/markbenvenuto -* Brian Buvinghausen https://github.com/buvinghausen -* Bit Diffusion Limited code@bitdiff.com -* Jimmy Bogard https://github.com/jbogard -* Ross Buggins https://github.com/rbugginsvia -* Nima Boscarino https://github.com/NimaBoscarino -* Oscar Bralo https://github.com/Oscarbralo -* Alex Brown https://github.com/alexjamesbrown -* Ethan Celletti https://github.com/Gekctek -* Chris Cho https://github.com/ccho-mongodb -* Adam Avery Cole https://github.com/adamaverycole -* Nate Contino https://github.com/nathan-contino-mongo -* Alex Dawes https://github.com/alexdawes -* Justin Dearing zippy1981@gmail.com -* Dan DeBilt dan.debilt@gmail.com -* Teun Duynstee teun@duynstee.com -* Einar Egilsson https://github.com/einaregilsson -* Ken Egozi mail@kenegozi.com -* Alexander Endris https://github.com/AlexEndris -* Daniel Goldman daniel@stackwave.com -* David Golub https://github.com/dgolub -* Simon Green simon@captaincodeman.com -* Bouke Haarsma https://github.com/Bouke -* James Hadwen james.hadwen@sociustec.com -* Nuri Halperin https://github.com/nurih -* Daniel Hegener daniel.hegener@fisglobal.com -* Nikola Irinchev https://github.com/nirinchev -* Jacob Jewell jacobjewell@eflexsystems.com -* Vincent Kam https://github.com/vincentkam -* Danny Kendrick https://github.com/dkendrick -* Ruslan Khasanbaev https://github.com/flaksirus -* Konstantin Khitrykh https://github.com/KonH -* Brian Knight brianknight10@gmail.com -* John Knoop https://github.com/johnknoop -* Andrey Kondratyev https://github.com/byTimo -* Anatoly Koperin https://github.com/ExM -* Nik Kolev nkolev@gmail.com -* Oleg Kosmakov https://github.com/kosmakoff -* Maksim Krautsou https://github.com/MaKCbIMKo -* Richard Kreuter richard@10gen.com -* Daniel Lee https://github.com/dlee148 -* Ming Yau Lee https://github.com/mingyaulee -* Kevin Lewis kevin.l.lewis@gmail.com -* Dow Liu redforks@gmail.com -* Chuck Lu https://github.com/chucklu -* Alex Lyman mail.alex.lyman@gmail.com -* Tomasz Masternak https://github.com/tmasternak -* Mikalai Mazurenka mikalai.mazurenka@mongodb.com -* John Murphy https://github.com/jsmurphy -* Alexander Nagy optimiz3@gmail.com -* Sridhar Nanjundeswaran https://github.com/sridharn -* Nathan https://github.com/terakilobyte -* Adelin Owona https://github.com/adelinowona -* Rachelle Palmer https://github.com/techbelle -* Rich Quackenbush rich.quackenbush@captiveaire.com -* Carl Reinke https://github.com/mindless2112 -* Rodrigo Reis https://github.com/rodrigoreis -* Gian Maria Ricci https://github.com/alkampfergit -* Andrew Rondeau github@andrewrondeau.com -* Ed Rooth edward.rooth@wallstreetjapan.com -* Katie Sadoff https://github.com/ksadoff -* Manas Sahu https://github.com/Zangetsu112 -* Sam558 https://github.com/Sam558 -* Vladimir Setyaev setyaev_v@pgstudio.io -* Sergey Shushlyapin https://github.com/sergeyshushlyapin -* Alexey Skalozub pieceofsummer@gmail.com -* Kevin Smith https://github.com/kevbite -* Pete Smith roysvork@gmail.com -* Matteo Spreafico https://github.com/MatteoSp -* staywellandy https://github.com/staywellandy -* Vyacheslav Stroy https://github.com/kreig -* Jake Sta. Teresa https://github.com/JakeStaTeresa -* Testo test1@doramail.com -* TimTim https://github.com/wegylexy -* Craig Wilson https://github.com/craiggwilson -* Zhmayev Yaroslav https://github.com/salaros -* Aristarkh Zagorodnikov https://github.com/onyxmaster -* Samir Boulema https://github.com/sboulema -* Dmitry Lukyanov https://github.com/DmitryLukyanov -* Andrea Balducci https://github.com/andreabalducci -* Sergei Lipin https://github.com/prchaoz - -If you have contributed and we have neglected to add you to this list please contact one of the maintainers to be added to the list (with apologies). +Thank you to [everyone](https://github.com/mongodb/mongo-csharp-driver/graphs/contributors) who has contributed to this project. diff --git a/packageIcon.png b/packageIcon.png index d5dfcc632b4a7570f278e3b044f2ded3756bfb52..e55bc6a1c91f1cbffea1b6f894d0c48f05a557f9 100644 GIT binary patch literal 3810 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 © 2010-present MongoDB Inc. MongoDB Inc. packageIcon.png + README.md + https://github.com/mongodb/mongo-csharp-driver/releases/tag/v$(Version) true https://www.mongodb.com/docs/drivers/csharp/ mongodb;mongo;nosql @@ -50,5 +52,6 @@ + From ce1a39470bc86f4e69e9fc8c2996af4807e5711c Mon Sep 17 00:00:00 2001 From: rstam Date: Tue, 16 Jul 2024 11:40:16 -0700 Subject: [PATCH 12/21] CSHARP-5180: Support additional numeric conversions involving Nullable. --- .../Ast/Optimizers/AstSimplifier.cs | 59 +- ...essionToAggregationExpressionTranslator.cs | 331 ++-- .../Jira/CSharp4048Tests.cs | 4 +- .../Jira/CSharp5180Tests.cs | 1596 +++++++++++++++++ 4 files changed, 1814 insertions(+), 176 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs index 3e4d72745fe..be03832fa6d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs @@ -16,7 +16,6 @@ using MongoDB.Bson; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; -using MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors; namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers @@ -37,6 +36,48 @@ public static TNode SimplifyAndConvert(TNode node) } #endregion + public override AstNode VisitCondExpression(AstCondExpression node) + { + // { $cond : [{ $eq : [expr1, null] }, null, expr2] } + if (node.If is AstBinaryExpression binaryIfpression && + binaryIfpression.Operator == AstBinaryOperator.Eq && + binaryIfpression.Arg1 is AstExpression expr1 && + binaryIfpression.Arg2 is AstConstantExpression constantComparandExpression && + constantComparandExpression.Value == BsonNull.Value && + node.Then is AstConstantExpression constantThenExpression && + constantThenExpression.Value == BsonNull.Value && + node.Else is AstExpression expr2) + { + // { $cond : [{ $eq : [expr, null] }, null, expr] } => expr + if (expr1 == expr2) + { + return Visit(expr2); + } + + // { $cond : [{ $eq : [expr, null] }, null, { $toT : expr }] } => { $toT : expr } for operators that map null to null + if (expr2 is AstUnaryExpression unaryElseExpression && + OperatorMapsNullToNull(unaryElseExpression.Operator) && + unaryElseExpression.Arg == expr1) + { + return Visit(expr2); + } + } + + return base.VisitCondExpression(node); + + static bool OperatorMapsNullToNull(AstUnaryOperator @operator) + { + return @operator switch + { + AstUnaryOperator.ToDecimal => true, + AstUnaryOperator.ToDouble => true, + AstUnaryOperator.ToInt => true, + AstUnaryOperator.ToLong => true, + _ => false + }; + } + } + public override AstNode VisitFieldOperationFilter(AstFieldOperationFilter node) { node = (AstFieldOperationFilter)base.VisitFieldOperationFilter(node); @@ -281,6 +322,22 @@ bool TrySimplifyAsLet(AstGetFieldExpression node, out AstExpression simplified) } } + public override AstNode VisitLetExpression(AstLetExpression node) + { + node = (AstLetExpression)base.VisitLetExpression(node); + + // { $let : { vars : { var : expr }, in : "$$var" } } => expr + if (node.Vars.Count == 1 && + node.Vars[0].Var.Name is string varName && + node.In is AstVarExpression varExpression && + varExpression.Name == varName) + { + return node.Vars[0].Value; + } + + return node; + } + public override AstNode VisitMapExpression(AstMapExpression node) { // { $map : { input : , as : "v", in : "$$v.x" } } => { $getField : { field : "x", input : } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs index 8596f64e2b0..e511e24bc6a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs @@ -30,110 +30,121 @@ public static AggregationExpression Translate(TranslationContext context, UnaryE { if (expression.NodeType == ExpressionType.Convert || expression.NodeType == ExpressionType.TypeAs) { - var expressionType = expression.Type; - if (expressionType == typeof(BsonValue)) + var sourceExpression = expression.Operand; + var sourceType = sourceExpression.Type; + var targetType = expression.Type; + + // handle double conversions like `(BsonValue)(object)x` + if (targetType == typeof(BsonValue) && + sourceExpression is UnaryExpression unarySourceExpression && + unarySourceExpression.NodeType == ExpressionType.Convert && + unarySourceExpression.Type == typeof(object)) { - return TranslateConvertToBsonValue(context, expression, expression.Operand); + sourceExpression = unarySourceExpression.Operand; } - var operandExpression = expression.Operand; - var operandTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, operandExpression); + var sourceTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, sourceExpression); + return Translate(expression, sourceType, targetType, sourceTranslation); + } - if (expressionType == operandExpression.Type) - { - return operandTranslation; - } + throw new ExpressionNotSupportedException(expression); + } - if (IsConvertEnumToUnderlyingType(expression)) - { - return TranslateConvertEnumToUnderlyingType(expression, operandTranslation); - } + private static AggregationExpression Translate(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) + { + if (targetType == sourceType) + { + return sourceTranslation; + } - if (IsConvertUnderlyingTypeToEnum(expression)) - { - return TranslateConvertUnderlyingTypeToEnum(expression, operandTranslation); - } + // from Nullable must be handled before to Nullable + if (IsConvertFromNullableType(sourceType)) + { + return TranslateConvertFromNullableType(expression, sourceType, targetType, sourceTranslation); + } - if (IsConvertEnumToEnum(expression)) - { - return TranslateConvertEnumToEnum(expression, operandTranslation); - } + if (IsConvertToNullableType(targetType)) + { + return TranslateConvertToNullableType(expression, sourceType, targetType, sourceTranslation); + } - if (IsConvertToBaseType(sourceType: operandExpression.Type, targetType: expressionType)) - { - return TranslateConvertToBaseType(expression, operandTranslation); - } + // from here on we know there are no longer any Nullable types involved - if (IsConvertToDerivedType(sourceType: operandExpression.Type, targetType: expressionType)) - { - return TranslateConvertToDerivedType(expression, operandTranslation); - } + if (targetType == typeof(BsonValue)) + { + return TranslateConvertToBsonValue(expression, sourceTranslation); + } - if (expressionType.IsConstructedGenericType && expressionType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - var valueType = expressionType.GetGenericArguments()[0]; - if (operandExpression.Type == valueType) - { - // use the same AST but with a new nullable serializer - var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(valueType); - var valueSerializerType = typeof(IBsonSerializer<>).MakeGenericType(valueType); - var constructorInfo = nullableSerializerType.GetConstructor(new[] { valueSerializerType }); - var nullableSerializer = (IBsonSerializer)constructorInfo.Invoke(new[] { operandTranslation.Serializer }); - return new AggregationExpression(expression, operandTranslation.Ast, nullableSerializer); - } - } + if (IsConvertEnumToUnderlyingType(sourceType, targetType)) + { + return TranslateConvertEnumToUnderlyingType(expression, sourceType, targetType, sourceTranslation); + } - var ast = operandTranslation.Ast; - IBsonSerializer serializer; - if (expressionType.IsInterface) - { - // when an expression is cast to an interface it's a no-op as far as we're concerned - // and we can just use the serializer for the concrete type and members not defined in the interface will just be ignored - serializer = operandTranslation.Serializer; - } - else + if (IsConvertUnderlyingTypeToEnum(sourceType, targetType)) + { + return TranslateConvertUnderlyingTypeToEnum(expression, sourceType, targetType, sourceTranslation); + } + + if (IsConvertEnumToEnum(sourceType, targetType)) + { + return TranslateConvertEnumToEnum(expression, sourceType, targetType, sourceTranslation); + } + + if (IsConvertToBaseType(sourceType, targetType)) + { + return TranslateConvertToBaseType(expression, sourceType, targetType, sourceTranslation); + } + + if (IsConvertToDerivedType(sourceType, targetType)) + { + return TranslateConvertToDerivedType(expression, targetType, sourceTranslation); + } + + var ast = sourceTranslation.Ast; + IBsonSerializer serializer; + if (targetType.IsInterface) + { + // when an expression is cast to an interface it's a no-op as far as we're concerned + // and we can just use the serializer for the concrete type and members not defined in the interface will just be ignored + serializer = sourceTranslation.Serializer; + } + else + { + AstExpression to; + switch (targetType.FullName) { - AstExpression to; - switch (expressionType.FullName) - { - case "MongoDB.Bson.ObjectId": to = "objectId"; serializer = ObjectIdSerializer.Instance; break; - case "System.Boolean": to = "bool"; serializer = BooleanSerializer.Instance; break; - case "System.DateTime": to = "date"; serializer = DateTimeSerializer.Instance; break; - case "System.Decimal": to = "decimal"; serializer = DecimalSerializer.Decimal128Instance; break; // not the default representation - case "System.Double": to = "double"; serializer = DoubleSerializer.Instance; break; - case "System.Int32": to = "int"; serializer = Int32Serializer.Instance; break; - case "System.Int64": to = "long"; serializer = Int64Serializer.Instance; break; - case "System.String": to = "string"; serializer = StringSerializer.Instance; break; - default: throw new ExpressionNotSupportedException(expression, because: $"conversion to {expressionType} is not supported"); - } - - ast = AstExpression.Convert(ast, to); + case "MongoDB.Bson.ObjectId": to = "objectId"; serializer = ObjectIdSerializer.Instance; break; + case "System.Boolean": to = "bool"; serializer = BooleanSerializer.Instance; break; + case "System.DateTime": to = "date"; serializer = DateTimeSerializer.Instance; break; + case "System.Decimal": to = "decimal"; serializer = DecimalSerializer.Decimal128Instance; break; // not the default representation + case "System.Double": to = "double"; serializer = DoubleSerializer.Instance; break; + case "System.Int32": to = "int"; serializer = Int32Serializer.Instance; break; + case "System.Int64": to = "long"; serializer = Int64Serializer.Instance; break; + case "System.String": to = "string"; serializer = StringSerializer.Instance; break; + default: throw new ExpressionNotSupportedException(expression, because: $"conversion to {targetType} is not supported"); } - return new AggregationExpression(expression, ast, serializer); + ast = AstExpression.Convert(ast, to); } - throw new ExpressionNotSupportedException(expression); + return new AggregationExpression(expression, ast, serializer); } - private static bool IsConvertEnumToEnum(UnaryExpression expression) + private static bool IsConvertEnumToEnum(Type sourceType, Type targetType) { - var sourceType = expression.Operand.Type; - var targetType = expression.Type; + return sourceType.IsEnum && targetType.IsEnum; + } + private static bool IsConvertEnumToUnderlyingType(Type sourceType, Type targetType) + { return - sourceType.IsEnumOrNullableEnum(out _, out _) && - targetType.IsEnumOrNullableEnum(out _, out _); + sourceType.IsEnum(out var underlyingType) && + targetType == underlyingType; } - private static bool IsConvertEnumToUnderlyingType(UnaryExpression expression) + private static bool IsConvertFromNullableType(Type sourceType) { - var sourceType = expression.Operand.Type; - var targetType = expression.Type; - - return - sourceType.IsEnumOrNullableEnum(out _, out var underlyingType) && - targetType.IsSameAsOrNullableOf(underlyingType); + return sourceType.IsNullable(); } private static bool IsConvertToBaseType(Type sourceType, Type targetType) @@ -146,127 +157,112 @@ private static bool IsConvertToDerivedType(Type sourceType, Type targetType) return targetType.IsSubclassOf(sourceType); } - private static bool IsConvertUnderlyingTypeToEnum(UnaryExpression expression) + private static bool IsConvertToNullableType(Type targetType) { - var sourceType = expression.Operand.Type; - var targetType = expression.Type; + return targetType.IsNullable(); + } + private static bool IsConvertUnderlyingTypeToEnum(Type sourceType, Type targetType) + { return - targetType.IsEnumOrNullableEnum(out _, out var underlyingType) && - sourceType.IsSameAsOrNullableOf(underlyingType); + targetType.IsEnum(out var underlyingType) && + sourceType == underlyingType; } - private static AggregationExpression TranslateConvertToBaseType(UnaryExpression expression, AggregationExpression operandTranslation) + private static AggregationExpression TranslateConvertToBaseType(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) { - var baseType = expression.Type; - var derivedType = expression.Operand.Type; - var derivedTypeSerializer = operandTranslation.Serializer; - var downcastingSerializer = DowncastingSerializer.Create(baseType, derivedType, derivedTypeSerializer); + var derivedTypeSerializer = sourceTranslation.Serializer; + var downcastingSerializer = DowncastingSerializer.Create(targetType, sourceType, derivedTypeSerializer); - return new AggregationExpression(expression, operandTranslation.Ast, downcastingSerializer); + return new AggregationExpression(expression, sourceTranslation.Ast, downcastingSerializer); } - private static AggregationExpression TranslateConvertToDerivedType(UnaryExpression expression, AggregationExpression operandTranslation) + private static AggregationExpression TranslateConvertToDerivedType(UnaryExpression expression, Type targetType, AggregationExpression sourceTranslation) { - var serializer = BsonSerializer.LookupSerializer(expression.Type); + var serializer = BsonSerializer.LookupSerializer(targetType); - return new AggregationExpression(expression, operandTranslation.Ast, serializer); + return new AggregationExpression(expression, sourceTranslation.Ast, serializer); } - private static AggregationExpression TranslateConvertToBsonValue(TranslationContext context, UnaryExpression expression, Expression operand) + private static AggregationExpression TranslateConvertToBsonValue(UnaryExpression expression, AggregationExpression sourceTranslation) { - // handle double conversions like `(BsonValue)(object)x.Anything` - if (operand is UnaryExpression unaryExpression && - unaryExpression.NodeType == ExpressionType.Convert && - unaryExpression.Type == typeof(object)) - { - operand = unaryExpression.Operand; - } - - var operandTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, operand); - - return new AggregationExpression(expression, operandTranslation.Ast, BsonValueSerializer.Instance); + return new AggregationExpression(expression, sourceTranslation.Ast, BsonValueSerializer.Instance); } - private static AggregationExpression TranslateConvertEnumToEnum(UnaryExpression expression, AggregationExpression operandTranslation) + private static AggregationExpression TranslateConvertEnumToEnum(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) { - var sourceType = expression.Operand.Type; - var targetType = expression.Type; - - if (!sourceType.IsEnumOrNullableEnum(out var sourceEnumType, out _)) + if (!sourceType.IsEnum) { - throw new ExpressionNotSupportedException(expression, because: "source type is not an enum or nullable enum"); + throw new ExpressionNotSupportedException(expression, because: "source type is not an enum"); } - if (!targetType.IsEnumOrNullableEnum(out var targetEnumType, out _)) + if (!targetType.IsEnum) { - throw new ExpressionNotSupportedException(expression, because: "target type is not an enum or nullable enum"); + throw new ExpressionNotSupportedException(expression, because: "target type is not an enum"); } - var sourceSerializer = operandTranslation.Serializer; - IBsonSerializer targetEnumSerializer; - if (targetEnumType == sourceEnumType) + var sourceSerializer = sourceTranslation.Serializer; + if (sourceSerializer is IHasRepresentationSerializer sourceHasRepresentationSerializer && + !SerializationHelper.IsNumericRepresentation(sourceHasRepresentationSerializer.Representation)) { - targetEnumSerializer = sourceSerializer is INullableSerializer sourceNullableSerializer ? - sourceNullableSerializer.ValueSerializer : - sourceSerializer; + throw new ExpressionNotSupportedException(expression, because: "source enum is not represented as a number"); } - else + + var targetSerializer = EnumSerializer.Create(targetType); + return new AggregationExpression(expression, sourceTranslation.Ast, targetSerializer); + } + + private static AggregationExpression TranslateConvertEnumToUnderlyingType(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) + { + var enumSerializer = sourceTranslation.Serializer; + var targetSerializer = EnumUnderlyingTypeSerializer.Create(enumSerializer); + return new AggregationExpression(expression, sourceTranslation.Ast, targetSerializer); + } + + private static AggregationExpression TranslateConvertFromNullableType(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) + { + if (sourceType.IsNullable(out var sourceValueType)) { - if (sourceSerializer is IHasRepresentationSerializer sourceHasRepresentationSerializer && - !SerializationHelper.IsNumericRepresentation(sourceHasRepresentationSerializer.Representation)) - { - throw new ExpressionNotSupportedException(expression, because: "source enum is not represented as a number"); - } + var (sourceVarBinding, sourceAst) = AstExpression.UseVarIfNotSimple("source", sourceTranslation.Ast); + var sourceNullableSerializer = (INullableSerializer)sourceTranslation.Serializer; + var sourceValueSerializer = sourceNullableSerializer.ValueSerializer; + var sourceValueAggregationExpression = new AggregationExpression(expression.Operand, sourceAst, sourceValueSerializer); + var convertTranslation = Translate(expression, sourceValueType, targetType, sourceValueAggregationExpression); - targetEnumSerializer = EnumSerializer.Create(targetEnumType); - } + // note: we would have liked to throw a query execution error here if the value is null and the target type is not nullable but there is no way to do that in MQL + // so we just return null instead and the user must check for null themselves if they want to define what happens when the value is null + // but see SERVER-78092 and the proposed $error operator - var targetSerializer = targetType.IsNullable() ? - NullableSerializer.Create(targetEnumSerializer) : - targetEnumSerializer; + var ast = AstExpression.Let( + sourceVarBinding, + AstExpression.Cond(AstExpression.Eq(sourceAst, BsonNull.Value), BsonNull.Value, convertTranslation.Ast)); - return new AggregationExpression(expression, operandTranslation.Ast, targetSerializer); + return new AggregationExpression(expression, ast, convertTranslation.Serializer); + } + + throw new ExpressionNotSupportedException(expression, because: "sourceType is not nullable"); } - private static AggregationExpression TranslateConvertEnumToUnderlyingType(UnaryExpression expression, AggregationExpression operandTranslation) + private static AggregationExpression TranslateConvertToNullableType(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) { - var sourceType = expression.Operand.Type; - var targetType = expression.Type; - - IBsonSerializer enumSerializer; if (sourceType.IsNullable()) { - var nullableSerializer = (INullableSerializer)operandTranslation.Serializer; - enumSerializer = nullableSerializer.ValueSerializer; - } - else - { - enumSerializer = operandTranslation.Serializer; + // ConvertFromNullableType should have been called first + throw new ExpressionNotSupportedException(expression, because: "sourceType is nullable"); } - IBsonSerializer targetSerializer; - var enumUnderlyingTypeSerializer = EnumUnderlyingTypeSerializer.Create(enumSerializer); - if (targetType.IsNullable()) - { - targetSerializer = NullableSerializer.Create(enumUnderlyingTypeSerializer); - } - else + if (targetType.IsNullable(out var targetValueType)) { - targetSerializer = enumUnderlyingTypeSerializer; + var convertTranslation = Translate(expression, sourceType, targetValueType, sourceTranslation); + var nullableSerializer = NullableSerializer.Create(convertTranslation.Serializer); + return new AggregationExpression(expression, convertTranslation.Ast, nullableSerializer); } - return new AggregationExpression(expression, operandTranslation.Ast, targetSerializer); + throw new ExpressionNotSupportedException(expression, because: "targetType is not nullable"); } - private static AggregationExpression TranslateConvertUnderlyingTypeToEnum(UnaryExpression expression, AggregationExpression operandTranslation) + private static AggregationExpression TranslateConvertUnderlyingTypeToEnum(UnaryExpression expression, Type sourceType, Type targetType, AggregationExpression sourceTranslation) { - var targetType = expression.Type; - - var valueSerializer = operandTranslation.Serializer; - if (valueSerializer is INullableSerializer nullableSerializer) - { - valueSerializer = nullableSerializer.ValueSerializer; - } + var valueSerializer = sourceTranslation.Serializer; IBsonSerializer targetSerializer; if (valueSerializer is IEnumUnderlyingTypeSerializer enumUnderlyingTypeSerializer) @@ -275,21 +271,10 @@ private static AggregationExpression TranslateConvertUnderlyingTypeToEnum(UnaryE } else { - var enumType = targetType; - if (targetType.IsNullable(out var wrappedType)) - { - enumType = wrappedType; - } - - targetSerializer = EnumSerializer.Create(enumType); - } - - if (targetType.IsNullableEnum()) - { - targetSerializer = NullableSerializer.Create(targetSerializer); + targetSerializer = EnumSerializer.Create(targetType); } - return new AggregationExpression(expression, operandTranslation.Ast, targetSerializer); + return new AggregationExpression(expression, sourceTranslation.Ast, targetSerializer); } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4048Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4048Tests.cs index 6cb2e712fb9..402a780bc0d 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4048Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4048Tests.cs @@ -237,7 +237,7 @@ public void IGrouping_Aggregate_with_seed_and_func_and_resultSelector_of_root_sh var expectedStages = new[] { "{ $group : { _id : '$_id', _elements : { $push: '$$ROOT' } } }", - "{ $project : { _id : '$_id', Result : { $let : { vars : { a : { $reduce : { input : '$_elements', initialValue : 0, in : { $add : ['$$value', '$$this.X'] } } } }, in : '$$a' } } } }", + "{ $project : { _id : '$_id', Result : { $reduce : { input : '$_elements', initialValue : 0, in : { $add : ['$$value', '$$this.X'] } } } } }", "{ $sort : { _id : 1 } }" }; AssertStages(stages, expectedStages); @@ -262,7 +262,7 @@ public void IGrouping_Aggregate_with_seed_and_func_and_resultSelector_of_scalar_ var expectedStages = new[] { "{ $group : { _id : '$_id', _elements : { $push: '$X' } } }", - "{ $project : { _id : '$_id', Result : { $let : { vars : { a : { $reduce : { input : '$_elements', initialValue : 0, in : { $add : ['$$value', '$$this'] } } } }, in : '$$a' } } } }", + "{ $project : { _id : '$_id', Result : { $reduce : { input : '$_elements', initialValue : 0, in : { $add : ['$$value', '$$this'] } } } } }", "{ $sort : { _id : 1 } }" }; AssertStages(stages, expectedStages); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs new file mode 100644 index 00000000000..69bfcee131f --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs @@ -0,0 +1,1596 @@ +/* Copyright 2010-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. +*/ + +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp5180Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { Decimal : '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Decimal', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$Decimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$Decimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$Decimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Decimal', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$Decimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$Decimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Decimal_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.Decimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Decimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$Decimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$Double' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { Double : '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Double', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$Double' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$Double' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$Double' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Double', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$Double' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Double_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.Double); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Double', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$Double' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$Int' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$Int' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { Int : '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Int', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$Int' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$Int' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$Int' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Int', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Int_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.Int); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Int', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$Int' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$Long' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$Long' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$Long' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { Long: '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Long', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$Long' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$Long' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$Long' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_Long_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.Long); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$Long', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$Long', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1L); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableDecimal : '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableDecimal', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$NullableDecimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$NullableDecimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$NullableDecimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableDecimal: '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableDecimal', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$NullableDecimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$NullableDecimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDecimal_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.NullableDecimal); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDecimal', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$NullableDecimal' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$NullableDouble' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableDouble : '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableDouble', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$NullableDouble' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$NullableDouble' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$NullableDouble' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableDouble: '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableDouble', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$NullableDouble' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableDouble_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.NullableDouble); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableDouble', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$NullableDouble' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$NullableInt' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$NullableInt' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableInt : '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableInt', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$NullableInt' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$NullableInt' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$NullableInt' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableInt: '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableInt', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableInt_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.NullableInt); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableInt', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toLong : '$NullableInt' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$NullableLong' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$NullableLong' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$NullableLong' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableLong: '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableLong', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_nullable_decimal_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (decimal?)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDecimal : '$NullableLong' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0M); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_nullable_double_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (double?)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toDouble : '$NullableLong' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1.0); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_nullable_int_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (int?)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0: '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : { $toInt : '$NullableLong' }, _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + [Theory] + [ParameterAttributeData] + public void Cast_NullableLong_to_nullable_long_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (long?)x.NullableLong); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { NullableLong: '$NullableLong', _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : '$NullableLong', _id : 0 } }"); + } + + var result = queryable.First(); + result.Should().Be(1); + } + + private IMongoCollection GetCollection(LinqProvider linqProvider) + { + var collection = GetCollection("test", linqProvider); + CreateCollection( + collection, + new C + { + Id = 1, + Decimal = 1.0M, + Double = 1.0, + Int = 1, + Long = 1L, + NullableDecimal = 1.0M, + NullableDouble = 1.0, + NullableInt = 1, + NullableLong = 1L + }); + return collection; + } + + private class C + { + public int Id { get; set; } + [BsonRepresentation(BsonType.Decimal128)] public decimal Decimal { get; set; } + public double Double { get; set; } + public int Int { get; set; } + public long Long { get; set; } + [BsonRepresentation(BsonType.Decimal128)] public decimal? NullableDecimal { get; set; } + public double? NullableDouble { get; set; } + public int? NullableInt { get; set; } + public long? NullableLong { get; set; } + } + } +} From db235b7c75378cad0a7c0a325e54622aa1c26341 Mon Sep 17 00:00:00 2001 From: rstam Date: Tue, 16 Jul 2024 13:41:47 -0700 Subject: [PATCH 13/21] CSHARP-5180: Skip new tests on older servers. --- .../Jira/CSharp5180Tests.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs index 69bfcee131f..cf22255b317 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5180Tests.cs @@ -16,6 +16,8 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using MongoDB.TestHelpers.XunitExtensions; using Xunit; @@ -53,6 +55,7 @@ public void Cast_Decimal_to_decimal_should_work( public void Cast_Decimal_to_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -77,6 +80,7 @@ public void Cast_Decimal_to_double_should_work( public void Cast_Decimal_to_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -101,6 +105,7 @@ public void Cast_Decimal_to_int_should_work( public void Cast_Decimal_to_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -149,6 +154,7 @@ public void Cast_Decimal_to_nullable_decimal_should_work( public void Cast_Decimal_to_nullable_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -173,6 +179,7 @@ public void Cast_Decimal_to_nullable_double_should_work( public void Cast_Decimal_to_nullable_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -197,6 +204,7 @@ public void Cast_Decimal_to_nullable_int_should_work( public void Cast_Decimal_to_nullable_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -221,6 +229,7 @@ public void Cast_Decimal_to_nullable_long_should_work( public void Cast_Double_to_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -269,6 +278,7 @@ public void Cast_Double_to_double_should_work( public void Cast_Double_to_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -293,6 +303,7 @@ public void Cast_Double_to_int_should_work( public void Cast_Double_to_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -317,6 +328,7 @@ public void Cast_Double_to_long_should_work( public void Cast_Double_to_nullable_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -365,6 +377,7 @@ public void Cast_Double_to_nullable_double_should_work( public void Cast_Double_to_nullable_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -389,6 +402,7 @@ public void Cast_Double_to_nullable_int_should_work( public void Cast_Double_to_nullable_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -413,6 +427,7 @@ public void Cast_Double_to_nullable_long_should_work( public void Cast_Int_to_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -437,6 +452,7 @@ public void Cast_Int_to_decimal_should_work( public void Cast_Int_to_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -485,6 +501,7 @@ public void Cast_Int_to_int_should_work( public void Cast_Int_to_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -509,6 +526,7 @@ public void Cast_Int_to_long_should_work( public void Cast_Int_to_nullable_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -533,6 +551,7 @@ public void Cast_Int_to_nullable_decimal_should_work( public void Cast_Int_to_nullable_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -581,6 +600,7 @@ public void Cast_Int_to_nullable_int_should_work( public void Cast_Int_to_nullable_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -605,6 +625,7 @@ public void Cast_Int_to_nullable_long_should_work( public void Cast_Long_to_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -629,6 +650,7 @@ public void Cast_Long_to_decimal_should_work( public void Cast_Long_to_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -653,6 +675,7 @@ public void Cast_Long_to_double_should_work( public void Cast_Long_to_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -701,6 +724,7 @@ public void Cast_Long_to_long_should_work( public void Cast_Long_to_nullable_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -725,6 +749,7 @@ public void Cast_Long_to_nullable_decimal_should_work( public void Cast_Long_to_nullable_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -749,6 +774,7 @@ public void Cast_Long_to_nullable_double_should_work( public void Cast_Long_to_nullable_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -821,6 +847,7 @@ public void Cast_NullableDecimal_to_decimal_should_work( public void Cast_NullableDecimal_to_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -845,6 +872,7 @@ public void Cast_NullableDecimal_to_double_should_work( public void Cast_NullableDecimal_to_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -869,6 +897,7 @@ public void Cast_NullableDecimal_to_int_should_work( public void Cast_NullableDecimal_to_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -917,6 +946,7 @@ public void Cast_NullableDecimal_to_nullable_decimal_should_work( public void Cast_NullableDecimal_to_nullable_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -941,6 +971,7 @@ public void Cast_NullableDecimal_to_nullable_double_should_work( public void Cast_NullableDecimal_to_nullable_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -965,6 +996,7 @@ public void Cast_NullableDecimal_to_nullable_int_should_work( public void Cast_NullableDecimal_to_nullable_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -989,6 +1021,7 @@ public void Cast_NullableDecimal_to_nullable_long_should_work( public void Cast_NullableDouble_to_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1037,6 +1070,7 @@ public void Cast_NullableDouble_to_double_should_work( public void Cast_NullableDouble_to_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1061,6 +1095,7 @@ public void Cast_NullableDouble_to_int_should_work( public void Cast_NullableDouble_to_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1085,6 +1120,7 @@ public void Cast_NullableDouble_to_long_should_work( public void Cast_NullableDouble_to_nullable_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1133,6 +1169,7 @@ public void Cast_NullableDouble_to_nullable_double_should_work( public void Cast_NullableDouble_to_nullable_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1157,6 +1194,7 @@ public void Cast_NullableDouble_to_nullable_int_should_work( public void Cast_NullableDouble_to_nullable_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1181,6 +1219,7 @@ public void Cast_NullableDouble_to_nullable_long_should_work( public void Cast_NullableInt_to_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1205,6 +1244,7 @@ public void Cast_NullableInt_to_decimal_should_work( public void Cast_NullableInt_to_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1253,6 +1293,7 @@ public void Cast_NullableInt_to_int_should_work( public void Cast_NullableInt_to_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1277,6 +1318,7 @@ public void Cast_NullableInt_to_long_should_work( public void Cast_NullableInt_to_nullable_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1301,6 +1343,7 @@ public void Cast_NullableInt_to_nullable_decimal_should_work( public void Cast_NullableInt_to_nullable_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1349,6 +1392,7 @@ public void Cast_NullableInt_to_nullable_int_should_work( public void Cast_NullableInt_to_nullable_long_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1373,6 +1417,7 @@ public void Cast_NullableInt_to_nullable_long_should_work( public void Cast_NullableLong_to_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1397,6 +1442,7 @@ public void Cast_NullableLong_to_decimal_should_work( public void Cast_NullableLong_to_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1421,6 +1467,7 @@ public void Cast_NullableLong_to_double_should_work( public void Cast_NullableLong_to_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1469,6 +1516,7 @@ public void Cast_NullableLong_to_long_should_work( public void Cast_NullableLong_to_nullable_decimal_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1493,6 +1541,7 @@ public void Cast_NullableLong_to_nullable_decimal_should_work( public void Cast_NullableLong_to_nullable_double_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() @@ -1517,6 +1566,7 @@ public void Cast_NullableLong_to_nullable_double_should_work( public void Cast_NullableLong_to_nullable_int_should_work( [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) { + RequireServer.Check().Supports(Feature.ToConversionOperators); var collection = GetCollection(linqProvider); var queryable = collection.AsQueryable() From 41e352e7e6d4d64cfafe6de7381e31bdaf13d89a Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:39:21 -0700 Subject: [PATCH 14/21] CSHARP-5181: Remove Serverless Proxy Incremental Rollout Tests (#1389) --- evergreen/evergreen.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/evergreen/evergreen.yml b/evergreen/evergreen.yml index 7a96695fad9..da5703f2715 100644 --- a/evergreen/evergreen.yml +++ b/evergreen/evergreen.yml @@ -2106,10 +2106,6 @@ axes: display_name: "Serverless Passthrough Proxy" variables: VAULT_NAME: "serverless" - - id: "Terminating" - display_name: "Serverless Terminating Proxy" - variables: - VAULT_NAME: "serverless_next" task_groups: - name: testazurekms-task-group @@ -2830,7 +2826,7 @@ buildvariants: - matrix_name: analyzer-tests batchtime: 720 # 12 hours matrix_spec: - os: ["ubuntu-2004", "windows-64"] + os: ["ubuntu-2004", "windows-64"] display_name: "Analyzer tests on ${os}" tasks: - name: test-analyzer From 46f39d58fdb78efa76ffa7f940b2e382c3a9fdaa Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Thu, 18 Jul 2024 08:58:06 -0700 Subject: [PATCH 15/21] CSHARP-4017: Fix flaky Aggregate_with__out_includes_read_preference_for_5.0__server error (#1391) --- .../ServerSelectors/WritableServerSelector.cs | 7 +- .../CompositeServerSelectorTests.cs | 2 +- .../DelegateServerSelectorTests.cs | 2 +- .../EndPointServerSelectorTests.cs | 2 +- .../LatencyLimitingServerSelectorTests.cs | 2 +- .../RandomServerSelectorTests.cs | 2 +- .../ReadPreferenceServerSelectorTests.cs | 2 +- .../WritableServerSelectorTests.cs | 116 ++++++++++++++++++ 8 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/WritableServerSelectorTests.cs diff --git a/src/MongoDB.Driver.Core/Core/Clusters/ServerSelectors/WritableServerSelector.cs b/src/MongoDB.Driver.Core/Core/Clusters/ServerSelectors/WritableServerSelector.cs index 34e12fa8eda..c48478fbb27 100644 --- a/src/MongoDB.Driver.Core/Core/Clusters/ServerSelectors/WritableServerSelector.cs +++ b/src/MongoDB.Driver.Core/Core/Clusters/ServerSelectors/WritableServerSelector.cs @@ -98,7 +98,7 @@ public override string ToString() private bool CanUseSecondaries(ClusterDescription cluster, List servers) { - if (_mayUseSecondary?.ReadPreference == null || servers.Count == 0) + if (_mayUseSecondary?.ReadPreference == null) { return false; } @@ -107,6 +107,11 @@ private bool CanUseSecondaries(ClusterDescription cluster, List _mayUseSecondary.CanUseSecondary(s)); case ClusterType.LoadBalanced: diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/CompositeServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/CompositeServerSelectorTests.cs index 9265951cebf..78878dc0f51 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/CompositeServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/CompositeServerSelectorTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-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. diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/DelegateServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/DelegateServerSelectorTests.cs index 5b539cf4a12..8c05d0aa3d2 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/DelegateServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/DelegateServerSelectorTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-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. diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/EndPointServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/EndPointServerSelectorTests.cs index 80c77dad84c..6dba1ea486f 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/EndPointServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/EndPointServerSelectorTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-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. diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/LatencyLimitingServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/LatencyLimitingServerSelectorTests.cs index 5f55c16151b..787cd17c5ce 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/LatencyLimitingServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/LatencyLimitingServerSelectorTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-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. diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/RandomServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/RandomServerSelectorTests.cs index aeab3484cd3..53b0a895e74 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/RandomServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/RandomServerSelectorTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-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. diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/ReadPreferenceServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/ReadPreferenceServerSelectorTests.cs index 9986f367d49..aac2575f627 100644 --- a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/ReadPreferenceServerSelectorTests.cs +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/ReadPreferenceServerSelectorTests.cs @@ -1,4 +1,4 @@ -/* Copyright 2013-present MongoDB Inc. +/* Copyright 2010-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. diff --git a/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/WritableServerSelectorTests.cs b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/WritableServerSelectorTests.cs new file mode 100644 index 00000000000..4e96954d55b --- /dev/null +++ b/tests/MongoDB.Driver.Core.Tests/Core/Clusters/ServerSelectors/WritableServerSelectorTests.cs @@ -0,0 +1,116 @@ +/* Copyright 2010-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. +*/ + +using System; +using System.Collections.Generic; +using System.Net; +using FluentAssertions; +using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Helpers; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.Operations; +using MongoDB.Driver.Core.Servers; +using Xunit; + +namespace MongoDB.Driver.Core.Clusters.ServerSelectors +{ + public class WritableServerSelectorTests + { + [Theory] + [MemberData(nameof(ReplicaSetTestCases))] + public void WritableServerSelector_should_work( + ReadPreference readPreference, + ClusterDescription cluster, + ReadPreference expectedReadPreference, + IEnumerable expectedServers) + { + IMayUseSecondaryCriteria mayUseSecondary = null; + if (readPreference != null) + { + mayUseSecondary = new AggregateToCollectionOperation.MayUseSecondary(readPreference); + } + + var selector = new WritableServerSelector(mayUseSecondary); + var results = selector.SelectServers(cluster, cluster.Servers); + + results.Should().BeEquivalentTo(expectedServers); + if (readPreference != null) + { + selector.MayUseSecondary.EffectiveReadPreference.Should().Be(expectedReadPreference); + } + } + + public static IEnumerable ReplicaSetTestCases() + { + var clusterId = new ClusterId(); + var primary = ServerDescriptionHelper.Connected(clusterId, new DnsEndPoint("localhost", 27017), ServerType.ReplicaSetPrimary, wireVersionRange: new Range(WireVersion.Server70, WireVersion.Server70)); + var secondary1Server70 = ServerDescriptionHelper.Connected(clusterId, new DnsEndPoint("localhost", 27018), ServerType.ReplicaSetSecondary, wireVersionRange: new Range(WireVersion.Server70, WireVersion.Server70)); + var secondary2Server70 = ServerDescriptionHelper.Connected(clusterId, new DnsEndPoint("localhost", 27019), ServerType.ReplicaSetSecondary, wireVersionRange: new Range(WireVersion.Server70, WireVersion.Server70)); + var secondary3Server42 = ServerDescriptionHelper.Connected(clusterId, new DnsEndPoint("localhost", 27020), ServerType.ReplicaSetSecondary, wireVersionRange: new Range(WireVersion.Server42, WireVersion.Server42)); + + yield return new object[] + { + ReadPreference.SecondaryPreferred, + new ClusterDescription( + clusterId, + false, + null, + ClusterType.ReplicaSet, + Array.Empty()), + ReadPreference.SecondaryPreferred, + Array.Empty() + }; + + yield return new object[] + { + ReadPreference.SecondaryPreferred, + new ClusterDescription( + clusterId, + false, + null, + ClusterType.ReplicaSet, + new[] { primary }), + ReadPreference.SecondaryPreferred, + new[] { primary } + }; + + yield return new object[] + { + ReadPreference.SecondaryPreferred, + new ClusterDescription( + clusterId, + false, + null, + ClusterType.ReplicaSet, + new[] { primary, secondary1Server70, secondary2Server70 }), + ReadPreference.SecondaryPreferred, + new[] { secondary1Server70, secondary2Server70 } + }; + + yield return new object[] + { + ReadPreference.SecondaryPreferred, + new ClusterDescription( + clusterId, + false, + null, + ClusterType.ReplicaSet, + new[] { primary, secondary1Server70, secondary2Server70, secondary3Server42 }), + ReadPreference.Primary, + new[] { primary } + }; + } + } +} From 74fba1394199eacb1d758db8bda23e5c1fb4cd83 Mon Sep 17 00:00:00 2001 From: rstam Date: Tue, 16 Jul 2024 16:23:37 -0700 Subject: [PATCH 16/21] CSHARP-4957: Fix issue with new array expression. --- ...essionToAggregationExpressionTranslator.cs | 22 ++- .../Jira/CSharp4957Tests.cs | 159 ++++++++++++++++++ 2 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4957Tests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs index 10e9e5f9a13..c6d95c36a43 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs @@ -13,8 +13,11 @@ * limitations under the License. */ +using System; using System.Collections.Generic; using System.Linq.Expressions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators @@ -24,14 +27,29 @@ internal static class NewArrayInitExpressionToAggregationExpressionTranslator public static AggregationExpression Translate(TranslationContext context, NewArrayExpression expression) { var items = new List(); + IBsonSerializer itemSerializer = null; foreach (var itemExpression in expression.Expressions) { var itemTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, itemExpression); items.Add(itemTranslation.Ast); + itemSerializer ??= itemTranslation.Serializer; + + // make sure all items are serialized using the same serializer + if (!itemTranslation.Serializer.Equals(itemSerializer)) + { + throw new ExpressionNotSupportedException(expression, because: "all items in the array must be serialized using the same serializer"); + } } + var ast = AstExpression.ComputedArray(items); - var serializer = context.KnownSerializersRegistry.GetSerializer(expression); - return new AggregationExpression(expression, ast, serializer); + + var arrayType = expression.Type; + var itemType = arrayType.GetElementType(); + itemSerializer ??= BsonSerializer.LookupSerializer(itemType); // if the array is empty itemSerializer will be null + var arraySerializerType = typeof(ArraySerializer<>).MakeGenericType(itemType); + var arraySerializer = (IBsonSerializer)Activator.CreateInstance(arraySerializerType, itemSerializer); + + return new AggregationExpression(expression, ast, arraySerializer); } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4957Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4957Tests.cs new file mode 100644 index 00000000000..e356cd4e5f8 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4957Tests.cs @@ -0,0 +1,159 @@ +/* Copyright 2010-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. +*/ + +using System; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp4957Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void New_array_with_zero_items_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (new int[] { })); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : [], _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : [], _id : 0 } }"); + + } + + var results = queryable.ToArray(); + results.Should().HaveCount(1); + results[0].Should().Equal(); + } + + [Theory] + [ParameterAttributeData] + public void New_array_with_one_items_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (new[] { x.X })); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : ['$X'], _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : ['$X'], _id : 0 } }"); + + } + + var results = queryable.ToArray(); + results.Should().HaveCount(1); + results[0].Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void New_array_with_two_items_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (new[] { x.X, x.X + 1 })); + + var stages = Translate(collection, queryable); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { __fld0 : ['$X', { $add : ['$X', 1] }], _id : 0 } }"); + } + else + { + AssertStages(stages, "{ $project : { _v : ['$X', { $add : ['$X', 1] }], _id : 0 } }"); + + } + + var results = queryable.ToArray(); + results.Should().HaveCount(1); + results[0].Should().Equal(1, 2); + } + + [Theory] + [ParameterAttributeData] + public void New_array_with_two_items_with_different_serializers_should_throw( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => (new[] { x.X, x.Y })); + + if (linqProvider == LinqProvider.V2) + { + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { __fld0 : ['$X', '$Y'], _id : 0 } }"); + + var exception = Record.Exception(() => queryable.ToArray()); + exception.Should().BeOfType(); // LINQ2 doesn't fail until deserialization + } + else + { + var exception = Record.Exception(() => Translate(collection, queryable)); + exception.Should().BeOfType(); + exception.Message.Should().Contain("all items in the array must be serialized using the same serializer"); + } + } + + private IMongoCollection GetCollection(LinqProvider linqProvider) + { + var collection = GetCollection("test", linqProvider); + CreateCollection( + collection, + new C { Id = 1, X = 1, Y = 2 }); + return collection; + } + + private class C + { + public int Id { get; set; } + public int X { get; set; } + [BsonSerializer(typeof(YSerializer))] public int Y { get; set; } + } + + private class YSerializer : StructSerializerBase + { + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, int value) + { + var writer = context.Writer; + writer.WriteString($"<{value}>"); // not parsable by int.Parse + } + } + } +} From d3a0baa5a03a4bc8baee8b6a553d21dde7ca95c4 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 2 Jul 2024 21:03:30 +0100 Subject: [PATCH 17/21] CSHARP-5171: Add IReadOnlyDictionary to existing IDictionary indexer translation. --- .../Misc/TypeExtensions.cs | 62 +++--- ...ictionaryMethod.cs => DictionaryMethod.cs} | 4 +- ...MethodToAggregationExpressionTranslator.cs | 2 +- .../GetItemMethodToFilterFieldTranslator.cs | 6 +- .../Jira/CSharp5171Tests.cs | 198 ++++++++++++++++++ 5 files changed, 231 insertions(+), 41 deletions(-) rename src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/{IDictionaryMethod.cs => DictionaryMethod.cs} (89%) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs index ce33c65ab9c..f32b6fa6a68 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs @@ -1,17 +1,17 @@ /* Copyright 2010-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. -*/ + * + * 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. + */ using System; using System.Collections.Generic; @@ -22,6 +22,12 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Misc { internal static class TypeExtensions { + private static readonly Type[] __dictionaryInterfaces = + { + typeof(IDictionary<,>), + typeof(IReadOnlyDictionary<,>) + }; + private static Type[] __tupleTypeDefinitions = { typeof(Tuple<>), @@ -84,11 +90,11 @@ public static bool Implements(this Type type, Type @interface) return false; } - public static bool ImplementsIDictionary(this Type type, out Type keyType, out Type valueType) + public static bool ImplementsDictionaryInterface(this Type type, out Type keyType, out Type valueType) { - if (TryGetIDictionaryGenericInterface(type, out var idictionaryType)) + if (TryGetGenericInterface(type, __dictionaryInterfaces, out var dictionaryInterface)) { - var genericArguments = idictionaryType.GetGenericArguments(); + var genericArguments = dictionaryInterface.GetGenericArguments(); keyType = genericArguments[0]; valueType = genericArguments[1]; return true; @@ -255,28 +261,14 @@ public static bool IsValueTuple(this Type type) type.IsConstructedGenericType && type.GetGenericTypeDefinition() is var typeDefinition && __valueTupleTypeDefinitions.Contains(typeDefinition); - } - public static bool TryGetIDictionaryGenericInterface(this Type type, out Type idictionaryGenericInterface) + public static bool TryGetGenericInterface(this Type type, Type[] interfaceDefinitions, out Type genericInterface) { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - idictionaryGenericInterface = type; - return true; - } - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - idictionaryGenericInterface = interfaceType; - return true; - } - } - - idictionaryGenericInterface = null; - return false; + genericInterface = type.IsConstructedGenericType && interfaceDefinitions.Contains(type.GetGenericTypeDefinition()) + ? type + : type.GetInterfaces().FirstOrDefault(i => i.IsGenericType && interfaceDefinitions.Contains(i.GetGenericTypeDefinition())); + return genericInterface != null; } public static bool TryGetIEnumerableGenericInterface(this Type type, out Type ienumerableGenericInterface) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/IDictionaryMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DictionaryMethod.cs similarity index 89% rename from src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/IDictionaryMethod.cs rename to src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DictionaryMethod.cs index 8069b446025..49e2ac05809 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/IDictionaryMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/DictionaryMethod.cs @@ -18,7 +18,7 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection { - internal static class IDictionaryMethod + internal static class DictionaryMethod { // public static methods public static bool IsGetItemWithStringMethod(MethodInfo method) @@ -29,7 +29,7 @@ public static bool IsGetItemWithStringMethod(MethodInfo method) method.GetParameters() is var parameters && parameters.Length == 1 && parameters[0].ParameterType == typeof(string) && - method.DeclaringType.ImplementsIDictionary(out var keyType, out var valueType) && + method.DeclaringType.ImplementsDictionaryInterface(out var keyType, out var valueType) && keyType == typeof(string) && method.ReturnType == valueType; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/GetItemMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/GetItemMethodToAggregationExpressionTranslator.cs index 744bbacfff3..463eb4f1e2a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/GetItemMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/GetItemMethodToAggregationExpressionTranslator.cs @@ -55,7 +55,7 @@ public static AggregationExpression Translate(TranslationContext context, Expres return TranslateIListGetItemWithInt(context, expression, sourceExpression, arguments[0]); } - if (IDictionaryMethod.IsGetItemWithStringMethod(method)) + if (DictionaryMethod.IsGetItemWithStringMethod(method)) { return TranslateIDictionaryGetItemWithString(context, expression, sourceExpression, arguments[0]); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs index 063129ab81c..9871395c580 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/GetItemMethodToFilterFieldTranslator.cs @@ -53,9 +53,9 @@ public static AstFilterField Translate(TranslationContext context, Expression ex return TranslateIListGetItemWithInt(context, expression, fieldExpression, arguments[0]); } - if (IDictionaryMethod.IsGetItemWithStringMethod(method)) + if (DictionaryMethod.IsGetItemWithStringMethod(method)) { - return TranslateIDictionaryGetItemWithString(context, expression, fieldExpression, arguments[0]); + return TranslateDictionaryGetItemWithString(context, expression, fieldExpression, arguments[0]); } throw new ExpressionNotSupportedException(expression); @@ -80,7 +80,7 @@ private static AstFilterField TranslateIListGetItemWithInt(TranslationContext co return ArrayIndexExpressionToFilterFieldTranslator.Translate(context, expression, fieldExpression, indexExpression); } - private static AstFilterField TranslateIDictionaryGetItemWithString(TranslationContext context, Expression expression, Expression fieldExpression, Expression keyExpression) + private static AstFilterField TranslateDictionaryGetItemWithString(TranslationContext context, Expression expression, Expression fieldExpression, Expression keyExpression) { var field = ExpressionToFilterFieldTranslator.Translate(context, fieldExpression); var key = keyExpression.GetConstantValue(containingExpression: expression); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs new file mode 100644 index 00000000000..72d972b4fad --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs @@ -0,0 +1,198 @@ +/* Copyright 2010-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. + */ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp5171Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void Select_ReadOnlyDictionary_item_with_string_using_compiler_generated_expression_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Select(x => x.Dictionary["a"]); + + var stages = Translate(collection, queryable); + var expectedStage = linqProvider == LinqProvider.V2 ? + "{ $project : { a : '$Dictionary.a', _id : 0 } }" : + "{ $project : { _v : '$Dictionary.a', _id : 0 } }"; + AssertStages(stages, expectedStage); + + var results = queryable.ToList(); + results.Should().Equal(1, 0); + } + + [Theory] + [ParameterAttributeData] + public void Select_ReadOnlyDictionary_item_with_string_using_call_to_get_item_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + var x = Expression.Parameter(typeof(C), "x"); + var body = Expression.Call( + Expression.Property(x, typeof(C).GetProperty("Dictionary")!), + typeof(IReadOnlyDictionary).GetProperty("Item")!.GetGetMethod(), + Expression.Constant("a")); + var selector = Expression.Lambda>(body, [x]); + + var queryable = collection.AsQueryable() + .Select(selector); + + var stages = Translate(collection, queryable); + var expectedStage = linqProvider == LinqProvider.V2 ? + "{ $project : { a : '$Dictionary.a', _id : 0 } }" : + "{ $project : { _v : '$Dictionary.a', _id : 0 } }"; + AssertStages(stages, expectedStage); + + var results = queryable.ToList(); + results.Should().Equal(1, 0); + } + + [Theory] + [ParameterAttributeData] + public void Select_ReadOnlyDictionary_item_with_string_using_MakeIndex_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + var x = Expression.Parameter(typeof(C), "x"); + var body = Expression.MakeIndex( + Expression.Property(x, typeof(C).GetProperty("Dictionary")!), + typeof(IReadOnlyDictionary).GetProperty("Item"), + new Expression[] {Expression.Constant("a")}); + var selector = Expression.Lambda>(body, [x]); + + var queryable = collection.AsQueryable() + .Select(selector); + + if (linqProvider == LinqProvider.V2) + { + var exception = Record.Exception(() => Translate(collection, queryable)); + exception.Should().BeOfType(); + } + else + { + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { _v : '$Dictionary.a', _id : 0 } }"); + + var results = queryable.ToList(); + results.Should().Equal(1, 0); + } + } + + [Theory] + [ParameterAttributeData] + public void Where_ReadOnlyDictionary_item_with_string_using_compiler_generated_expression_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + + var queryable = collection.AsQueryable() + .Where(x => x.Dictionary["a"] == 1); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Dictionary.a' : 1 } }"); + + var results = queryable.ToList(); + results.Select(x => x.Id).Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void Where_ReadOnlyDictionary_item_with_string_using_call_to_get_item_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + var x = Expression.Parameter(typeof(C), "x"); + var body = Expression.Equal( + Expression.Call( + Expression.Property(x, typeof(C).GetProperty("Dictionary")!), + typeof(IReadOnlyDictionary).GetProperty("Item")!.GetGetMethod(), + Expression.Constant("a")), + Expression.Constant(1)); + var predicate = Expression.Lambda>(body, [x]); + + var queryable = collection.AsQueryable() + .Where(predicate); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Dictionary.a' : 1 } }"); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(1); + } + + [Theory] + [ParameterAttributeData] + public void Where_ReadOnlyDictionary_item_with_string_using_MakeIndex_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCollection(linqProvider); + var x = Expression.Parameter(typeof(C), "x"); + var body = Expression.Equal( + Expression.MakeIndex( + Expression.Property(x, typeof(C).GetProperty("Dictionary")!), + typeof(IReadOnlyDictionary).GetProperty("Item")!, + new Expression[] {Expression.Constant("a")}), + Expression.Constant(1)); + var predicate = Expression.Lambda>(body, [x]); + + var queryable = collection.AsQueryable() + .Where(predicate); + + if (linqProvider == LinqProvider.V2) + { + var exception = Record.Exception(() => Translate(collection, queryable)); + exception.Should().BeOfType(); + } + else + { + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $match : { 'Dictionary.a' : 1 } }"); + + var results = queryable.ToList(); + results.Select(r => r.Id).Should().Equal(1); + } + } + + private IMongoCollection GetCollection(LinqProvider linqProvider) + { + var collection = GetCollection("test", linqProvider); + CreateCollection( + collection, + new C { Id = 1, Dictionary = new ReadOnlyDictionary(new Dictionary { ["a"] = 1 }) }, + new C { Id = 2, Dictionary = new ReadOnlyDictionary(new Dictionary { ["b"] = 2 }) }); + return collection; + } + + private class C + { + public int Id { get; set; } + public IReadOnlyDictionary Dictionary { get; set; } + } + } +} From acbcd721822deb765f2d5b4ae35c081632b6586b Mon Sep 17 00:00:00 2001 From: rstam Date: Wed, 10 Jul 2024 17:32:39 -0700 Subject: [PATCH 18/21] CSHARP-5171: Adjust changes to driver conventions. --- .../Misc/TypeExtensions.cs | 33 +++++++------- .../Jira/CSharp5171Tests.cs | 44 +++++++++---------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs index f32b6fa6a68..9d6ce0b3da0 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs @@ -1,17 +1,17 @@ /* Copyright 2010-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. - */ +* +* 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. +*/ using System; using System.Collections.Generic; @@ -265,9 +265,10 @@ public static bool IsValueTuple(this Type type) public static bool TryGetGenericInterface(this Type type, Type[] interfaceDefinitions, out Type genericInterface) { - genericInterface = type.IsConstructedGenericType && interfaceDefinitions.Contains(type.GetGenericTypeDefinition()) - ? type - : type.GetInterfaces().FirstOrDefault(i => i.IsGenericType && interfaceDefinitions.Contains(i.GetGenericTypeDefinition())); + genericInterface = + type.IsConstructedGenericType && interfaceDefinitions.Contains(type.GetGenericTypeDefinition()) ? + type : + type.GetInterfaces().FirstOrDefault(i => i.IsConstructedGenericType && interfaceDefinitions.Contains(i.GetGenericTypeDefinition())); return genericInterface != null; } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs index 72d972b4fad..8065bfac667 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5171Tests.cs @@ -1,17 +1,17 @@ /* Copyright 2010-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. - */ +* +* 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. +*/ using System; using System.Collections.Generic; @@ -55,8 +55,8 @@ public void Select_ReadOnlyDictionary_item_with_string_using_call_to_get_item_sh var collection = GetCollection(linqProvider); var x = Expression.Parameter(typeof(C), "x"); var body = Expression.Call( - Expression.Property(x, typeof(C).GetProperty("Dictionary")!), - typeof(IReadOnlyDictionary).GetProperty("Item")!.GetGetMethod(), + Expression.Property(x, typeof(C).GetProperty("Dictionary")), + typeof(IReadOnlyDictionary).GetProperty("Item").GetGetMethod(), Expression.Constant("a")); var selector = Expression.Lambda>(body, [x]); @@ -81,9 +81,9 @@ public void Select_ReadOnlyDictionary_item_with_string_using_MakeIndex_should_wo var collection = GetCollection(linqProvider); var x = Expression.Parameter(typeof(C), "x"); var body = Expression.MakeIndex( - Expression.Property(x, typeof(C).GetProperty("Dictionary")!), + Expression.Property(x, typeof(C).GetProperty("Dictionary")), typeof(IReadOnlyDictionary).GetProperty("Item"), - new Expression[] {Expression.Constant("a")}); + [Expression.Constant("a")]); var selector = Expression.Lambda>(body, [x]); var queryable = collection.AsQueryable() @@ -130,8 +130,8 @@ public void Where_ReadOnlyDictionary_item_with_string_using_call_to_get_item_sho var x = Expression.Parameter(typeof(C), "x"); var body = Expression.Equal( Expression.Call( - Expression.Property(x, typeof(C).GetProperty("Dictionary")!), - typeof(IReadOnlyDictionary).GetProperty("Item")!.GetGetMethod(), + Expression.Property(x, typeof(C).GetProperty("Dictionary")), + typeof(IReadOnlyDictionary).GetProperty("Item").GetGetMethod(), Expression.Constant("a")), Expression.Constant(1)); var predicate = Expression.Lambda>(body, [x]); @@ -155,9 +155,9 @@ public void Where_ReadOnlyDictionary_item_with_string_using_MakeIndex_should_wor var x = Expression.Parameter(typeof(C), "x"); var body = Expression.Equal( Expression.MakeIndex( - Expression.Property(x, typeof(C).GetProperty("Dictionary")!), - typeof(IReadOnlyDictionary).GetProperty("Item")!, - new Expression[] {Expression.Constant("a")}), + Expression.Property(x, typeof(C).GetProperty("Dictionary")), + typeof(IReadOnlyDictionary).GetProperty("Item"), + [Expression.Constant("a")]), Expression.Constant(1)); var predicate = Expression.Lambda>(body, [x]); From e0849def2404ac17a4ace87808c1ff681fab509c Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:55:05 -0700 Subject: [PATCH 19/21] CSHARP-1276: Provide Strong-Named Assemblies (#1393) --- MongoDB.Driver.snk | Bin 0 -> 596 bytes .../BenchmarkRunner.cs | 57 ++++++++++-------- .../MongoDB.Driver.Benchmarks.csproj | 4 +- src/Directory.Build.props | 2 + src/MongoDB.Bson/Properties/AssemblyInfo.cs | 3 +- .../Serialization/TypeNameDiscriminator.cs | 4 +- .../MongoDB.Driver.Core.csproj | 2 +- .../Properties/AssemblyInfo.cs | 20 +++--- .../Properties/AssemblyInfo.cs | 2 +- .../Properties/AssemblyInfo.cs | 4 +- src/MongoDB.Driver/MongoDB.Driver.csproj | 2 +- src/MongoDB.Driver/Properties/AssemblyInfo.cs | 15 ++--- tests/BuildProps/Tests.Build.props | 2 + .../MongoDB.Bson.Tests/Jira/CSharp515Tests.cs | 7 --- 14 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 MongoDB.Driver.snk diff --git a/MongoDB.Driver.snk b/MongoDB.Driver.snk new file mode 100644 index 0000000000000000000000000000000000000000..f8eaad4c83ffd99202de3aac958f115824211313 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096sD1Qw&gTSD5$c_x!Jn1`P5CiPJUXl$; zjB+?uPfagRXc214D9N%DLEJirc6q@?Rv9ZRFJI!p9b!%hl+y_Rdz8O#*DCKDK>>gM zC35MI8PUcWW9E#|?PqgJLd!EdfQ~q4%jPkYaunk(K%cStV*R$ANlYa$=1M^17uN1? zdmXUaa~!|IBHp7E?aR_%7KCJv^}uS7FU%Srm0@vcEl4xlOWz`B0Ha~gPV7)iJqKN- zYCdAnbTaPCtM&JrRI7CQ4%v|tCH)E1$_P6^gbm^%Y6$o^ySnqD3S_^1fTR@R8Q2g< z5_OAp2`vXnoJ6~+RW4drhH#lsu$1xvS9aV(m2L6L_=O?sQ4AYpB)37Ro7%PcDyXJVheS*_("--evergreen", () => false); + rootCommand.AddOption(evergreenOption); + var driverBenchmarksOption = new Option("--driverBenchmarks", () => false); + rootCommand.AddOption(driverBenchmarksOption); + var evergreenOutputFileOption = new Option(["--o", "--output-file"], () => "evergreen-results.json"); + rootCommand.AddOption(evergreenOutputFileOption); - var parser = new OptionSet + rootCommand.SetHandler(invocationContext => { - { "evergreen", v => exportingToEvergreen = v != null }, - { "driverBenchmarks", v => executingDriverBenchmarks = v != null }, - { "o|output-file=", v => evergreenOutputFile = v } - }; + var evergreenValue = invocationContext.ParseResult.GetValueForOption(evergreenOption); + var driverBenchmarksValue = invocationContext.ParseResult.GetValueForOption(driverBenchmarksOption); + var evergreenOutputFileValue = invocationContext.ParseResult.GetValueForOption(evergreenOutputFileOption); - // the parser will try to parse the options defined above and will return any extra options - var benchmarkSwitcherArgs = parser.Parse(args).ToArray(); + var config = DefaultConfig.Instance; - var config = DefaultConfig.Instance; + // use a modified config if running driver benchmarks + if (driverBenchmarksValue) + { + config = config + .WithOption(ConfigOptions.JoinSummary, true) + .AddExporter(new LocalExporter()) + .HideColumns("BenchmarkDataSetSize"); + } - // use a modified config if running driver benchmarks - if (executingDriverBenchmarks) - { - config = config - .WithOption(ConfigOptions.JoinSummary, true) - .AddExporter(new LocalExporter()) - .HideColumns("BenchmarkDataSetSize"); - } + if (evergreenValue) + { + config = config.AddExporter(new EvergreenExporter(evergreenOutputFileValue)); + } - if (exportingToEvergreen) - { - config = config.AddExporter(new EvergreenExporter(evergreenOutputFile)); - } + BenchmarkSwitcher.FromAssembly(typeof(BenchmarkRunner).Assembly).Run(invocationContext.ParseResult.UnmatchedTokens.ToArray(), config); + }); - BenchmarkSwitcher.FromAssembly(typeof(BenchmarkRunner).Assembly).Run(benchmarkSwitcherArgs, config); + return rootCommand.Invoke(args); } } } diff --git a/benchmarks/MongoDB.Driver.Benchmarks/MongoDB.Driver.Benchmarks.csproj b/benchmarks/MongoDB.Driver.Benchmarks/MongoDB.Driver.Benchmarks.csproj index c51d7740066..ff084c2de45 100644 --- a/benchmarks/MongoDB.Driver.Benchmarks/MongoDB.Driver.Benchmarks.csproj +++ b/benchmarks/MongoDB.Driver.Benchmarks/MongoDB.Driver.Benchmarks.csproj @@ -20,11 +20,13 @@ NU1701,CA1001 true false + true + ..\..\MongoDB.Driver.snk - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 73e00d7e9e4..bedb17c036b 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -27,6 +27,8 @@ snupkg true true + true + ..\..\MongoDB.Driver.snk diff --git a/src/MongoDB.Bson/Properties/AssemblyInfo.cs b/src/MongoDB.Bson/Properties/AssemblyInfo.cs index b888e73b149..ee574295e6c 100644 --- a/src/MongoDB.Bson/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Bson/Properties/AssemblyInfo.cs @@ -26,4 +26,5 @@ // as Xamarin.iOS/Xamarin.Mac. [assembly: Preserve(AllMembers = true)] -[assembly: InternalsVisibleTo("MongoDB.Bson.Tests")] +[assembly: InternalsVisibleTo("MongoDB.Bson.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Analyzer.MQLGenerator, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] diff --git a/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs b/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs index 4032179f934..fe60becf7d0 100644 --- a/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs +++ b/src/MongoDB.Bson/Serialization/TypeNameDiscriminator.cs @@ -145,7 +145,9 @@ public static string GetDiscriminator(Type type) if (match.Success) { var publicKeyToken = match.Groups["token"].Value; - if (publicKeyToken == "null") + if (publicKeyToken == "null" || + // MongoDB's assemblies should use "type name, assembly name" discriminator format for backward compatibility + (assembly.FullName.StartsWith("MongoDB") && publicKeyToken == "94992a530f44e321")) { var dllName = match.Groups["dll"].Value; assemblyName = dllName; diff --git a/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj b/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj index 1397aacca3c..14172351815 100644 --- a/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj +++ b/src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs b/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs index 9ff4bda3868..f7c8ad484dc 100644 --- a/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Driver.Core/Properties/AssemblyInfo.cs @@ -25,13 +25,13 @@ // Required for most of the reflection usage in Xamarin.iOS/Xamarin.Mac. [assembly: Preserve(AllMembers = true)] -[assembly: InternalsVisibleTo("MongoDB.Driver")] -[assembly: InternalsVisibleTo("MongoDB.Driver.TestHelpers")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Core.FunctionalTests")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Core.TestHelpers")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Core.Tests")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.TestHelpers")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Tests")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +[assembly: InternalsVisibleTo("MongoDB.Driver, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Core.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Core.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("MongoDB.Analyzer.MQLGenerator, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] diff --git a/src/MongoDB.Driver.GridFS/Properties/AssemblyInfo.cs b/src/MongoDB.Driver.GridFS/Properties/AssemblyInfo.cs index 868fbb12165..9547924c6bf 100644 --- a/src/MongoDB.Driver.GridFS/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Driver.GridFS/Properties/AssemblyInfo.cs @@ -25,4 +25,4 @@ // Required for most of the reflection usage in Xamarin.iOS/Xamarin.Mac. [assembly: Preserve(AllMembers = true)] -[assembly: InternalsVisibleTo("MongoDB.Driver.GridFS.Tests")] +[assembly: InternalsVisibleTo("MongoDB.Driver.GridFS.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] diff --git a/src/MongoDB.Driver.Legacy/Properties/AssemblyInfo.cs b/src/MongoDB.Driver.Legacy/Properties/AssemblyInfo.cs index fcec451374e..44df7c4a81d 100644 --- a/src/MongoDB.Driver.Legacy/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Driver.Legacy/Properties/AssemblyInfo.cs @@ -25,5 +25,5 @@ // Required for most of the reflection usage in Xamarin.iOS/Xamarin.Mac. [assembly: Preserve(AllMembers = true)] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.TestHelpers")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] diff --git a/src/MongoDB.Driver/MongoDB.Driver.csproj b/src/MongoDB.Driver/MongoDB.Driver.csproj index a945d57e7bc..4ddae4cc5a6 100644 --- a/src/MongoDB.Driver/MongoDB.Driver.csproj +++ b/src/MongoDB.Driver/MongoDB.Driver.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/MongoDB.Driver/Properties/AssemblyInfo.cs b/src/MongoDB.Driver/Properties/AssemblyInfo.cs index 9eb0f4c7cee..bb0e7428bb5 100644 --- a/src/MongoDB.Driver/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Driver/Properties/AssemblyInfo.cs @@ -25,11 +25,12 @@ // Required for most of the reflection usage in Xamarin.iOS/Xamarin.Mac. [assembly: Preserve(AllMembers = true)] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.TestHelpers")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Tests")] -[assembly: InternalsVisibleTo("MongoDB.Driver.TestHelpers")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -[assembly: InternalsVisibleTo("MongoDB.Driver.Benchmarks")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Analyzer.MQLGenerator, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] diff --git a/tests/BuildProps/Tests.Build.props b/tests/BuildProps/Tests.Build.props index e6150099b89..7b9984d15f7 100644 --- a/tests/BuildProps/Tests.Build.props +++ b/tests/BuildProps/Tests.Build.props @@ -11,6 +11,8 @@ netstandard2.0;netstandard2.1 $(StandardTargetFrameworks);net472 false + true + ..\..\MongoDB.Driver.snk diff --git a/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs b/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs index 54920f37ef1..5b72b0fb7fc 100644 --- a/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs +++ b/tests/MongoDB.Bson.Tests/Jira/CSharp515Tests.cs @@ -43,14 +43,7 @@ public S(IList list) } } -#if NET472 - private static readonly AssemblyName __assemblyName = Assembly.GetExecutingAssembly().GetName(); - private static readonly bool __assemblyIsSigned = __assemblyName.GetPublicKey().Length > 0; - private static readonly string __discriminatorAssemblyName = __assemblyIsSigned ? __assemblyName.FullName : __assemblyName.Name; -#else private static readonly string __discriminatorAssemblyName = "MongoDB.Bson.Tests"; -#endif - private string _jsonTemplate = ("{ '_id' : 1, 'R' : #V, 'S' : #V, 'RS' : { '_t' : 'S`1', '_v' : #V }, 'OR' : { '_t' : 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.Int32]', '_v' : #V }, 'OS' : { '_t' : 'MongoDB.Bson.Tests.Jira.CSharp515.CSharp515Tests+S`1[System.Int32], " + __discriminatorAssemblyName + "', '_v' : #V } }").Replace("'", "\""); [Fact] From 2ab28688cc0cd34d16cb5169737065700cfb7448 Mon Sep 17 00:00:00 2001 From: rstam Date: Wed, 17 Jul 2024 13:43:38 -0700 Subject: [PATCH 20/21] CSHARP-5162: Continue to use FindExpressionProjectionDefinition to avoid regression against LINQ2. --- src/MongoDB.Driver/ProjectionDefinition.cs | 15 ++ .../ProjectionDefinitionBuilder.cs | 4 +- ...FindExpressionProjectionDefinitionTests.cs | 60 ++++-- .../Jira/CSharp4681Tests.cs | 7 +- .../Jira/CSharp5162Tests.cs | 184 ++++++++++++++++++ 5 files changed, 251 insertions(+), 19 deletions(-) create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5162Tests.cs diff --git a/src/MongoDB.Driver/ProjectionDefinition.cs b/src/MongoDB.Driver/ProjectionDefinition.cs index 121621196d8..116bec34a79 100644 --- a/src/MongoDB.Driver/ProjectionDefinition.cs +++ b/src/MongoDB.Driver/ProjectionDefinition.cs @@ -306,6 +306,21 @@ public Expression> Expression /// public override RenderedProjectionDefinition Render(IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) + { + if (linqProvider == LinqProvider.V2) + { + // this is slightly wrong because we're not actually rendering for a Find + // but this is required to avoid a regression with LINQ2 + return RenderForFind(sourceSerializer, serializerRegistry, linqProvider); + } + else + { + return linqProvider.GetAdapter().TranslateExpressionToProjection(_expression, sourceSerializer, serializerRegistry, translationOptions: null); + } + } + + /// + internal override RenderedProjectionDefinition RenderForFind(IBsonSerializer sourceSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) { return linqProvider.GetAdapter().TranslateExpressionToFindProjection(_expression, sourceSerializer, serializerRegistry); } diff --git a/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs b/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs index 34971cd803d..18f32ce0a59 100644 --- a/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs +++ b/src/MongoDB.Driver/ProjectionDefinitionBuilder.cs @@ -544,7 +544,9 @@ public ProjectionDefinition Exclude(Expression> f /// public ProjectionDefinition Expression(Expression> expression) { - return new ExpressionProjectionDefinition(expression, null); + // TODO: replace FindExpressionProjectionDefinition with ExpressionProjectionDefinition when LINQ2 is removed + // in the meantime we have to keep using FindExpressionProjectionDefinition here for compatibility with LINQ2 + return new FindExpressionProjectionDefinition(expression); } /// diff --git a/tests/MongoDB.Driver.Tests/FindExpressionProjectionDefinitionTests.cs b/tests/MongoDB.Driver.Tests/FindExpressionProjectionDefinitionTests.cs index cfa3172e4dd..5adc3e34f47 100644 --- a/tests/MongoDB.Driver.Tests/FindExpressionProjectionDefinitionTests.cs +++ b/tests/MongoDB.Driver.Tests/FindExpressionProjectionDefinitionTests.cs @@ -17,35 +17,69 @@ using System.Linq.Expressions; using FluentAssertions; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; using Xunit; namespace MongoDB.Driver.Tests { public class FindExpressionProjectionDefinitionTests { - [Fact] - public void Projection_to_class_should_work() - => AssertProjection( + [Theory] + [ParameterAttributeData] + public void Projection_to_class_should_work( + [Values(false, true)] bool renderForFind, + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var expectedRenderedProjection = (linqProvider, renderForFind) switch + { + (LinqProvider.V2, _) => "{ A : 1, B : 1, _id : 0 }", // note: result serializer does client-side projection + (LinqProvider.V3, true) => "{ A : 1, X : '$B', _id : 0 }", + (LinqProvider.V3, false) => "{ A : '$A', X : '$B', _id : 0 }", + _ => throw new Exception() + }; + AssertProjection( x => new Projection { A = x.A, X = x.B }, - "{ A : 1, X : '$B', _id : 0 }"); + renderForFind, + linqProvider, + expectedRenderedProjection); + } - [Fact] - public void Projection_to_anonymous_type_should_work() - => AssertProjection( + [Theory] + [ParameterAttributeData] + public void Projection_to_anonymous_type_should_work( + [Values(false, true)] bool renderForFind, + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var expectedRenderedProjection = (linqProvider, renderForFind) switch + { + (LinqProvider.V2, _) => "{ A : 1, B : 1, _id : 0 }", // note: result serializer does client-side projection + (LinqProvider.V3, true) => "{ A : 1, X : '$B', _id : 0 }", + (LinqProvider.V3, false) => "{ A : '$A', X : '$B', _id : 0 }", + _ => throw new Exception() + }; + AssertProjection( x => new { x.A, X = x.B }, - "{ A : 1, X : '$B', _id : 0 }"); + renderForFind, + linqProvider, + expectedRenderedProjection); + } private void AssertProjection( Expression> expression, - string expectedProjection) + bool renderForFind, + LinqProvider linqProvider, + string expectedRenderedProjection) { var projection = new FindExpressionProjectionDefinition(expression); + var serializerRegistry = BsonSerializer.SerializerRegistry; + var documentSerializer = serializerRegistry.GetSerializer(); - var renderedProjection = projection.Render( - BsonSerializer.LookupSerializer(), - BsonSerializer.SerializerRegistry); + var renderedProjection = renderForFind ? + projection.RenderForFind(documentSerializer, serializerRegistry, linqProvider) : + projection.Render(documentSerializer, serializerRegistry, linqProvider); - renderedProjection.Document.Should().BeEquivalentTo(expectedProjection); + renderedProjection.Document.Should().BeEquivalentTo(expectedRenderedProjection); } private class Document diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4681Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4681Tests.cs index d07d9878aec..dba0133a44e 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4681Tests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp4681Tests.cs @@ -32,11 +32,8 @@ public void Find_projection_render_should_work( var fluentFind = collection.Find(a => a.Id == "1").Project(a => a.Id); - var documentSerializer = collection.DocumentSerializer; - var serializerRegistry = BsonSerializer.SerializerRegistry; - var renderedProjection = fluentFind.Options.Projection.Render(documentSerializer, serializerRegistry, linqProvider); - - renderedProjection.Document.Should().Be("{ _id : 1 }"); + var renderedProjection = TranslateFindProjection(collection, fluentFind); + renderedProjection.Should().Be("{ _id : 1 }"); var result = fluentFind.Single(); result.Should().Be("1"); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5162Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5162Tests.cs new file mode 100644 index 00000000000..139c5bc8e97 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5162Tests.cs @@ -0,0 +1,184 @@ +/* Copyright 2010-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. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Linq; +using MongoDB.TestHelpers.XunitExtensions; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp5162Tests : Linq3IntegrationTest + { + [Theory] + [ParameterAttributeData] + public void Builders_Projection_Expression_with_camel_casing_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCamelCollection(linqProvider); + + var projection = Builders.Projection.Expression(x => new CamelDocument { Id = x.Id, Name = x.Name }); + var aggregate = collection.Aggregate().Project(projection); + + var stages = Translate(collection, aggregate); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { _id : 1, name : 1 } }"); + } + else + { + AssertStages(stages, "{ $project : { _id : '$_id', name : '$name' } }"); + } + + var result = aggregate.ToList().Single(); + result.Id.Should().Be(1); + result.Name.Should().Be("John Doe"); + } + + [Theory] + [ParameterAttributeData] + public void Builders_Projection_Expression_with_pascal_casing_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetPascalCollection(linqProvider); + + var projection = Builders.Projection.Expression(x => new PascalDocument { Id = x.Id, Name = x.Name }); + var aggregate = collection.Aggregate().Project(projection); + + var stages = Translate(collection, aggregate); + if (linqProvider == LinqProvider.V2) + { + stages = NormalizeProjectFieldOrder(stages); + AssertStages(stages, "{ $project : { _id : 1, Name : 1 } }"); + } + else + { + AssertStages(stages, "{ $project : { _id : '$_id', Name : '$Name' } }"); + } + + var result = aggregate.ToList().Single(); + result.Id.Should().Be(1); + result.Name.Should().Be("John Doe"); + } + + [Theory] + [ParameterAttributeData] + public void FindExpressionDefinition_with_camel_casing_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetCamelCollection(linqProvider); + + var projection = new FindExpressionProjectionDefinition(x => new CamelDocument { Id = x.Id, Name = x.Name }); + var aggregate = collection.Aggregate().Project(projection); + + var stages = Translate(collection, aggregate); + if (linqProvider == LinqProvider.V2) + { + AssertStages(stages, "{ $project : { _id : 1, name : 1 } }"); + } + else + { + AssertStages(stages, "{ $project : { _id : '$_id', name : '$name' } }"); + } + + var result = aggregate.ToList().Single(); + result.Id.Should().Be(1); + result.Name.Should().Be("John Doe"); + } + + [Theory] + [ParameterAttributeData] + public void FindExpressionDefinition_with_pascal_casing_should_work( + [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider) + { + var collection = GetPascalCollection(linqProvider); + + var projection = new FindExpressionProjectionDefinition(x => new PascalDocument { Id = x.Id, Name = x.Name }); + var aggregate = collection.Aggregate().Project(projection); + + var stages = Translate(collection, aggregate); + if (linqProvider == LinqProvider.V2) + { + stages = NormalizeProjectFieldOrder(stages); + AssertStages(stages, "{ $project : { _id : 1, Name : 1 } }"); + } + else + { + AssertStages(stages, "{ $project : { _id : '$_id', Name : '$Name' } }"); + } + + var result = aggregate.ToList().Single(); + result.Id.Should().Be(1); + result.Name.Should().Be("John Doe"); + } + + private IMongoCollection GetCamelCollection(LinqProvider linqProvider) + { + var collection = GetCollection("test", linqProvider); + var document = new CamelDocument { Id = 1, Name = "John Doe" }; + CreateCollection(collection, document); + return collection; + } + + private IMongoCollection GetPascalCollection(LinqProvider linqProvider) + { + var collection = GetCollection("test", linqProvider); + var document = new PascalDocument { Id = 1, Name = "John Doe" }; + CreateCollection(collection, document); + return collection; + } + + private List NormalizeProjectFieldOrder(List stages) + { + if (stages.Count == 1 && + stages[0] is BsonDocument projectStage && + projectStage.ElementCount == 1 && + projectStage.GetElement(0).Name == "$project" && + projectStage[0] is BsonDocument projection && + projection.ElementCount == 2 && + projection.Names.SequenceEqual(["Name", "_id"])) + { + stages[0]["$project"] = new BsonDocument + { + { "_id", projection["_id"] }, + { "Name", projection["Name"] } + }; + } + + return stages; + } + + private class CamelDocument + { + public int Id { get; set; } + [BsonElement("name")] public string Name { get; set; } + [BsonElement("activeSince")] public DateTime ActiveSince { get; set; } + [BsonElement("isActive")] public bool IsActive { get; set; } + } + + private class PascalDocument + { + public int Id { get; set; } + public string Name { get; set; } + public DateTime ActiveSince { get; set; } + public bool IsActive { get; set; } + } + } +} From 0d8134ec990cad5b2ad4d74a244f6ecf4666b498 Mon Sep 17 00:00:00 2001 From: Oleksandr Poliakov <31327136+sanych-sun@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:30:58 -0700 Subject: [PATCH 21/21] 2.28.0 Release notes (#1394) --- Release Notes/Release Notes v2.28.0.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Release Notes/Release Notes v2.28.0.md diff --git a/Release Notes/Release Notes v2.28.0.md b/Release Notes/Release Notes v2.28.0.md new file mode 100644 index 00000000000..635709c65e3 --- /dev/null +++ b/Release Notes/Release Notes v2.28.0.md @@ -0,0 +1,22 @@ +# .NET Driver Version 2.28.0 Release Notes + +This is the general availability release for the 2.28.0 version of the driver. + +NOTICE: MongoDB 3.6 reached end-of-life in April 2021. The .NET/C# Driver will be removing support for MongoDB 3.6 in an upcoming release. + +The main new features in 2.28.0 include: + ++ Provide Strong-Named Assemblies - [CSHARP-1276](https://jira.mongodb.org/browse/CSHARP-1276) ++ Support additional numeric conversions involving Nullable - [CSHARP-5180](https://jira.mongodb.org/browse/CSHARP-5180) ++ CSFLE/QE KMIP support "delegated" protocol - [CSHARP-4941](https://jira.mongodb.org/browse/CSHARP-4941) + +## Bug fixes: ++ Verify that operands to numeric operators in LINQ expressions are represented as numbers on the server - [CSHARP-4985](https://jira.mongodb.org/browse/CSHARP-4985) ++ IReadOnlyDictionary indexer access fails to translate in v3 - [CSHARP-5171](https://jira.mongodb.org/browse/CSHARP-5171) ++ Projection Expressions Fail to Deserialize Data Correctly - [CSHARP-5162](https://jira.mongodb.org/browse/CSHARP-5162) ++ Enum conversion within IQueryable fails with Expression not supported exception - [CSHARP-5043](https://jira.mongodb.org/browse/CSHARP-5043) ++ IMongoCollection.AsQueryable().Select() fails for array type (regression) - [CSHARP-4957](https://jira.mongodb.org/browse/CSHARP-4957) + +The full list of issues resolved in this release is available at [CSHARP JIRA project](https://jira.mongodb.org/issues/?jql=project%20%3D%20CSHARP%20AND%20fixVersion%20%3D%202.28.0%20ORDER%20BY%20key%20ASC). + +Documentation on the .NET driver can be found [here](https://www.mongodb.com/docs/drivers/csharp/v2.28/).